OpenJDK / jdk / hs
changeset 39998:984b45c2e79f
Merge
author | amurillo |
---|---|
date | Fri, 22 Jul 2016 04:05:04 +0000 |
parents | c4787ceda7a8 a65e14a0d827 |
children | 82ece130a37b |
files | jdk/test/java/net/URLPermission/policy.1 jdk/test/java/net/URLPermission/policy.2 jdk/test/java/net/URLPermission/policy.3 jdk/test/java/util/jar/JarFile/mrjar/MultiReleaseJarIterators.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/ClassTracker.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/FailOverExecutionControl.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIConnection.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIEventHandler.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIExecutionControl.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDINotConnectedException.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteClassLoader.java langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/Internal.java langtools/test/jdk/jshell/LocalExecutionControl.java nashorn/test/script/currently-failing/logcoverage.js |
diffstat | 458 files changed, 16030 insertions(+), 9771 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Thu Jul 21 19:29:33 2016 -0700 +++ b/.hgtags Fri Jul 22 04:05:04 2016 +0000 @@ -370,3 +370,4 @@ 3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125 03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126 8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127 +5bf88dce615f6804f9e101a96ffa7c9dfb4fbbbe jdk-9+128
--- a/.hgtags-top-repo Thu Jul 21 19:29:33 2016 -0700 +++ b/.hgtags-top-repo Fri Jul 22 04:05:04 2016 +0000 @@ -370,3 +370,4 @@ 9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125 3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126 8fa686245bd2a072ece3392743460030f0854520 jdk-9+127 +b30ae794d974d7dd3eb4e84203f70021823fa6c6 jdk-9+128
--- a/common/autoconf/boot-jdk.m4 Thu Jul 21 19:29:33 2016 -0700 +++ b/common/autoconf/boot-jdk.m4 Fri Jul 22 04:05:04 2016 +0000 @@ -345,6 +345,9 @@ # Disable special log output when a debug build is used as Boot JDK... ADD_JVM_ARG_IF_OK([-XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput],boot_jdk_jvmargs,[$JAVA]) + # Force en-US environment + ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA]) + # Apply user provided options. ADD_JVM_ARG_IF_OK([$with_boot_jdk_jvmargs],boot_jdk_jvmargs,[$JAVA])
--- a/common/autoconf/generated-configure.sh Thu Jul 21 19:29:33 2016 -0700 +++ b/common/autoconf/generated-configure.sh Fri Jul 22 04:05:04 2016 +0000 @@ -5094,7 +5094,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1467960715 +DATE_WHEN_GENERATED=1469202305 ############################################################################### # @@ -65048,6 +65048,23 @@ fi + # Force en-US environment + + $ECHO "Check if jvm arg is ok: -Duser.language=en -Duser.country=US" >&5 + $ECHO "Command: $JAVA -Duser.language=en -Duser.country=US -version" >&5 + OUTPUT=`$JAVA -Duser.language=en -Duser.country=US -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | $GREP -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | $GREP " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs="$boot_jdk_jvmargs -Duser.language=en -Duser.country=US" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + # Apply user provided options. $ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5
--- a/corba/.hgtags Thu Jul 21 19:29:33 2016 -0700 +++ b/corba/.hgtags Fri Jul 22 04:05:04 2016 +0000 @@ -370,3 +370,4 @@ 1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125 c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126 8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127 +1f093d3f8cd99cd37c3b0af4cf5c3bffaa9c8b98 jdk-9+128
--- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java Thu Jul 21 19:29:33 2016 -0700 +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,4 @@ /* - * * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,7 +21,6 @@ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. - * */ package com.sun.corba.se.impl.activation;
--- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java Thu Jul 21 19:29:33 2016 -0700 +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java Fri Jul 22 04:05:04 2016 +0000 @@ -34,21 +34,13 @@ import java.security.Policy; import java.security.PrivilegedAction; import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Map; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; -import java.util.Map.Entry; -import java.util.Collection; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Enumeration; -import java.util.Properties; -import java.util.IdentityHashMap; import java.util.StringTokenizer; import java.util.NoSuchElementException; @@ -165,8 +157,18 @@ * Return default ValueHandler */ public static ValueHandler createValueHandler() { + ValueHandler vh; + try { + vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() { + public ValueHandler run() throws Exception { return Util.createValueHandler(); } + }); + } catch (PrivilegedActionException e) { + throw new InternalError(e.getCause()); + } + return vh; + } /** * Returns true if it was accurately determined that the remote ORB is @@ -664,7 +666,16 @@ * ValueHandler. */ public static byte getMaxStreamFormatVersion() { - ValueHandler vh = Util.createValueHandler(); + ValueHandler vh; + try { + vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() { + public ValueHandler run() throws Exception { + return Util.createValueHandler(); + } + }); + } catch (PrivilegedActionException e) { + throw new InternalError(e.getCause()); + } if (!(vh instanceof javax.rmi.CORBA.ValueHandlerMultiFormat)) return ORBConstants.STREAM_FORMAT_VERSION_1;
--- a/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java Thu Jul 21 19:29:33 2016 -0700 +++ b/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, 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 @@ -45,6 +45,7 @@ import java.rmi.Remote; import java.io.File; import java.io.FileInputStream; +import java.io.SerializablePermission; import java.net.MalformedURLException ; import java.security.AccessController; import java.security.PrivilegedAction; @@ -195,6 +196,8 @@ */ public static ValueHandler createValueHandler() { + isCustomSerializationPermitted(); + if (utilDelegate != null) { return utilDelegate.createValueHandler(); } @@ -337,6 +340,7 @@ // security reasons. If you know a better solution how to share this code // then remove it from PortableRemoteObject. Also in Stub.java private static Object createDelegate(String classKey) { + String className = (String) AccessController.doPrivileged(new GetPropertyAction(classKey)); if (className == null) { @@ -345,7 +349,6 @@ className = props.getProperty(classKey); } } - if (className == null) { return new com.sun.corba.se.impl.javax.rmi.CORBA.Util(); } @@ -389,4 +392,14 @@ new GetORBPropertiesFileAction()); } + private static void isCustomSerializationPermitted() { + SecurityManager sm = System.getSecurityManager(); + if ( sm != null) { + // check that a serialization permission has been + // set to allow the loading of the Util delegate + // which provides access to custom ValueHandler + sm.checkPermission(new SerializablePermission( + "enableCustomValueHandler")); } + } +}
--- a/hotspot/.hgtags Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/.hgtags Fri Jul 22 04:05:04 2016 +0000 @@ -530,3 +530,4 @@ bb640b49741af3f57f9994129934c46fc173219f jdk-9+125 adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126 352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127 +22bf6db9767b1b3a1994cbf32eb3331f31ae2093 jdk-9+128
--- a/hotspot/src/share/vm/classfile/compactHashtable.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -270,6 +270,10 @@ // For reading from/writing to the CDS archive void serialize(SerializeClosure* soc); + + uintx base_address() { + return (uintx) _base_address; + } }; ////////////////////////////////////////////////////////////////////////
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -238,6 +238,29 @@ } } +u4 SymbolTable::encode_shared(Symbol* sym) { + assert(DumpSharedSpaces, "called only during dump time"); + uintx base_address = uintx(MetaspaceShared::shared_rs()->base()); + uintx offset = uintx(sym) - base_address; + assert(offset < 0x7fffffff, "sanity"); + return u4(offset); +} + +Symbol* SymbolTable::decode_shared(u4 offset) { + assert(!DumpSharedSpaces, "called only during runtime"); + uintx base_address = _shared_table.base_address(); + Symbol* sym = (Symbol*)(base_address + offset); + +#ifndef PRODUCT + const char* s = (const char*)sym->bytes(); + int len = sym->utf8_length(); + unsigned int hash = hash_symbol(s, len); + assert(sym == lookup_shared(s, len, hash), "must be shared symbol"); +#endif + + return sym; +} + // Pick hashing algorithm. unsigned int SymbolTable::hash_symbol(const char* s, int len) { return use_alternate_hashcode() ?
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -253,6 +253,8 @@ // Sharing static void serialize(SerializeClosure* soc); + static u4 encode_shared(Symbol* sym); + static Symbol* decode_shared(u4 offset); // Rehash the symbol table if it gets out of balance static void rehash_table();
--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -78,7 +78,19 @@ TRAPS) { return NULL; } + static void serialize(SerializeClosure* soc) {} + + // The (non-application) CDS implementation supports only classes in the boot + // class loader, which ensures that the verification constraints are the same + // during archive creation time and runtime. Thus we can do the constraint checks + // entirely during archive creation time. + static bool add_verification_constraint(Klass* k, Symbol* name, + Symbol* from_name, bool from_field_is_protected, + bool from_is_array, bool from_is_object) {return false;} + static void finalize_verification_constraints() {} + static void check_verification_constraints(instanceKlassHandle klass, + TRAPS) {} }; #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
--- a/hotspot/src/share/vm/classfile/verificationType.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/verificationType.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/symbolTable.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/verifier.hpp" @@ -41,6 +42,39 @@ } } +bool VerificationType::resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name, + Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, TRAPS) { + Klass* obj = SystemDictionary::resolve_or_fail( + name, Handle(THREAD, klass->class_loader()), + Handle(THREAD, klass->protection_domain()), true, CHECK_false); + if (log_is_enabled(Debug, class, resolve)) { + Verifier::trace_class_resolution(obj, klass()); + } + + KlassHandle this_class(THREAD, obj); + + if (this_class->is_interface() && (!from_field_is_protected || + from_name != vmSymbols::java_lang_Object())) { + // If we are not trying to access a protected field or method in + // java.lang.Object then, for arrays, we only allow assignability + // to interfaces java.lang.Cloneable and java.io.Serializable. + // Otherwise, we treat interfaces as java.lang.Object. + return !from_is_array || + this_class == SystemDictionary::Cloneable_klass() || + this_class == SystemDictionary::Serializable_klass(); + } else if (from_is_object) { + Klass* from_class = SystemDictionary::resolve_or_fail( + from_name, Handle(THREAD, klass->class_loader()), + Handle(THREAD, klass->protection_domain()), true, CHECK_false); + if (log_is_enabled(Debug, class, resolve)) { + Verifier::trace_class_resolution(from_class, klass()); + } + return InstanceKlass::cast(from_class)->is_subclass_of(this_class()); + } + + return false; +} + bool VerificationType::is_reference_assignable_from( const VerificationType& from, ClassVerifier* context, bool from_field_is_protected, TRAPS) const { @@ -58,33 +92,17 @@ // any object or array is assignable to java.lang.Object return true; } - Klass* obj = SystemDictionary::resolve_or_fail( - name(), Handle(THREAD, klass->class_loader()), - Handle(THREAD, klass->protection_domain()), true, CHECK_false); - if (log_is_enabled(Debug, class, resolve)) { - Verifier::trace_class_resolution(obj, klass()); + + if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass(), + name(), from.name(), from_field_is_protected, from.is_array(), + from.is_object())) { + // If add_verification_constraint() returns true, the resolution/check should be + // delayed until runtime. + return true; } - KlassHandle this_class(THREAD, obj); - - if (this_class->is_interface() && (!from_field_is_protected || - from.name() != vmSymbols::java_lang_Object())) { - // If we are not trying to access a protected field or method in - // java.lang.Object then, for arrays, we only allow assignability - // to interfaces java.lang.Cloneable and java.io.Serializable. - // Otherwise, we treat interfaces as java.lang.Object. - return !from.is_array() || - this_class == SystemDictionary::Cloneable_klass() || - this_class == SystemDictionary::Serializable_klass(); - } else if (from.is_object()) { - Klass* from_class = SystemDictionary::resolve_or_fail( - from.name(), Handle(THREAD, klass->class_loader()), - Handle(THREAD, klass->protection_domain()), true, CHECK_false); - if (log_is_enabled(Debug, class, resolve)) { - Verifier::trace_class_resolution(from_class, klass()); - } - return InstanceKlass::cast(from_class)->is_subclass_of(this_class()); - } + return resolve_and_check_assignability(klass(), name(), from.name(), + from_field_is_protected, from.is_array(), from.is_object(), THREAD); } else if (is_array() && from.is_array()) { VerificationType comp_this = get_component(context, CHECK_false); VerificationType comp_from = from.get_component(context, CHECK_false);
--- a/hotspot/src/share/vm/classfile/verificationType.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/verificationType.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -333,6 +333,12 @@ bool is_reference_assignable_from( const VerificationType&, ClassVerifier*, bool from_field_is_protected, TRAPS) const; + + public: + static bool resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name, + Symbol* from_name, bool from_field_is_protected, + bool from_is_array, bool from_is_object, + TRAPS); }; #endif // SHARE_VM_CLASSFILE_VERIFICATIONTYPE_HPP
--- a/hotspot/src/share/vm/classfile/verifier.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/classfile/verifier.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -2377,9 +2377,17 @@ case Bytecodes::_ifnonnull: target = bcs.dest(); if (visited_branches->contains(bci)) { - if (bci_stack->is_empty()) return true; - // Pop a bytecode starting offset and scan from there. - bcs.set_start(bci_stack->pop()); + if (bci_stack->is_empty()) { + if (handler_stack->is_empty()) { + return true; + } else { + // Parse the catch handlers for try blocks containing athrow. + bcs.set_start(handler_stack->pop()); + } + } else { + // Pop a bytecode starting offset and scan from there. + bcs.set_start(bci_stack->pop()); + } } else { if (target > bci) { // forward branch if (target >= code_length) return false; @@ -2402,9 +2410,17 @@ case Bytecodes::_goto_w: target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); if (visited_branches->contains(bci)) { - if (bci_stack->is_empty()) return true; - // Been here before, pop new starting offset from stack. - bcs.set_start(bci_stack->pop()); + if (bci_stack->is_empty()) { + if (handler_stack->is_empty()) { + return true; + } else { + // Parse the catch handlers for try blocks containing athrow. + bcs.set_start(handler_stack->pop()); + } + } else { + // Been here before, pop new starting offset from stack. + bcs.set_start(bci_stack->pop()); + } } else { if (target >= code_length) return false; // Continue scanning from the target onward.
--- a/hotspot/src/share/vm/interpreter/bytecodeStream.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/interpreter/bytecodeStream.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, 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,12 +31,12 @@ // set next bytecode position address bcp = RawBytecodeStream::bcp(); address end = method()->code_base() + end_bci(); - int l = Bytecodes::raw_special_length_at(bcp, end); - if (l <= 0 || (_bci + l) > _end_bci) { + int len = Bytecodes::raw_special_length_at(bcp, end); + // Very large tableswitch or lookupswitch size can cause _next_bci to overflow. + if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) { code = Bytecodes::_illegal; } else { - _next_bci += l; - assert(_bci < _next_bci, "length must be > 0"); + _next_bci += len; // set attributes _is_wide = false; // check for special (uncommon) cases
--- a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, 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 @@ -135,12 +135,15 @@ code = Bytecodes::code_or_bp_at(bcp); // set next bytecode position - int l = Bytecodes::length_for(code); - if (l > 0 && (_bci + l) <= _end_bci) { + int len = Bytecodes::length_for(code); + if (len > 0 && (_bci <= _end_bci - len)) { assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch && code != Bytecodes::_lookupswitch, "can't be special bytecode"); _is_wide = false; - _next_bci += l; + _next_bci += len; + if (_next_bci <= _bci) { // Check for integer overflow + code = Bytecodes::_illegal; + } _raw_code = code; return code; } else { @@ -189,19 +192,23 @@ // note that we cannot advance before having the // tty bytecode otherwise the stepping is wrong! // (carefull: length_for(...) must be used first!) - int l = Bytecodes::length_for(code); - if (l == 0) l = Bytecodes::length_at(_method(), bcp); - _next_bci += l; - assert(_bci < _next_bci, "length must be > 0"); - // set attributes - _is_wide = false; - // check for special (uncommon) cases - if (code == Bytecodes::_wide) { - raw_code = (Bytecodes::Code)bcp[1]; - code = raw_code; // wide BCs are always Java-normal - _is_wide = true; + int len = Bytecodes::length_for(code); + if (len == 0) len = Bytecodes::length_at(_method(), bcp); + if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) { + raw_code = code = Bytecodes::_illegal; + } else { + _next_bci += len; + assert(_bci < _next_bci, "length must be > 0"); + // set attributes + _is_wide = false; + // check for special (uncommon) cases + if (code == Bytecodes::_wide) { + raw_code = (Bytecodes::Code)bcp[1]; + code = raw_code; // wide BCs are always Java-normal + _is_wide = true; + } + assert(Bytecodes::is_java_code(code), "sanity check"); } - assert(Bytecodes::is_java_code(code), "sanity check"); } _raw_code = raw_code; _code = code;
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -60,6 +60,7 @@ bool MetaspaceShared::_check_classes_made_progress; bool MetaspaceShared::_has_error_classes; bool MetaspaceShared::_archive_loading_failed = false; +bool MetaspaceShared::_remapped_readwrite = false; address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL; size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0; SharedMiscRegion MetaspaceShared::_mc; @@ -806,6 +807,10 @@ exit(1); } } + + // Copy the verification constraints from C_HEAP-alloced GrowableArrays to RO-alloced + // Arrays + SystemDictionaryShared::finalize_verification_constraints(); } void MetaspaceShared::prepare_for_dumping() { @@ -1181,6 +1186,7 @@ if (!mapinfo->remap_shared_readonly_as_readwrite()) { return false; } + _remapped_readwrite = true; } return true; }
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -125,6 +125,7 @@ static bool _check_classes_made_progress; static bool _has_error_classes; static bool _archive_loading_failed; + static bool _remapped_readwrite; static address _cds_i2i_entry_code_buffers; static size_t _cds_i2i_entry_code_buffers_size; @@ -205,6 +206,10 @@ // sharing is enabled. Simply returns true if sharing is not enabled // or if the remapping has already been done by a prior call. static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true); + static bool remapped_readwrite() { + CDS_ONLY(return _remapped_readwrite); + NOT_CDS(return false); + } static void print_shared_spaces();
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -27,6 +27,7 @@ #include "classfile/classFileStream.hpp" #include "classfile/javaClasses.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verifier.hpp" #include "classfile/vmSymbols.hpp" #include "code/dependencyContext.hpp" @@ -597,6 +598,8 @@ // also sets rewritten this_k->rewrite_class(CHECK_false); + } else if (this_k->is_shared()) { + SystemDictionaryShared::check_verification_constraints(this_k, CHECK_false); } // relocate jsrs and link methods after they are all rewritten @@ -606,7 +609,12 @@ // methods have been rewritten since rewrite may // fabricate new Method*s. // also does loader constraint checking - if (!this_k()->is_shared()) { + // + // initialize_vtable and initialize_itable need to be rerun for + // a shared class if the class is not loaded by the NULL classloader. + ClassLoaderData * loader_data = this_k->class_loader_data(); + if (!(this_k->is_shared() && + loader_data->is_the_null_class_loader_data())) { ResourceMark rm(THREAD); this_k->vtable()->initialize_vtable(true, CHECK_false); this_k->itable()->initialize_itable(true, CHECK_false);
--- a/hotspot/src/share/vm/oops/klassVtable.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/oops/klassVtable.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -27,6 +27,7 @@ #include "classfile/vmSymbols.hpp" #include "gc/shared/gcLocker.hpp" #include "logging/log.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.inline.hpp" #include "oops/instanceKlass.hpp" @@ -42,6 +43,10 @@ return InstanceKlass::cast(_klass()); } +bool klassVtable::is_preinitialized_vtable() { + return _klass->is_shared() && !MetaspaceShared::remapped_readwrite(); +} + // this function computes the vtable size (including the size needed for miranda // methods) and the number of miranda methods in this class. @@ -126,6 +131,12 @@ int klassVtable::initialize_from_super(KlassHandle super) { if (super.is_null()) { return 0; + } else if (is_preinitialized_vtable()) { + // A shared class' vtable is preinitialized at dump time. No need to copy + // methods from super class for shared class, as that was already done + // during archiving time. However, if Jvmti has redefined a class, + // copy super class's vtable in case the super class has changed. + return super->vtable()->length(); } else { // copy methods from superKlass klassVtable* superVtable = super->vtable(); @@ -152,6 +163,8 @@ KlassHandle super (THREAD, klass()->java_super()); int nofNewEntries = 0; + bool is_shared = _klass->is_shared(); + if (!klass()->is_array_klass()) { ResourceMark rm(THREAD); log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string()); @@ -164,6 +177,7 @@ #endif if (Universe::is_bootstrapping()) { + assert(!is_shared, "sanity"); // just clear everything for (int i = 0; i < _length; i++) table()[i].clear(); return; @@ -203,6 +217,7 @@ if (len > 0) { Array<int>* def_vtable_indices = NULL; if ((def_vtable_indices = ik()->default_vtable_indices()) == NULL) { + assert(!is_shared, "shared class def_vtable_indices does not exist"); def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK); } else { assert(def_vtable_indices->length() == len, "reinit vtable len?"); @@ -217,7 +232,15 @@ // needs new entry if (needs_new_entry) { put_method_at(mh(), initialized); - def_vtable_indices->at_put(i, initialized); //set vtable index + if (is_preinitialized_vtable()) { + // At runtime initialize_vtable is rerun for a shared class + // (loaded by the non-boot loader) as part of link_class_impl(). + // The dumptime vtable index should be the same as the runtime index. + assert(def_vtable_indices->at(i) == initialized, + "dump time vtable index is different from runtime index"); + } else { + def_vtable_indices->at_put(i, initialized); //set vtable index + } initialized++; } } @@ -378,7 +401,8 @@ } // we need a new entry if there is no superclass - if (klass->super() == NULL) { + Klass* super = klass->super(); + if (super == NULL) { return allocate_new; } @@ -407,7 +431,15 @@ Symbol* target_classname = target_klass->name(); for(int i = 0; i < super_vtable_len; i++) { - Method* super_method = method_at(i); + Method* super_method; + if (is_preinitialized_vtable()) { + // If this is a shared class, the vtable is already in the final state (fully + // initialized). Need to look at the super's vtable. + klassVtable* superVtable = super->vtable(); + super_method = superVtable->method_at(i); + } else { + super_method = method_at(i); + } // Check if method name matches if (super_method->name() == name && super_method->signature() == signature) { @@ -475,7 +507,15 @@ target_method()->set_vtable_index(i); } else { if (def_vtable_indices != NULL) { - def_vtable_indices->at_put(default_index, i); + if (is_preinitialized_vtable()) { + // At runtime initialize_vtable is rerun as part of link_class_impl() + // for a shared class loaded by the non-boot loader. + // The dumptime vtable index should be the same as the runtime index. + assert(def_vtable_indices->at(default_index) == i, + "dump time vtable index is different from runtime index"); + } else { + def_vtable_indices->at_put(default_index, i); + } } assert(super_method->is_default_method() || super_method->is_overpass() || super_method->is_abstract(), "default override error"); @@ -490,17 +530,26 @@ } void klassVtable::put_method_at(Method* m, int index) { - if (log_develop_is_enabled(Trace, vtables)) { - ResourceMark rm; - outputStream* logst = Log(vtables)::trace_stream(); - const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>"; - logst->print("adding %s at index %d, flags: ", sig, index); - if (m != NULL) { - m->print_linkage_flags(logst); + if (is_preinitialized_vtable()) { + // At runtime initialize_vtable is rerun as part of link_class_impl() + // for shared class loaded by the non-boot loader to obtain the loader + // constraints based on the runtime classloaders' context. The dumptime + // method at the vtable index should be the same as the runtime method. + assert(table()[index].method() == m, + "archived method is different from the runtime method"); + } else { + if (log_develop_is_enabled(Trace, vtables)) { + ResourceMark rm; + outputStream* logst = Log(vtables)::trace_stream(); + const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>"; + logst->print("adding %s at index %d, flags: ", sig, index); + if (m != NULL) { + m->print_linkage_flags(logst); + } + logst->cr(); } - logst->cr(); + table()[index].set(m); } - table()[index].set(m); } // Find out if a method "m" with superclass "super", loader "classloader" and @@ -950,7 +999,15 @@ void itableMethodEntry::initialize(Method* m) { if (m == NULL) return; - _method = m; + if (MetaspaceShared::is_in_shared_space((void*)&_method) && + !MetaspaceShared::remapped_readwrite()) { + // At runtime initialize_itable is rerun as part of link_class_impl() + // for a shared class loaded by the non-boot loader. + // The dumptime itable method entry should be the same as the runtime entry. + assert(_method == m, "sanity"); + } else { + _method = m; + } } klassItable::klassItable(instanceKlassHandle klass) { @@ -1054,7 +1111,11 @@ logst->cr(); } if (!m->has_vtable_index()) { - assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable"); + // A shared method could have an initialized itable_index that + // is < 0. + assert(m->vtable_index() == Method::pending_itable_index || + m->is_shared(), + "set by initialize_vtable"); m->set_itable_index(ime_num); // Progress to next itable entry ime_num++; @@ -1248,7 +1309,6 @@ } #endif // INCLUDE_JVMTI - // Setup class InterfaceVisiterClosure : public StackObj { public:
--- a/hotspot/src/share/vm/oops/klassVtable.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/oops/klassVtable.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -153,6 +153,19 @@ Array<Klass*>* local_interfaces); void verify_against(outputStream* st, klassVtable* vt, int index); inline InstanceKlass* ik() const; + // When loading a class from CDS archive at run time, and no class redefintion + // has happened, it is expected that the class's itable/vtables are + // laid out exactly the same way as they had been during dump time. + // Therefore, in klassVtable::initialize_[iv]table, we do not layout the + // tables again. Instead, we only rerun the process to create/check + // the class loader constraints. In non-product builds, we add asserts to + // guarantee that the table's layout would be the same as at dump time. + // + // If JVMTI redefines any class, the read-only shared memory are remapped + // as read-write. A shared class' vtable/itable are re-initialized and + // might have different layout due to class redefinition of the shared class + // or its super types. + bool is_preinitialized_vtable(); };
--- a/hotspot/src/share/vm/oops/method.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/oops/method.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -313,6 +313,33 @@ unlink_method(); } +void Method::set_vtable_index(int index) { + if (is_shared() && !MetaspaceShared::remapped_readwrite()) { + // At runtime initialize_vtable is rerun as part of link_class_impl() + // for a shared class loaded by the non-boot loader to obtain the loader + // constraints based on the runtime classloaders' context. + return; // don't write into the shared class + } else { + _vtable_index = index; + } +} + +void Method::set_itable_index(int index) { + if (is_shared() && !MetaspaceShared::remapped_readwrite()) { + // At runtime initialize_itable is rerun as part of link_class_impl() + // for a shared class loaded by the non-boot loader to obtain the loader + // constraints based on the runtime classloaders' context. The dumptime + // itable index should be the same as the runtime index. + assert(_vtable_index == itable_index_max - index, + "archived itable index is different from runtime index"); + return; // don’t write into the shared class + } else { + _vtable_index = itable_index_max - index; + } + assert(valid_itable_index(), ""); +} + + bool Method::was_executed_more_than(int n) { // Invocation counter is reset when the Method* is compiled.
--- a/hotspot/src/share/vm/oops/method.hpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/oops/method.hpp Fri Jul 22 04:05:04 2016 +0000 @@ -470,12 +470,12 @@ DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; }) bool has_vtable_index() const { return _vtable_index >= 0; } int vtable_index() const { return _vtable_index; } - void set_vtable_index(int index) { _vtable_index = index; } + void set_vtable_index(int index); DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; }) bool has_itable_index() const { return _vtable_index <= itable_index_max; } int itable_index() const { assert(valid_itable_index(), ""); return itable_index_max - _vtable_index; } - void set_itable_index(int index) { _vtable_index = itable_index_max - index; assert(valid_itable_index(), ""); } + void set_itable_index(int index); // interpreter entry address interpreter_entry() const { return _i2i_entry; }
--- a/hotspot/src/share/vm/opto/library_call.cpp Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/src/share/vm/opto/library_call.cpp Fri Jul 22 04:05:04 2016 +0000 @@ -2405,8 +2405,13 @@ Compile::AliasType* alias_type = C->alias_type(adr_type); assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here"); - assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM || - alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown"); + // Only field, array element or unknown locations are supported. + if (alias_type->adr_type() != TypeRawPtr::BOTTOM && + alias_type->adr_type() != TypeOopPtr::BOTTOM && + alias_type->basic_type() == T_ILLEGAL) { + return false; + } + bool mismatched = false; BasicType bt = alias_type->basic_type(); if (bt != T_ILLEGAL) { @@ -2782,12 +2787,6 @@ ShouldNotReachHere(); } - // Null check receiver. - receiver = null_check(receiver); - if (stopped()) { - return true; - } - // Build field offset expression. // We currently rely on the cookies produced by Unsafe.xxxFieldOffset // to be plain byte offsets, which are also the same as those accepted @@ -2799,8 +2798,6 @@ const TypePtr *adr_type = _gvn.type(adr)->isa_ptr(); Compile::AliasType* alias_type = C->alias_type(adr_type); - assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM || - alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown"); BasicType bt = alias_type->basic_type(); if (bt != T_ILLEGAL && ((bt == T_OBJECT || bt == T_ARRAY) != (type == T_OBJECT))) { @@ -2832,6 +2829,12 @@ ShouldNotReachHere(); } + // Null check receiver. + receiver = null_check(receiver); + if (stopped()) { + return true; + } + int alias_idx = C->get_alias_index(adr_type); // Memory-model-wise, a LoadStore acts like a little synchronized
--- a/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java Fri Jul 22 04:05:04 2016 +0000 @@ -25,6 +25,7 @@ * @test * @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64") * @library ../../../../../ + * @ignore 8161550 * @modules java.base/jdk.internal.reflect * jdk.vm.ci/jdk.vm.ci.meta * jdk.vm.ci/jdk.vm.ci.runtime
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/unsafe/OpaqueAccesses.java Fri Jul 22 04:05:04 2016 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016, 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. + */ + +/* + * @test + * @bug 8155781 + * @modules java.base/jdk.internal.misc + * + * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test* + * compiler.unsafe.OpaqueAccesses + */ +package compiler.unsafe; + +import jdk.internal.misc.Unsafe; + +import java.lang.reflect.Field; + +public class OpaqueAccesses { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + private static final Object INSTANCE = new OpaqueAccesses(); + + private static final Object[] ARRAY = new Object[10]; + + private static final long F_OFFSET; + private static final long E_OFFSET; + + static { + try { + Field field = OpaqueAccesses.class.getDeclaredField("f"); + F_OFFSET = UNSAFE.objectFieldOffset(field); + + E_OFFSET = UNSAFE.arrayBaseOffset(ARRAY.getClass()); + } catch (NoSuchFieldException e) { + throw new Error(e); + } + } + + private Object f = new Object(); + + static Object testFixedOffsetField(Object o) { + return UNSAFE.getObject(o, F_OFFSET); + } + + static int testFixedOffsetHeader0(Object o) { + return UNSAFE.getInt(o, 0); + } + + static int testFixedOffsetHeader4(Object o) { + return UNSAFE.getInt(o, 4); + } + + static Object testFixedBase(long off) { + return UNSAFE.getObject(INSTANCE, off); + } + + static Object testOpaque(Object o, long off) { + return UNSAFE.getObject(o, off); + } + + static int testFixedOffsetHeaderArray0(Object[] arr) { + return UNSAFE.getInt(arr, 0); + } + + static int testFixedOffsetHeaderArray4(Object[] arr) { + return UNSAFE.getInt(arr, 4); + } + + static Object testFixedOffsetArray(Object[] arr) { + return UNSAFE.getObject(arr, E_OFFSET); + } + + static Object testFixedBaseArray(long off) { + return UNSAFE.getObject(ARRAY, off); + } + + static Object testOpaqueArray(Object[] o, long off) { + return UNSAFE.getObject(o, off); + } + + static final long ADDR = UNSAFE.allocateMemory(10); + static boolean flag; + + static int testMixedAccess() { + flag = !flag; + Object o = (flag ? INSTANCE : null); + long off = (flag ? F_OFFSET : ADDR); + return UNSAFE.getInt(o, off); + } + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + // Instance + testFixedOffsetField(INSTANCE); + testFixedOffsetHeader0(INSTANCE); + testFixedOffsetHeader4(INSTANCE); + testFixedBase(F_OFFSET); + testOpaque(INSTANCE, F_OFFSET); + testMixedAccess(); + + // Array + testFixedOffsetHeaderArray0(ARRAY); + testFixedOffsetHeaderArray4(ARRAY); + testFixedOffsetArray(ARRAY); + testFixedBaseArray(E_OFFSET); + testOpaqueArray(ARRAY, E_OFFSET); + } + System.out.println("TEST PASSED"); + } +}
--- a/hotspot/test/gc/TestSmallHeap.java Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/test/gc/TestSmallHeap.java Fri Jul 22 04:05:04 2016 +0000 @@ -27,6 +27,7 @@ * @requires vm.gc=="null" * @summary Verify that starting the VM with a small heap works * @library /testlibrary /test/lib /test/lib/share/classes + * @ignore 8161552 * @modules java.base/jdk.internal.misc * @modules java.management/sun.management * @build TestSmallHeap
--- a/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java Thu Jul 21 19:29:33 2016 -0700 +++ b/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java Fri Jul 22 04:05:04 2016 +0000 @@ -29,6 +29,7 @@ * parallel collectors. * @requires vm.gc=="null" * @library /testlibrary /test/lib + * @ignore 8161552 * @modules java.base/jdk.internal.misc * java.management * @build TestParallelHeapSizeFlags TestMaxHeapSizeTools
--- a/jaxp/.hgtags Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/.hgtags Fri Jul 22 04:05:04 2016 +0000 @@ -370,3 +370,4 @@ 493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125 15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126 bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127 +8a7681a9d70640ac7fbf05c28f53c1d51d8d00a1 jdk-9+128
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/XalanConstants.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/XalanConstants.java Fri Jul 22 04:05:04 2016 +0000 @@ -80,6 +80,14 @@ */ public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT = ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit"; + + /** + * JDK node count limit in entities that limits the total number of nodes + * in all of entity references. + */ + public static final String JDK_ENTITY_REPLACEMENT_LIMIT = + ORACLE_JAXP_PROPERTY_PREFIX + "entityReplacementLimit"; + /** * JDK maximum parameter entity size limit */ @@ -136,6 +144,13 @@ * JDK maximum general entity size limit */ public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit"; + + /** + * JDK node count limit in entities that limits the total number of nodes + * in all of entity references. + */ + public static final String SP_ENTITY_REPLACEMENT_LIMIT = "jdk.xml.entityReplacementLimit"; + /** * JDK maximum parameter entity size limit */
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, 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 @@ -82,7 +82,9 @@ MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH, XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0), MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT, - XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000); + XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000), + ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", XalanConstants.JDK_ENTITY_REPLACEMENT_LIMIT, + XalanConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000); final String key; final String apiProperty;
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Constants.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Constants.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,15 +1,15 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright 2001-2004 The Apache Software Foundation. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -98,6 +98,10 @@ public static final int ACC_STATIC = com.sun.org.apache.bcel.internal.Constants.ACC_STATIC; + public static final String MODULE_SIG + = "Ljava/lang/reflect/Module;"; + public static final String CLASS_SIG + = "Ljava/lang/Class;"; public static final String STRING_SIG = "Ljava/lang/String;"; public static final String STRING_BUFFER_SIG @@ -246,8 +250,12 @@ = "com.sun.org.apache.xalan.internal.xsltc.DOM"; public static final String DOM_IMPL = "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl"; - public static final String SAX_IMPL + public static final String SAX_IMPL = "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl"; + public static final String CLASS_CLASS + = "java.lang.Class"; + public static final String MODULE_CLASS + = "java.lang.reflect.Module"; public static final String STRING_CLASS = "java.lang.String"; public static final String OBJECT_CLASS @@ -293,7 +301,7 @@ = "()D"; public static final String DOM_PNAME - = "dom"; + = "dom"; public static final String NODE_PNAME = "node"; public static final String TRANSLET_OUTPUT_PNAME @@ -335,6 +343,19 @@ = "setStartNode"; public static final String RESET = "reset"; + public static final String GET_MODULE + = "getModule"; + public static final String FOR_NAME + = "forName"; + public static final String ADD_READS + = "addReads"; + + public static final String GET_MODULE_SIG + = "()" + MODULE_SIG; + public static final String FOR_NAME_SIG + = "(" + STRING_SIG + ")" + CLASS_SIG; + public static final String ADD_READS_SIG + = "(" + MODULE_SIG + ")" + MODULE_SIG; public static final String ATTR_SET_SIG = "(" + DOM_INTF_SIG + NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + "I)V";
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -17,9 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * $Id: FunctionCall.java,v 1.2.4.1 2005/09/12 10:31:32 pvedula Exp $ - */ package com.sun.org.apache.xalan.internal.xsltc.compiler; @@ -32,6 +29,7 @@ import com.sun.org.apache.bcel.internal.generic.InstructionConstants; import com.sun.org.apache.bcel.internal.generic.InstructionList; import com.sun.org.apache.bcel.internal.generic.InvokeInstruction; +import com.sun.org.apache.bcel.internal.generic.LDC; import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; import com.sun.org.apache.bcel.internal.generic.NEW; import com.sun.org.apache.bcel.internal.generic.PUSH; @@ -792,6 +790,11 @@ final String clazz = _chosenConstructor.getDeclaringClass().getName(); + + // Generate call to Module.addReads: + // <TransletClass>.class.getModule().addReads( + generateAddReads(classGen, methodGen, clazz); + Class[] paramTypes = _chosenConstructor.getParameterTypes(); LocalVariableGen[] paramTemp = new LocalVariableGen[n]; @@ -855,6 +858,12 @@ final String clazz = _chosenMethod.getDeclaringClass().getName(); Class[] paramTypes = _chosenMethod.getParameterTypes(); + + // Generate call to Module.addReads: + // <TransletClass>.class.getModule().addReads( + // Class.forName(<clazz>).getModule()); + generateAddReads(classGen, methodGen, clazz); + // Push "this" if it is an instance method if (_thisArgument != null) { _thisArgument.translate(classGen, methodGen); @@ -896,6 +905,41 @@ } } + private void generateAddReads(ClassGenerator classGen, MethodGenerator methodGen, + String clazz) { + final ConstantPoolGen cpg = classGen.getConstantPool(); + final InstructionList il = methodGen.getInstructionList(); + + // Generate call to Module.addReads: + // <TransletClass>.class.getModule().addReads( + // Class.forName(<clazz>).getModule()); + // Class.forName may throw ClassNotFoundException. + // This is OK as it will caught higher up the stack in + // TransformerImpl.transform() and wrapped into a + // TransformerException. + methodGen.markChunkStart(); + + int index = cpg.addMethodref(CLASS_CLASS, + GET_MODULE, + GET_MODULE_SIG); + int index2 = cpg.addMethodref(CLASS_CLASS, + FOR_NAME, + FOR_NAME_SIG); + il.append(new LDC(cpg.addString(classGen.getClassName()))); + il.append(new INVOKESTATIC(index2)); + il.append(new INVOKEVIRTUAL(index)); + il.append(new LDC(cpg.addString(clazz))); + il.append(new INVOKESTATIC(index2)); + il.append(new INVOKEVIRTUAL(index)); + index = cpg.addMethodref(MODULE_CLASS, + ADD_READS, + ADD_READS_SIG); + il.append(new INVOKEVIRTUAL(index)); + il.append(InstructionConstants.POP); + + methodGen.markChunkEnd(); + } + @Override public String toString() { return "funcall(" + _fname + ", " + _arguments + ')';
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -58,7 +58,6 @@ import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.URIResolver; -import jdk.internal.module.Modules; /** * @author Morten Jorgensen @@ -486,10 +485,6 @@ thisModule.addExports(p, m); }); - // For now, the module reads all unnnamed modules. This will be changed once - // the XSLT compiler is updated to generate code to invoke addReads. - Modules.addReadsAllUnnamed(m); - // java.xml needs to instanitate the translet class thisModule.addReads(m); @@ -513,7 +508,7 @@ } catch (ClassFormatError e) { ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name); - throw new TransformerConfigurationException(err.toString()); + throw new TransformerConfigurationException(err.toString(), e); } catch (LinkageError e) { ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -239,6 +239,14 @@ */ public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT = ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit"; + + /** + * JDK node count limit in entities that limits the total number of nodes + * in all of entity references. + */ + public static final String JDK_ENTITY_REPLACEMENT_LIMIT = + ORACLE_JAXP_PROPERTY_PREFIX + "entityReplacementLimit"; + /** * JDK maximum parameter entity size limit */ @@ -292,6 +300,13 @@ * JDK maximum general entity size limit */ public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit"; + + /** + * JDK node count limit in entities that limits the total number of nodes + * in all of entity references. + */ + public static final String SP_ENTITY_REPLACEMENT_LIMIT = "jdk.xml.entityReplacementLimit"; + /** * JDK maximum parameter entity size limit */
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DTDScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DTDScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,62 +1,21 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */ /* - * The Apache Software License, Version 1.1 - * - * - * Copyright (c) 1999-2004 The Apache Software Foundation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * 4. The names "Xerces" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation and was - * originally based on software copyright (c) 1999, International - * Business Machines, Inc., http://www.apache.org. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.sun.org.apache.xerces.internal.impl; @@ -146,7 +105,7 @@ protected boolean scanPubidLiteral(XMLString literal) throws IOException, XNIException { - int quote = fEntityScanner.scanChar(); + int quote = fEntityScanner.scanChar(null); if (quote != '\'' && quote != '"') { reportFatalError("QuoteRequiredInPublicID", null); return false; @@ -157,7 +116,7 @@ boolean skipSpace = true; boolean dataok = true; while (true) { - int c = fEntityScanner.scanChar(); + int c = fEntityScanner.scanChar(null); // REVISIT: it could really only be \n or 0x20; all else is normalized, no? - neilg if (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) { if (!skipSpace) {
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DocumentScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DocumentScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,62 +1,21 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */ /* - * The Apache Software License, Version 1.1 - * - * - * Copyright (c) 1999-2004 The Apache Software Foundation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * 4. The names "Xerces" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation and was - * originally based on software copyright (c) 1999, International - * Business Machines, Inc., http://www.apache.org. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.sun.org.apache.xerces.internal.impl; @@ -134,7 +93,7 @@ // happens when there is the character reference // but scanContent doesn't do entity expansions... // is this *really* necessary??? - NG - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); content.append((char)c); c = -1; } @@ -143,7 +102,7 @@ } */ if (c == ']') { - content.append((char)fEntityScanner.scanChar()); + content.append((char)fEntityScanner.scanChar(null)); // remember where we are in case we get an endEntity before we // could flush the buffer out - this happens when we're parsing an // entity which ends with a ] @@ -152,12 +111,12 @@ // We work on a single character basis to handle cases such as: // ']]]>' which we might otherwise miss. // - if (fEntityScanner.skipChar(']')) { + if (fEntityScanner.skipChar(']', null)) { content.append(']'); - while (fEntityScanner.skipChar(']')) { + while (fEntityScanner.skipChar(']', null)) { content.append(']'); } - if (fEntityScanner.skipChar('>')) { + if (fEntityScanner.skipChar('>', null)) { reportFatalError("CDEndInContent", null); } } @@ -184,6 +143,7 @@ * @param checkEntities true if undeclared entities should be reported as VC violation, * false if undeclared entities should be reported as WFC violation. * @param eleName The name of element to which this attribute belongs. + * @param isNSURI The flag indicating whether the content is a namespace URI * * @return true if the non-normalized and normalized value are the same * @@ -193,7 +153,7 @@ protected boolean scanAttributeValue(XMLString value, XMLString nonNormalizedValue, String atName, - boolean checkEntities,String eleName) + boolean checkEntities,String eleName, boolean isNSURI) throws IOException, XNIException { // quote @@ -202,10 +162,10 @@ reportFatalError("OpenQuoteExpected", new Object[]{eleName,atName}); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.ATTRIBUTE); int entityDepth = fEntityDepth; - int c = fEntityScanner.scanLiteral(quote, value); + int c = fEntityScanner.scanLiteral(quote, value, isNSURI); if (DEBUG_ATTR_NORMALIZATION) { System.out.println("** scanLiteral -> \"" + value.toString() + "\""); @@ -215,7 +175,7 @@ if (c == quote && (fromIndex = isUnchangedByNormalization(value)) == -1) { /** Both the non-normalized and normalized attribute values are equal. **/ nonNormalizedValue.setValues(value); - int cquote = fEntityScanner.scanChar(); + int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE); if (cquote != quote) { reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName}); } @@ -238,11 +198,11 @@ + fStringBuffer.toString() + "\""); } if (c == '&') { - fEntityScanner.skipChar('&'); + fEntityScanner.skipChar('&', NameType.REFERENCE); if (entityDepth == fEntityDepth) { fStringBuffer2.append('&'); } - if (fEntityScanner.skipChar('#')) { + if (fEntityScanner.skipChar('#', NameType.REFERENCE)) { if (entityDepth == fEntityDepth) { fStringBuffer2.append('#'); } @@ -256,59 +216,22 @@ } } else { - String entityName = fEntityScanner.scanName(); + String entityName = fEntityScanner.scanName(NameType.REFERENCE); if (entityName == null) { reportFatalError("NameRequiredInReference", null); } else if (entityDepth == fEntityDepth) { fStringBuffer2.append(entityName); } - if (!fEntityScanner.skipChar(';')) { + if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInReference", new Object []{entityName}); } else if (entityDepth == fEntityDepth) { fStringBuffer2.append(';'); } - if (entityName == fAmpSymbol) { - fStringBuffer.append('&'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** value5: \"" - + fStringBuffer.toString() - + "\""); - } - } - else if (entityName == fAposSymbol) { - fStringBuffer.append('\''); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** value7: \"" - + fStringBuffer.toString() - + "\""); - } - } - else if (entityName == fLtSymbol) { - fStringBuffer.append('<'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** value9: \"" - + fStringBuffer.toString() - + "\""); - } - } - else if (entityName == fGtSymbol) { - fStringBuffer.append('>'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** valueB: \"" - + fStringBuffer.toString() - + "\""); - } - } - else if (entityName == fQuotSymbol) { - fStringBuffer.append('"'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** valueD: \"" - + fStringBuffer.toString() - + "\""); - } + if (resolveCharacter(entityName, fStringBuffer)) { + checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1); } else { if (fEntityManager.isExternalEntity(entityName)) { @@ -339,13 +262,13 @@ else if (c == '<') { reportFatalError("LessthanInAttValue", new Object[] { eleName, atName }); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); if (entityDepth == fEntityDepth) { fStringBuffer2.append((char)c); } } else if (c == '%' || c == ']') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fStringBuffer.append((char)c); if (entityDepth == fEntityDepth) { fStringBuffer2.append((char)c); @@ -359,7 +282,7 @@ // XML11EntityScanner. Not sure why // this check was originally necessary. - NG else if (c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fStringBuffer.append(' '); if (entityDepth == fEntityDepth) { fStringBuffer2.append('\n'); @@ -382,12 +305,12 @@ else if (c != -1 && isInvalidLiteral(c)) { reportFatalError("InvalidCharInAttValue", new Object[] {eleName, atName, Integer.toString(c, 16)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); if (entityDepth == fEntityDepth) { fStringBuffer2.append((char)c); } } - c = fEntityScanner.scanLiteral(quote, value); + c = fEntityScanner.scanLiteral(quote, value, isNSURI); if (entityDepth == fEntityDepth) { fStringBuffer2.append(value); } @@ -404,7 +327,7 @@ nonNormalizedValue.setValues(fStringBuffer2); // quote - int cquote = fEntityScanner.scanChar(); + int cquote = fEntityScanner.scanChar(null); if (cquote != quote) { reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName}); } @@ -439,7 +362,7 @@ protected boolean scanPubidLiteral(XMLString literal) throws IOException, XNIException { - int quote = fEntityScanner.scanChar(); + int quote = fEntityScanner.scanChar(null); if (quote != '\'' && quote != '"') { reportFatalError("QuoteRequiredInPublicID", null); return false; @@ -450,7 +373,7 @@ boolean skipSpace = true; boolean dataok = true; while (true) { - int c = fEntityScanner.scanChar(); + int c = fEntityScanner.scanChar(null); // REVISIT: none of these except \n and 0x20 should make it past the entity scanner if (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) { if (!skipSpace) {
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11EntityScanner.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11EntityScanner.java Fri Jul 22 04:05:04 2016 +0000 @@ -21,6 +21,7 @@ package com.sun.org.apache.xerces.internal.impl; +import com.sun.org.apache.xerces.internal.impl.XMLScanner.NameType; import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; import com.sun.org.apache.xerces.internal.util.XML11Char; import com.sun.org.apache.xerces.internal.util.XMLChar; @@ -92,7 +93,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public int scanChar() throws IOException { + protected int scanChar(NameType nt) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -100,6 +101,7 @@ } // scan character + int offset = fCurrentEntity.position; int c = fCurrentEntity.ch[fCurrentEntity.position++]; boolean external = false; if (c == '\n' || @@ -110,6 +112,7 @@ invokeListeners(1); fCurrentEntity.ch[0] = (char)c; load(1, false, false); + offset = 0; } if (c == '\r' && external) { int cc = fCurrentEntity.ch[fCurrentEntity.position++]; @@ -122,6 +125,9 @@ // return character that was scanned fCurrentEntity.columnNumber++; + if (!detectingVersion) { + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); + } return c; } // scanChar():int @@ -141,7 +147,7 @@ * @see com.sun.org.apache.xerces.internal.util.SymbolTable * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name */ - public String scanNmtoken() throws IOException { + protected String scanNmtoken() throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { load(0, true, true); @@ -248,6 +254,8 @@ * <strong>Note:</strong> The string returned must be a symbol. The * SymbolTable can be used for this purpose. * + * @param nt The type of the name (element or attribute) + * * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. * @@ -255,7 +263,7 @@ * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart */ - public String scanName() throws IOException { + protected String scanName(NameType nt) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { load(0, true, true); @@ -310,23 +318,11 @@ return null; } + int length = 0; do { ch = fCurrentEntity.ch[fCurrentEntity.position]; if (XML11Char.isXML11Name(ch)) { - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - invokeListeners(length); - if (length == fCurrentEntity.ch.length) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.ch.length << 1]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - } - else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) { offset = 0; if (load(length, false, false)) { break; @@ -334,20 +330,7 @@ } } else if (XML11Char.isXML11NameHighSurrogate(ch)) { - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - invokeListeners(length); - if (length == fCurrentEntity.ch.length) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.ch.length << 1]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - } - else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) { offset = 0; if (load(length, false, false)) { --fCurrentEntity.position; @@ -361,20 +344,7 @@ --fCurrentEntity.position; break; } - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - invokeListeners(length); - if (length == fCurrentEntity.ch.length) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.ch.length << 1]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - } - else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) { offset = 0; if (load(length, false, false)) { break; @@ -387,12 +357,14 @@ } while (true); - int length = fCurrentEntity.position - offset; + length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length; // return name String symbol = null; if (length > 0) { + checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length); + checkEntityLimit(nt, fCurrentEntity, offset, length); symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length); } return symbol; @@ -415,7 +387,7 @@ * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCName * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCNameStart */ - public String scanNCName() throws IOException { + protected String scanNCName() throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -571,6 +543,7 @@ * this purpose. * * @param qname The qualified name structure to fill. + * @param nt The type of the name (element or attribute) * * @return Returns true if a qualified name appeared immediately on * the input and was scanned, false otherwise. @@ -582,7 +555,7 @@ * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart */ - public boolean scanQName(QName qname) throws IOException { + protected boolean scanQName(QName qname, XMLScanner.NameType nt) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -602,6 +575,7 @@ fCurrentEntity.columnNumber++; String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 1); qname.setValues(null, name, name, null); + checkEntityLimit(nt, fCurrentEntity, 0, 1); return true; } } @@ -632,6 +606,7 @@ fCurrentEntity.columnNumber += 2; String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 2); qname.setValues(null, name, name, null); + checkEntityLimit(nt, fCurrentEntity, 0, 2); return true; } } @@ -641,6 +616,7 @@ } int index = -1; + int length = 0; boolean sawIncompleteSurrogatePair = false; do { ch = fCurrentEntity.ch[fCurrentEntity.position]; @@ -653,22 +629,7 @@ //check prefix before further read checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset); } - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - //check localpart before loading more data - checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length - index - 1); - invokeListeners(length); - if (length == fCurrentEntity.ch.length) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.ch.length << 1]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - } - else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) { if (index != -1) { index = index - offset; } @@ -679,20 +640,7 @@ } } else if (XML11Char.isXML11NameHighSurrogate(ch)) { - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - invokeListeners(length); - if (length == fCurrentEntity.ch.length) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.ch.length << 1]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - } - else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) { if (index != -1) { index = index - offset; } @@ -711,20 +659,7 @@ --fCurrentEntity.position; break; } - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - invokeListeners(length); - if (length == fCurrentEntity.ch.length) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.ch.length << 1]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - } - else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) { if (index != -1) { index = index - offset; } @@ -740,7 +675,7 @@ } while (true); - int length = fCurrentEntity.position - offset; + length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length; if (length > 0) { @@ -776,6 +711,7 @@ checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length); } qname.setValues(prefix, localpart, rawname, null); + checkEntityLimit(nt, fCurrentEntity, offset, length); return true; } return false; @@ -808,7 +744,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public int scanContent(XMLString content) throws IOException { + protected int scanContent(XMLString content) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -826,6 +762,7 @@ int offset = fCurrentEntity.position; int c = fCurrentEntity.ch[offset]; int newlines = 0; + boolean counted = false; boolean external = fCurrentEntity.isExternal(); if (c == '\n' || ((c == '\r' || c == 0x85 || c == 0x2028) && external)) { do { @@ -835,11 +772,13 @@ fCurrentEntity.lineNumber++; fCurrentEntity.columnNumber = 1; if (fCurrentEntity.position == fCurrentEntity.count) { + checkEntityLimit(null, fCurrentEntity, offset, newlines); offset = 0; fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); fCurrentEntity.position = newlines; fCurrentEntity.startPosition = newlines; if (load(newlines, false, true)) { + counted = true; break; } } @@ -858,11 +797,13 @@ fCurrentEntity.lineNumber++; fCurrentEntity.columnNumber = 1; if (fCurrentEntity.position == fCurrentEntity.count) { + checkEntityLimit(null, fCurrentEntity, offset, newlines); offset = 0; fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); fCurrentEntity.position = newlines; fCurrentEntity.startPosition = newlines; if (load(newlines, false, true)) { + counted = true; break; } } @@ -877,6 +818,7 @@ } int length = fCurrentEntity.position - offset; if (fCurrentEntity.position == fCurrentEntity.count - 1) { + checkEntityLimit(null, fCurrentEntity, offset, length); content.setValues(fCurrentEntity.ch, offset, length); return -1; } @@ -904,8 +846,8 @@ } int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; - if (fCurrentEntity.isGE) { - checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length); + if (!counted) { + checkEntityLimit(null, fCurrentEntity, offset, length); } content.setValues(fCurrentEntity.ch, offset, length); @@ -945,6 +887,7 @@ * @param quote The quote character that signifies the end of the * attribute value data. * @param content The content structure to fill. + * @param isNSURI a flag indicating whether the content is a Namespace URI * * @return Returns the next character on the input, if known. This * value may be -1 but this does <em>note</em> designate @@ -953,7 +896,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public int scanLiteral(int quote, XMLString content) + protected int scanLiteral(int quote, XMLString content, boolean isNSURI) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -1051,8 +994,10 @@ } int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; - if (fCurrentEntity.isGE) { - checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length); + + checkEntityLimit(null, fCurrentEntity, offset, length); + if (isNSURI) { + checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length); } content.setValues(fCurrentEntity.ch, offset, length); @@ -1103,7 +1048,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public boolean scanData(String delimiter, XMLStringBuffer buffer) + protected boolean scanData(String delimiter, XMLStringBuffer buffer) throws IOException { boolean done = false; @@ -1135,6 +1080,7 @@ if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) { // something must be wrong with the input: e.g., file ends an unterminated comment int length = fCurrentEntity.count - fCurrentEntity.position; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length); buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length); fCurrentEntity.columnNumber += fCurrentEntity.count; fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); @@ -1199,6 +1145,7 @@ } int length = fCurrentEntity.position - offset; if (fCurrentEntity.position == fCurrentEntity.count - 1) { + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); buffer.append(fCurrentEntity.ch, offset, length); return true; } @@ -1237,6 +1184,7 @@ fCurrentEntity.position--; int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); buffer.append(fCurrentEntity.ch, offset, length); return true; } @@ -1274,6 +1222,7 @@ fCurrentEntity.position--; int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); buffer.append(fCurrentEntity.ch, offset, length); return true; } @@ -1281,6 +1230,7 @@ } int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); if (done) { length -= delimLen; } @@ -1305,7 +1255,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public boolean skipChar(int c) throws IOException { + protected boolean skipChar(int c, NameType nt) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -1313,6 +1263,7 @@ } // skip character + int offset = fCurrentEntity.position; int cc = fCurrentEntity.ch[fCurrentEntity.position]; if (cc == c) { fCurrentEntity.position++; @@ -1323,12 +1274,14 @@ else { fCurrentEntity.columnNumber++; } + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); return true; } else if (c == '\n' && ((cc == 0x2028 || cc == 0x85) && fCurrentEntity.isExternal())) { fCurrentEntity.position++; fCurrentEntity.lineNumber++; fCurrentEntity.columnNumber = 1; + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); return true; } else if (c == '\n' && (cc == '\r' ) && fCurrentEntity.isExternal()) { @@ -1344,6 +1297,7 @@ } fCurrentEntity.lineNumber++; fCurrentEntity.columnNumber = 1; + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); return true; } @@ -1366,7 +1320,7 @@ * @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace * @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Space */ - public boolean skipSpaces() throws IOException { + protected boolean skipSpaces() throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -1386,7 +1340,7 @@ // skip spaces int c = fCurrentEntity.ch[fCurrentEntity.position]; - + int offset = fCurrentEntity.position - 1; // External -- Match: S + 0x85 + 0x2028, and perform end of line normalization if (fCurrentEntity.isExternal()) { if (XML11Char.isXML11Space(c)) { @@ -1422,6 +1376,11 @@ else { fCurrentEntity.columnNumber++; } + + //If this is a general entity, spaces within a start element should be counted + checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset); + offset = fCurrentEntity.position; + // load more characters, if needed if (!entityChanged) fCurrentEntity.position++; @@ -1462,6 +1421,11 @@ else { fCurrentEntity.columnNumber++; } + + //If this is a general entity, spaces within a start element should be counted + checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset); + offset = fCurrentEntity.position; + // load more characters, if needed if (!entityChanged) fCurrentEntity.position++; @@ -1495,7 +1459,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public boolean skipString(String s) throws IOException { + protected boolean skipString(String s) throws IOException { // load more characters, if needed if (fCurrentEntity.position == fCurrentEntity.count) { @@ -1504,6 +1468,7 @@ // skip string final int length = s.length(); + final int beforeSkip = fCurrentEntity.position ; for (int i = 0; i < length; i++) { char c = fCurrentEntity.ch[fCurrentEntity.position++]; if (c != s.charAt(i)) { @@ -1523,6 +1488,9 @@ } } fCurrentEntity.columnNumber += length; + if (!detectingVersion) { + checkEntityLimit(null, fCurrentEntity, beforeSkip, length); + } return true; } // skipString(String):boolean
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11NSDocumentScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11NSDocumentScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. */ /* @@ -135,7 +135,7 @@ if (DEBUG_START_END_ELEMENT) System.out.println(">>> scanStartElementNS()"); // Note: namespace processing is on by default - fEntityScanner.scanQName(fElementQName); + fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE); // REVISIT - [Q] Why do we need this local variable? -- mrglavas String rawname = fElementQName.rawname; if (fBindNamespaces) { @@ -173,11 +173,11 @@ // end tag? int c = fEntityScanner.peekChar(); if (c == '>') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); break; } else if (c == '/') { - fEntityScanner.scanChar(); - if (!fEntityScanner.skipChar('>')) { + fEntityScanner.scanChar(null); + if (!fEntityScanner.skipChar('>', null)) { reportFatalError( "ElementUnterminated", new Object[] { rawname }); @@ -345,7 +345,7 @@ protected void scanStartElementName () throws IOException, XNIException { // Note: namespace processing is on by default - fEntityScanner.scanQName(fElementQName); + fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE); // Must skip spaces here because the DTD scanner // would consume them at the end of the external subset. fSawSpace = fEntityScanner.skipSpaces(); @@ -395,11 +395,11 @@ // end tag? int c = fEntityScanner.peekChar(); if (c == '>') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); break; } else if (c == '/') { - fEntityScanner.scanChar(); - if (!fEntityScanner.skipChar('>')) { + fEntityScanner.scanChar(null); + if (!fEntityScanner.skipChar('>', null)) { reportFatalError( "ElementUnterminated", new Object[] { rawname }); @@ -571,11 +571,11 @@ System.out.println(">>> scanAttribute()"); // name - fEntityScanner.scanQName(fAttributeQName); + fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE); // equals fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('=')) { + if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) { reportFatalError( "EqRequiredInAttribute", new Object[] { @@ -614,13 +614,20 @@ //REVISIT: one more case needs to be included: external PE and standalone is no boolean isVC = fHasExternalDTD && !fStandalone; - // REVISIT: it seems that this function should not take attributes, and length - scanAttributeValue( - this.fTempString, - fTempString2, - fAttributeQName.rawname, - isVC, - fCurrentElement.rawname); + /** + * Determine whether this is a namespace declaration that will be subject + * to the name limit check in the scanAttributeValue operation. + * Namespace declaration format: xmlns="..." or xmlns:prefix="..." + * Note that prefix:xmlns="..." isn't a namespace. + */ + String localpart = fAttributeQName.localpart; + String prefix = fAttributeQName.prefix != null + ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING; + boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS || + prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS); + + scanAttributeValue(this.fTempString, fTempString2, fAttributeQName.rawname, + isVC, fCurrentElement.rawname, isNSDecl); String value = fTempString.toString(); attributes.setValue(attrIndex, value); attributes.setNonNormalizedValue(attrIndex, fTempString2.toString()); @@ -628,17 +635,7 @@ // record namespace declarations if any. if (fBindNamespaces) { - - String localpart = fAttributeQName.localpart; - String prefix = - fAttributeQName.prefix != null - ? fAttributeQName.prefix - : XMLSymbols.EMPTY_STRING; - // when it's of form xmlns="..." or xmlns:prefix="...", - // it's a namespace declaration. but prefix:xmlns="..." isn't. - if (prefix == XMLSymbols.PREFIX_XMLNS - || prefix == XMLSymbols.EMPTY_STRING - && localpart == XMLSymbols.PREFIX_XMLNS) { + if (isNSDecl) { if (value.length() > fXMLNameLimit) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MaxXMLNameLimit", @@ -758,7 +755,7 @@ // end fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) { reportFatalError( "ETagUnterminated", new Object[] { endElementName.rawname });
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDTDScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDTDScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -21,10 +21,7 @@ package com.sun.org.apache.xerces.internal.impl; -import com.sun.org.apache.xerces.internal.impl.Constants; import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; -import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; -import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler; import com.sun.org.apache.xerces.internal.util.SymbolTable; import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl; import com.sun.org.apache.xerces.internal.util.XMLChar; @@ -367,6 +364,7 @@ // we're done, set starting state for external subset setScannerState(SCANNER_STATE_TEXT_DECL); // we're done scanning DTD. + fLimitAnalyzer.reset(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT); fLimitAnalyzer.reset(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT); return false; } @@ -399,7 +397,7 @@ if (isInvalidLiteral(c)) { reportFatalError("InvalidCharInDTD", new Object[] { Integer.toHexString(c) }); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } } } @@ -767,7 +765,7 @@ fStringBuffer.clear(); fStringBuffer.append("xml"); while (isValidNameChar(fEntityScanner.peekChar())) { - fStringBuffer.append((char)fEntityScanner.scanChar()); + fStringBuffer.append((char)fEntityScanner.scanChar(null)); } String target = fSymbolTable.addSymbol(fStringBuffer.ch, @@ -867,7 +865,7 @@ } // element name - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.ELEMENTSTART); if (name == null) { reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL", null); @@ -900,7 +898,7 @@ } } else { - if (!fEntityScanner.skipChar('(')) { + if (!fEntityScanner.skipChar('(', null)) { reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", new Object[]{name}); } @@ -930,7 +928,7 @@ fReportEntity = false; skipSeparator(false, !scanningInternalSubset()); // end - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', null)) { reportFatalError("ElementDeclUnterminated", new Object[]{name}); } fReportEntity = true; @@ -967,7 +965,7 @@ fDTDContentModelHandler.pcdata(null); } skipSeparator(false, !scanningInternalSubset()); - while (fEntityScanner.skipChar('|')) { + while (fEntityScanner.skipChar('|', null)) { fStringBuffer.append('|'); // call handler if (fDTDContentModelHandler != null) { @@ -976,7 +974,7 @@ } skipSeparator(false, !scanningInternalSubset()); - childName = fEntityScanner.scanName(); + childName = fEntityScanner.scanName(NameType.ENTITY); if (childName == null) { reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT", new Object[]{elName}); @@ -1005,7 +1003,7 @@ reportFatalError("MixedContentUnterminated", new Object[]{elName}); } - else if (fEntityScanner.skipChar(')')){ + else if (fEntityScanner.skipChar(')', null)){ fStringBuffer.append(')'); // call handler if (fDTDContentModelHandler != null) { @@ -1043,7 +1041,7 @@ int currentOp = 0; int c; while (true) { - if (fEntityScanner.skipChar('(')) { + if (fEntityScanner.skipChar('(', null)) { fMarkUpDepth++; fStringBuffer.append('('); // call handler @@ -1057,7 +1055,7 @@ continue; } skipSeparator(false, !scanningInternalSubset()); - String childName = fEntityScanner.scanName(); + String childName = fEntityScanner.scanName(NameType.ELEMENTSTART); if (childName == null) { reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", new Object[]{elName}); @@ -1084,7 +1082,7 @@ } fDTDContentModelHandler.occurrence(oc, null); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fStringBuffer.append((char)c); } while (true) { @@ -1097,7 +1095,7 @@ fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE, null); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fStringBuffer.append(','); break; } @@ -1108,7 +1106,7 @@ fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE, null); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fStringBuffer.append('|'); break; } @@ -1154,7 +1152,7 @@ } else { // no occurrence specified - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fStringBuffer.append(')'); } fMarkUpDepth--; @@ -1186,7 +1184,7 @@ } // element name - String elName = fEntityScanner.scanName(); + String elName = fEntityScanner.scanName(NameType.ELEMENTSTART); if (elName == null) { reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL", null); @@ -1200,7 +1198,7 @@ // spaces if (!skipSeparator(true, !scanningInternalSubset())) { // no space, is it the end yet? - if (fEntityScanner.skipChar('>')) { + if (fEntityScanner.skipChar('>', null)) { // yes, stop here // call handler if (fDTDHandler != null) { @@ -1216,8 +1214,8 @@ } // definitions - while (!fEntityScanner.skipChar('>')) { - String name = fEntityScanner.scanName(); + while (!fEntityScanner.skipChar('>', null)) { + String name = fEntityScanner.scanName(NameType.ATTRIBUTE); if (name == null) { reportFatalError("AttNameRequiredInAttDef", new Object[]{elName}); @@ -1353,7 +1351,7 @@ new Object[]{elName, atName}); } // open paren - int c = fEntityScanner.scanChar(); + int c = fEntityScanner.scanChar(null); if (c != '(') { reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE", new Object[]{elName, atName}); @@ -1361,7 +1359,7 @@ fMarkUpDepth++; do { skipSeparator(false, !scanningInternalSubset()); - String aName = fEntityScanner.scanName(); + String aName = fEntityScanner.scanName(NameType.ATTRIBUTE); if (aName == null) { reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE", new Object[]{elName, atName}); @@ -1369,7 +1367,7 @@ ensureEnumerationSize(fEnumerationCount + 1); fEnumeration[fEnumerationCount++] = aName; skipSeparator(false, !scanningInternalSubset()); - c = fEntityScanner.scanChar(); + c = fEntityScanner.scanChar(null); } while (c == '|'); if (c != ')') { reportFatalError("NotationTypeUnterminated", @@ -1380,7 +1378,7 @@ else { // Enumeration type = "ENUMERATION"; // open paren - int c = fEntityScanner.scanChar(); + int c = fEntityScanner.scanChar(null); if (c != '(') { // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL", reportFatalError("AttTypeRequiredInAttDef", @@ -1397,7 +1395,7 @@ ensureEnumerationSize(fEnumerationCount + 1); fEnumeration[fEnumerationCount++] = token; skipSeparator(false, !scanningInternalSubset()); - c = fEntityScanner.scanChar(); + c = fEntityScanner.scanChar(null); } while (c == '|'); if (c != ')') { reportFatalError("EnumerationUnterminated", @@ -1447,7 +1445,7 @@ // AttValue boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ; scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName, - fAttributes, 0, isVC, elName); + fAttributes, 0, isVC, elName, false); } return defaultType; @@ -1475,7 +1473,7 @@ boolean sawPERef = false; fReportEntity = false; if (fEntityScanner.skipSpaces()) { - if (!fEntityScanner.skipChar('%')) { + if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) { isPEDecl = false; // <!ENTITY x "x"> } else if (skipSeparator(true, !scanningInternalSubset())) { @@ -1496,7 +1494,7 @@ sawPERef = true; } } - else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) { + else if (scanningInternalSubset() || !fEntityScanner.skipChar('%', NameType.REFERENCE)) { // <!ENTITY[^ ]...> or <!ENTITY[^ %]...> reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", null); @@ -1513,11 +1511,11 @@ } if (sawPERef) { while (true) { - String peName = fEntityScanner.scanName(); + String peName = fEntityScanner.scanName(NameType.REFERENCE); if (peName == null) { reportFatalError("NameRequiredInPEReference", null); } - else if (!fEntityScanner.skipChar(';')) { + else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInPEReference", new Object[]{peName}); } @@ -1525,20 +1523,20 @@ startPE(peName, false); } fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('%')) + if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) break; if (!isPEDecl) { if (skipSeparator(true, !scanningInternalSubset())) { isPEDecl = true; break; } - isPEDecl = fEntityScanner.skipChar('%'); + isPEDecl = fEntityScanner.skipChar('%', NameType.REFERENCE); } } } // name - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.ENTITY); if (name == null) { reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null); } @@ -1573,7 +1571,7 @@ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL", new Object[]{name}); } - notation = fEntityScanner.scanName(); + notation = fEntityScanner.scanName(NameType.NOTATION); if (notation == null) { reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL", new Object[]{name}); @@ -1595,7 +1593,7 @@ skipSeparator(false, !scanningInternalSubset()); // end - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', null)) { reportFatalError("EntityDeclUnterminated", new Object[]{name}); } fMarkUpDepth--; @@ -1650,7 +1648,7 @@ protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value, XMLString nonNormalizedValue) throws IOException, XNIException { - int quote = fEntityScanner.scanChar(); + int quote = fEntityScanner.scanChar(null); if (quote != '\'' && quote != '"') { reportFatalError("OpenQuoteMissingInDecl", null); } @@ -1665,23 +1663,24 @@ } fLimitAnalyzer.startEntity(entityName); - if (fEntityScanner.scanLiteral(quote, fString) != quote) { + if (fEntityScanner.scanLiteral(quote, fString, false) != quote) { fStringBuffer.clear(); fStringBuffer2.clear(); + int offset; do { - checkEntityLimit(isPEDecl, entityName, fString.length + countChar); countChar = 0; + offset = fStringBuffer.length; fStringBuffer.append(fString); fStringBuffer2.append(fString); - if (fEntityScanner.skipChar('&')) { - if (fEntityScanner.skipChar('#')) { + if (fEntityScanner.skipChar('&', NameType.REFERENCE)) { + if (fEntityScanner.skipChar('#', NameType.REFERENCE)) { fStringBuffer2.append("&#"); scanCharReferenceValue(fStringBuffer, fStringBuffer2); } else { fStringBuffer.append('&'); fStringBuffer2.append('&'); - String eName = fEntityScanner.scanName(); + String eName = fEntityScanner.scanName(NameType.REFERENCE); if (eName == null) { reportFatalError("NameRequiredInReference", null); @@ -1690,7 +1689,7 @@ fStringBuffer.append(eName); fStringBuffer2.append(eName); } - if (!fEntityScanner.skipChar(';')) { + if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInReference", new Object[]{eName}); } @@ -1700,15 +1699,15 @@ } } } - else if (fEntityScanner.skipChar('%')) { + else if (fEntityScanner.skipChar('%', NameType.REFERENCE)) { while (true) { fStringBuffer2.append('%'); - String peName = fEntityScanner.scanName(); + String peName = fEntityScanner.scanName(NameType.REFERENCE); if (peName == null) { reportFatalError("NameRequiredInPEReference", null); } - else if (!fEntityScanner.skipChar(';')) { + else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInPEReference", new Object[]{peName}); } @@ -1725,20 +1724,20 @@ // REVISIT: This will make returning the non- // normalized value harder. -Ac fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('%')) + if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) break; } } else { - countChar++; int c = fEntityScanner.peekChar(); if (XMLChar.isHighSurrogate(c)) { + countChar++; scanSurrogates(fStringBuffer2); } else if (isInvalidLiteral(c)) { reportFatalError("InvalidCharInLiteral", new Object[]{Integer.toHexString(c)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } // if it's not the delimiting quote or if it is but from a // different entity than the one this literal started from, @@ -1746,10 +1745,12 @@ else if (c != quote || entityDepth != fEntityDepth) { fStringBuffer.append((char)c); fStringBuffer2.append((char)c); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } } - } while (fEntityScanner.scanLiteral(quote, fString) != quote); + checkEntityLimit(isPEDecl, entityName, fStringBuffer.length - offset + countChar); + } while (fEntityScanner.scanLiteral(quote, fString, false) != quote); + checkEntityLimit(isPEDecl, entityName, fString.length); fStringBuffer.append(fString); fStringBuffer2.append(fString); literal = fStringBuffer; @@ -1760,10 +1761,14 @@ value.setValues(literal); nonNormalizedValue.setValues(literal2); if (fLimitAnalyzer != null) { - fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName); + if (isPEDecl) { + fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName); + } else { + fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName); + } } - if (!fEntityScanner.skipChar(quote)) { + if (!fEntityScanner.skipChar(quote, null)) { reportFatalError("CloseQuoteMissingInDecl", null); } } // scanEntityValue(XMLString,XMLString):void @@ -1788,7 +1793,7 @@ } // notation name - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.NOTATION); if (name == null) { reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL", null); @@ -1815,7 +1820,7 @@ skipSeparator(false, !scanningInternalSubset()); // end - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', null)) { reportFatalError("NotationDeclUnterminated", new Object[]{name}); } fMarkUpDepth--; @@ -1863,7 +1868,7 @@ XMLErrorReporter.SEVERITY_ERROR); } // call handler - if (!fEntityScanner.skipChar('[')) { + if (!fEntityScanner.skipChar('[', null)) { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } @@ -1888,7 +1893,7 @@ fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE, null); } - if (!fEntityScanner.skipChar('[')) { + if (!fEntityScanner.skipChar('[', null)) { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } fReportEntity = true; @@ -1897,7 +1902,7 @@ fIgnoreConditionalBuffer.clear(); } while (true) { - if (fEntityScanner.skipChar('<')) { + if (fEntityScanner.skipChar('<', null)) { if (fDTDHandler != null) { fIgnoreConditionalBuffer.append('<'); } @@ -1905,8 +1910,8 @@ // These tests are split so that we handle cases like // '<<![' and '<!<![' which we might otherwise miss. // - if (fEntityScanner.skipChar('!')) { - if(fEntityScanner.skipChar('[')) { + if (fEntityScanner.skipChar('!', null)) { + if(fEntityScanner.skipChar('[', null)) { if (fDTDHandler != null) { fIgnoreConditionalBuffer.append("!["); } @@ -1918,24 +1923,24 @@ } } } - else if (fEntityScanner.skipChar(']')) { + else if (fEntityScanner.skipChar(']', null)) { if (fDTDHandler != null) { fIgnoreConditionalBuffer.append(']'); } // // The same thing goes for ']<![' and '<]]>', etc. // - if (fEntityScanner.skipChar(']')) { + if (fEntityScanner.skipChar(']', null)) { if (fDTDHandler != null) { fIgnoreConditionalBuffer.append(']'); } - while (fEntityScanner.skipChar(']')) { + while (fEntityScanner.skipChar(']', null)) { /* empty loop body */ if (fDTDHandler != null) { fIgnoreConditionalBuffer.append(']'); } } - if (fEntityScanner.skipChar('>')) { + if (fEntityScanner.skipChar('>', null)) { if (fIncludeSectDepth-- == initialDepth) { fMarkUpDepth--; // call handler @@ -1953,7 +1958,7 @@ } } else { - int c = fEntityScanner.scanChar(); + int c = fEntityScanner.scanChar(null); if (fScannerState == SCANNER_STATE_END_OF_INPUT) { reportFatalError("IgnoreSectUnterminated", null); return; @@ -1990,16 +1995,16 @@ //System.out.println("scanDecls"+fScannerState); while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) { again = complete; - if (fEntityScanner.skipChar('<')) { + if (fEntityScanner.skipChar('<', null)) { fMarkUpDepth++; - if (fEntityScanner.skipChar('?')) { + if (fEntityScanner.skipChar('?', null)) { fStringBuffer.clear(); scanPI(fStringBuffer); fMarkUpDepth--; // we're done with this decl } - else if (fEntityScanner.skipChar('!')) { - if (fEntityScanner.skipChar('-')) { - if (!fEntityScanner.skipChar('-')) { + else if (fEntityScanner.skipChar('!', null)) { + if (fEntityScanner.skipChar('-', null)) { + if (!fEntityScanner.skipChar('-', null)) { reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } else { @@ -2018,7 +2023,7 @@ else if (fEntityScanner.skipString("NOTATION")) { scanNotationDecl(); } - else if (fEntityScanner.skipChar('[') && + else if (fEntityScanner.skipChar('[', null) && !scanningInternalSubset()) { scanConditionalSect(fPEDepth); } @@ -2033,10 +2038,10 @@ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); } } - else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) { + else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']', null)) { // end of conditional section? - if (!fEntityScanner.skipChar(']') - || !fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar(']', null) + || !fEntityScanner.skipChar('>', null)) { reportFatalError("IncludeSectUnterminated", null); } // call handler @@ -2083,21 +2088,21 @@ throws IOException, XNIException { int depth = fPEDepth; boolean sawSpace = fEntityScanner.skipSpaces(); - if (!lookForPERefs || !fEntityScanner.skipChar('%')) { + if (!lookForPERefs || !fEntityScanner.skipChar('%', NameType.REFERENCE)) { return !spaceRequired || sawSpace || (depth != fPEDepth); } while (true) { - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.ENTITY); if (name == null) { reportFatalError("NameRequiredInPEReference", null); } - else if (!fEntityScanner.skipChar(';')) { + else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInPEReference", new Object[]{name}); } startPE(name, false); fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('%')) + if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) return true; } } @@ -2181,56 +2186,6 @@ fSecurityManager = fEntityManager.fSecurityManager; } - /** - * Add the count of the content buffer and check if the accumulated - * value exceeds the limit - * @param isPEDecl a flag to indicate whether the entity is parameter - * @param entityName entity name - * @param buffer content buffer - */ - private void checkEntityLimit(boolean isPEDecl, String entityName, XMLString buffer) { - checkEntityLimit(isPEDecl, entityName, buffer.length); - } - - /** - * Add the count and check limit - * @param isPEDecl a flag to indicate whether the entity is parameter - * @param entityName entity name - * @param len length of the buffer - */ - private void checkEntityLimit(boolean isPEDecl, String entityName, int len) { - if (fLimitAnalyzer == null) { - fLimitAnalyzer = fEntityManager.fLimitAnalyzer; - } - if (isPEDecl) { - fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, "%" + entityName, len); - if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { - fSecurityManager.debugPrint(fLimitAnalyzer); - reportFatalError("MaxEntitySizeLimit", new Object[]{"%" + entityName, - fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), - fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), - fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)}); - } - } else { - fLimitAnalyzer.addValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName, len); - if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { - fSecurityManager.debugPrint(fLimitAnalyzer); - reportFatalError("MaxEntitySizeLimit", new Object[]{entityName, - fLimitAnalyzer.getValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT), - fSecurityManager.getLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT), - fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT)}); - } - } - if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { - fSecurityManager.debugPrint(fLimitAnalyzer); - reportFatalError("TotalEntitySizeLimit", - new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), - fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), - fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)}); - } - - } - public DTDGrammar getGrammar(){ return nvGrammarInfo; }
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -21,14 +21,6 @@ package com.sun.org.apache.xerces.internal.impl; -import com.sun.xml.internal.stream.XMLBufferListener; -import com.sun.xml.internal.stream.XMLEntityStorage; -import com.sun.xml.internal.stream.dtd.DTDGrammarUtil; - -import java.io.EOFException; -import java.io.IOException; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.events.XMLEvent; import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; import com.sun.org.apache.xerces.internal.util.AugmentationsImpl; import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl; @@ -47,13 +39,18 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner; import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; import com.sun.org.apache.xerces.internal.xni.Augmentations; -import com.sun.org.apache.xerces.internal.impl.Constants; -import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler; import com.sun.org.apache.xerces.internal.utils.SecuritySupport; import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit; import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; +import com.sun.xml.internal.stream.XMLBufferListener; +import com.sun.xml.internal.stream.XMLEntityStorage; +import com.sun.xml.internal.stream.dtd.DTDGrammarUtil; +import java.io.EOFException; +import java.io.IOException; +import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.events.XMLEvent; /** * @@ -454,6 +451,7 @@ //fDocumentHandler.startElement(getElementQName(),fAttributes,null); break; case XMLStreamConstants.CHARACTERS : + fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); fDocumentHandler.characters(getCharacterData(),null); break; case XMLStreamConstants.SPACE: @@ -462,13 +460,15 @@ //fDocumentHandler.ignorableWhitespace(getCharacterData(), null); break; case XMLStreamConstants.ENTITY_REFERENCE : + fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); //entity reference callback are given in startEntity break; case XMLStreamConstants.PROCESSING_INSTRUCTION : + fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null); break; case XMLStreamConstants.COMMENT : - //System.out.println(" in COMMENT of the XMLNSDocumentScannerImpl"); + fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); fDocumentHandler.comment(getCharacterData(),null); break; case XMLStreamConstants.DTD : @@ -477,6 +477,7 @@ //therefore we don't need to take care of anything here. So Just break; break; case XMLStreamConstants.CDATA: + fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); fDocumentHandler.startCDATA(null); //xxx: check if CDATA values comes from getCharacterData() function fDocumentHandler.characters(getCharacterData(),null); @@ -1273,9 +1274,9 @@ fElementQName = fElementStack.nextElement(); // name if (fNamespaces) { - fEntityScanner.scanQName(fElementQName); + fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART); } else { - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.ELEMENTSTART); fElementQName.setValues(null, name, name, null); } @@ -1376,11 +1377,11 @@ // end tag? final int c = fEntityScanner.peekChar(); if (c == '>') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); return true; } else if (c == '/') { - fEntityScanner.scanChar(); - if (!fEntityScanner.skipChar('>')) { + fEntityScanner.scanChar(null); + if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) { reportFatalError("ElementUnterminated", new Object[]{fElementQName.rawname}); } @@ -1518,15 +1519,15 @@ // name if (fNamespaces) { - fEntityScanner.scanQName(fAttributeQName); + fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME); } else { - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.ATTRIBUTENAME); fAttributeQName.setValues(null, name, name, null); } // equals fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('=')) { + if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) { reportFatalError("EqRequiredInAttribute", new Object[] {fCurrentElement.rawname, fAttributeQName.rawname}); } @@ -1544,9 +1545,8 @@ //can safely add the attribute later.. XMLString tmpStr = getString(); - scanAttributeValue(tmpStr, fTempString2, - fAttributeQName.rawname, attributes, - attIndex, isVC, fCurrentElement.rawname); + scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes, + attIndex, isVC, fCurrentElement.rawname, false); // content int oldLen = attributes.getLength(); @@ -1594,13 +1594,13 @@ if (c == '\r') { // happens when there is the character reference //xxx: We know the next chracter.. we should just skip it and add ']' directlry - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); content.append((char)c); c = -1; } else if (c == ']') { //fStringBuffer.clear(); //xxx: We know the next chracter.. we should just skip it and add ']' directlry - content.append((char)fEntityScanner.scanChar()); + content.append((char)fEntityScanner.scanChar(null)); // remember where we are in case we get an endEntity before we // could flush the buffer out - this happens when we're parsing an // entity which ends with a ] @@ -1609,12 +1609,12 @@ // We work on a single character basis to handle cases such as: // ']]]>' which we might otherwise miss. // - if (fEntityScanner.skipChar(']')) { + if (fEntityScanner.skipChar(']', null)) { content.append(']'); - while (fEntityScanner.skipChar(']')) { + while (fEntityScanner.skipChar(']', null)) { content.append(']'); } - if (fEntityScanner.skipChar('>')) { + if (fEntityScanner.skipChar('>', null)) { reportFatalError("CDEndInContent", null); } } @@ -1689,7 +1689,7 @@ } else { reportFatalError("InvalidCharInCDSect", new Object[]{Integer.toString(c,16)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } } //by this time we have also read surrogate contents if any... @@ -1751,7 +1751,7 @@ // end fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) { reportFatalError("ETagUnterminated", new Object[]{rawname}); } @@ -1841,12 +1841,12 @@ * notification. */ protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException { - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.REFERENCE); if (name == null) { reportFatalError("NameRequiredInReference", null); return; } - if (!fEntityScanner.skipChar(';')) { + if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInReference", new Object []{name}); } if (fEntityStore.isUnparsedEntity(name)) { @@ -1943,6 +1943,7 @@ */ private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException { foundBuiltInRefs = true; + checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1); content.append(c); if (fDocumentHandler != null) { fSingleChar[0] = c; @@ -2608,13 +2609,13 @@ switch(ch){ case '?' :{ setScannerState(SCANNER_STATE_PI); - fEntityScanner.skipChar(ch); + fEntityScanner.skipChar(ch, null); break; } case '!' :{ - fEntityScanner.skipChar(ch); - if (fEntityScanner.skipChar('-')) { - if (!fEntityScanner.skipChar('-')) { + fEntityScanner.skipChar(ch, null); + if (fEntityScanner.skipChar('-', null)) { + if (!fEntityScanner.skipChar('-', NameType.COMMENT)) { reportFatalError("InvalidCommentStart", null); } @@ -2629,7 +2630,7 @@ } case '/' :{ setScannerState(SCANNER_STATE_END_ELEMENT_TAG); - fEntityScanner.skipChar(ch); + fEntityScanner.skipChar(ch, NameType.ELEMENTEND); break; } default :{ @@ -2641,9 +2642,9 @@ }//startOfMarkup private void startOfContent() throws IOException { - if (fEntityScanner.skipChar('<')) { + if (fEntityScanner.skipChar('<', null)) { setScannerState(SCANNER_STATE_START_OF_MARKUP); - } else if (fEntityScanner.skipChar('&')) { + } else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) { setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE } else { //element content is there.. @@ -2716,10 +2717,10 @@ case SCANNER_STATE_CONTENT: { final int ch = fEntityScanner.peekChar(); if (ch == '<') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); setScannerState(SCANNER_STATE_START_OF_MARKUP); } else if (ch == '&') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.REFERENCE); setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE break; } else { @@ -2819,9 +2820,9 @@ if(DEBUG){ System.out.println("fTempString = " + fTempString); } - if(fEntityScanner.skipChar('<')){ + if(fEntityScanner.skipChar('<', null)){ //check if we have reached end of element - if(fEntityScanner.skipChar('/')){ + if(fEntityScanner.skipChar('/', NameType.ELEMENTEND)){ //increase the mark up depth fMarkupDepth++; fLastSectionWasCharacterData = false; @@ -2871,7 +2872,7 @@ } // happens when there is the character reference //xxx: We know the next chracter.. we should just skip it and add ']' directlry - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); fUsebuffer = true; fContentBuffer.append((char)c); c = -1 ; @@ -2879,7 +2880,7 @@ //fStringBuffer.clear(); //xxx: We know the next chracter.. we should just skip it and add ']' directlry fUsebuffer = true; - fContentBuffer.append((char)fEntityScanner.scanChar()); + fContentBuffer.append((char)fEntityScanner.scanChar(null)); // remember where we are in case we get an endEntity before we // could flush the buffer out - this happens when we're parsing an // entity which ends with a ] @@ -2888,12 +2889,12 @@ // We work on a single character basis to handle cases such as: // ']]]>' which we might otherwise miss. // - if (fEntityScanner.skipChar(']')) { + if (fEntityScanner.skipChar(']', null)) { fContentBuffer.append(']'); - while (fEntityScanner.skipChar(']')) { + while (fEntityScanner.skipChar(']', null)) { fContentBuffer.append(']'); } - if (fEntityScanner.skipChar('>')) { + if (fEntityScanner.skipChar('>', null)) { reportFatalError("CDEndInContent", null); } } @@ -2906,12 +2907,12 @@ // we need not to grow the buffer only when isCoalesce() is not true; if (c == '<') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); setScannerState(SCANNER_STATE_START_OF_MARKUP); break; }//xxx what should be the behavior if entity reference is present in the content ? else if (c == '&') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.REFERENCE); setScannerState(SCANNER_STATE_REFERENCE); break; }///xxx since this part is also characters, it should be merged... @@ -2924,7 +2925,7 @@ reportFatalError("InvalidCharInContent", new Object[] { Integer.toString(c, 16)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } break; } @@ -3050,7 +3051,7 @@ } fUsebuffer = true ; //take care of character reference - if (fEntityScanner.skipChar('#')) { + if (fEntityScanner.skipChar('#', NameType.REFERENCE)) { scanCharReferenceValue(fContentBuffer, null); fMarkupDepth--; if(!fIsCoalesce){ @@ -3106,11 +3107,11 @@ if (fNamespaces) { while (isValidNCName(fEntityScanner.peekChar())) { - fStringBuffer.append((char)fEntityScanner.scanChar()); + fStringBuffer.append((char)fEntityScanner.scanChar(null)); } } else { while (isValidNameChar(fEntityScanner.peekChar())) { - fStringBuffer.append((char)fEntityScanner.scanChar()); + fStringBuffer.append((char)fEntityScanner.scanChar(null)); } } String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -631,7 +631,7 @@ } // root element name - fDoctypeName = fEntityScanner.scanName(); + fDoctypeName = fEntityScanner.scanName(NameType.DOCTYPE); if (fDoctypeName == null) { reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null); } @@ -671,10 +671,10 @@ // is there an internal subset? boolean internalSubset = true; - if (!fEntityScanner.skipChar('[')) { + if (!fEntityScanner.skipChar('[', null)) { internalSubset = false; fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', null)) { reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName}); } fMarkupDepth--; @@ -753,7 +753,7 @@ fStringBuffer.clear(); fStringBuffer.append("xml"); while (XMLChar.isName(fEntityScanner.peekChar())) { - fStringBuffer.append((char)fEntityScanner.scanChar()); + fStringBuffer.append((char)fEntityScanner.scanChar(null)); } String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length); //this function should fill the data.. and set the fEvent object to this event. @@ -831,9 +831,9 @@ switch (fScannerState) { case SCANNER_STATE_PROLOG: { fEntityScanner.skipSpaces(); - if (fEntityScanner.skipChar('<')) { + if (fEntityScanner.skipChar('<', null)) { setScannerState(SCANNER_STATE_START_OF_MARKUP); - } else if (fEntityScanner.skipChar('&')) { + } else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) { setScannerState(SCANNER_STATE_REFERENCE); } else { setScannerState(SCANNER_STATE_CONTENT); @@ -849,9 +849,9 @@ setDriver(fContentDriver); //from now onwards this would be handled by fContentDriver,in the same next() call return fContentDriver.next(); - } else if (fEntityScanner.skipChar('!')) { - if (fEntityScanner.skipChar('-')) { - if (!fEntityScanner.skipChar('-')) { + } else if (fEntityScanner.skipChar('!', null)) { + if (fEntityScanner.skipChar('-', null)) { + if (!fEntityScanner.skipChar('-', null)) { reportFatalError("InvalidCommentStart", null); } @@ -871,7 +871,7 @@ reportFatalError("MarkupNotRecognizedInProlog", null); } - } else if (fEntityScanner.skipChar('?')) { + } else if (fEntityScanner.skipChar('?', null)) { setScannerState(SCANNER_STATE_PI); } else { reportFatalError("MarkupNotRecognizedInProlog", @@ -991,7 +991,7 @@ case SCANNER_STATE_CONTENT: { reportFatalError("ContentIllegalInProlog", null); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } case SCANNER_STATE_REFERENCE: { reportFatalError("ReferenceIllegalInProlog", null); @@ -1105,11 +1105,11 @@ fReadingDTD=false; if (!moreToScan) { // end doctype declaration - if (!fEntityScanner.skipChar(']')) { + if (!fEntityScanner.skipChar(']', null)) { reportFatalError("DoctypedeclNotClosed", new Object[]{fDoctypeName}); } fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', null)) { reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName}); } fMarkupDepth--; @@ -1373,7 +1373,7 @@ if(fScannerState == SCANNER_STATE_TERMINATED ){ return XMLEvent.END_DOCUMENT ; } - if (fEntityScanner.skipChar('<')) { + if (fEntityScanner.skipChar('<', null)) { setScannerState(SCANNER_STATE_START_OF_MARKUP); } else { setScannerState(SCANNER_STATE_CONTENT); @@ -1382,11 +1382,11 @@ } case SCANNER_STATE_START_OF_MARKUP: { fMarkupDepth++; - if (fEntityScanner.skipChar('?')) { + if (fEntityScanner.skipChar('?', null)) { setScannerState(SCANNER_STATE_PI); - } else if (fEntityScanner.skipChar('!')) { + } else if (fEntityScanner.skipChar('!', null)) { setScannerState(SCANNER_STATE_COMMENT); - } else if (fEntityScanner.skipChar('/')) { + } else if (fEntityScanner.skipChar('/', null)) { reportFatalError("MarkupNotRecognizedInMisc", null); } else if (isValidNameStartChar(fEntityScanner.peekChar()) || @@ -1429,7 +1429,7 @@ } else{ reportFatalError("ContentIllegalInTrailingMisc", null); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); setScannerState(SCANNER_STATE_TRAILING_MISC); return XMLEvent.CHARACTERS; }
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java Fri Jul 22 04:05:04 2016 +0000 @@ -2066,6 +2066,7 @@ // system id has to be a valid URI if (strict) { + try { // if it's already an absolute one, return it new URI(systemId);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityScanner.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityScanner.java Fri Jul 22 04:05:04 2016 +0000 @@ -21,6 +21,7 @@ package com.sun.org.apache.xerces.internal.impl; +import com.sun.org.apache.xerces.internal.impl.XMLScanner.NameType; import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader; import com.sun.org.apache.xerces.internal.impl.io.UCSReader; import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader; @@ -144,6 +145,9 @@ // so that XMLStreamReader.getVersion() can find that out. protected boolean xmlVersionSetExplicitly = false; + // indicates that the operation is for detecting XML version + boolean detectingVersion = false; + // // Constructors // @@ -530,10 +534,12 @@ * <p> * <strong>Note:</strong> The character is consumed. * + * @param nt The type of the name (element or attribute) + * * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public int scanChar() throws IOException { + protected int scanChar(NameType nt) throws IOException { if (DEBUG_BUFFER) { System.out.print("(scanChar: "); print(); @@ -546,6 +552,7 @@ } // scan character + int offset = fCurrentEntity.position; int c = fCurrentEntity.ch[fCurrentEntity.position++]; if (c == '\n' || (c == '\r' && isExternal)) { fCurrentEntity.lineNumber++; @@ -554,6 +561,7 @@ invokeListeners(1); fCurrentEntity.ch[0] = (char)c; load(1, false, false); + offset = 0; } if (c == '\r' && isExternal) { if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') { @@ -570,6 +578,9 @@ System.out.println(" -> '"+(char)c+"'"); } fCurrentEntity.columnNumber++; + if (!detectingVersion) { + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); + } return c; } // scanChar():int @@ -589,7 +600,7 @@ * @see com.sun.org.apache.xerces.internal.util.SymbolTable * @see com.sun.org.apache.xerces.internal.util.XMLChar#isName */ - public String scanNmtoken() throws IOException { + protected String scanNmtoken() throws IOException { if (DEBUG_BUFFER) { System.out.print("(scanNmtoken: "); print(); @@ -661,6 +672,8 @@ * <strong>Note:</strong> The string returned must be a symbol. The * SymbolTable can be used for this purpose. * + * @param nt The type of the name (element or attribute) + * * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. * @@ -668,7 +681,7 @@ * @see com.sun.org.apache.xerces.internal.util.XMLChar#isName * @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart */ - public String scanName() throws IOException { + protected String scanName(NameType nt) throws IOException { if (DEBUG_BUFFER) { System.out.print("(scanName: "); print(); @@ -682,6 +695,7 @@ // scan name int offset = fCurrentEntity.position; + int length; if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) { if (++fCurrentEntity.position == fCurrentEntity.count) { invokeListeners(1); @@ -709,20 +723,7 @@ vc = XMLChar.isName(c); } if(!vc)break; - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - invokeListeners(length); - if (length == fCurrentEntity.fBufferSize) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.fBufferSize * 2]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - fCurrentEntity.fBufferSize *= 2; - } else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) { offset = 0; if (load(length, false, false)) { break; @@ -730,12 +731,14 @@ } } } - int length = fCurrentEntity.position - offset; + length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length; // return name String symbol; if (length > 0) { + checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length); + checkEntityLimit(nt, fCurrentEntity, offset, length); symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length); } else symbol = null; @@ -759,6 +762,7 @@ * this purpose. * * @param qname The qualified name structure to fill. + * @param nt The type of the name (element or attribute) * * @return Returns true if a qualified name appeared immediately on * the input and was scanned, false otherwise. @@ -770,7 +774,7 @@ * @see com.sun.org.apache.xerces.internal.util.XMLChar#isName * @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart */ - public boolean scanQName(QName qname) throws IOException { + protected boolean scanQName(QName qname, NameType nt) throws IOException { if (DEBUG_BUFFER) { System.out.print("(scanQName, "+qname+": "); print(); @@ -806,11 +810,13 @@ print(); System.out.println(" -> true"); } + checkEntityLimit(nt, fCurrentEntity, 0, 1); return true; } } int index = -1; boolean vc = false; + int length; while ( true){ //XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) ; @@ -829,22 +835,7 @@ //check prefix before further read checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset); } - if (++fCurrentEntity.position == fCurrentEntity.count) { - int length = fCurrentEntity.position - offset; - //check localpart before loading more data - checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length - index - 1); - invokeListeners(length); - if (length == fCurrentEntity.fBufferSize) { - // bad luck we have to resize our buffer - char[] tmp = new char[fCurrentEntity.fBufferSize * 2]; - System.arraycopy(fCurrentEntity.ch, offset, - tmp, 0, length); - fCurrentEntity.ch = tmp; - fCurrentEntity.fBufferSize *= 2; - } else { - System.arraycopy(fCurrentEntity.ch, offset, - fCurrentEntity.ch, 0, length); - } + if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) { if (index != -1) { index = index - offset; } @@ -854,7 +845,7 @@ } } } - int length = fCurrentEntity.position - offset; + length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length; if (length > 0) { String prefix = null; @@ -885,6 +876,7 @@ print(); System.out.println(" -> true"); } + checkEntityLimit(nt, fCurrentEntity, offset, length); return true; } } @@ -900,22 +892,104 @@ } // scanQName(QName):boolean /** + * Checks whether the end of the entity buffer has been reached. If yes, + * checks against the limit and buffer size before loading more characters. + * + * @param entity the current entity + * @param offset the offset from which the current read was started + * @param nameOffset the offset from which the current name starts + * @return the length of characters scanned before the end of the buffer, + * zero if there is more to be read in the buffer + */ + protected int checkBeforeLoad(Entity.ScannedEntity entity, int offset, + int nameOffset) throws IOException { + int length = 0; + if (++entity.position == entity.count) { + length = entity.position - offset; + int nameLength = length; + if (nameOffset != -1) { + nameOffset = nameOffset - offset; + nameLength = length - nameOffset; + } else { + nameOffset = offset; + } + //check limit before loading more data + checkLimit(Limit.MAX_NAME_LIMIT, entity, nameOffset, nameLength); + invokeListeners(length); + if (length == entity.ch.length) { + // bad luck we have to resize our buffer + char[] tmp = new char[entity.fBufferSize * 2]; + System.arraycopy(entity.ch, offset, tmp, 0, length); + entity.ch = tmp; + entity.fBufferSize *= 2; + } + else { + System.arraycopy(entity.ch, offset, entity.ch, 0, length); + } + } + return length; + } + + /** + * If the current entity is an Entity reference, check the accumulated size + * against the limit. + * + * @param nt type of name (element, attribute or entity) + * @param entity The current entity + * @param offset The index of the first byte + * @param length The length of the entity scanned + */ + protected void checkEntityLimit(NameType nt, ScannedEntity entity, int offset, int length) { + if (entity == null || !entity.isGE) { + return; + } + + if (nt != NameType.REFERENCE) { + checkLimit(Limit.GENERAL_ENTITY_SIZE_LIMIT, entity, offset, length); + } + if (nt == NameType.ELEMENTSTART || nt == NameType.ATTRIBUTENAME) { + checkNodeCount(entity); + } + } + + /** + * If the current entity is an Entity reference, counts the total nodes in + * the entity and checks the accumulated value against the limit. + * + * @param entity The current entity + */ + protected void checkNodeCount(ScannedEntity entity) { + if (entity != null && entity.isGE) { + checkLimit(Limit.ENTITY_REPLACEMENT_LIMIT, entity, 0, 1); + } + } + + /** * Checks whether the value of the specified Limit exceeds its limit * - * @param limit The Limit to be checked. - * @param entity The current entity. + * @param limit The Limit to be checked + * @param entity The current entity * @param offset The index of the first byte - * @param length The length of the entity scanned. + * @param length The length of the entity scanned */ protected void checkLimit(Limit limit, ScannedEntity entity, int offset, int length) { - fLimitAnalyzer.addValue(limit, null, length); + fLimitAnalyzer.addValue(limit, entity.name, length); if (fSecurityManager.isOverLimit(limit, fLimitAnalyzer)) { fSecurityManager.debugPrint(fLimitAnalyzer); + Object[] e = (limit == Limit.ENTITY_REPLACEMENT_LIMIT) ? + new Object[]{fLimitAnalyzer.getValue(limit), + fSecurityManager.getLimit(limit), fSecurityManager.getStateLiteral(limit)} : + new Object[]{entity.name, fLimitAnalyzer.getValue(limit), + fSecurityManager.getLimit(limit), fSecurityManager.getStateLiteral(limit)}; fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, limit.key(), - new Object[]{new String(entity.ch, offset, length), - fLimitAnalyzer.getTotalValue(limit), - fSecurityManager.getLimit(limit), - fSecurityManager.getStateLiteral(limit)}, + e, XMLErrorReporter.SEVERITY_FATAL_ERROR); + } + if (fSecurityManager.isOverLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { + fSecurityManager.debugPrint(fLimitAnalyzer); + fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "TotalEntitySizeLimit", + new Object[]{fLimitAnalyzer.getTotalValue(Limit.TOTAL_ENTITY_SIZE_LIMIT), + fSecurityManager.getLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT), + fSecurityManager.getStateLiteral(Limit.TOTAL_ENTITY_SIZE_LIMIT)}, XMLErrorReporter.SEVERITY_FATAL_ERROR); } } @@ -942,7 +1016,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public int scanContent(XMLString content) throws IOException { + protected int scanContent(XMLString content) throws IOException { if (DEBUG_BUFFER) { System.out.print("(scanContent: "); print(); @@ -963,6 +1037,7 @@ int offset = fCurrentEntity.position; int c = fCurrentEntity.ch[offset]; int newlines = 0; + boolean counted = false; if (c == '\n' || (c == '\r' && isExternal)) { if (DEBUG_BUFFER) { System.out.print("[newline, "+offset+", "+fCurrentEntity.position+": "); @@ -976,9 +1051,11 @@ fCurrentEntity.lineNumber++; fCurrentEntity.columnNumber = 1; if (fCurrentEntity.position == fCurrentEntity.count) { + checkEntityLimit(null, fCurrentEntity, offset, newlines); offset = 0; fCurrentEntity.position = newlines; if (load(newlines, false, true)) { + counted = true; break; } } @@ -995,9 +1072,11 @@ fCurrentEntity.lineNumber++; fCurrentEntity.columnNumber = 1; if (fCurrentEntity.position == fCurrentEntity.count) { + checkEntityLimit(null, fCurrentEntity, offset, newlines); offset = 0; fCurrentEntity.position = newlines; if (load(newlines, false, true)) { + counted = true; break; } } @@ -1011,6 +1090,7 @@ } int length = fCurrentEntity.position - offset; if (fCurrentEntity.position == fCurrentEntity.count - 1) { + checkEntityLimit(null, fCurrentEntity, offset, length); //CHANGED: dont replace the value.. append to the buffer. This gives control to the callee //on buffering the data.. content.setValues(fCurrentEntity.ch, offset, length); @@ -1038,8 +1118,8 @@ } int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; - if (fCurrentEntity.isGE) { - checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length); + if (!counted) { + checkEntityLimit(null, fCurrentEntity, offset, length); } //CHANGED: dont replace the value.. append to the buffer. This gives control to the callee @@ -1086,6 +1166,7 @@ * @param quote The quote character that signifies the end of the * attribute value data. * @param content The content structure to fill. + * @param isNSURI a flag indicating whether the content is a Namespace URI * * @return Returns the next character on the input, if known. This * value may be -1 but this does <em>note</em> designate @@ -1094,7 +1175,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public int scanLiteral(int quote, XMLString content) + protected int scanLiteral(int quote, XMLString content, boolean isNSURI) throws IOException { if (DEBUG_BUFFER) { System.out.print("(scanLiteral, '"+(char)quote+"': "); @@ -1205,8 +1286,10 @@ } int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; - if (fCurrentEntity.isGE) { - checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length); + + checkEntityLimit(null, fCurrentEntity, offset, length); + if (isNSURI) { + checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length); } content.setValues(fCurrentEntity.ch, offset, length); @@ -1273,7 +1356,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public boolean scanData(String delimiter, XMLStringBuffer buffer) + protected boolean scanData(String delimiter, XMLStringBuffer buffer) throws IOException { boolean done = false; @@ -1311,6 +1394,7 @@ if (fCurrentEntity.position > fCurrentEntity.count - delimLen) { // something must be wrong with the input: e.g., file ends in an unterminated comment int length = fCurrentEntity.count - fCurrentEntity.position; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length); buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length); fCurrentEntity.columnNumber += fCurrentEntity.count; fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); @@ -1373,6 +1457,7 @@ } int length = fCurrentEntity.position - offset; if (fCurrentEntity.position == fCurrentEntity.count - 1) { + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); buffer.append(fCurrentEntity.ch, offset, length); if (DEBUG_BUFFER) { System.out.print("]newline, "+offset+", "+fCurrentEntity.position+": "); @@ -1416,12 +1501,14 @@ fCurrentEntity.position--; int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); buffer.append(fCurrentEntity.ch, offset, length); return true; } } int length = fCurrentEntity.position - offset; fCurrentEntity.columnNumber += length - newlines; + checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length); if (done) { length -= delimLen; } @@ -1445,13 +1532,14 @@ * the specified character. * * @param c The character to skip. + * @param nt The type of the name (element or attribute) * * @return Returns true if the character was skipped. * * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public boolean skipChar(int c) throws IOException { + protected boolean skipChar(int c, NameType nt) throws IOException { if (DEBUG_BUFFER) { System.out.print("(skipChar, '"+(char)c+"': "); print(); @@ -1464,6 +1552,7 @@ } // skip character + int offset = fCurrentEntity.position; int cc = fCurrentEntity.ch[fCurrentEntity.position]; if (cc == c) { fCurrentEntity.position++; @@ -1478,6 +1567,7 @@ print(); System.out.println(" -> true"); } + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); return true; } else if (c == '\n' && cc == '\r' && isExternal) { // handle newlines @@ -1497,6 +1587,7 @@ print(); System.out.println(" -> true"); } + checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset); return true; } @@ -1526,7 +1617,7 @@ * * @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace */ - public boolean skipSpaces() throws IOException { + protected boolean skipSpaces() throws IOException { if (DEBUG_BUFFER) { System.out.print("(skipSpaces: "); print(); @@ -1550,6 +1641,7 @@ // skip spaces int c = fCurrentEntity.ch[fCurrentEntity.position]; + int offset = fCurrentEntity.position - 1; if (XMLChar.isSpace(c)) { do { boolean entityChanged = false; @@ -1579,6 +1671,11 @@ } else { fCurrentEntity.columnNumber++; } + + //If this is a general entity, spaces within a start element should be counted + checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset); + offset = fCurrentEntity.position; + // load more characters, if needed if (!entityChanged){ fCurrentEntity.position++; @@ -1620,7 +1717,7 @@ /** - * @param legnth This function checks that following number of characters are available. + * @param length This function checks that following number of characters are available. * to the underlying buffer. * @return This function returns true if capacity asked is available. */ @@ -1629,9 +1726,9 @@ } /** - * @param legnth This function checks that following number of characters are available. + * @param length This function checks that following number of characters are available. * to the underlying buffer. - * @param if the underlying function should change the entity + * @param changeEntity a flag to indicate that the underlying function should change the entity * @return This function returns true if capacity asked is available. * */ @@ -1694,7 +1791,7 @@ * @throws IOException Thrown if i/o error occurs. * @throws EOFException Thrown on end of file. */ - public boolean skipString(String s) throws IOException { + protected boolean skipString(String s) throws IOException { final int length = s.length(); @@ -1714,6 +1811,9 @@ if(afterSkip-- == beforeSkip){ fCurrentEntity.position = fCurrentEntity.position + length ; fCurrentEntity.columnNumber += length; + if (!detectingVersion) { + checkEntityLimit(null, fCurrentEntity, beforeSkip, length); + } return true; } } @@ -1722,7 +1822,7 @@ return false; } // skipString(String):boolean - public boolean skipString(char [] s) throws IOException { + protected boolean skipString(char [] s) throws IOException { final int length = s.length; //first make sure that required capacity is avaible @@ -1741,6 +1841,9 @@ } fCurrentEntity.position = fCurrentEntity.position + length ; fCurrentEntity.columnNumber += length; + if (!detectingVersion) { + checkEntityLimit(null, fCurrentEntity, beforeSkip, length); + } return true; } @@ -2138,7 +2241,7 @@ * * @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace */ - public final boolean skipDeclSpaces() throws IOException { + protected final boolean skipDeclSpaces() throws IOException { if (DEBUG_BUFFER) { System.out.print("(skipDeclSpaces: "); //XMLEntityManager.print(fCurrentEntity);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLNSDocumentScannerImpl.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLNSDocumentScannerImpl.java Fri Jul 22 04:05:04 2016 +0000 @@ -189,9 +189,9 @@ // There are two variables,fNamespaces and fBindNamespaces //StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained if (fNamespaces) { - fEntityScanner.scanQName(fElementQName); + fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART); } else { - String name = fEntityScanner.scanName(); + String name = fEntityScanner.scanName(NameType.ELEMENTSTART); fElementQName.setValues(null, name, name, null); } @@ -404,11 +404,11 @@ if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()"); // name - fEntityScanner.scanQName(fAttributeQName); + fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE); // equals fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('=')) { + if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) { reportFatalError("EqRequiredInAttribute", new Object[]{fCurrentElement.rawname,fAttributeQName.rawname}); } @@ -430,23 +430,28 @@ //since scanAttributeValue doesn't use attIndex parameter therefore we //can safely add the attribute later.. XMLString tmpStr = getString(); - scanAttributeValue(tmpStr, fTempString2, - fAttributeQName.rawname, attributes, - attrIndex, isVC, fCurrentElement.rawname); + + /** + * Determine whether this is a namespace declaration that will be subject + * to the name limit check in the scanAttributeValue operation. + * Namespace declaration format: xmlns="..." or xmlns:prefix="..." + * Note that prefix:xmlns="..." isn't a namespace. + */ + String localpart = fAttributeQName.localpart; + String prefix = fAttributeQName.prefix != null + ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING; + boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS || + prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS); + + scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes, + attrIndex, isVC, fCurrentElement.rawname, isNSDecl); String value = null; //fTempString.toString(); // record namespace declarations if any. if (fBindNamespaces) { - - String localpart = fAttributeQName.localpart; - String prefix = fAttributeQName.prefix != null - ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING; - // when it's of form xmlns="..." or xmlns:prefix="...", - // it's a namespace declaration. but prefix:xmlns="..." isn't. - if (prefix == XMLSymbols.PREFIX_XMLNS || - prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) { + if (isNSDecl) { //check the length of URI if (tmpStr.length > fXMLNameLimit) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java Fri Jul 22 04:05:04 2016 +0000 @@ -114,6 +114,30 @@ /** Debug attribute normalization. */ protected static final boolean DEBUG_ATTR_NORMALIZATION = false; + /** + * Type of names + */ + public static enum NameType { + ATTRIBUTE("attribute"), + ATTRIBUTENAME("attribute name"), + COMMENT("comment"), + DOCTYPE("doctype"), + ELEMENTSTART("startelement"), + ELEMENTEND("endelement"), + ENTITY("entity"), + NOTATION("notation"), + PI("pi"), + REFERENCE("reference"); + + final String literal; + NameType(String literal) { + this.literal = literal; + } + + String literal() { + return literal; + } + } //xxx: setting the default value as false, as we dont need to calculate this value //we should have a feature when set to true computes this value @@ -144,7 +168,7 @@ protected boolean fNotifyCharRefs = false; /** Internal parser-settings feature */ - protected boolean fParserSettings = true; + protected boolean fParserSettings = true; // properties @@ -173,13 +197,13 @@ /** event type */ protected XMLEvent fEvent ; - /** Entity scanner, this alwasy works on last entity that was opened. */ + /** Entity scanner, this always works on last entity that was opened. */ protected XMLEntityScanner fEntityScanner = null; /** Entity depth. */ protected int fEntityDepth; - /** Literal value of the last character refence scanned. */ + /** Literal value of the last character reference scanned. */ protected String fCharRefLiteral = null; /** Scanning attribute. */ @@ -547,10 +571,10 @@ } // end - if (!fEntityScanner.skipChar('?')) { + if (!fEntityScanner.skipChar('?', null)) { reportFatalError("XMLDeclUnterminated", null); } - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', null)) { reportFatalError("XMLDeclUnterminated", null); } @@ -577,7 +601,7 @@ * <strong>Note:</strong> This method uses fStringBuffer2, anything in it * at the time of calling is lost. */ - public String scanPseudoAttribute(boolean scanningTextDecl, + protected String scanPseudoAttribute(boolean scanningTextDecl, XMLString value) throws IOException, XNIException { @@ -588,7 +612,7 @@ reportFatalError("PseudoAttrNameExpected", null); } fEntityScanner.skipSpaces(); - if (!fEntityScanner.skipChar('=')) { + if (!fEntityScanner.skipChar('=', null)) { reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl" : "EqRequiredInXMLDecl", new Object[]{name}); } @@ -598,15 +622,15 @@ reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl" : "QuoteRequiredInXMLDecl" , new Object[]{name}); } - fEntityScanner.scanChar(); - int c = fEntityScanner.scanLiteral(quote, value); + fEntityScanner.scanChar(NameType.ATTRIBUTE); + int c = fEntityScanner.scanLiteral(quote, value, false); if (c != quote) { fStringBuffer2.clear(); do { fStringBuffer2.append(value); if (c != -1) { if (c == '&' || c == '%' || c == '<' || c == ']') { - fStringBuffer2.append((char)fEntityScanner.scanChar()); + fStringBuffer2.append((char)fEntityScanner.scanChar(NameType.ATTRIBUTE)); } else if (XMLChar.isHighSurrogate(c)) { scanSurrogates(fStringBuffer2); } else if (isInvalidLiteral(c)) { @@ -614,15 +638,15 @@ ? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl"; reportFatalError(key, new Object[] {Integer.toString(c, 16)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } } - c = fEntityScanner.scanLiteral(quote, value); + c = fEntityScanner.scanLiteral(quote, value, false); } while (c != quote); fStringBuffer2.append(value); value.setValues(fStringBuffer2); } - if (!fEntityScanner.skipChar(quote)) { + if (!fEntityScanner.skipChar(quote, null)) { reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl" : "CloseQuoteMissingInXMLDecl", new Object[]{name}); @@ -680,7 +704,7 @@ // target fReportEntity = false; - String target = fEntityScanner.scanName(); + String target = fEntityScanner.scanName(NameType.PI); if (target == null) { reportFatalError("PITargetRequired", null); } @@ -745,7 +769,7 @@ } else if (isInvalidLiteral(c)) { reportFatalError("InvalidCharInPI", new Object[]{Integer.toHexString(c)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); } } } while (fEntityScanner.scanData("?>", data)); @@ -786,11 +810,11 @@ else if (isInvalidLiteral(c)) { reportFatalError("InvalidCharInComment", new Object[] { Integer.toHexString(c) }); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.COMMENT); } } } - if (!fEntityScanner.skipChar('>')) { + if (!fEntityScanner.skipChar('>', NameType.COMMENT)) { reportFatalError("DashDashInComment", null); } @@ -811,15 +835,14 @@ * @param checkEntities true if undeclared entities should be reported as VC violation, * false if undeclared entities should be reported as WFC violation. * @param eleName The name of element to which this attribute belongs. + * @param isNSURI a flag indicating whether the content is a Namespace URI * * <strong>Note:</strong> This method uses fStringBuffer2, anything in it * at the time of calling is lost. **/ - protected void scanAttributeValue(XMLString value, - XMLString nonNormalizedValue, - String atName, - XMLAttributes attributes, int attrIndex, - boolean checkEntities, String eleName) + protected void scanAttributeValue(XMLString value, XMLString nonNormalizedValue, + String atName, XMLAttributes attributes, int attrIndex, boolean checkEntities, + String eleName, boolean isNSURI) throws IOException, XNIException { XMLStringBuffer stringBuffer = null; // quote @@ -828,10 +851,10 @@ reportFatalError("OpenQuoteExpected", new Object[]{eleName, atName}); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.ATTRIBUTE); int entityDepth = fEntityDepth; - int c = fEntityScanner.scanLiteral(quote, value); + int c = fEntityScanner.scanLiteral(quote, value, isNSURI); if (DEBUG_ATTR_NORMALIZATION) { System.out.println("** scanLiteral -> \"" + value.toString() + "\""); @@ -857,11 +880,11 @@ + stringBuffer.toString() + "\""); } if (c == '&') { - fEntityScanner.skipChar('&'); + fEntityScanner.skipChar('&', NameType.REFERENCE); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) { fStringBuffer2.append('&'); } - if (fEntityScanner.skipChar('#')) { + if (fEntityScanner.skipChar('#', NameType.REFERENCE)) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) { fStringBuffer2.append('#'); } @@ -879,53 +902,20 @@ } } } else { - String entityName = fEntityScanner.scanName(); + String entityName = fEntityScanner.scanName(NameType.ENTITY); if (entityName == null) { reportFatalError("NameRequiredInReference", null); } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append(entityName); } - if (!fEntityScanner.skipChar(';')) { + if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInReference", new Object []{entityName}); } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append(';'); } - if (entityName == fAmpSymbol) { - stringBuffer.append('&'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** value5: \"" - + stringBuffer.toString() - + "\""); - } - } else if (entityName == fAposSymbol) { - stringBuffer.append('\''); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** value7: \"" - + stringBuffer.toString() - + "\""); - } - } else if (entityName == fLtSymbol) { - stringBuffer.append('<'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** value9: \"" - + stringBuffer.toString() - + "\""); - } - } else if (entityName == fGtSymbol) { - stringBuffer.append('>'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** valueB: \"" - + stringBuffer.toString() - + "\""); - } - } else if (entityName == fQuotSymbol) { - stringBuffer.append('"'); - if (DEBUG_ATTR_NORMALIZATION) { - System.out.println("** valueD: \"" - + stringBuffer.toString() - + "\""); - } + if (resolveCharacter(entityName, stringBuffer)) { + checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1); } else { if (fEntityStore.isExternalEntity(entityName)) { reportFatalError("ReferenceToExternalEntity", @@ -952,12 +942,12 @@ } else if (c == '<') { reportFatalError("LessthanInAttValue", new Object[] { eleName, atName }); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append((char)c); } } else if (c == '%' || c == ']') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); stringBuffer.append((char)c); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append((char)c); @@ -967,7 +957,7 @@ + stringBuffer.toString() + "\""); } } else if (c == '\n' || c == '\r') { - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); stringBuffer.append(' '); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append('\n'); @@ -988,12 +978,12 @@ } else if (c != -1 && isInvalidLiteral(c)) { reportFatalError("InvalidCharInAttValue", new Object[] {eleName, atName, Integer.toString(c, 16)}); - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append((char)c); } } - c = fEntityScanner.scanLiteral(quote, value); + c = fEntityScanner.scanLiteral(quote, value, isNSURI); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { fStringBuffer2.append(value); } @@ -1014,7 +1004,7 @@ nonNormalizedValue.setValues(fStringBuffer2); // quote - int cquote = fEntityScanner.scanChar(); + int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE); if (cquote != quote) { reportFatalError("CloseQuoteExpected", new Object[]{eleName, atName}); } @@ -1022,6 +1012,39 @@ /** + * Resolves character entity references. + * @param entityName the name of the entity + * @param stringBuffer the current XMLStringBuffer to append the character to. + * @return true if resolved, false otherwise + */ + protected boolean resolveCharacter(String entityName, XMLStringBuffer stringBuffer) { + /** + * entityNames (symbols) are interned. The equals method would do the same, + * but I'm leaving it as comparisons by references are common in the impl + * and it made it explicit to others who read this code. + */ + if (entityName == fAmpSymbol) { + stringBuffer.append('&'); + return true; + } else if (entityName == fAposSymbol) { + stringBuffer.append('\''); + return true; + } else if (entityName == fLtSymbol) { + stringBuffer.append('<'); + return true; + } else if (entityName == fGtSymbol) { + checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1); + stringBuffer.append('>'); + return true; + } else if (entityName == fQuotSymbol) { + checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1); + stringBuffer.append('"'); + return true; + } + return false; + } + + /** * Scans External ID and return the public and system IDs. * * @param identifiers An array of size 2 to return the system id, @@ -1064,25 +1087,25 @@ } reportFatalError("QuoteRequiredInSystemID", null); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); XMLString ident = fString; - if (fEntityScanner.scanLiteral(quote, ident) != quote) { + if (fEntityScanner.scanLiteral(quote, ident, false) != quote) { fStringBuffer.clear(); do { fStringBuffer.append(ident); int c = fEntityScanner.peekChar(); if (XMLChar.isMarkup(c) || c == ']') { - fStringBuffer.append((char)fEntityScanner.scanChar()); + fStringBuffer.append((char)fEntityScanner.scanChar(null)); } else if (c != -1 && isInvalidLiteral(c)) { reportFatalError("InvalidCharInSystemID", new Object[] {Integer.toString(c, 16)}); } - } while (fEntityScanner.scanLiteral(quote, ident) != quote); + } while (fEntityScanner.scanLiteral(quote, ident, false) != quote); fStringBuffer.append(ident); ident = fStringBuffer; } systemId = ident.toString(); - if (!fEntityScanner.skipChar(quote)) { + if (!fEntityScanner.skipChar(quote, null)) { reportFatalError("SystemIDUnterminated", null); } } @@ -1114,7 +1137,7 @@ */ protected boolean scanPubidLiteral(XMLString literal) throws IOException, XNIException { - int quote = fEntityScanner.scanChar(); + int quote = fEntityScanner.scanChar(null); if (quote != '\'' && quote != '"') { reportFatalError("QuoteRequiredInPublicID", null); return false; @@ -1125,7 +1148,7 @@ boolean skipSpace = true; boolean dataok = true; while (true) { - int c = fEntityScanner.scanChar(); + int c = fEntityScanner.scanChar(null); if (c == ' ' || c == '\n' || c == '\r') { if (!skipSpace) { // take the first whitespace as a space and skip the others @@ -1241,9 +1264,10 @@ */ protected int scanCharReferenceValue(XMLStringBuffer buf, XMLStringBuffer buf2) throws IOException, XNIException { + int initLen = buf.length; // scan hexadecimal value boolean hex = false; - if (fEntityScanner.skipChar('x')) { + if (fEntityScanner.skipChar('x', NameType.REFERENCE)) { if (buf2 != null) { buf2.append('x'); } hex = true; fStringBuffer3.clear(); @@ -1255,7 +1279,7 @@ (c >= 'A' && c <= 'F'); if (digit) { if (buf2 != null) { buf2.append((char)c); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.REFERENCE); fStringBuffer3.append((char)c); do { @@ -1265,7 +1289,7 @@ (c >= 'A' && c <= 'F'); if (digit) { if (buf2 != null) { buf2.append((char)c); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.REFERENCE); fStringBuffer3.append((char)c); } } while (digit); @@ -1283,7 +1307,7 @@ digit = c >= '0' && c <= '9'; if (digit) { if (buf2 != null) { buf2.append((char)c); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.REFERENCE); fStringBuffer3.append((char)c); do { @@ -1291,7 +1315,7 @@ digit = c >= '0' && c <= '9'; if (digit) { if (buf2 != null) { buf2.append((char)c); } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(NameType.REFERENCE); fStringBuffer3.append((char)c); } } while (digit); @@ -1301,7 +1325,7 @@ } // end - if (!fEntityScanner.skipChar(';')) { + if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { reportFatalError("SemicolonRequiredInCharRef", null); } if (buf2 != null) { buf2.append(';'); } @@ -1347,6 +1371,9 @@ } } + if (fEntityScanner.fCurrentEntity.isGE) { + checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, buf.length - initLen); + } return value; } // returns true if the given character is not @@ -1408,14 +1435,14 @@ protected boolean scanSurrogates(XMLStringBuffer buf) throws IOException, XNIException { - int high = fEntityScanner.scanChar(); + int high = fEntityScanner.scanChar(null); int low = fEntityScanner.peekChar(); if (!XMLChar.isLowSurrogate(low)) { reportFatalError("InvalidCharInContent", new Object[] {Integer.toString(high, 16)}); return false; } - fEntityScanner.scanChar(); + fEntityScanner.scanChar(null); // convert surrogates to supplemental character int c = XMLChar.supplemental((char)high, (char)low); @@ -1478,5 +1505,52 @@ } } + /** + * Add the count of the content buffer and check if the accumulated + * value exceeds the limit + * @param isPEDecl a flag to indicate whether the entity is parameter + * @param entityName entity name + * @param buffer content buffer + */ + void checkEntityLimit(boolean isPEDecl, String entityName, XMLString buffer) { + checkEntityLimit(isPEDecl, entityName, buffer.length); + } + /** + * Add the count and check limit + * @param isPEDecl a flag to indicate whether the entity is parameter + * @param entityName entity name + * @param len length of the buffer + */ + void checkEntityLimit(boolean isPEDecl, String entityName, int len) { + if (fLimitAnalyzer == null) { + fLimitAnalyzer = fEntityManager.fLimitAnalyzer; + } + if (isPEDecl) { + fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, "%" + entityName, len); + if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { + fSecurityManager.debugPrint(fLimitAnalyzer); + reportFatalError("MaxEntitySizeLimit", new Object[]{"%" + entityName, + fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), + fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), + fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)}); + } + } else { + fLimitAnalyzer.addValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName, len); + if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { + fSecurityManager.debugPrint(fLimitAnalyzer); + reportFatalError("MaxEntitySizeLimit", new Object[]{entityName, + fLimitAnalyzer.getValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT), + fSecurityManager.getLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT), + fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT)}); + } + } + if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) { + fSecurityManager.debugPrint(fLimitAnalyzer); + reportFatalError("TotalEntitySizeLimit", + new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), + fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), + fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)}); + } + } } // class XMLScanner
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLVersionDetector.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLVersionDetector.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,62 +1,21 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */ /* - * The Apache Software License, Version 1.1 - * - * - * Copyright (c) 1999-2003 The Apache Software Foundation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * 4. The names "Xerces" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation and was - * originally based on software copyright (c) 2003, International - * Business Machines, Inc., http://www.apache.org. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.sun.org.apache.xerces.internal.impl; @@ -192,40 +151,46 @@ // in the XML declaration. fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0); XMLEntityScanner scanner = fEntityManager.getEntityScanner(); + scanner.detectingVersion = true; try { if (!scanner.skipString("<?xml")) { // definitely not a well-formed 1.1 doc! + scanner.detectingVersion = false; return Constants.XML_VERSION_1_0; } if (!scanner.skipDeclSpaces()) { fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5); + scanner.detectingVersion = false; return Constants.XML_VERSION_1_0; } if (!scanner.skipString("version")) { fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6); + scanner.detectingVersion = false; return Constants.XML_VERSION_1_0; } scanner.skipDeclSpaces(); // Check if the next character is '='. If it is then consume it. if (scanner.peekChar() != '=') { fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13); + scanner.detectingVersion = false; return Constants.XML_VERSION_1_0; } - scanner.scanChar(); + scanner.scanChar(null); scanner.skipDeclSpaces(); - int quoteChar = scanner.scanChar(); + int quoteChar = scanner.scanChar(null); fExpectedVersionString[14] = (char) quoteChar; for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) { - fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar(); + fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar(null); } // REVISIT: should we check whether this equals quoteChar? - fExpectedVersionString[18] = (char) scanner.scanChar(); + fExpectedVersionString[18] = (char) scanner.scanChar(null); fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19); int matched = 0; for (; matched < XML11_VERSION.length; matched++) { if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched]) break; } + scanner.detectingVersion = false; if (matched == XML11_VERSION.length) return Constants.XML_VERSION_1_1; return Constants.XML_VERSION_1_0; @@ -237,10 +202,9 @@ "PrematureEOF", null, XMLErrorReporter.SEVERITY_FATAL_ERROR); + scanner.detectingVersion = false; return Constants.XML_VERSION_1_0; - } - } // This method prepends "length" chars from the char array,
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties Fri Jul 22 04:05:04 2016 +0000 @@ -298,7 +298,8 @@ EntityExpansionLimit=JAXP00010001: The parser has encountered more than \"{0}\" entity expansions in this document; this is the limit imposed by the JDK. ElementAttributeLimit=JAXP00010002: Element \"{0}\" has more than \"{1}\" attributes, \"{1}\" is the limit imposed by the JDK. MaxEntitySizeLimit=JAXP00010003: The length of entity \"{0}\" is \"{1}\" that exceeds the \"{2}\" limit set by \"{3}\". - TotalEntitySizeLimit=JAXP00010004: The accumulated size of entities is \"{1}\" that exceeded the \"{2}\" limit set by \"{3}\". + TotalEntitySizeLimit=JAXP00010004: The accumulated size of entities is \"{0}\" that exceeded the \"{1}\" limit set by \"{2}\". MaxXMLNameLimit=JAXP00010005: The length of entity \"{0}\" is \"{1}\" that exceeds the \"{2}\" limit set by \"{3}\". MaxElementDepthLimit=JAXP00010006: The element \"{0}\" has a depth of \"{1}\" that exceeds the limit \"{2}\" set by \"{3}\". + EntityReplacementLimit=JAXP00010007: The total number of nodes in entity references is \"{0}\" that is over the limit \"{1}\" set by \"{2}\".
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLLimitAnalyzer.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLLimitAnalyzer.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -129,13 +129,15 @@ if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || - index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() + index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() ) { totalValue[index] += value; return; } if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || index == Limit.MAX_NAME_LIMIT.ordinal()) { + values[index] = value; totalValue[index] = value; return; } @@ -175,10 +177,13 @@ * @return the value of the property */ public int getValue(Limit limit) { - return values[limit.ordinal()]; + return getValue(limit.ordinal()); } public int getValue(int index) { + if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) { + return totalValue[index]; + } return values[index]; } /** @@ -233,6 +238,11 @@ public void reset(Limit limit) { if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { totalValue[limit.ordinal()] = 0; + } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) { + names[limit.ordinal()] = null; + values[limit.ordinal()] = 0; + caches[limit.ordinal()] = null; + totalValue[limit.ordinal()] = 0; } }
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLSecurityManager.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLSecurityManager.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, 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 @@ -78,7 +78,9 @@ MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0), MAX_NAME_LIMIT("MaxXMLNameLimit", - Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000); + Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000), + ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", + Constants.JDK_ENTITY_REPLACEMENT_LIMIT, Constants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000); final String key; final String apiProperty; @@ -450,6 +452,7 @@ if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() || index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || index == Limit.MAX_NAME_LIMIT.ordinal() ) {
--- a/jaxp/test/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxp/test/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, 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 @@ -46,7 +46,7 @@ public class XSLTFunctionsTest { /** - * @bug 8062518 + * @bug 8062518 8153082 * Verifies that a reference to the DTM created by XSLT document function is * actually read from the DTM by an extension function. * @param xml Content of xml file to process
--- a/jaxws/.hgtags Thu Jul 21 19:29:33 2016 -0700 +++ b/jaxws/.hgtags Fri Jul 22 04:05:04 2016 +0000 @@ -373,3 +373,4 @@ 5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125 264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126 06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127 +fe4e11bd2423635dc0f5f5cb9a64eb2f2cce7f4c jdk-9+128
--- a/jdk/.hgtags Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/.hgtags Fri Jul 22 04:05:04 2016 +0000 @@ -370,3 +370,4 @@ 073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125 6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126 7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127 +9446c534f0222b4eecfd9d9e25ab37c4fd4400a5 jdk-9+128
--- a/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java Fri Jul 22 04:05:04 2016 +0000 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.lang; import java.io.File;
--- a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c Fri Jul 22 04:05:04 2016 +0000 @@ -177,8 +177,14 @@ OSVerStruct (*procInfoFn)(id rec, SEL sel) = (OSVerStruct(*)(id, SEL))objc_msgSend_stret; OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo], @selector(operatingSystemVersion)); - NSString *nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld", - (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion]; + NSString *nsVerStr; + if (osVer.patchVersion == 0) { // Omit trailing ".0" + nsVerStr = [NSString stringWithFormat:@"%ld.%ld", + (long)osVer.majorVersion, (long)osVer.minorVersion]; + } else { + nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld", + (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion]; + } // Copy out the char* osVersionCStr = strdup([nsVerStr UTF8String]); }
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, 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 @@ -172,6 +172,11 @@ */ private final int fixedKeySize; // in bytes, -1 if no restriction + /* + * needed to enforce ISE thrown when updateAAD is called after update for GCM mode. + */ + private boolean updateCalled; + /** * Creates an instance of AES cipher with default ECB mode and * PKCS5Padding. @@ -304,6 +309,7 @@ protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { checkKeySize(key, fixedKeySize); + updateCalled = false; core.init(opmode, key, random); } @@ -336,6 +342,7 @@ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { checkKeySize(key, fixedKeySize); + updateCalled = false; core.init(opmode, key, params, random); } @@ -344,6 +351,7 @@ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { checkKeySize(key, fixedKeySize); + updateCalled = false; core.init(opmode, key, params, random); } @@ -368,6 +376,7 @@ */ protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + updateCalled = true; return core.update(input, inputOffset, inputLen); } @@ -397,6 +406,7 @@ protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { + updateCalled = true; return core.update(input, inputOffset, inputLen, output, outputOffset); } @@ -433,7 +443,9 @@ */ protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { - return core.doFinal(input, inputOffset, inputLen); + byte[] out = core.doFinal(input, inputOffset, inputLen); + updateCalled = false; + return out; } /** @@ -476,8 +488,10 @@ byte[] output, int outputOffset) throws IllegalBlockSizeException, ShortBufferException, BadPaddingException { - return core.doFinal(input, inputOffset, inputLen, output, - outputOffset); + int outLen = core.doFinal(input, inputOffset, inputLen, output, + outputOffset); + updateCalled = false; + return outLen; } /** @@ -574,6 +588,9 @@ */ @Override protected void engineUpdateAAD(byte[] src, int offset, int len) { + if (core.getMode() == CipherCore.GCM_MODE && updateCalled) { + throw new IllegalStateException("AAD must be supplied before encryption/decryption starts"); + } core.updateAAD(src, offset, len); } @@ -606,6 +623,9 @@ */ @Override protected void engineUpdateAAD(ByteBuffer src) { + if (core.getMode() == CipherCore.GCM_MODE && updateCalled) { + throw new IllegalStateException("AAD must be supplied before encryption/decryption starts"); + } if (src != null) { int aadLen = src.limit() - src.position(); if (aadLen != 0) {
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, 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 @@ -124,7 +124,7 @@ private static final int PCBC_MODE = 4; private static final int CTR_MODE = 5; private static final int CTS_MODE = 6; - private static final int GCM_MODE = 7; + static final int GCM_MODE = 7; /* * variables used for performing the GCM (key+iv) uniqueness check. @@ -196,7 +196,7 @@ cipher = new CounterMode(rawImpl); unitBytes = 1; padding = null; - } else if (modeUpperCase.startsWith("GCM")) { + } else if (modeUpperCase.equals("GCM")) { // can only be used for block ciphers w/ 128-bit block size if (blockSize != 16) { throw new NoSuchAlgorithmException @@ -223,6 +223,15 @@ } } + /** + * Returns the mode of this cipher. + * + * @return the parsed cipher mode + */ + int getMode() { + return cipherMode; + } + private static int getNumOfUnit(String mode, int offset, int blockSize) throws NoSuchAlgorithmException { int result = blockSize; // use blockSize as default value
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, 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 @@ -49,6 +49,16 @@ static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE; static int DEFAULT_IV_LEN = 12; // in bytes + // In NIST SP 800-38D, GCM input size is limited to be no longer + // than (2^36 - 32) bytes. Otherwise, the counter will wrap + // around and lead to a leak of plaintext. + // However, given the current GCM spec requirement that recovered + // text can only be returned after successful tag verification, + // we are bound by limiting the data size to the size limit of + // java byte array, e.g. Integer.MAX_VALUE, since all data + // can only be returned by the doFinal(...) call. + private static final int MAX_BUF_SIZE = Integer.MAX_VALUE; + // buffer for AAD data; if null, meaning update has been called private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream(); private int sizeOfAAD = 0; @@ -89,9 +99,13 @@ } } - // ivLen in bits - private static byte[] getLengthBlock(int ivLen) { + private static byte[] getLengthBlock(int ivLenInBytes) { + long ivLen = ((long)ivLenInBytes) << 3; byte[] out = new byte[AES_BLOCK_SIZE]; + out[8] = (byte)(ivLen >>> 56); + out[9] = (byte)(ivLen >>> 48); + out[10] = (byte)(ivLen >>> 40); + out[11] = (byte)(ivLen >>> 32); out[12] = (byte)(ivLen >>> 24); out[13] = (byte)(ivLen >>> 16); out[14] = (byte)(ivLen >>> 8); @@ -99,13 +113,22 @@ return out; } - // aLen and cLen both in bits - private static byte[] getLengthBlock(int aLen, int cLen) { + private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) { + long aLen = ((long)aLenInBytes) << 3; + long cLen = ((long)cLenInBytes) << 3; byte[] out = new byte[AES_BLOCK_SIZE]; + out[0] = (byte)(aLen >>> 56); + out[1] = (byte)(aLen >>> 48); + out[2] = (byte)(aLen >>> 40); + out[3] = (byte)(aLen >>> 32); out[4] = (byte)(aLen >>> 24); out[5] = (byte)(aLen >>> 16); out[6] = (byte)(aLen >>> 8); out[7] = (byte)aLen; + out[8] = (byte)(cLen >>> 56); + out[9] = (byte)(cLen >>> 48); + out[10] = (byte)(cLen >>> 40); + out[11] = (byte)(cLen >>> 32); out[12] = (byte)(cLen >>> 24); out[13] = (byte)(cLen >>> 16); out[14] = (byte)(cLen >>> 8); @@ -142,13 +165,20 @@ } else { g.update(iv); } - byte[] lengthBlock = getLengthBlock(iv.length*8); + byte[] lengthBlock = getLengthBlock(iv.length); g.update(lengthBlock); j0 = g.digest(); } return j0; } + private static void checkDataLength(int processed, int len) { + if (processed > MAX_BUF_SIZE - len) { + throw new ProviderException("SunJCE provider only supports " + + "input size up to " + MAX_BUF_SIZE + " bytes"); + } + } + GaloisCounterMode(SymmetricCipher embeddedCipher) { super(embeddedCipher); aadBuffer = new ByteArrayOutputStream(); @@ -319,20 +349,22 @@ // Feed the AAD data to GHASH, pad if necessary void processAAD() { - if (aadBuffer != null && aadBuffer.size() > 0) { - byte[] aad = aadBuffer.toByteArray(); - sizeOfAAD = aad.length; - aadBuffer = null; + if (aadBuffer != null) { + if (aadBuffer.size() > 0) { + byte[] aad = aadBuffer.toByteArray(); + sizeOfAAD = aad.length; - int lastLen = aad.length % AES_BLOCK_SIZE; - if (lastLen != 0) { - ghashAllToS.update(aad, 0, aad.length - lastLen); - byte[] padded = expandToOneBlock(aad, aad.length - lastLen, - lastLen); - ghashAllToS.update(padded); - } else { - ghashAllToS.update(aad); + int lastLen = aad.length % AES_BLOCK_SIZE; + if (lastLen != 0) { + ghashAllToS.update(aad, 0, aad.length - lastLen); + byte[] padded = expandToOneBlock(aad, aad.length - lastLen, + lastLen); + ghashAllToS.update(padded); + } else { + ghashAllToS.update(aad); + } } + aadBuffer = null; } } @@ -384,6 +416,9 @@ if ((len % blockSize) != 0) { throw new ProviderException("Internal error in input buffering"); } + + checkDataLength(processed, len); + processAAD(); if (len > 0) { gctrPAndC.update(in, inOfs, len, out, outOfs); @@ -405,17 +440,23 @@ */ int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs) throws IllegalBlockSizeException, ShortBufferException { + if (len > MAX_BUF_SIZE - tagLenBytes) { + throw new ShortBufferException + ("Can't fit both data and tag into one buffer"); + } if (out.length - outOfs < (len + tagLenBytes)) { throw new ShortBufferException("Output buffer too small"); } + checkDataLength(processed, len); + processAAD(); if (len > 0) { doLastBlock(in, inOfs, len, out, outOfs, true); } byte[] lengthBlock = - getLengthBlock(sizeOfAAD*8, processed*8); + getLengthBlock(sizeOfAAD, processed); ghashAllToS.update(lengthBlock); byte[] s = ghashAllToS.digest(); byte[] sOut = new byte[s.length]; @@ -447,6 +488,9 @@ if ((len % blockSize) != 0) { throw new ProviderException("Internal error in input buffering"); } + + checkDataLength(ibuffer.size(), len); + processAAD(); if (len > 0) { @@ -481,10 +525,21 @@ if (len < tagLenBytes) { throw new AEADBadTagException("Input too short - need tag"); } + // do this check here can also catch the potential integer overflow + // scenario for the subsequent output buffer capacity check. + checkDataLength(ibuffer.size(), (len - tagLenBytes)); + if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) { throw new ShortBufferException("Output buffer too small"); } + processAAD(); + + // get the trailing tag bytes from 'in' + byte[] tag = new byte[tagLenBytes]; + System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes); + len -= tagLenBytes; + if (len != 0) { ibuffer.write(in, inOfs, len); } @@ -495,17 +550,12 @@ len = in.length; ibuffer.reset(); - byte[] tag = new byte[tagLenBytes]; - // get the trailing tag bytes from 'in' - System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes); - len -= tagLenBytes; - if (len > 0) { doLastBlock(in, inOfs, len, out, outOfs, false); } byte[] lengthBlock = - getLengthBlock(sizeOfAAD*8, processed*8); + getLengthBlock(sizeOfAAD, processed); ghashAllToS.update(lengthBlock); byte[] s = ghashAllToS.digest();
--- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/classes/java/lang/Class.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/Class.java Fri Jul 22 04:05:04 2016 +0000 @@ -238,15 +238,11 @@ TypeVariable<?>[] typeparms = component.getTypeParameters(); if (typeparms.length > 0) { - boolean first = true; - sb.append('<'); + StringJoiner sj = new StringJoiner(",", "<", ">"); for(TypeVariable<?> typeparm: typeparms) { - if (!first) - sb.append(','); - sb.append(typeparm.getTypeName()); - first = false; + sj.add(typeparm.getTypeName()); } - sb.append('>'); + sb.append(sj.toString()); } for (int i = 0; i < arrayDepth; i++)
--- a/jdk/src/java.base/share/classes/java/lang/Runtime.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java Fri Jul 22 04:05:04 2016 +0000 @@ -945,7 +945,7 @@ } /** - * A representation of a version string for an implemenation of the + * A representation of a version string for an implementation of the * Java SE Platform. A version string contains a version number * optionally followed by pre-release and build information. * @@ -1058,10 +1058,10 @@ * <p> When comparing two version strings, the value of {@code $OPT}, if * present, may or may not be significant depending on the chosen * comparison method. The comparison methods {@link #compareTo(Version) - * compareTo()} and {@link #compareToIgnoreOpt(Version) - * compareToIgnoreOpt()} should be used consistently with the + * compareTo()} and {@link #compareToIgnoreOptional(Version) + * compareToIgnoreOptional()} should be used consistently with the * corresponding methods {@link #equals(Object) equals()} and {@link - * #equalsIgnoreOpt(Object) equalsIgnoreOpt()}. </p> + * #equalsIgnoreOptional(Object) equalsIgnoreOptional()}. </p> * * <p> A <em>short version string</em>, {@code $SVSTR}, often useful in * less formal contexts, is a version number optionally followed by a @@ -1249,7 +1249,7 @@ * @throws NullPointerException * If the given object is {@code null} */ - public int compareToIgnoreOpt(Version ob) { + public int compareToIgnoreOptional(Version ob) { return compare(ob, true); } @@ -1270,7 +1270,7 @@ return ret; if (!ignoreOpt) - return compareOpt(ob); + return compareOptional(ob); return 0; } @@ -1325,7 +1325,7 @@ return 0; } - private int compareOpt(Version ob) { + private int compareOptional(Version ob) { Optional<String> oOpt = ob.optional(); if (!optional.isPresent()) { if (oOpt.isPresent()) @@ -1384,7 +1384,7 @@ */ @Override public boolean equals(Object ob) { - boolean ret = equalsIgnoreOpt(ob); + boolean ret = equalsIgnoreOptional(ob); if (!ret) return false; @@ -1407,7 +1407,7 @@ * ignoring the optinal build information * */ - public boolean equalsIgnoreOpt(Object ob) { + public boolean equalsIgnoreOptional(Object ob) { if (this == ob) return true; if (!(ob instanceof Version))
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Fri Jul 22 04:05:04 2016 +0000 @@ -155,7 +155,7 @@ private static LambdaForm preparedLambdaForm(MemberName m) { assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead MethodType mtype = m.getInvocationType().basicType(); - assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m; + assert(!m.isMethodHandleInvoke()) : m; int which; switch (m.getReferenceKind()) { case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Fri Jul 22 04:05:04 2016 +0000 @@ -1049,7 +1049,7 @@ this.member = member; this.resolvedHandle = resolvedHandle; // The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest. - //assert(!isInvokeBasic()); + //assert(!isInvokeBasic(member)); } NamedFunction(MethodType basicInvokerType) { assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType; @@ -1060,13 +1060,13 @@ // necessary to pass BigArityTest this.member = Invokers.invokeBasicMethod(basicInvokerType); } - assert(isInvokeBasic()); + assert(isInvokeBasic(member)); } - private boolean isInvokeBasic() { + private static boolean isInvokeBasic(MemberName member) { return member != null && - member.isMethodHandleInvoke() && - "invokeBasic".equals(member.getName()); + member.getDeclaringClass() == MethodHandle.class && + "invokeBasic".equals(member.getName()); } // The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc. @@ -1204,7 +1204,7 @@ assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype)) : Arrays.asList(mh, rtype, arity); MemberName member = mh.internalMemberName(); - if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) { + if (isInvokeBasic(member)) { assert(arity > 0); assert(a[0] instanceof MethodHandle); MethodHandle mh2 = (MethodHandle) a[0];
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Fri Jul 22 04:05:04 2016 +0000 @@ -346,7 +346,6 @@ } /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). - * Also returns true for the non-public MH.invokeBasic. */ public boolean isMethodHandleInvoke() { final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC; @@ -361,7 +360,6 @@ switch (name) { case "invoke": case "invokeExact": - case "invokeBasic": // internal sig-poly method return true; default: return false;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Fri Jul 22 04:05:04 2016 +0000 @@ -951,8 +951,6 @@ return invoker(type); if ("invokeExact".equals(name)) return exactInvoker(type); - if ("invokeBasic".equals(name)) - return basicInvoker(type); assert(!MemberName.isMethodHandleInvokeName(name)); return null; } @@ -3268,6 +3266,16 @@ */ public static MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) { + return dropArguments0(target, pos, copyTypes(valueTypes)); + } + + private static List<Class<?>> copyTypes(List<Class<?>> types) { + Object[] a = types.toArray(); + return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class)); + } + + private static + MethodHandle dropArguments0(MethodHandle target, int pos, List<Class<?>> valueTypes) { MethodType oldType = target.type(); // get NPE int dropped = dropArgumentChecks(oldType, pos, valueTypes); MethodType newType = oldType.insertParameterTypes(pos, valueTypes); @@ -3348,6 +3356,7 @@ // private version which allows caller some freedom with error handling private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos, boolean nullOnFailure) { + newTypes = copyTypes(newTypes); List<Class<?>> oldTypes = target.type().parameterList(); int match = oldTypes.size(); if (skip != 0) { @@ -3379,11 +3388,11 @@ // target: ( S*[skip], M*[match] ) MethodHandle adapter = target; if (add > 0) { - adapter = dropArguments(adapter, skip+ match, addTypes); + adapter = dropArguments0(adapter, skip+ match, addTypes); } // adapter: (S*[skip], M*[match], A*[add] ) if (pos > 0) { - adapter = dropArguments(adapter, skip, newTypes.subList(0, pos)); + adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos)); } // adapter: (S*[skip], P*[pos], M*[match], A*[add] ) return adapter; @@ -3787,7 +3796,7 @@ int filterValues = filterType.parameterCount(); if (filterValues == 0 ? (rtype != void.class) - : (rtype != filterType.parameterType(0))) + : (rtype != filterType.parameterType(0) || filterValues != 1)) throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); } @@ -4290,7 +4299,7 @@ step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i)); } if (pred.get(i) == null) { - pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence)); + pred.set(i, dropArguments0(constant(boolean.class, true), 0, commonParameterSequence)); } if (fini.get(i) == null) { fini.set(i, empty(methodType(t, commonParameterSequence))); @@ -4315,7 +4324,7 @@ return hs.stream().map(h -> { int pc = h.type().parameterCount(); int tpsize = targetParams.size(); - return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h; + return pc < tpsize ? dropArguments0(h, pc, targetParams.subList(pc, tpsize)) : h; }).collect(Collectors.toList()); }
--- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Fri Jul 22 04:05:04 2016 +0000 @@ -52,6 +52,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -420,7 +421,7 @@ // scan the entries in the JAR file to locate the .class and service // configuration file Map<Boolean, Set<String>> map = - jf.stream() + versionedStream(jf) .map(JarEntry::getName) .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX))) .collect(Collectors.partitioningBy(s -> s.endsWith(".class"), @@ -503,8 +504,21 @@ return mn; } + private Stream<JarEntry> versionedStream(JarFile jf) { + if (jf.isMultiRelease()) { + // a stream of JarEntries whose names are base names and whose + // contents are from the corresponding versioned entries in + // a multi-release jar file + return jf.stream().map(JarEntry::getName) + .filter(name -> !name.startsWith("META-INF/versions/")) + .map(jf::getJarEntry); + } else { + return jf.stream(); + } + } + private Set<String> jarPackages(JarFile jf) { - return jf.stream() + return versionedStream(jf) .filter(e -> e.getName().endsWith(".class")) .map(e -> toPackageName(e.getName())) .filter(pkg -> pkg.length() > 0) // module-info
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -28,6 +28,7 @@ import java.lang.annotation.*; import java.util.Map; import java.util.Objects; +import java.util.StringJoiner; import jdk.internal.misc.SharedSecrets; import sun.reflect.annotation.AnnotationParser; @@ -86,15 +87,6 @@ getDeclaringClass()); } - void separateWithCommas(Class<?>[] types, StringBuilder sb) { - for (int j = 0; j < types.length; j++) { - sb.append(types[j].getTypeName()); - if (j < (types.length - 1)) - sb.append(","); - } - - } - void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) { int mod = getModifiers() & mask; @@ -121,13 +113,20 @@ printModifiersIfNonzero(sb, modifierMask, isDefault); specificToStringHeader(sb); - sb.append('('); - separateWithCommas(parameterTypes, sb); + StringJoiner sj = new StringJoiner(","); + for (Class<?> parameterType : parameterTypes) { + sj.add(parameterType.getTypeName()); + } + sb.append(sj.toString()); sb.append(')'); + if (exceptionTypes.length > 0) { - sb.append(" throws "); - separateWithCommas(exceptionTypes, sb); + StringJoiner joiner = new StringJoiner(",", "throws ", ""); + for (Class<?> exceptionType : exceptionTypes) { + joiner.add(exceptionType.getTypeName()); + } + sb.append(joiner.toString()); } return sb.toString(); } catch (Exception e) { @@ -149,42 +148,34 @@ TypeVariable<?>[] typeparms = getTypeParameters(); if (typeparms.length > 0) { - boolean first = true; - sb.append('<'); + StringJoiner sj = new StringJoiner(",", "<", "> "); for(TypeVariable<?> typeparm: typeparms) { - if (!first) - sb.append(','); - // Class objects can't occur here; no need to test - // and call Class.getName(). - sb.append(typeparm.toString()); - first = false; + sj.add(typeparm.getTypeName()); } - sb.append("> "); + sb.append(sj.toString()); } specificToGenericStringHeader(sb); sb.append('('); + StringJoiner sj = new StringJoiner(","); Type[] params = getGenericParameterTypes(); for (int j = 0; j < params.length; j++) { String param = params[j].getTypeName(); if (isVarArgs() && (j == params.length - 1)) // replace T[] with T... param = param.replaceFirst("\\[\\]$", "..."); - sb.append(param); - if (j < (params.length - 1)) - sb.append(','); + sj.add(param); } + sb.append(sj.toString()); sb.append(')'); - Type[] exceptions = getGenericExceptionTypes(); - if (exceptions.length > 0) { - sb.append(" throws "); - for (int k = 0; k < exceptions.length; k++) { - sb.append((exceptions[k] instanceof Class)? - ((Class)exceptions[k]).getName(): - exceptions[k].toString()); - if (k < (exceptions.length - 1)) - sb.append(','); + + Type[] exceptionTypes = getGenericExceptionTypes(); + if (exceptionTypes.length > 0) { + StringJoiner joiner = new StringJoiner(",", " throws ", ""); + for (Type exceptionType : exceptionTypes) { + joiner.add(exceptionType.getTypeName()); } + sb.append(joiner.toString()); } return sb.toString(); } catch (Exception e) {
--- a/jdk/src/java.base/share/classes/java/net/URLPermission.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/URLPermission.java Fri Jul 22 04:05:04 2016 +0000 @@ -461,11 +461,10 @@ } private String actions() { - String b = String.join(",", methods); - if (!requestHeaders.isEmpty()) { - b += ":" + String.join(",", requestHeaders); - } - return b; + // The colon separator is optional when the request headers list is + // empty.This implementation chooses to include it even when the request + // headers list is empty. + return String.join(",", methods) + ":" + String.join(",", requestHeaders); } /**
--- a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java Fri Jul 22 04:05:04 2016 +0000 @@ -132,7 +132,7 @@ /* the PermissionCollection is static (pre 1.4 constructor) or dynamic (via a policy refresh) */ - private boolean staticPermissions; + private final boolean staticPermissions; /* * An object used as a key when the ProtectionDomain is stored in a Map. @@ -143,8 +143,12 @@ * Creates a new ProtectionDomain with the given CodeSource and * Permissions. If the permissions object is not null, then * {@code setReadOnly()} will be called on the passed in - * Permissions object. The only permissions granted to this domain - * are the ones specified; the current Policy will not be consulted. + * Permissions object. + * <p> + * The permissions granted to this domain are static, i.e. + * invoking the {@link #staticPermissionsOnly()} method returns true. + * They contain only the ones passed to this constructor and + * the current Policy will not be consulted. * * @param codesource the codesource associated with this domain * @param permissions the permissions granted to this domain @@ -170,9 +174,11 @@ * Permissions, ClassLoader and array of Principals. If the * permissions object is not null, then {@code setReadOnly()} * will be called on the passed in Permissions object. - * The permissions granted to this domain are dynamic; they include - * both the static permissions passed to this constructor, and any - * permissions granted to this domain by the current Policy at the + * <p> + * The permissions granted to this domain are dynamic, i.e. + * invoking the {@link #staticPermissionsOnly()} method returns false. + * They include both the static permissions passed to this constructor, + * and any permissions granted to this domain by the current Policy at the * time a permission is checked. * <p> * This constructor is typically used by @@ -256,6 +262,19 @@ } /** + * Returns true if this domain contains only static permissions + * and does not check the current {@code Policy} at the time of + * permission checking. + * + * @return true if this domain contains only static permissions. + * + * @since 9 + */ + public final boolean staticPermissionsOnly() { + return this.staticPermissions; + } + + /** * Check and see if this ProtectionDomain implies the permissions * expressed in the Permission object. * <p> @@ -263,25 +282,19 @@ * ProtectionDomain was constructed with a static set of permissions * or it was bound to a dynamically mapped set of permissions. * <p> - * If the ProtectionDomain was constructed to a - * {@link #ProtectionDomain(CodeSource, PermissionCollection) - * statically bound} PermissionCollection then the permission will - * only be checked against the PermissionCollection supplied at - * construction. + * If the {@link #staticPermissionsOnly()} method returns + * true, then the permission will only be checked against the + * PermissionCollection supplied at construction. * <p> - * However, if the ProtectionDomain was constructed with - * the constructor variant which supports - * {@link #ProtectionDomain(CodeSource, PermissionCollection, - * ClassLoader, java.security.Principal[]) dynamically binding} - * permissions, then the permission will be checked against the - * combination of the PermissionCollection supplied at construction and + * Otherwise, the permission will be checked against the combination + * of the PermissionCollection supplied at construction and * the current Policy binding. * - * @param permission the Permission object to check. + * @param perm the Permission object to check. * - * @return true if "permission" is implicit to this ProtectionDomain. + * @return true if {@code perm} is implied by this ProtectionDomain. */ - public boolean implies(Permission permission) { + public boolean implies(Permission perm) { if (hasAllPerm) { // internal permission collection already has AllPermission - @@ -290,10 +303,10 @@ } if (!staticPermissions && - Policy.getPolicyNoCheck().implies(this, permission)) + Policy.getPolicyNoCheck().implies(this, perm)) return true; if (permissions != null) - return permissions.implies(permission); + return permissions.implies(perm); return false; }
--- a/jdk/src/java.base/share/classes/java/security/Provider.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/security/Provider.java Fri Jul 22 04:05:04 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved + * Copyright (c) 1996, 2016, 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
--- a/jdk/src/java.base/share/classes/java/util/Queue.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Queue.java Fri Jul 22 04:05:04 2016 +0000 @@ -124,7 +124,6 @@ * always well-defined for queues with the same elements but different * ordering properties. * - * * <p>This interface is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>.
--- a/jdk/src/java.base/share/classes/java/util/ResourceBundle.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/ResourceBundle.java Fri Jul 22 04:05:04 2016 +0000 @@ -660,6 +660,7 @@ // ResourceBundleProviders for loading ResourceBundles private ServiceLoader<ResourceBundleProvider> providers; + private boolean providersChecked; // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier. private Boolean callerHasProvider; @@ -675,7 +676,6 @@ this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this); } this.moduleRef = new KeyElementReference<>(module, referenceQueue, this); - this.providers = getServiceLoader(module, baseName); calculateHashCode(); } @@ -712,11 +712,15 @@ } ServiceLoader<ResourceBundleProvider> getProviders() { + if (!providersChecked) { + providers = getServiceLoader(getModule(), name); + providersChecked = true; + } return providers; } boolean hasProviders() { - return providers != null; + return getProviders() != null; } boolean callerHasProvider() { @@ -789,8 +793,9 @@ } clone.moduleRef = new KeyElementReference<>(getModule(), referenceQueue, clone); - // Clear the reference to ResourceBundleProviders + // Clear the reference to ResourceBundleProviders and the flag clone.providers = null; + clone.providersChecked = false; // Clear the reference to a Throwable clone.cause = null; // Clear callerHasProvider @@ -1841,6 +1846,9 @@ private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module, String baseName) { + if (!module.isNamed()) { + return null; + } PrivilegedAction<ClassLoader> pa = module::getClassLoader; ClassLoader loader = AccessController.doPrivileged(pa); return getServiceLoader(module, loader, baseName);
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Fri Jul 22 04:05:04 2016 +0000 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -149,26 +151,29 @@ * applies across normal vs exceptional outcomes, sync vs async * actions, binary triggers, and various forms of completions. * - * Non-nullness of field result (set via CAS) indicates done. An - * AltResult is used to box null as a result, as well as to hold - * exceptions. Using a single field makes completion simple to - * detect and trigger. Encoding and decoding is straightforward - * but adds to the sprawl of trapping and associating exceptions - * with targets. Minor simplifications rely on (static) NIL (to - * box null results) being the only AltResult with a null - * exception field, so we don't usually need explicit comparisons. - * Even though some of the generics casts are unchecked (see - * SuppressWarnings annotations), they are placed to be - * appropriate even if checked. + * Non-nullness of volatile field "result" indicates done. It may + * be set directly if known to be thread-confined, else via CAS. + * An AltResult is used to box null as a result, as well as to + * hold exceptions. Using a single field makes completion simple + * to detect and trigger. Result encoding and decoding is + * straightforward but tedious and adds to the sprawl of trapping + * and associating exceptions with targets. Minor simplifications + * rely on (static) NIL (to box null results) being the only + * AltResult with a null exception field, so we don't usually need + * explicit comparisons. Even though some of the generics casts + * are unchecked (see SuppressWarnings annotations), they are + * placed to be appropriate even if checked. * * Dependent actions are represented by Completion objects linked * as Treiber stacks headed by field "stack". There are Completion - * classes for each kind of action, grouped into single-input - * (UniCompletion), two-input (BiCompletion), projected - * (BiCompletions using either (not both) of two inputs), shared - * (CoCompletion, used by the second of two sources), zero-input - * source actions, and Signallers that unblock waiters. Class - * Completion extends ForkJoinTask to enable async execution + * classes for each kind of action, grouped into: + * - single-input (UniCompletion), + * - two-input (BiCompletion), + * - projected (BiCompletions using exactly one of two inputs), + * - shared (CoCompletion, used by the second of two sources), + * - zero-input source actions, + * - Signallers that unblock waiters. + * Class Completion extends ForkJoinTask to enable async execution * (adding no space overhead because we exploit its "tag" methods * to maintain claims). It is also declared as Runnable to allow * usage with arbitrary executors. @@ -184,7 +189,7 @@ * encounter layers of adapters in common usages. * * * Boolean CompletableFuture method x(...) (for example - * uniApply) takes all of the arguments needed to check that an + * biApply) takes all of the arguments needed to check that an * action is triggerable, and then either runs the action or * arranges its async execution by executing its Completion * argument, if present. The method returns true if known to be @@ -194,24 +199,32 @@ * method with its held arguments, and on success cleans up. * The mode argument allows tryFire to be called twice (SYNC, * then ASYNC); the first to screen and trap exceptions while - * arranging to execute, and the second when called from a - * task. (A few classes are not used async so take slightly - * different forms.) The claim() callback suppresses function - * invocation if already claimed by another thread. + * arranging to execute, and the second when called from a task. + * (A few classes are not used async so take slightly different + * forms.) The claim() callback suppresses function invocation + * if already claimed by another thread. + * + * * Some classes (for example UniApply) have separate handling + * code for when known to be thread-confined ("now" methods) and + * for when shared (in tryFire), for efficiency. * * * CompletableFuture method xStage(...) is called from a public - * stage method of CompletableFuture x. It screens user + * stage method of CompletableFuture f. It screens user * arguments and invokes and/or creates the stage object. If - * not async and x is already complete, the action is run - * immediately. Otherwise a Completion c is created, pushed to - * x's stack (unless done), and started or triggered via - * c.tryFire. This also covers races possible if x completes - * while pushing. Classes with two inputs (for example BiApply) - * deal with races across both while pushing actions. The - * second completion is a CoCompletion pointing to the first, - * shared so that at most one performs the action. The - * multiple-arity methods allOf and anyOf do this pairwise to - * form trees of completions. + * not async and already triggerable, the action is run + * immediately. Otherwise a Completion c is created, and + * submitted to the executor if triggerable, or pushed onto f's + * stack if not. Completion actions are started via c.tryFire. + * We recheck after pushing to a source future's stack to cover + * possible races if the source completes while pushing. + * Classes with two inputs (for example BiApply) deal with races + * across both while pushing actions. The second completion is + * a CoCompletion pointing to the first, shared so that at most + * one performs the action. The multiple-arity methods allOf + * does this pairwise to form trees of completions. Method + * anyOf is handled differently from allOf because completion of + * any source should trigger a cleanStack of other sources. + * Each AnyOf completion can reach others via a shared array. * * Note that the generic type parameters of methods vary according * to whether "this" is a source, dependent, or completion. @@ -236,29 +249,30 @@ * pointing back to its sources. So we null out fields as soon as * possible. The screening checks needed anyway harmlessly ignore * null arguments that may have been obtained during races with - * threads nulling out fields. We also try to unlink fired - * Completions from stacks that might never be popped (see method - * postFire). Completion fields need not be declared as final or - * volatile because they are only visible to other threads upon - * safe publication. + * threads nulling out fields. We also try to unlink non-isLive + * (fired or cancelled) Completions from stacks that might + * otherwise never be popped: Method cleanStack always unlinks non + * isLive completions from the head of stack; others may + * occasionally remain if racing with other cancellations or + * removals. + * + * Completion fields need not be declared as final or volatile + * because they are only visible to other threads upon safe + * publication. */ volatile Object result; // Either the result or boxed AltResult volatile Completion stack; // Top of Treiber stack of dependent actions final boolean internalComplete(Object r) { // CAS from null to r - return U.compareAndSwapObject(this, RESULT, null, r); - } - - final boolean casStack(Completion cmp, Completion val) { - return U.compareAndSwapObject(this, STACK, cmp, val); + return RESULT.compareAndSet(this, null, r); } /** Returns true if successfully pushed c onto stack. */ final boolean tryPushStack(Completion c) { Completion h = stack; - lazySetNext(c, h); - return U.compareAndSwapObject(this, STACK, h, c); + NEXT.set(c, h); // CAS piggyback + return STACK.compareAndSet(this, h, c); } /** Unconditionally pushes c onto stack, retrying if necessary. */ @@ -278,8 +292,7 @@ /** Completes with the null value, unless already completed. */ final boolean completeNull() { - return U.compareAndSwapObject(this, RESULT, null, - NIL); + return RESULT.compareAndSet(this, null, NIL); } /** Returns the encoding of the given non-exceptional value. */ @@ -289,8 +302,7 @@ /** Completes with a non-exceptional result, unless already completed. */ final boolean completeValue(T t) { - return U.compareAndSwapObject(this, RESULT, null, - (t == null) ? NIL : t); + return RESULT.compareAndSet(this, null, (t == null) ? NIL : t); } /** @@ -304,8 +316,7 @@ /** Completes with an exceptional result, unless already completed. */ final boolean completeThrowable(Throwable x) { - return U.compareAndSwapObject(this, RESULT, null, - encodeThrowable(x)); + return RESULT.compareAndSet(this, null, encodeThrowable(x)); } /** @@ -332,8 +343,7 @@ * existing CompletionException. */ final boolean completeThrowable(Throwable x, Object r) { - return U.compareAndSwapObject(this, RESULT, null, - encodeThrowable(x, r)); + return RESULT.compareAndSet(this, null, encodeThrowable(x, r)); } /** @@ -351,10 +361,11 @@ */ static Object encodeRelay(Object r) { Throwable x; - return (((r instanceof AltResult) && - (x = ((AltResult)r).ex) != null && - !(x instanceof CompletionException)) ? - new AltResult(new CompletionException(x)) : r); + if (r instanceof AltResult + && (x = ((AltResult)r).ex) != null + && !(x instanceof CompletionException)) + r = new AltResult(new CompletionException(x)); + return r; } /** @@ -362,14 +373,13 @@ * If exceptional, r is first coerced to a CompletionException. */ final boolean completeRelay(Object r) { - return U.compareAndSwapObject(this, RESULT, null, - encodeRelay(r)); + return RESULT.compareAndSet(this, null, encodeRelay(r)); } /** * Reports result using Future.get conventions. */ - private static <T> T reportGet(Object r) + private static Object reportGet(Object r) throws InterruptedException, ExecutionException { if (r == null) // by convention below, null means interrupted throw new InterruptedException(); @@ -384,14 +394,13 @@ x = cause; throw new ExecutionException(x); } - @SuppressWarnings("unchecked") T t = (T) r; - return t; + return r; } /** * Decodes outcome to return result or throw unchecked exception. */ - private static <T> T reportJoin(Object r) { + private static Object reportJoin(Object r) { if (r instanceof AltResult) { Throwable x; if ((x = ((AltResult)r).ex) == null) @@ -402,8 +411,7 @@ throw (CompletionException)x; throw new CompletionException(x); } - @SuppressWarnings("unchecked") T t = (T) r; - return t; + return r; } /* ------------- Async task preliminaries -------------- */ @@ -449,12 +457,6 @@ static final int ASYNC = 1; static final int NESTED = -1; - /** - * Spins before blocking in waitingGet - */ - static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ? - 1 << 8 : 0); - /* ------------- Base Completion classes and operations -------------- */ @SuppressWarnings("serial") @@ -479,10 +481,6 @@ public final void setRawResult(Void v) {} } - static void lazySetNext(Completion c, Completion next) { - U.putObjectRelease(c, NEXT, next); - } - /** * Pops and tries to trigger all reachable dependents. Call only * when known to be done. @@ -497,40 +495,47 @@ while ((h = f.stack) != null || (f != this && (h = (f = this).stack) != null)) { CompletableFuture<?> d; Completion t; - if (f.casStack(h, t = h.next)) { + if (STACK.compareAndSet(f, h, t = h.next)) { if (t != null) { if (f != this) { pushStack(h); continue; } - h.next = null; // detach + NEXT.compareAndSet(h, t, null); // try to detach } f = (d = h.tryFire(NESTED)) == null ? this : d; } } } - /** Traverses stack and unlinks dead Completions. */ + /** Traverses stack and unlinks one or more dead Completions, if found. */ final void cleanStack() { - for (Completion p = null, q = stack; q != null;) { + Completion p = stack; + // ensure head of stack live + for (boolean unlinked = false;;) { + if (p == null) + return; + else if (p.isLive()) { + if (unlinked) + return; + else + break; + } + else if (STACK.weakCompareAndSetVolatile(this, p, (p = p.next))) + unlinked = true; + else + p = stack; + } + // try to unlink first non-live + for (Completion q = p.next; q != null;) { Completion s = q.next; if (q.isLive()) { p = q; q = s; - } - else if (p == null) { - casStack(q, s); - q = stack; - } - else { - p.next = s; - if (p.isLive()) - q = s; - else { - p = null; // restart - q = stack; - } - } + } else if (NEXT.weakCompareAndSetVolatile(p, q, s)) + break; + else + q = p.next; } } @@ -568,24 +573,34 @@ final boolean isLive() { return dep != null; } } - /** Pushes the given completion (if it exists) unless done. */ - final void push(UniCompletion<?,?> c) { + /** + * Pushes the given completion unless it completes while trying. + * Caller should first check that result is null. + */ + final void unipush(Completion c) { if (c != null) { - while (result == null && !tryPushStack(c)) - lazySetNext(c, null); // clear on failure + while (!tryPushStack(c)) { + if (result != null) { + NEXT.set(c, null); + break; + } + } + if (result != null) + c.tryFire(SYNC); } } /** - * Post-processing by dependent after successful UniCompletion - * tryFire. Tries to clean stack of source a, and then either runs - * postComplete or returns this to caller, depending on mode. + * Post-processing by dependent after successful UniCompletion tryFire. + * Tries to clean stack of source a, and then either runs postComplete + * or returns this to caller, depending on mode. */ final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) { if (a != null && a.stack != null) { - if (a.result == null) + Object r; + if ((r = a.result) == null) a.cleanStack(); - else if (mode >= 0) + if (mode >= 0 && (r != null || a.result != null)) a.postComplete(); } if (result != null && stack != null) { @@ -607,48 +622,65 @@ } final CompletableFuture<V> tryFire(int mode) { CompletableFuture<V> d; CompletableFuture<T> a; - if ((d = dep) == null || - !d.uniApply(a = src, fn, mode > 0 ? null : this)) + Object r; Throwable x; Function<? super T,? extends V> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null) return null; + tryComplete: if (d.result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + try { + if (mode <= 0 && !claim()) + return null; + else { + @SuppressWarnings("unchecked") T t = (T) r; + d.completeValue(f.apply(t)); + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final <S> boolean uniApply(CompletableFuture<S> a, - Function<? super S,? extends T> f, - UniApply<S,T> c) { - Object r; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; - tryComplete: if (result == null) { - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - try { - if (c != null && !c.claim()) - return false; - @SuppressWarnings("unchecked") S s = (S) r; - completeValue(f.apply(s)); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private <V> CompletableFuture<V> uniApplyStage( Executor e, Function<? super T,? extends V> f) { if (f == null) throw new NullPointerException(); + Object r; + if ((r = result) != null) + return uniApplyNow(r, e, f); CompletableFuture<V> d = newIncompleteFuture(); - if (e != null || !d.uniApply(this, f, null)) { - UniApply<T,V> c = new UniApply<T,V>(e, d, this, f); - push(c); - c.tryFire(SYNC); + unipush(new UniApply<T,V>(e, d, this, f)); + return d; + } + + private <V> CompletableFuture<V> uniApplyNow( + Object r, Executor e, Function<? super T,? extends V> f) { + Throwable x; + CompletableFuture<V> d = newIncompleteFuture(); + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.result = encodeThrowable(x, r); + return d; + } + r = null; + } + try { + if (e != null) { + e.execute(new UniApply<T,V>(null, d, this, f)); + } else { + @SuppressWarnings("unchecked") T t = (T) r; + d.result = d.encodeValue(f.apply(t)); + } + } catch (Throwable ex) { + d.result = encodeThrowable(ex); } return d; } @@ -662,48 +694,67 @@ } final CompletableFuture<Void> tryFire(int mode) { CompletableFuture<Void> d; CompletableFuture<T> a; - if ((d = dep) == null || - !d.uniAccept(a = src, fn, mode > 0 ? null : this)) + Object r; Throwable x; Consumer<? super T> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null) return null; + tryComplete: if (d.result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + try { + if (mode <= 0 && !claim()) + return null; + else { + @SuppressWarnings("unchecked") T t = (T) r; + f.accept(t); + d.completeNull(); + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final <S> boolean uniAccept(CompletableFuture<S> a, - Consumer<? super S> f, UniAccept<S> c) { - Object r; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; - tryComplete: if (result == null) { - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - try { - if (c != null && !c.claim()) - return false; - @SuppressWarnings("unchecked") S s = (S) r; - f.accept(s); - completeNull(); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private CompletableFuture<Void> uniAcceptStage(Executor e, Consumer<? super T> f) { if (f == null) throw new NullPointerException(); + Object r; + if ((r = result) != null) + return uniAcceptNow(r, e, f); CompletableFuture<Void> d = newIncompleteFuture(); - if (e != null || !d.uniAccept(this, f, null)) { - UniAccept<T> c = new UniAccept<T>(e, d, this, f); - push(c); - c.tryFire(SYNC); + unipush(new UniAccept<T>(e, d, this, f)); + return d; + } + + private CompletableFuture<Void> uniAcceptNow( + Object r, Executor e, Consumer<? super T> f) { + Throwable x; + CompletableFuture<Void> d = newIncompleteFuture(); + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.result = encodeThrowable(x, r); + return d; + } + r = null; + } + try { + if (e != null) { + e.execute(new UniAccept<T>(null, d, this, f)); + } else { + @SuppressWarnings("unchecked") T t = (T) r; + f.accept(t); + d.result = NIL; + } + } catch (Throwable ex) { + d.result = encodeThrowable(ex); } return d; } @@ -717,42 +768,56 @@ } final CompletableFuture<Void> tryFire(int mode) { CompletableFuture<Void> d; CompletableFuture<T> a; - if ((d = dep) == null || - !d.uniRun(a = src, fn, mode > 0 ? null : this)) + Object r; Throwable x; Runnable f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null) return null; + if (d.result == null) { + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + d.completeThrowable(x, r); + else + try { + if (mode <= 0 && !claim()) + return null; + else { + f.run(); + d.completeNull(); + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final boolean uniRun(CompletableFuture<?> a, Runnable f, UniRun<?> c) { - Object r; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; - if (result == null) { - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else - try { - if (c != null && !c.claim()) - return false; - f.run(); - completeNull(); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; + private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) { + if (f == null) throw new NullPointerException(); + Object r; + if ((r = result) != null) + return uniRunNow(r, e, f); + CompletableFuture<Void> d = newIncompleteFuture(); + unipush(new UniRun<T>(e, d, this, f)); + return d; } - private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) { - if (f == null) throw new NullPointerException(); + private CompletableFuture<Void> uniRunNow(Object r, Executor e, Runnable f) { + Throwable x; CompletableFuture<Void> d = newIncompleteFuture(); - if (e != null || !d.uniRun(this, f, null)) { - UniRun<T> c = new UniRun<T>(e, d, this, f); - push(c); - c.tryFire(SYNC); - } + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + d.result = encodeThrowable(x, r); + else + try { + if (e != null) { + e.execute(new UniRun<T>(null, d, this, f)); + } else { + f.run(); + d.result = NIL; + } + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -766,20 +831,20 @@ } final CompletableFuture<T> tryFire(int mode) { CompletableFuture<T> d; CompletableFuture<T> a; - if ((d = dep) == null || - !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this)) + Object r; BiConsumer<? super T, ? super Throwable> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || !d.uniWhenComplete(r, f, mode > 0 ? null : this)) return null; dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final boolean uniWhenComplete(CompletableFuture<T> a, + final boolean uniWhenComplete(Object r, BiConsumer<? super T,? super Throwable> f, UniWhenComplete<T> c) { - Object r; T t; Throwable x = null; - if (a == null || (r = a.result) == null || f == null) - return false; + T t; Throwable x = null; if (result == null) { try { if (c != null && !c.claim()) @@ -811,10 +876,17 @@ Executor e, BiConsumer<? super T, ? super Throwable> f) { if (f == null) throw new NullPointerException(); CompletableFuture<T> d = newIncompleteFuture(); - if (e != null || !d.uniWhenComplete(this, f, null)) { - UniWhenComplete<T> c = new UniWhenComplete<T>(e, d, this, f); - push(c); - c.tryFire(SYNC); + Object r; + if ((r = result) == null) + unipush(new UniWhenComplete<T>(e, d, this, f)); + else if (e == null) + d.uniWhenComplete(r, f, null); + else { + try { + e.execute(new UniWhenComplete<T>(null, d, this, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } } return d; } @@ -829,20 +901,20 @@ } final CompletableFuture<V> tryFire(int mode) { CompletableFuture<V> d; CompletableFuture<T> a; - if ((d = dep) == null || - !d.uniHandle(a = src, fn, mode > 0 ? null : this)) + Object r; BiFunction<? super T, Throwable, ? extends V> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || !d.uniHandle(r, f, mode > 0 ? null : this)) return null; dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final <S> boolean uniHandle(CompletableFuture<S> a, + final <S> boolean uniHandle(Object r, BiFunction<? super S, Throwable, ? extends T> f, UniHandle<S,T> c) { - Object r; S s; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; + S s; Throwable x; if (result == null) { try { if (c != null && !c.claim()) @@ -867,10 +939,17 @@ Executor e, BiFunction<? super T, Throwable, ? extends V> f) { if (f == null) throw new NullPointerException(); CompletableFuture<V> d = newIncompleteFuture(); - if (e != null || !d.uniHandle(this, f, null)) { - UniHandle<T,V> c = new UniHandle<T,V>(e, d, this, f); - push(c); - c.tryFire(SYNC); + Object r; + if ((r = result) == null) + unipush(new UniHandle<T,V>(e, d, this, f)); + else if (e == null) + d.uniHandle(r, f, null); + else { + try { + e.execute(new UniHandle<T,V>(null, d, this, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } } return d; } @@ -885,19 +964,20 @@ final CompletableFuture<T> tryFire(int mode) { // never ASYNC // assert mode != ASYNC; CompletableFuture<T> d; CompletableFuture<T> a; - if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this)) + Object r; Function<? super Throwable, ? extends T> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || !d.uniExceptionally(r, f, this)) return null; dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final boolean uniExceptionally(CompletableFuture<T> a, + final boolean uniExceptionally(Object r, Function<? super Throwable, ? extends T> f, UniExceptionally<T> c) { - Object r; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; + Throwable x; if (result == null) { try { if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) { @@ -917,47 +997,39 @@ Function<Throwable, ? extends T> f) { if (f == null) throw new NullPointerException(); CompletableFuture<T> d = newIncompleteFuture(); - if (!d.uniExceptionally(this, f, null)) { - UniExceptionally<T> c = new UniExceptionally<T>(d, this, f); - push(c); - c.tryFire(SYNC); - } + Object r; + if ((r = result) == null) + unipush(new UniExceptionally<T>(d, this, f)); + else + d.uniExceptionally(r, f, null); return d; } @SuppressWarnings("serial") - static final class UniRelay<T> extends UniCompletion<T,T> { // for Compose - UniRelay(CompletableFuture<T> dep, CompletableFuture<T> src) { + static final class UniRelay<U, T extends U> extends UniCompletion<T,U> { + UniRelay(CompletableFuture<U> dep, CompletableFuture<T> src) { super(null, dep, src); } - final CompletableFuture<T> tryFire(int mode) { - CompletableFuture<T> d; CompletableFuture<T> a; - if ((d = dep) == null || !d.uniRelay(a = src)) + final CompletableFuture<U> tryFire(int mode) { + CompletableFuture<U> d; CompletableFuture<T> a; Object r; + if ((d = dep) == null + || (a = src) == null || (r = a.result) == null) return null; + if (d.result == null) + d.completeRelay(r); src = null; dep = null; return d.postFire(a, mode); } } - final boolean uniRelay(CompletableFuture<T> a) { + private static <U, T extends U> CompletableFuture<U> uniCopyStage( + CompletableFuture<T> src) { Object r; - if (a == null || (r = a.result) == null) - return false; - if (result == null) // no need to claim - completeRelay(r); - return true; - } - - private CompletableFuture<T> uniCopyStage() { - Object r; - CompletableFuture<T> d = newIncompleteFuture(); - if ((r = result) != null) - d.completeRelay(r); - else { - UniRelay<T> c = new UniRelay<T>(d, this); - push(c); - c.tryFire(SYNC); - } + CompletableFuture<U> d = src.newIncompleteFuture(); + if ((r = src.result) != null) + d.result = encodeRelay(r); + else + src.unipush(new UniRelay<U,T>(d, src)); return d; } @@ -966,9 +1038,7 @@ if ((r = result) != null) return new MinimalStage<T>(encodeRelay(r)); MinimalStage<T> d = new MinimalStage<T>(); - UniRelay<T> c = new UniRelay<T>(d, this); - push(c); - c.tryFire(SYNC); + unipush(new UniRelay<T,T>(d, this)); return d; } @@ -982,54 +1052,48 @@ } final CompletableFuture<V> tryFire(int mode) { CompletableFuture<V> d; CompletableFuture<T> a; - if ((d = dep) == null || - !d.uniCompose(a = src, fn, mode > 0 ? null : this)) + Function<? super T, ? extends CompletionStage<V>> f; + Object r; Throwable x; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null) return null; + tryComplete: if (d.result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + try { + if (mode <= 0 && !claim()) + return null; + @SuppressWarnings("unchecked") T t = (T) r; + CompletableFuture<V> g = f.apply(t).toCompletableFuture(); + if ((r = g.result) != null) + d.completeRelay(r); + else { + g.unipush(new UniRelay<V,V>(d, g)); + if (d.result == null) + return null; + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final <S> boolean uniCompose( - CompletableFuture<S> a, - Function<? super S, ? extends CompletionStage<T>> f, - UniCompose<S,T> c) { - Object r; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; - tryComplete: if (result == null) { - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - try { - if (c != null && !c.claim()) - return false; - @SuppressWarnings("unchecked") S s = (S) r; - CompletableFuture<T> g = f.apply(s).toCompletableFuture(); - if (g.result == null || !uniRelay(g)) { - UniRelay<T> copy = new UniRelay<T>(this, g); - g.push(copy); - copy.tryFire(SYNC); - if (result == null) - return false; - } - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private <V> CompletableFuture<V> uniComposeStage( Executor e, Function<? super T, ? extends CompletionStage<V>> f) { if (f == null) throw new NullPointerException(); + CompletableFuture<V> d = newIncompleteFuture(); Object r, s; Throwable x; - CompletableFuture<V> d = newIncompleteFuture(); - if (e == null && (r = result) != null) { + if ((r = result) == null) + unipush(new UniCompose<T,V>(e, d, this, f)); + else if (e == null) { if (r instanceof AltResult) { if ((x = ((AltResult)r).ex) != null) { d.result = encodeThrowable(x, r); @@ -1041,21 +1105,20 @@ @SuppressWarnings("unchecked") T t = (T) r; CompletableFuture<V> g = f.apply(t).toCompletableFuture(); if ((s = g.result) != null) - d.completeRelay(s); + d.result = encodeRelay(s); else { - UniRelay<V> c = new UniRelay<V>(d, g); - g.push(c); - c.tryFire(SYNC); + g.unipush(new UniRelay<V,V>(d, g)); } - return d; } catch (Throwable ex) { d.result = encodeThrowable(ex); - return d; } } - UniCompose<T,V> c = new UniCompose<T,V>(e, d, this, f); - push(c); - c.tryFire(SYNC); + else + try { + e.execute(new UniCompose<T,V>(null, d, this, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -1085,21 +1148,28 @@ } final boolean isLive() { BiCompletion<?,?,?> c; - return (c = base) != null && c.dep != null; + return (c = base) != null + // && c.isLive() + && c.dep != null; } } - /** Pushes completion to this and b unless both done. */ + /** + * Pushes completion to this and b unless both done. + * Caller should first check that either result or b.result is null. + */ final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) { if (c != null) { - Object r; - while ((r = result) == null && !tryPushStack(c)) - lazySetNext(c, null); // clear on failure - if (b != null && b != this && b.result == null) { - Completion q = (r != null) ? c : new CoCompletion(c); - while (b.result == null && !b.tryPushStack(q)) - lazySetNext(q, null); // clear on failure + while (result == null) { + if (tryPushStack(c)) { + if (b.result == null) + b.unipush(new CoCompletion(c)); + else if (result != null) + c.tryFire(SYNC); + return; + } } + b.unipush(c); } } @@ -1107,9 +1177,10 @@ final CompletableFuture<T> postFire(CompletableFuture<?> a, CompletableFuture<?> b, int mode) { if (b != null && b.stack != null) { // clean second source - if (b.result == null) + Object r; + if ((r = b.result) == null) b.cleanStack(); - else if (mode >= 0) + if (mode >= 0 && (r != null || b.result != null)) b.postComplete(); } return postFire(a, mode); @@ -1127,22 +1198,21 @@ CompletableFuture<V> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || - !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r, s; BiFunction<? super T,? super U,? extends V> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null + || !d.biApply(r, s, f, mode > 0 ? null : this)) return null; dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final <R,S> boolean biApply(CompletableFuture<R> a, - CompletableFuture<S> b, + final <R,S> boolean biApply(Object r, Object s, BiFunction<? super R,? super S,? extends T> f, BiApply<R,S,T> c) { - Object r, s; Throwable x; - if (a == null || (r = a.result) == null || - b == null || (s = b.result) == null || f == null) - return false; + Throwable x; tryComplete: if (result == null) { if (r instanceof AltResult) { if ((x = ((AltResult)r).ex) != null) { @@ -1174,15 +1244,20 @@ private <U,V> CompletableFuture<V> biApplyStage( Executor e, CompletionStage<U> o, BiFunction<? super T,? super U,? extends V> f) { - CompletableFuture<U> b; + CompletableFuture<U> b; Object r, s; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); CompletableFuture<V> d = newIncompleteFuture(); - if (e != null || !d.biApply(this, b, f, null)) { - BiApply<T,U,V> c = new BiApply<T,U,V>(e, d, this, b, f); - bipush(b, c); - c.tryFire(SYNC); - } + if ((r = result) == null || (s = b.result) == null) + bipush(b, new BiApply<T,U,V>(e, d, this, b, f)); + else if (e == null) + d.biApply(r, s, f, null); + else + try { + e.execute(new BiApply<T,U,V>(null, d, this, b, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -1198,22 +1273,21 @@ CompletableFuture<Void> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || - !d.biAccept(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r, s; BiConsumer<? super T,? super U> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null + || !d.biAccept(r, s, f, mode > 0 ? null : this)) return null; dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final <R,S> boolean biAccept(CompletableFuture<R> a, - CompletableFuture<S> b, + final <R,S> boolean biAccept(Object r, Object s, BiConsumer<? super R,? super S> f, BiAccept<R,S> c) { - Object r, s; Throwable x; - if (a == null || (r = a.result) == null || - b == null || (s = b.result) == null || f == null) - return false; + Throwable x; tryComplete: if (result == null) { if (r instanceof AltResult) { if ((x = ((AltResult)r).ex) != null) { @@ -1246,15 +1320,20 @@ private <U> CompletableFuture<Void> biAcceptStage( Executor e, CompletionStage<U> o, BiConsumer<? super T,? super U> f) { - CompletableFuture<U> b; + CompletableFuture<U> b; Object r, s; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); CompletableFuture<Void> d = newIncompleteFuture(); - if (e != null || !d.biAccept(this, b, f, null)) { - BiAccept<T,U> c = new BiAccept<T,U>(e, d, this, b, f); - bipush(b, c); - c.tryFire(SYNC); - } + if ((r = result) == null || (s = b.result) == null) + bipush(b, new BiAccept<T,U>(e, d, this, b, f)); + else if (e == null) + d.biAccept(r, s, f, null); + else + try { + e.execute(new BiAccept<T,U>(null, d, this, b, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -1262,8 +1341,7 @@ static final class BiRun<T,U> extends BiCompletion<T,U,Void> { Runnable fn; BiRun(Executor executor, CompletableFuture<Void> dep, - CompletableFuture<T> src, - CompletableFuture<U> snd, + CompletableFuture<T> src, CompletableFuture<U> snd, Runnable fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1271,25 +1349,25 @@ CompletableFuture<Void> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || - !d.biRun(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r, s; Runnable f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null + || !d.biRun(r, s, f, mode > 0 ? null : this)) return null; dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final boolean biRun(CompletableFuture<?> a, CompletableFuture<?> b, - Runnable f, BiRun<?,?> c) { - Object r, s; Throwable x; - if (a == null || (r = a.result) == null || - b == null || (s = b.result) == null || f == null) - return false; + final boolean biRun(Object r, Object s, Runnable f, BiRun<?,?> c) { + Throwable x; Object z; if (result == null) { - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) - completeThrowable(x, s); + if ((r instanceof AltResult + && (x = ((AltResult)(z = r)).ex) != null) || + (s instanceof AltResult + && (x = ((AltResult)(z = s)).ex) != null)) + completeThrowable(x, z); else try { if (c != null && !c.claim()) @@ -1305,52 +1383,52 @@ private CompletableFuture<Void> biRunStage(Executor e, CompletionStage<?> o, Runnable f) { - CompletableFuture<?> b; + CompletableFuture<?> b; Object r, s; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); CompletableFuture<Void> d = newIncompleteFuture(); - if (e != null || !d.biRun(this, b, f, null)) { - BiRun<T,?> c = new BiRun<>(e, d, this, b, f); - bipush(b, c); - c.tryFire(SYNC); - } + if ((r = result) == null || (s = b.result) == null) + bipush(b, new BiRun<>(e, d, this, b, f)); + else if (e == null) + d.biRun(r, s, f, null); + else + try { + e.execute(new BiRun<>(null, d, this, b, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @SuppressWarnings("serial") static final class BiRelay<T,U> extends BiCompletion<T,U,Void> { // for And BiRelay(CompletableFuture<Void> dep, - CompletableFuture<T> src, - CompletableFuture<U> snd) { + CompletableFuture<T> src, CompletableFuture<U> snd) { super(null, dep, src, snd); } final CompletableFuture<Void> tryFire(int mode) { CompletableFuture<Void> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || !d.biRelay(a = src, b = snd)) + Object r, s, z; Throwable x; + if ((d = dep) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null) return null; + if (d.result == null) { + if ((r instanceof AltResult + && (x = ((AltResult)(z = r)).ex) != null) || + (s instanceof AltResult + && (x = ((AltResult)(z = s)).ex) != null)) + d.completeThrowable(x, z); + else + d.completeNull(); + } src = null; snd = null; dep = null; return d.postFire(a, b, mode); } } - boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) { - Object r, s; Throwable x; - if (a == null || (r = a.result) == null || - b == null || (s = b.result) == null) - return false; - if (result == null) { - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) - completeThrowable(x, s); - else - completeNull(); - } - return true; - } - /** Recursively constructs a tree of completions. */ static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs, int lo, int hi) { @@ -1358,39 +1436,44 @@ if (lo > hi) // empty d.result = NIL; else { - CompletableFuture<?> a, b; + CompletableFuture<?> a, b; Object r, s, z; Throwable x; int mid = (lo + hi) >>> 1; if ((a = (lo == mid ? cfs[lo] : andTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : andTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); - if (!d.biRelay(a, b)) { - BiRelay<?,?> c = new BiRelay<>(d, a, b); - a.bipush(b, c); - c.tryFire(SYNC); - } + if ((r = a.result) == null || (s = b.result) == null) + a.bipush(b, new BiRelay<>(d, a, b)); + else if ((r instanceof AltResult + && (x = ((AltResult)(z = r)).ex) != null) || + (s instanceof AltResult + && (x = ((AltResult)(z = s)).ex) != null)) + d.result = encodeThrowable(x, z); + else + d.result = NIL; } return d; } /* ------------- Projected (Ored) BiCompletions -------------- */ - /** Pushes completion to this and b unless either done. */ + /** + * Pushes completion to this and b unless either done. + * Caller should first check that result and b.result are both null. + */ final void orpush(CompletableFuture<?> b, BiCompletion<?,?,?> c) { if (c != null) { - while ((b == null || b.result == null) && result == null) { - if (tryPushStack(c)) { - if (b != null && b != this && b.result == null) { - Completion q = new CoCompletion(c); - while (result == null && b.result == null && - !b.tryPushStack(q)) - lazySetNext(q, null); // clear on failure - } + while (!tryPushStack(c)) { + if (result != null) { + NEXT.set(c, null); break; } - lazySetNext(c, null); // clear on failure } + if (result != null) + c.tryFire(SYNC); + else + b.unipush(new CoCompletion(c)); } } @@ -1398,8 +1481,7 @@ static final class OrApply<T,U extends T,V> extends BiCompletion<T,U,V> { Function<? super T,? extends V> fn; OrApply(Executor executor, CompletableFuture<V> dep, - CompletableFuture<T> src, - CompletableFuture<U> snd, + CompletableFuture<T> src, CompletableFuture<U> snd, Function<? super T,? extends V> fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1407,54 +1489,46 @@ CompletableFuture<V> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || - !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r; Throwable x; Function<? super T,? extends V> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + tryComplete: if (d.result == null) { + try { + if (mode <= 0 && !claim()) + return null; + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + @SuppressWarnings("unchecked") T t = (T) r; + d.completeValue(f.apply(t)); + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final <R,S extends R> boolean orApply(CompletableFuture<R> a, - CompletableFuture<S> b, - Function<? super R, ? extends T> f, - OrApply<R,S,T> c) { - Object r; Throwable x; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null) || f == null) - return false; - tryComplete: if (result == null) { - try { - if (c != null && !c.claim()) - return false; - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - @SuppressWarnings("unchecked") R rr = (R) r; - completeValue(f.apply(rr)); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private <U extends T,V> CompletableFuture<V> orApplyStage( - Executor e, CompletionStage<U> o, - Function<? super T, ? extends V> f) { + Executor e, CompletionStage<U> o, Function<? super T, ? extends V> f) { CompletableFuture<U> b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); + + Object r; CompletableFuture<? extends T> z; + if ((r = (z = this).result) != null || + (r = (z = b).result) != null) + return z.uniApplyNow(r, e, f); + CompletableFuture<V> d = newIncompleteFuture(); - if (e != null || !d.orApply(this, b, f, null)) { - OrApply<T,U,V> c = new OrApply<T,U,V>(e, d, this, b, f); - orpush(b, c); - c.tryFire(SYNC); - } + orpush(b, new OrApply<T,U,V>(e, d, this, b, f)); return d; } @@ -1462,8 +1536,7 @@ static final class OrAccept<T,U extends T> extends BiCompletion<T,U,Void> { Consumer<? super T> fn; OrAccept(Executor executor, CompletableFuture<Void> dep, - CompletableFuture<T> src, - CompletableFuture<U> snd, + CompletableFuture<T> src, CompletableFuture<U> snd, Consumer<? super T> fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1471,54 +1544,47 @@ CompletableFuture<Void> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || - !d.orAccept(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r; Throwable x; Consumer<? super T> f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + tryComplete: if (d.result == null) { + try { + if (mode <= 0 && !claim()) + return null; + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + @SuppressWarnings("unchecked") T t = (T) r; + f.accept(t); + d.completeNull(); + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final <R,S extends R> boolean orAccept(CompletableFuture<R> a, - CompletableFuture<S> b, - Consumer<? super R> f, - OrAccept<R,S> c) { - Object r; Throwable x; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null) || f == null) - return false; - tryComplete: if (result == null) { - try { - if (c != null && !c.claim()) - return false; - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - @SuppressWarnings("unchecked") R rr = (R) r; - f.accept(rr); - completeNull(); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private <U extends T> CompletableFuture<Void> orAcceptStage( Executor e, CompletionStage<U> o, Consumer<? super T> f) { CompletableFuture<U> b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); + + Object r; CompletableFuture<? extends T> z; + if ((r = (z = this).result) != null || + (r = (z = b).result) != null) + return z.uniAcceptNow(r, e, f); + CompletableFuture<Void> d = newIncompleteFuture(); - if (e != null || !d.orAccept(this, b, f, null)) { - OrAccept<T,U> c = new OrAccept<T,U>(e, d, this, b, f); - orpush(b, c); - c.tryFire(SYNC); - } + orpush(b, new OrAccept<T,U>(e, d, this, b, f)); return d; } @@ -1526,8 +1592,7 @@ static final class OrRun<T,U> extends BiCompletion<T,U,Void> { Runnable fn; OrRun(Executor executor, CompletableFuture<Void> dep, - CompletableFuture<T> src, - CompletableFuture<U> snd, + CompletableFuture<T> src, CompletableFuture<U> snd, Runnable fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1535,97 +1600,81 @@ CompletableFuture<Void> d; CompletableFuture<T> a; CompletableFuture<U> b; - if ((d = dep) == null || - !d.orRun(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r; Throwable x; Runnable f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + if (d.result == null) { + try { + if (mode <= 0 && !claim()) + return null; + else if (r instanceof AltResult + && (x = ((AltResult)r).ex) != null) + d.completeThrowable(x, r); + else { + f.run(); + d.completeNull(); + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final boolean orRun(CompletableFuture<?> a, CompletableFuture<?> b, - Runnable f, OrRun<?,?> c) { - Object r; Throwable x; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null) || f == null) - return false; - if (result == null) { - try { - if (c != null && !c.claim()) - return false; - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else { - f.run(); - completeNull(); - } - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private CompletableFuture<Void> orRunStage(Executor e, CompletionStage<?> o, Runnable f) { CompletableFuture<?> b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); + + Object r; CompletableFuture<?> z; + if ((r = (z = this).result) != null || + (r = (z = b).result) != null) + return z.uniRunNow(r, e, f); + CompletableFuture<Void> d = newIncompleteFuture(); - if (e != null || !d.orRun(this, b, f, null)) { - OrRun<T,?> c = new OrRun<>(e, d, this, b, f); - orpush(b, c); - c.tryFire(SYNC); - } + orpush(b, new OrRun<>(e, d, this, b, f)); return d; } + /** Completion for an anyOf input future. */ @SuppressWarnings("serial") - static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or - OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src, - CompletableFuture<U> snd) { - super(null, dep, src, snd); + static class AnyOf extends Completion { + CompletableFuture<Object> dep; CompletableFuture<?> src; + CompletableFuture<?>[] srcs; + AnyOf(CompletableFuture<Object> dep, CompletableFuture<?> src, + CompletableFuture<?>[] srcs) { + this.dep = dep; this.src = src; this.srcs = srcs; } final CompletableFuture<Object> tryFire(int mode) { - CompletableFuture<Object> d; - CompletableFuture<T> a; - CompletableFuture<U> b; - if ((d = dep) == null || !d.orRelay(a = src, b = snd)) + // assert mode != ASYNC; + CompletableFuture<Object> d; CompletableFuture<?> a; + CompletableFuture<?>[] as; + Object r; + if ((d = dep) == null + || (a = src) == null || (r = a.result) == null + || (as = srcs) == null) return null; - src = null; snd = null; dep = null; - return d.postFire(a, b, mode); + dep = null; src = null; srcs = null; + if (d.completeRelay(r)) { + for (CompletableFuture<?> b : as) + if (b != a) + b.cleanStack(); + if (mode < 0) + return d; + else + d.postComplete(); + } + return null; } - } - - final boolean orRelay(CompletableFuture<?> a, CompletableFuture<?> b) { - Object r; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null)) - return false; - if (result == null) - completeRelay(r); - return true; - } - - /** Recursively constructs a tree of completions. */ - static CompletableFuture<Object> orTree(CompletableFuture<?>[] cfs, - int lo, int hi) { - CompletableFuture<Object> d = new CompletableFuture<Object>(); - if (lo <= hi) { - CompletableFuture<?> a, b; - int mid = (lo + hi) >>> 1; - if ((a = (lo == mid ? cfs[lo] : - orTree(cfs, lo, mid))) == null || - (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : - orTree(cfs, mid+1, hi))) == null) - throw new NullPointerException(); - if (!d.orRelay(a, b)) { - OrRelay<?,?> c = new OrRelay<>(d, a, b); - a.orpush(b, c); - c.tryFire(SYNC); - } + final boolean isLive() { + CompletableFuture<Object> d; + return (d = dep) != null && d.result == null; } - return d; } /* ------------- Zero-input Async forms -------------- */ @@ -1640,7 +1689,7 @@ public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} - public final boolean exec() { run(); return true; } + public final boolean exec() { run(); return false; } public void run() { CompletableFuture<T> d; Supplier<? extends T> f; @@ -1676,7 +1725,7 @@ public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} - public final boolean exec() { run(); return true; } + public final boolean exec() { run(); return false; } public void run() { CompletableFuture<Void> d; Runnable f; @@ -1760,15 +1809,13 @@ private Object waitingGet(boolean interruptible) { Signaller q = null; boolean queued = false; - int spins = SPINS; Object r; while ((r = result) == null) { - if (spins > 0) { - if (ThreadLocalRandom.nextSecondarySeed() >= 0) - --spins; + if (q == null) { + q = new Signaller(interruptible, 0L, 0L); + if (Thread.currentThread() instanceof ForkJoinWorkerThread) + ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); } - else if (q == null) - q = new Signaller(interruptible, 0L, 0L); else if (!queued) queued = tryPushStack(q); else { @@ -1781,16 +1828,14 @@ break; } } - if (q != null) { + if (q != null && queued) { q.thread = null; - if (q.interrupted) { - if (interruptible) - cleanStack(); - else - Thread.currentThread().interrupt(); - } + if (!interruptible && q.interrupted) + Thread.currentThread().interrupt(); + if (r == null) + cleanStack(); } - if (r != null) + if (r != null || (r = result) != null) postComplete(); return r; } @@ -1808,9 +1853,12 @@ Signaller q = null; boolean queued = false; Object r; - while ((r = result) == null) { // similar to untimed, without spins - if (q == null) + while ((r = result) == null) { // similar to untimed + if (q == null) { q = new Signaller(true, nanos, deadline); + if (Thread.currentThread() instanceof ForkJoinWorkerThread) + ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); + } else if (!queued) queued = tryPushStack(q); else if (q.nanos <= 0L) @@ -1825,12 +1873,13 @@ break; } } - if (q != null) + if (q != null && queued) { q.thread = null; - if (r != null) + if (r == null) + cleanStack(); + } + if (r != null || (r = result) != null) postComplete(); - else - cleanStack(); if (r != null || (q != null && q.interrupted)) return r; } @@ -1942,9 +1991,12 @@ * @throws InterruptedException if the current thread was interrupted * while waiting */ + @SuppressWarnings("unchecked") public T get() throws InterruptedException, ExecutionException { Object r; - return reportGet((r = result) == null ? waitingGet(true) : r); + if ((r = result) == null) + r = waitingGet(true); + return (T) reportGet(r); } /** @@ -1960,11 +2012,14 @@ * while waiting * @throws TimeoutException if the wait timed out */ + @SuppressWarnings("unchecked") public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + long nanos = unit.toNanos(timeout); Object r; - long nanos = unit.toNanos(timeout); - return reportGet((r = result) == null ? timedGet(nanos) : r); + if ((r = result) == null) + r = timedGet(nanos); + return (T) reportGet(r); } /** @@ -1981,9 +2036,12 @@ * @throws CompletionException if this future completed * exceptionally or a completion computation threw an exception */ + @SuppressWarnings("unchecked") public T join() { Object r; - return reportJoin((r = result) == null ? waitingGet(false) : r); + if ((r = result) == null) + r = waitingGet(false); + return (T) reportJoin(r); } /** @@ -1996,9 +2054,10 @@ * @throws CompletionException if this future completed * exceptionally or a completion computation threw an exception */ + @SuppressWarnings("unchecked") public T getNow(T valueIfAbsent) { Object r; - return ((r = result) == null) ? valueIfAbsent : reportJoin(r); + return ((r = result) == null) ? valueIfAbsent : (T) reportJoin(r); } /** @@ -2294,7 +2353,28 @@ * {@code null} */ public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) { - return orTree(cfs, 0, cfs.length - 1); + int n; Object r; + if ((n = cfs.length) <= 1) + return (n == 0) + ? new CompletableFuture<Object>() + : uniCopyStage(cfs[0]); + for (CompletableFuture<?> cf : cfs) + if ((r = cf.result) != null) + return new CompletableFuture<Object>(encodeRelay(r)); + cfs = cfs.clone(); + CompletableFuture<Object> d = new CompletableFuture<>(); + for (CompletableFuture<?> cf : cfs) + cf.unipush(new AnyOf(d, cf, cfs)); + // If d was completed while we were adding completions, we should + // clean the stack of any sources that may have had completions + // pushed on their stack after d was completed. + if (d.result != null) + for (int i = 0, len = cfs.length; i < len; i++) + if (cfs[i].result != null) + for (i++; i < len; i++) + if (cfs[i].result == null) + cfs[i].cleanStack(); + return d; } /* ------------- Control and status methods -------------- */ @@ -2466,7 +2546,7 @@ * @since 9 */ public CompletableFuture<T> copy() { - return uniCopyStage(); + return uniCopyStage(this); } /** @@ -2775,19 +2855,16 @@ throw new UnsupportedOperationException(); } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long RESULT; - private static final long STACK; - private static final long NEXT; + // VarHandle mechanics + private static final VarHandle RESULT; + private static final VarHandle STACK; + private static final VarHandle NEXT; static { try { - RESULT = U.objectFieldOffset - (CompletableFuture.class.getDeclaredField("result")); - STACK = U.objectFieldOffset - (CompletableFuture.class.getDeclaredField("stack")); - NEXT = U.objectFieldOffset - (Completion.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + RESULT = l.findVarHandle(CompletableFuture.class, "result", Object.class); + STACK = l.findVarHandle(CompletableFuture.class, "stack", Completion.class); + NEXT = l.findVarHandle(Completion.class, "next", Completion.class); } catch (ReflectiveOperationException e) { throw new Error(e); }
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Fri Jul 22 04:05:04 2016 +0000 @@ -68,6 +68,7 @@ import java.util.function.ToLongBiFunction; import java.util.function.ToLongFunction; import java.util.stream.Stream; +import jdk.internal.misc.Unsafe; /** * A hash table supporting full concurrency of retrievals and @@ -747,7 +748,7 @@ /* ---------------- Table element access -------------- */ /* - * Volatile access methods are used for table elements as well as + * Atomic access methods are used for table elements as well as * elements of in-progress next table while resizing. All uses of * the tab arguments must be null checked by callers. All callers * also paranoically precheck that tab's length is not zero (or an @@ -757,14 +758,12 @@ * errors by users, these checks must operate on local variables, * which accounts for some odd-looking inline assignments below. * Note that calls to setTabAt always occur within locked regions, - * and so in principle require only release ordering, not - * full volatile semantics, but are currently coded as volatile - * writes to be conservative. + * and so require only release ordering. */ @SuppressWarnings("unchecked") static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) { - return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); + return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE); } static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i, @@ -773,7 +772,7 @@ } static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) { - U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v); + U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v); } /* ---------------- Fields -------------- */ @@ -1024,7 +1023,7 @@ int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) { - Node<K,V> f; int n, i, fh; + Node<K,V> f; int n, i, fh; K fk; V fv; if (tab == null || (n = tab.length) == 0) tab = initTable(); else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { @@ -1033,6 +1032,10 @@ } else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); + else if (onlyIfAbsent && fh == hash && // check first node + ((fk = f.key) == key || fk != null && key.equals(fk)) && + (fv = f.val) != null) + return fv; else { V oldVal = null; synchronized (f) { @@ -1703,7 +1706,7 @@ V val = null; int binCount = 0; for (Node<K,V>[] tab = table;;) { - Node<K,V> f; int n, i, fh; + Node<K,V> f; int n, i, fh; K fk; V fv; if (tab == null || (n = tab.length) == 0) tab = initTable(); else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { @@ -1725,6 +1728,10 @@ } else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); + else if (fh == h && // check first node + ((fk = f.key) == key || fk != null && key.equals(fk)) && + (fv = f.val) != null) + return fv; else { boolean added = false; synchronized (f) { @@ -3298,7 +3305,7 @@ return true; } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long LOCKSTATE; static { try { @@ -4554,14 +4561,21 @@ return true; } - public final boolean removeAll(Collection<?> c) { + public boolean removeAll(Collection<?> c) { if (c == null) throw new NullPointerException(); boolean modified = false; - for (Iterator<E> it = iterator(); it.hasNext();) { - if (c.contains(it.next())) { - it.remove(); - modified = true; + // Use (c instanceof Set) as a hint that lookup in c is as + // efficient as this view + if (c instanceof Set<?> && c.size() > map.table.length) { + for (Iterator<?> it = iterator(); it.hasNext(); ) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } } + } else { + for (Object e : c) + modified |= remove(e); } return modified; } @@ -4748,6 +4762,18 @@ throw new UnsupportedOperationException(); } + @Override public boolean removeAll(Collection<?> c) { + if (c == null) throw new NullPointerException(); + boolean modified = false; + for (Iterator<V> it = iterator(); it.hasNext();) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + public boolean removeIf(Predicate<? super V> filter) { return map.removeValueIf(filter); } @@ -6341,7 +6367,7 @@ } // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long SIZECTL; private static final long TRANSFERINDEX; private static final long BASECOUNT;
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Thu Jul 21 19:29:33 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Fri Jul 22 04:05:04 2016 +0000 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractCollection; import java.util.Arrays; import java.util.Collection; @@ -292,64 +294,23 @@ volatile Node<E> prev; volatile E item; volatile Node<E> next; - - Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR - } - - /** - * Constructs a new node. Uses relaxed write because item can - * only be seen after publication via casNext or casPrev. - */ - Node(E item) { - U.putObject(this, ITEM, item); - } - - boolean casItem(E cmp, E val) { - return U.compareAndSwapObject(this, ITEM, cmp, val); - } - - void lazySetNext(Node<E> val) { - U.putObjectRelease(this, NEXT, val); - } - - boolean casNext(Node<E> cmp, Node<E> val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); - } + } - void lazySetPrev(Node<E> val) { - U.putObjectRelease(this, PREV, val); - } - - boolean casPrev(Node<E> cmp, Node<E> val) { - return U.compareAndSwapObject(this, PREV, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PREV; - private static final long ITEM; - private static final long NEXT; - - static { - try { - PREV = U.objectFieldOffset - (Node.class.getDeclaredField("prev")); - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } + /** + * Returns a new node holding item. Uses relaxed write because item + * can only be seen after piggy-backing publication via CAS. + */ + static <E> Node<E> newNode(E item) { + Node<E> node = new Node<E>(); + ITEM.set(node, item); + return node; } /** * Links e as first element. */ private void linkFirst(E e) { - final Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); + final Node<E> newNode = newNode(Objects.requireNonNull(e)); restartFromHead: for (;;) @@ -363,13 +324,13 @@ continue restartFromHead; else { // p is first node - newNode.lazySetNext(p); // CAS piggyback - if (p.casPrev(null, newNode)) { + NEXT.set(newNode, p); // CAS piggyback + if (PREV.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". - if (p != h) // hop two nodes at a time - casHead(h, newNode); // Failure is OK. + if (p != h) // hop two nodes at a time; failure is OK + HEAD.weakCompareAndSetVolatile(this, h, newNode); return; } // Lost CAS race to another thread; re-read prev @@ -381,7 +342,7 @@ * Links e as last element. */ private void linkLast(E e) { - final Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); + final Node<E> newNode = newNode(Objects.requireNonNull(e)); restartFromTail: for (;;) @@ -395,13 +356,13 @@ continue restartFromTail; else { // p is last node - newNode.lazySetPrev(p); // CAS piggyback - if (p.casNext(null, newNode)) { + PREV.set(newNode, p); // CAS piggyback + if (NEXT.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". - if (p != t) // hop two nodes at a time - casTail(t, newNode); // Failure is OK. + if (p != t) // hop two nodes at a time; failure is OK + TAIL.weakCompareAndSetVolatile(this, t, newNode); return; } // Lost CAS race to another thread; re-read next @@ -516,8 +477,8 @@ updateTail(); // Ensure x is not reachable from tail // Finally, actually gc-unlink - x.lazySetPrev(isFirst ? prevTerminator() : x); - x.lazySetNext(isLast ? nextTerminator() : x); + PREV.setRelease(x, isFirst ? prevTerminator() : x); + NEXT.setRelease(x, isLast ? nextTerminator() : x); } } } @@ -531,7 +492,8 @@ // assert first.item == null; for (Node<E> o = null, p = next, q;;) { if (p.item != null || (q = p.next) == null) { - if (o != null && p.prev != p && first.casNext(next, p)) { + if (o != null && p.prev != p && + NEXT.compareAndSet(first, next, p)) { skipDeletedPredecessors(p); if (first.prev == null && (p.next == null || p.item != null) && @@ -541,8 +503,8 @@ updateTail(); // Ensure o is not reachable from tail // Finally, actually gc-unlink - o.lazySetNext(o); - o.lazySetPrev(prevTerminator()); + NEXT.setRelease(o, o); + PREV.setRelease(o, prevTerminator()); } } return; @@ -565,7 +527,8 @@ // assert last.item == null; for (Node<E> o = null, p = prev, q;;) { if (p.item != null || (q = p.prev) == null) { - if (o != null && p.next != p && last.casPrev(prev, p)) { + if (o != null && p.next != p && + PREV.compareAndSet(last, prev, p)) { skipDeletedSuccessors(p); if (last.next == null && (p.prev == null || p.item != null) && @@ -575,8 +538,8 @@ updateTail(); // Ensure o is not reachable from tail // Finally, actually gc-unlink - o.lazySetPrev(o); - o.lazySetNext(nextTerminator()); + PREV.setRelease(o, o); + NEXT.setRelease(o, nextTerminator()); } } return; @@ -607,7 +570,7 @@ (q = (p = q).prev) == null) { // It is possible that p is PREV_TERMINATOR, // but if so, the CAS is guaranteed to fail. - if (casHead(h, p)) + if (HEAD.compareAndSet(this, h, p)) return; else continue restartFromHead; @@ -637,7 +600,7 @@ (q = (p = q).next) == null) { // It is possible that p is NEXT_TERMINATOR, // but if so, the CAS is guaranteed to fail. - if (casTail(t, p)) + if (TAIL.compareAndSet(this, t, p)) return; else continue restartFromTail; @@ -675,7 +638,7 @@ } // found active CAS target - if (prev == p || x.casPrev(prev, p)) + if (prev == p || PREV.compareAndSet(x, prev, p)) return; } while (x.item != null || x.next == null); @@ -706,7 +669,7 @@ } // found active CAS target - if (next == p || x.casNext(next, p)) + if (next == p || NEXT.compareAndSet(x, next, p)) return; } while (x.item != null || x.prev == null); @@ -751,7 +714,7 @@ else if (p == h // It is possible that p is PREV_TERMINATOR, // but if so, the CAS is guaranteed to fail. - || casHead(h, p)) + || HEAD.compareAndSet(this, h, p)) return p; else continue restartFromHead; @@ -776,7 +739,7 @@ else if (p == t // It is possible that p is NEXT_TERMINATOR, // but if so, the CAS is guaranteed to fail. - || casTail(t, p)) + || TAIL.compareAndSet(this, t, p)) return p; else continue restartFromTail; @@ -802,7 +765,7 @@ * Constructs an empty deque. */ public ConcurrentLinkedDeque() { - head = tail = new Node<E>(null); + head = tail = new Node<E>(); } /** @@ -818,12 +781,12 @@ // Copy c into a private chain of Nodes Node<E> h = null, t = null; for (E e : c) { - Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); + Node<E> newNode = newNode(Objects.requireNonNull(e)); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } @@ -836,12 +799,12 @@ private void initHeadTail(Node<E> h, Node<E> t) { if (h == t) { if (h == null) - h = t = new Node<E>(null); + h = t = new Node<E>(); else { // Avoid edge case of a single Node with non-null item. - Node<E> newNode = new Node<E>(null); - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + Node<E> newNode = new Node<E>(); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } @@ -934,7 +897,7 @@ public E pollFirst() { for (Node<E> p = first(); p != null; p = succ(p)) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } @@ -945,7 +908,7 @@ public E pollLast() { for (Node<E> p = last(); p != null; p = pred(p)) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } @@ -1031,7 +994,8 @@ Objects.requireNonNull(o); for (Node<E> p = first(); p != null; p = succ(p)) { E item = p.item; - if (item != null && o.equals(item) && p.casItem(item, null)) { + if (item != null && o.equals(item) && + ITEM.compareAndSet(p, item, null)) { unlink(p); return true; } @@ -1055,7 +1019,8 @@ Objects.requireNonNull(o); for (Node<E> p = last(); p != null; p = pred(p)) { E item = p.item; - if (item != null && o.equals(item) && p.casItem(item, null)) { + if (item != null && o.equals(item) && + ITEM.compareAndSet(p, item, null)) { unlink(p); return true; } @@ -1159,12 +1124,12 @@ // Copy c into a private chain of Nodes Node<E> beginningOfTheEnd = null, last = null; for (E e : c) {