This commit is contained in:
Alejandro Murillo 2016-07-22 04:05:04 +00:00
commit 15fafcc1fc
454 changed files with 15705 additions and 9446 deletions

View file

@ -370,3 +370,4 @@ d53037a90c441cb528dc41c30827985de0e67c62 jdk-9+123
3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125
03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126
8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127
5bf88dce615f6804f9e101a96ffa7c9dfb4fbbbe jdk-9+128

View file

@ -370,3 +370,4 @@ f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
8fa686245bd2a072ece3392743460030f0854520 jdk-9+127
b30ae794d974d7dd3eb4e84203f70021823fa6c6 jdk-9+128

View file

@ -345,6 +345,9 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS],
# 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])

View file

@ -5094,7 +5094,7 @@ VS_SDK_PLATFORM_NAME_2013=
#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 @@ $as_echo_n "checking flags for boot jdk java command ... " >&6; }
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

View file

@ -370,3 +370,4 @@ e33a34cc551907617d8129c4faaf1a5a7e61d21c jdk-9+123
1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127
1f093d3f8cd99cd37c3b0af4cf5c3bffaa9c8b98 jdk-9+128

View file

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

View file

@ -34,21 +34,13 @@ import java.security.PermissionCollection;
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 @@ public final class ORBUtility {
* 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 @@ public final class ORBUtility {
* 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;

View file

@ -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 javax.rmi.CORBA.Tie;
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 class Util {
*/
public static ValueHandler createValueHandler() {
isCustomSerializationPermitted();
if (utilDelegate != null) {
return utilDelegate.createValueHandler();
}
@ -337,6 +340,7 @@ public class Util {
// 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 @@ public class Util {
className = props.getProperty(classKey);
}
}
if (className == null) {
return new com.sun.corba.se.impl.javax.rmi.CORBA.Util();
}
@ -389,4 +392,14 @@ public class Util {
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"));
}
}
}

View file

@ -530,3 +530,4 @@ af6b4ad908e732d23021f12e8322b204433d5cf6 jdk-9+122
bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
22bf6db9767b1b3a1994cbf32eb3331f31ae2093 jdk-9+128

View file

@ -270,6 +270,10 @@ public:
// For reading from/writing to the CDS archive
void serialize(SerializeClosure* soc);
uintx base_address() {
return (uintx) _base_address;
}
};
////////////////////////////////////////////////////////////////////////

View file

@ -238,6 +238,29 @@ Symbol* SymbolTable::lookup(int index, const char* name,
}
}
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() ?

View file

@ -253,6 +253,8 @@ public:
// 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();

View file

@ -78,7 +78,19 @@ public:
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

View file

@ -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 @@ VerificationType VerificationType::from_tag(u1 tag) {
}
}
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 @@ bool VerificationType::is_reference_assignable_from(
// 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);

View file

@ -333,6 +333,12 @@ class VerificationType VALUE_OBJ_CLASS_SPEC {
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

View file

@ -2377,9 +2377,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
case Bytecodes::_ifnonnull:
target = bcs.dest();
if (visited_branches->contains(bci)) {
if (bci_stack->is_empty()) return true;
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 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
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;
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.

View file

@ -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 @@ Bytecodes::Code RawBytecodeStream::raw_next_special(Bytecodes::Code code) {
// 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

View file

@ -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 @@ class RawBytecodeStream: public BaseBytecodeStream {
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,9 +192,12 @@ class BytecodeStream: public BaseBytecodeStream {
// 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;
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;
@ -203,6 +209,7 @@ class BytecodeStream: public BaseBytecodeStream {
}
assert(Bytecodes::is_java_code(code), "sanity check");
}
}
_raw_code = raw_code;
_code = code;
return _code;

View file

@ -60,6 +60,7 @@ bool MetaspaceShared::_link_classes_made_progress;
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 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
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 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
return false;
}
_remapped_readwrite = true;
}
return true;
}

View file

@ -125,6 +125,7 @@ class MetaspaceShared : AllStatic {
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 @@ class MetaspaceShared : AllStatic {
// 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();

View file

@ -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 @@ bool InstanceKlass::link_class_impl(
// 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 @@ bool InstanceKlass::link_class_impl(
// 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);

View file

@ -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 @@ inline InstanceKlass* klassVtable::ik() const {
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::index_of(Method* m, int len) const {
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 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
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 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
#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 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
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 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
// needs new entry
if (needs_new_entry) {
put_method_at(mh(), initialized);
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 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
}
// 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 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
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,8 +507,16 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
target_method()->set_vtable_index(i);
} else {
if (def_vtable_indices != NULL) {
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,6 +530,14 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
}
void klassVtable::put_method_at(Method* m, int index) {
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();
@ -502,6 +550,7 @@ void klassVtable::put_method_at(Method* m, int index) {
}
table()[index].set(m);
}
}
// Find out if a method "m" with superclass "super", loader "classloader" and
// name "classname" needs a new vtable entry. Let P be a class package defined
@ -950,8 +999,16 @@ bool klassVtable::is_initialized() {
void itableMethodEntry::initialize(Method* m) {
if (m == NULL) return;
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) {
_klass = klass;
@ -1054,7 +1111,11 @@ int klassItable::assign_itable_indices_for_interface(Klass* klass) {
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 @@ void klassItable::dump_itable() {
}
#endif // INCLUDE_JVMTI
// Setup
class InterfaceVisiterClosure : public StackObj {
public:

View file

@ -153,6 +153,19 @@ class klassVtable : public ResourceObj {
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();
};

View file

@ -313,6 +313,33 @@ void Method::remove_unshareable_info() {
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; // dont 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.

View file

@ -470,12 +470,12 @@ class Method : public Metadata {
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; }

View file

@ -2405,8 +2405,13 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c
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 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
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 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
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 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
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

View file

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

View file

@ -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");
}
}

View file

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

View file

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

View file

@ -370,3 +370,4 @@ e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124
493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125
15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126
bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127
8a7681a9d70640ac7fbf05c28f53c1d51d8d00a1 jdk-9+128

View file

@ -80,6 +80,14 @@ public final class XalanConstants {
*/
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 @@ public final class XalanConstants {
* 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
*/

View file

@ -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 @@ public final class XMLSecurityManager {
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;

View file

@ -1,13 +1,13 @@
/*
* 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 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 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@ -98,6 +98,10 @@ public interface Constants extends InstructionConstants {
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
@ -248,6 +252,10 @@ public interface Constants extends InstructionConstants {
= "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl";
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
@ -335,6 +343,19 @@ public interface Constants extends InstructionConstants {
= "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";

View file

@ -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.INVOKEVIRTUAL;
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 @@ class FunctionCall extends Expression {
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 @@ class FunctionCall extends Expression {
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 @@ class FunctionCall extends Expression {
}
}
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 + ')';

View file

@ -58,7 +58,6 @@ import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.URIResolver;
import jdk.internal.module.Modules;
/**
* @author Morten Jorgensen
@ -486,10 +485,6 @@ public final class TemplatesImpl implements Templates, Serializable {
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 @@ public final class TemplatesImpl implements Templates, Serializable {
}
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);

View file

@ -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 final class Constants {
*/
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 @@ public final class Constants {
* 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
*/

View file

@ -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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* 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.
*
* 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 @@ public class XML11DTDScannerImpl
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 @@ public class XML11DTDScannerImpl
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) {

View file

@ -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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* 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.
*
* 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 @@ public class XML11DocumentScannerImpl
// happens when there is the character reference &#13;
// 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 @@ public class XML11DocumentScannerImpl
} */
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 @@ public class XML11DocumentScannerImpl
// 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 @@ public class XML11DocumentScannerImpl
* @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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
+ 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 @@ public class XML11DocumentScannerImpl
}
}
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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
// 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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
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 @@ public class XML11DocumentScannerImpl
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) {

View file

@ -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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
}
// scan character
int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[fCurrentEntity.position++];
boolean external = false;
if (c == '\n' ||
@ -110,6 +112,7 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
// return character that was scanned
fCurrentEntity.columnNumber++;
if (!detectingVersion) {
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
}
return c;
} // scanChar():int
@ -141,7 +147,7 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
* <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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
}
}
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 @@ public class XML11EntityScanner
--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 @@ public class XML11EntityScanner
}
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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
* 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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
}
int index = -1;
int length = 0;
boolean sawIncompleteSurrogatePair = false;
do {
ch = fCurrentEntity.ch[fCurrentEntity.position];
@ -653,22 +629,7 @@ public class XML11EntityScanner
//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 @@ public class XML11EntityScanner
}
}
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 @@ public class XML11EntityScanner
--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 @@ public class XML11EntityScanner
}
while (true);
int length = fCurrentEntity.position - offset;
length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length;
if (length > 0) {
@ -776,6 +711,7 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
}
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 @@ public class XML11EntityScanner
}
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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
}
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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
}
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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
if (done) {
length -= delimLen;
}
@ -1305,7 +1255,7 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
}
// skip character
int offset = fCurrentEntity.position;
int cc = fCurrentEntity.ch[fCurrentEntity.position];
if (cc == c) {
fCurrentEntity.position++;
@ -1323,12 +1274,14 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
}
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
}
@ -1366,7 +1320,7 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
// 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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
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 @@ public class XML11EntityScanner
* @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 @@ public class XML11EntityScanner
// 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 @@ public class XML11EntityScanner
}
}
fCurrentEntity.columnNumber += length;
if (!detectingVersion) {
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
}
return true;
} // skipString(String):boolean

View file

@ -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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// 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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// 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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
//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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// 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 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// end
fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) {
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError(
"ETagUnterminated",
new Object[] { endElementName.rawname });

View file

@ -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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInDTD",
new Object[] { Integer.toHexString(c) });
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
}
}
}
@ -767,7 +765,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fReportEntity = false;
skipSeparator(false, !scanningInternalSubset());
// end
if (!fEntityScanner.skipChar('>')) {
if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("ElementDeclUnterminated", new Object[]{name});
}
fReportEntity = true;
@ -967,7 +965,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDContentModelHandler.pcdata(null);
}
skipSeparator(false, !scanningInternalSubset());
while (fEntityScanner.skipChar('|')) {
while (fEntityScanner.skipChar('|', null)) {
fStringBuffer.append('|');
// call handler
if (fDTDContentModelHandler != null) {
@ -976,7 +974,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
int currentOp = 0;
int c;
while (true) {
if (fEntityScanner.skipChar('(')) {
if (fEntityScanner.skipChar('(', null)) {
fMarkUpDepth++;
fStringBuffer.append('(');
// call handler
@ -1057,7 +1055,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
fDTDContentModelHandler.occurrence(oc, null);
}
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
fStringBuffer.append((char)c);
}
while (true) {
@ -1097,7 +1095,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
null);
}
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
fStringBuffer.append(',');
break;
}
@ -1108,7 +1106,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
null);
}
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
fStringBuffer.append('|');
break;
}
@ -1154,7 +1152,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
else {
// no occurrence specified
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
fStringBuffer.append(')');
}
fMarkUpDepth--;
@ -1186,7 +1184,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
skipSeparator(false, !scanningInternalSubset());
// end
if (!fEntityScanner.skipChar('>')) {
if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("EntityDeclUnterminated", new Object[]{name});
}
fMarkUpDepth--;
@ -1650,7 +1648,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fStringBuffer.append(eName);
fStringBuffer2.append(eName);
}
if (!fEntityScanner.skipChar(';')) {
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference",
new Object[]{eName});
}
@ -1700,15 +1699,15 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
}
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
value.setValues(literal);
nonNormalizedValue.setValues(literal2);
if (fLimitAnalyzer != null) {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
skipSeparator(false, !scanningInternalSubset());
// end
if (!fEntityScanner.skipChar('>')) {
if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("NotationDeclUnterminated", new Object[]{name});
}
fMarkUpDepth--;
@ -1863,7 +1868,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
XMLErrorReporter.SEVERITY_ERROR);
}
// call handler
if (!fEntityScanner.skipChar('[')) {
if (!fEntityScanner.skipChar('[', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
}
@ -1888,7 +1893,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fIgnoreConditionalBuffer.clear();
}
while (true) {
if (fEntityScanner.skipChar('<')) {
if (fEntityScanner.skipChar('<', null)) {
if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append('<');
}
@ -1905,8 +1910,8 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// 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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
}
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
}
}
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
//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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
else if (fEntityScanner.skipString("NOTATION")) {
scanNotationDecl();
}
else if (fEntityScanner.skipChar('[') &&
else if (fEntityScanner.skipChar('[', null) &&
!scanningInternalSubset()) {
scanConditionalSect(fPEDepth);
}
@ -2033,10 +2038,10 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
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;
}

View file

@ -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.XMLConfigurationException;
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 @@ public class XMLDocumentFragmentScannerImpl
//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 @@ public class XMLDocumentFragmentScannerImpl
//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 @@ public class XMLDocumentFragmentScannerImpl
//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 @@ public class XMLDocumentFragmentScannerImpl
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 @@ public class XMLDocumentFragmentScannerImpl
// 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 @@ public class XMLDocumentFragmentScannerImpl
// 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 @@ public class XMLDocumentFragmentScannerImpl
//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 @@ public class XMLDocumentFragmentScannerImpl
if (c == '\r') {
// happens when there is the character reference &#13;
//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 @@ public class XMLDocumentFragmentScannerImpl
// 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 @@ public class XMLDocumentFragmentScannerImpl
} 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 @@ public class XMLDocumentFragmentScannerImpl
// end
fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) {
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError("ETagUnterminated",
new Object[]{rawname});
}
@ -1841,12 +1841,12 @@ public class XMLDocumentFragmentScannerImpl
* 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 @@ public class XMLDocumentFragmentScannerImpl
*/
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 @@ public class XMLDocumentFragmentScannerImpl
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 @@ public class XMLDocumentFragmentScannerImpl
}
case '/' :{
setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
fEntityScanner.skipChar(ch);
fEntityScanner.skipChar(ch, NameType.ELEMENTEND);
break;
}
default :{
@ -2641,9 +2642,9 @@ public class XMLDocumentFragmentScannerImpl
}//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 @@ public class XMLDocumentFragmentScannerImpl
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 @@ public class XMLDocumentFragmentScannerImpl
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 @@ public class XMLDocumentFragmentScannerImpl
}
// happens when there is the character reference &#13;
//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 @@ public class XMLDocumentFragmentScannerImpl
//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 @@ public class XMLDocumentFragmentScannerImpl
// 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 @@ public class XMLDocumentFragmentScannerImpl
// 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 @@ public class XMLDocumentFragmentScannerImpl
reportFatalError("InvalidCharInContent",
new Object[] {
Integer.toString(c, 16)});
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
}
break;
}
@ -3050,7 +3051,7 @@ public class XMLDocumentFragmentScannerImpl
}
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 @@ public class XMLDocumentFragmentScannerImpl
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);

View file

@ -631,7 +631,7 @@ public class XMLDocumentScannerImpl
}
// 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 @@ public class XMLDocumentScannerImpl
// 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 @@ public class XMLDocumentScannerImpl
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 @@ public class XMLDocumentScannerImpl
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 @@ public class XMLDocumentScannerImpl
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 @@ public class XMLDocumentScannerImpl
reportFatalError("MarkupNotRecognizedInProlog",
null);
}
} else if (fEntityScanner.skipChar('?')) {
} else if (fEntityScanner.skipChar('?', null)) {
setScannerState(SCANNER_STATE_PI);
} else {
reportFatalError("MarkupNotRecognizedInProlog",
@ -991,7 +991,7 @@ public class XMLDocumentScannerImpl
case SCANNER_STATE_CONTENT: {
reportFatalError("ContentIllegalInProlog", null);
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
}
case SCANNER_STATE_REFERENCE: {
reportFatalError("ReferenceIllegalInProlog", null);
@ -1105,11 +1105,11 @@ public class XMLDocumentScannerImpl
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 @@ public class XMLDocumentScannerImpl
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 @@ public class XMLDocumentScannerImpl
}
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 @@ public class XMLDocumentScannerImpl
} else{
reportFatalError("ContentIllegalInTrailingMisc",
null);
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_TRAILING_MISC);
return XMLEvent.CHARACTERS;
}

View file

@ -2066,6 +2066,7 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
// system id has to be a valid URI
if (strict) {
try {
// if it's already an absolute one, return it
new URI(systemId);

View file

@ -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 @@ public class XMLEntityScanner implements XMLLocator {
// 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 @@ public class XMLEntityScanner implements XMLLocator {
* <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 @@ public class XMLEntityScanner implements XMLLocator {
}
// scan character
int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[fCurrentEntity.position++];
if (c == '\n' || (c == '\r' && isExternal)) {
fCurrentEntity.lineNumber++;
@ -554,6 +561,7 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
System.out.println(" -> '"+(char)c+"'");
}
fCurrentEntity.columnNumber++;
if (!detectingVersion) {
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
}
return c;
} // scanChar():int
@ -589,7 +600,7 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
* <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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
// 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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
}
}
}
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 @@ public class XMLEntityScanner implements XMLLocator {
* 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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
//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 @@ public class XMLEntityScanner implements XMLLocator {
}
}
}
int length = fCurrentEntity.position - offset;
length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length;
if (length > 0) {
String prefix = null;
@ -885,6 +876,7 @@ public class XMLEntityScanner implements XMLLocator {
print();
System.out.println(" -> true");
}
checkEntityLimit(nt, fCurrentEntity, offset, length);
return true;
}
}
@ -899,23 +891,105 @@ public class XMLEntityScanner implements XMLLocator {
} // 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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
}
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 @@ public class XMLEntityScanner implements XMLLocator {
}
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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
}
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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
}
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 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
* 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 @@ public class XMLEntityScanner implements XMLLocator {
}
// skip character
int offset = fCurrentEntity.position;
int cc = fCurrentEntity.ch[fCurrentEntity.position];
if (cc == c) {
fCurrentEntity.position++;
@ -1478,6 +1567,7 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
print();
System.out.println(" -> true");
}
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
}
@ -1526,7 +1617,7 @@ public class XMLEntityScanner implements XMLLocator {
*
* @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 @@ public class XMLEntityScanner implements XMLLocator {
// 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 @@ public class XMLEntityScanner implements XMLLocator {
} 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 @@ public class XMLEntityScanner implements XMLLocator {
/**
* @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 @@ public class XMLEntityScanner implements XMLLocator {
}
/**
* @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 @@ public class XMLEntityScanner implements XMLLocator {
* @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 @@ public class XMLEntityScanner implements XMLLocator {
if(afterSkip-- == beforeSkip){
fCurrentEntity.position = fCurrentEntity.position + length ;
fCurrentEntity.columnNumber += length;
if (!detectingVersion) {
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
}
return true;
}
}
@ -1722,7 +1822,7 @@ public class XMLEntityScanner implements XMLLocator {
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 @@ public class XMLEntityScanner implements XMLLocator {
}
fCurrentEntity.position = fCurrentEntity.position + length ;
fCurrentEntity.columnNumber += length;
if (!detectingVersion) {
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
}
return true;
}
@ -2138,7 +2241,7 @@ public class XMLEntityScanner implements XMLLocator {
*
* @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);

View file

@ -189,9 +189,9 @@ public class XMLNSDocumentScannerImpl
// 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 @@ public class XMLNSDocumentScannerImpl
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 @@ public class XMLNSDocumentScannerImpl
//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,

View file

@ -114,6 +114,30 @@ public abstract class XMLScanner
/** 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
@ -173,13 +197,13 @@ public abstract class XMLScanner
/** 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 @@ public abstract class XMLScanner
}
// 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 @@ public abstract class XMLScanner
* <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 @@ public abstract class XMLScanner
reportFatalError("PseudoAttrNameExpected", null);
}
fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('=')) {
if (!fEntityScanner.skipChar('=', null)) {
reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl"
: "EqRequiredInXMLDecl", new Object[]{name});
}
@ -598,15 +622,15 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
? "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 @@ public abstract class XMLScanner
// target
fReportEntity = false;
String target = fEntityScanner.scanName();
String target = fEntityScanner.scanName(NameType.PI);
if (target == null) {
reportFatalError("PITargetRequired", null);
}
@ -745,7 +769,7 @@ public abstract class XMLScanner
} else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInPI",
new Object[]{Integer.toHexString(c)});
fEntityScanner.scanChar();
fEntityScanner.scanChar(null);
}
}
} while (fEntityScanner.scanData("?>", data));
@ -786,11 +810,11 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
* @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 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
+ 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 @@ public abstract class XMLScanner
}
}
} 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 @@ public abstract class XMLScanner
} 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 @@ public abstract class XMLScanner
+ 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 @@ public abstract class XMLScanner
} 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,13 +1004,46 @@ public abstract class XMLScanner
nonNormalizedValue.setValues(fStringBuffer2);
// quote
int cquote = fEntityScanner.scanChar();
int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName, atName});
}
} // scanAttributeValue()
/**
* 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.
*
@ -1064,25 +1087,25 @@ public abstract class XMLScanner
}
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 @@ public abstract class XMLScanner
*/
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 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
*/
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 @@ public abstract class XMLScanner
(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 @@ public abstract class XMLScanner
(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 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
}
// end
if (!fEntityScanner.skipChar(';')) {
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInCharRef", null);
}
if (buf2 != null) { buf2.append(';'); }
@ -1347,6 +1371,9 @@ public abstract class XMLScanner
}
}
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 @@ public abstract class XMLScanner
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 @@ public abstract class XMLScanner
}
}
/**
* 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

View file

@ -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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* 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.
*
* 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 @@ public class XMLVersionDetector {
// 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 @@ public class XMLVersionDetector {
"PrematureEOF",
null,
XMLErrorReporter.SEVERITY_FATAL_ERROR);
scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0;
}
}
// This method prepends "length" chars from the char array,

View file

@ -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}\".

View file

@ -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 @@ public final class XMLLimitAnalyzer {
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 @@ public final class XMLLimitAnalyzer {
* @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 final class XMLLimitAnalyzer {
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;
}
}

View file

@ -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 @@ public final class XMLSecurityManager {
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 @@ public final class XMLSecurityManager {
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()
) {

View file

@ -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 @@ import static org.testng.Assert.assertEquals;
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

View file

@ -373,3 +373,4 @@ c42decd28bbfa817347112ed6053b5fbd30517a2 jdk-9+123
5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125
264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126
06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127
fe4e11bd2423635dc0f5f5cb9a64eb2f2cce7f4c jdk-9+128

View file

@ -370,3 +370,4 @@ c40c8739bcdc88892ff58ebee3fd8a3f287be94d jdk-9+123
073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125
6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126
7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127
9446c534f0222b4eecfd9d9e25ab37c4fd4400a5 jdk-9+128

View file

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

View file

@ -177,8 +177,14 @@ void setOSNameAndVersion(java_props_t *sprops) {
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",
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]);
}

View file

@ -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 @@ abstract class AESCipher extends CipherSpi {
*/
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 @@ abstract class AESCipher extends CipherSpi {
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 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, params, random);
}
@ -344,6 +351,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, params, random);
}
@ -368,6 +376,7 @@ abstract class AESCipher extends CipherSpi {
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
updateCalled = true;
return core.update(input, inputOffset, inputLen);
}
@ -397,6 +406,7 @@ abstract class AESCipher extends CipherSpi {
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 @@ abstract class AESCipher extends CipherSpi {
*/
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 @@ abstract class AESCipher extends CipherSpi {
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
return core.doFinal(input, inputOffset, inputLen, output,
int outLen = core.doFinal(input, inputOffset, inputLen, output,
outputOffset);
updateCalled = false;
return outLen;
}
/**
@ -574,6 +588,9 @@ abstract class AESCipher extends CipherSpi {
*/
@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 @@ abstract class AESCipher extends CipherSpi {
*/
@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) {

View file

@ -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 @@ final class CipherCore {
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 @@ final class CipherCore {
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 @@ final class CipherCore {
}
}
/**
* 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

View file

@ -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 @@ final class GaloisCounterMode extends FeedbackCipher {
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 @@ final class GaloisCounterMode extends FeedbackCipher {
}
}
// 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 @@ final class GaloisCounterMode extends FeedbackCipher {
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 @@ final class GaloisCounterMode extends FeedbackCipher {
} 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,10 +349,10 @@ final class GaloisCounterMode extends FeedbackCipher {
// Feed the AAD data to GHASH, pad if necessary
void processAAD() {
if (aadBuffer != null && aadBuffer.size() > 0) {
if (aadBuffer != null) {
if (aadBuffer.size() > 0) {
byte[] aad = aadBuffer.toByteArray();
sizeOfAAD = aad.length;
aadBuffer = null;
int lastLen = aad.length % AES_BLOCK_SIZE;
if (lastLen != 0) {
@ -334,6 +364,8 @@ final class GaloisCounterMode extends FeedbackCipher {
ghashAllToS.update(aad);
}
}
aadBuffer = null;
}
}
// Utility to process the last block; used by encryptFinal and decryptFinal
@ -384,6 +416,9 @@ final class GaloisCounterMode extends FeedbackCipher {
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 @@ final class GaloisCounterMode extends FeedbackCipher {
*/
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 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
checkDataLength(ibuffer.size(), len);
processAAD();
if (len > 0) {
@ -481,10 +525,21 @@ final class GaloisCounterMode extends FeedbackCipher {
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 @@ final class GaloisCounterMode extends FeedbackCipher {
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();

View file

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

View file

@ -238,15 +238,11 @@ public final class Class<T> implements java.io.Serializable,
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++)

View file

@ -945,7 +945,7 @@ public class Runtime {
}
/**
* A representation of a version string for an implemenation of the
* A representation of a version string for an implementation of the
* Java&nbsp;SE Platform. A version string contains a version number
* optionally followed by pre-release and build information.
*
@ -1058,10 +1058,10 @@ public class Runtime {
* <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 @@ public class Runtime {
* @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 @@ public class Runtime {
return ret;
if (!ignoreOpt)
return compareOpt(ob);
return compareOptional(ob);
return 0;
}
@ -1325,7 +1325,7 @@ public class Runtime {
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 @@ public class Runtime {
*/
@Override
public boolean equals(Object ob) {
boolean ret = equalsIgnoreOpt(ob);
boolean ret = equalsIgnoreOptional(ob);
if (!ret)
return false;
@ -1407,7 +1407,7 @@ public class Runtime {
* ignoring the optinal build information
*
*/
public boolean equalsIgnoreOpt(Object ob) {
public boolean equalsIgnoreOptional(Object ob) {
if (this == ob)
return true;
if (!(ob instanceof Version))

View file

@ -155,7 +155,7 @@ class DirectMethodHandle extends MethodHandle {
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;

View file

@ -1049,7 +1049,7 @@ class LambdaForm {
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,12 +1060,12 @@ class LambdaForm {
// 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() &&
member.getDeclaringClass() == MethodHandle.class &&
"invokeBasic".equals(member.getName());
}
@ -1204,7 +1204,7 @@ class LambdaForm {
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];

View file

@ -346,7 +346,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
}
/** 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 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
switch (name) {
case "invoke":
case "invokeExact":
case "invokeBasic": // internal sig-poly method
return true;
default:
return false;

View file

@ -951,8 +951,6 @@ assertEquals("", (String) MH_newString.invokeExact());
return invoker(type);
if ("invokeExact".equals(name))
return exactInvoker(type);
if ("invokeBasic".equals(name))
return basicInvoker(type);
assert(!MemberName.isMethodHandleInvokeName(name));
return null;
}
@ -3268,6 +3266,16 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
*/
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 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// 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 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// 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 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
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 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
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 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
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());
}

View file

@ -52,6 +52,7 @@ import java.util.jar.Manifest;
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 @@ class ModulePath implements ConfigurableModuleFinder {
// 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 @@ class ModulePath implements ConfigurableModuleFinder {
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

View file

@ -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 @@ package java.lang.reflect;
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 @@ public abstract class Executable extends AccessibleObject
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 @@ public abstract class Executable extends AccessibleObject
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 @@ public abstract class Executable extends AccessibleObject
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) {

View file

@ -461,11 +461,10 @@ public final class URLPermission extends Permission {
}
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);
}
/**

View file

@ -132,7 +132,7 @@ public class ProtectionDomain {
/* 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 @@ public class ProtectionDomain {
* 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 @@ public class ProtectionDomain {
* 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
@ -255,6 +261,19 @@ public class ProtectionDomain {
return permissions;
}
/**
* 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.
@ -263,25 +282,19 @@ public class ProtectionDomain {
* 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 @@ public class ProtectionDomain {
}
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;
}

View file

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

View file

@ -124,7 +124,6 @@ package java.util;
* 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>.

View file

@ -660,6 +660,7 @@ public abstract class ResourceBundle {
// 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 @@ public abstract class ResourceBundle {
this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
}
this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
this.providers = getServiceLoader(module, baseName);
calculateHashCode();
}
@ -712,11 +712,15 @@ public abstract class ResourceBundle {
}
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 @@ public abstract class ResourceBundle {
}
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 @@ public abstract class ResourceBundle {
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);

View file

@ -68,6 +68,7 @@ import java.util.function.ToIntFunction;
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
/* ---------------- 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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
* 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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
}
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
}
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
}
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
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,15 +4561,22 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
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();) {
// 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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
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 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
}
// 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;

View file

@ -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 @@ public class ConcurrentLinkedDeque<E>
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.
* Returns a new node holding item. Uses relaxed write because item
* can only be seen after piggy-backing publication via CAS.
*/
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);
}
}
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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
* 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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
// 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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
// 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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
(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 @@ public class ConcurrentLinkedDeque<E>
(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 @@ public class ConcurrentLinkedDeque<E>
}
// 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 @@ public class ConcurrentLinkedDeque<E>
}
// 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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
* Constructs an empty deque.
*/
public ConcurrentLinkedDeque() {
head = tail = new Node<E>(null);
head = tail = new Node<E>();
}
/**
@ -818,12 +781,12 @@ public class ConcurrentLinkedDeque<E>
// 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 @@ public class ConcurrentLinkedDeque<E>
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 class ConcurrentLinkedDeque<E>
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 class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
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 @@ public class ConcurrentLinkedDeque<E>
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
Node<E> newNode = newNode(Objects.requireNonNull(e));
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
last.lazySetNext(newNode);
newNode.lazySetPrev(last);
NEXT.set(last, newNode);
PREV.set(newNode, last);
last = newNode;
}
}
@ -1184,16 +1149,16 @@ public class ConcurrentLinkedDeque<E>
continue restartFromTail;
else {
// p is last node
beginningOfTheEnd.lazySetPrev(p); // CAS piggyback
if (p.casNext(null, beginningOfTheEnd)) {
PREV.set(beginningOfTheEnd, p); // CAS piggyback
if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this deque.
if (!casTail(t, last)) {
if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
casTail(t, last);
TAIL.weakCompareAndSetVolatile(this, t, last);
}
return true;
}
@ -1586,41 +1551,38 @@ public class ConcurrentLinkedDeque<E>
Node<E> h = null, t = null;
for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked")
Node<E> newNode = new Node<E>((E) item);
Node<E> newNode = newNode((E) item);
if (h == null)
h = t = newNode;
else {
t.lazySetNext(newNode);
newNode.lazySetPrev(t);
NEXT.set(t, newNode);
PREV.set(newNode, t);
t = newNode;
}
}
initHeadTail(h, t);
}
private boolean casHead(Node<E> cmp, Node<E> val) {
return U.compareAndSwapObject(this, HEAD, cmp, val);
}
private boolean casTail(Node<E> cmp, Node<E> val) {
return U.compareAndSwapObject(this, TAIL, cmp, val);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long HEAD;
private static final long TAIL;
// VarHandle mechanics
private static final VarHandle HEAD;
private static final VarHandle TAIL;
private static final VarHandle PREV;
private static final VarHandle NEXT;
private static final VarHandle ITEM;
static {
PREV_TERMINATOR = new Node<Object>();
PREV_TERMINATOR.next = PREV_TERMINATOR;
NEXT_TERMINATOR = new Node<Object>();
NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
try {
HEAD = U.objectFieldOffset
(ConcurrentLinkedDeque.class.getDeclaredField("head"));
TAIL = U.objectFieldOffset
(ConcurrentLinkedDeque.class.getDeclaredField("tail"));
MethodHandles.Lookup l = MethodHandles.lookup();
HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head",
Node.class);
TAIL = l.findVarHandle(ConcurrentLinkedDeque.class, "tail",
Node.class);
PREV = l.findVarHandle(Node.class, "prev", Node.class);
NEXT = l.findVarHandle(Node.class, "next", Node.class);
ITEM = l.findVarHandle(Node.class, "item", Object.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@ -166,9 +168,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* this is merely an optimization.
*
* When constructing a Node (before enqueuing it) we avoid paying
* for a volatile write to item by using Unsafe.putObject instead
* of a normal write. This allows the cost of enqueue to be
* "one-and-a-half" CASes.
* for a volatile write to item. This allows the cost of enqueue
* to be "one-and-a-half" CASes.
*
* Both head and tail may or may not point to a Node with a
* non-null item. If the queue is empty, all items must of course
@ -178,33 +179,21 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* optimization.
*/
private static class Node<E> {
static final class Node<E> {
volatile E item;
volatile Node<E> next;
}
/**
* Returns a new node holding item. Uses relaxed write because item
* can only be seen after piggy-backing publication via casNext.
* can only be seen after piggy-backing publication via CAS.
*/
static <E> Node<E> newNode(E item) {
Node<E> node = new Node<E>();
U.putObject(node, ITEM, item);
ITEM.set(node, item);
return node;
}
static <E> boolean casItem(Node<E> node, E cmp, E val) {
return U.compareAndSwapObject(node, ITEM, cmp, val);
}
static <E> void lazySetNext(Node<E> node, Node<E> val) {
U.putObjectRelease(node, NEXT, val);
}
static <E> boolean casNext(Node<E> node, Node<E> cmp, Node<E> val) {
return U.compareAndSwapObject(node, NEXT, cmp, val);
}
/**
* A node from which the first live (non-deleted) node (if any)
* can be reached in O(1) time.
@ -256,7 +245,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
if (h == null)
h = t = newNode;
else {
lazySetNext(t, newNode);
NEXT.set(t, newNode);
t = newNode;
}
}
@ -286,8 +275,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
*/
final void updateHead(Node<E> h, Node<E> p) {
// assert h != null && p != null && (h == p || h.item == null);
if (h != p && casHead(h, p))
lazySetNext(h, h);
if (h != p && HEAD.compareAndSet(this, h, p))
NEXT.setRelease(h, h);
}
/**
@ -314,12 +303,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
Node<E> q = p.next;
if (q == null) {
// p is last node
if (casNext(p, null, newNode)) {
if (NEXT.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// 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 true;
}
// Lost CAS race to another thread; re-read next
@ -342,7 +331,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null && casItem(p, item, null)) {
if (item != null && ITEM.compareAndSet(p, item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
@ -483,12 +472,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
next = succ(p);
continue;
}
removed = casItem(p, item, null);
removed = ITEM.compareAndSet(p, item, null);
}
next = succ(p);
if (pred != null && next != null) // unlink
casNext(pred, p, next);
NEXT.weakCompareAndSetVolatile(pred, p, next);
if (removed)
return true;
}
@ -520,7 +509,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
lazySetNext(last, newNode);
NEXT.set(last, newNode);
last = newNode;
}
}
@ -532,15 +521,15 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
Node<E> q = p.next;
if (q == null) {
// p is last node
if (casNext(p, null, beginningOfTheEnd)) {
if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this queue.
if (!casTail(t, last)) {
if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
casTail(t, last);
TAIL.weakCompareAndSetVolatile(this, t, last);
}
return true;
}
@ -744,7 +733,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
}
// unlink deleted nodes
if ((q = succ(p)) != null)
casNext(pred, p, q);
NEXT.compareAndSet(pred, p, q);
}
}
@ -801,7 +790,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
if (h == null)
h = t = newNode;
else {
lazySetNext(t, newNode);
NEXT.set(t, newNode);
t = newNode;
}
}
@ -919,31 +908,20 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
return new CLQSpliterator<E>(this);
}
private boolean casTail(Node<E> cmp, Node<E> val) {
return U.compareAndSwapObject(this, TAIL, cmp, val);
}
private boolean casHead(Node<E> cmp, Node<E> val) {
return U.compareAndSwapObject(this, HEAD, cmp, val);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long HEAD;
private static final long TAIL;
private static final long ITEM;
private static final long NEXT;
// VarHandle mechanics
private static final VarHandle HEAD;
private static final VarHandle TAIL;
private static final VarHandle ITEM;
private static final VarHandle NEXT;
static {
try {
HEAD = U.objectFieldOffset
(ConcurrentLinkedQueue.class.getDeclaredField("head"));
TAIL = U.objectFieldOffset
(ConcurrentLinkedQueue.class.getDeclaredField("tail"));
ITEM = U.objectFieldOffset
(Node.class.getDeclaredField("item"));
NEXT = U.objectFieldOffset
(Node.class.getDeclaredField("next"));
MethodHandles.Lookup l = MethodHandles.lookup();
HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head",
Node.class);
TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail",
Node.class);
ITEM = l.findVarHandle(Node.class, "item", Object.class);
NEXT = l.findVarHandle(Node.class, "next", Node.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
@ -401,7 +403,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* compareAndSet head node.
*/
private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
return U.compareAndSwapObject(this, HEAD, cmp, val);
return HEAD.compareAndSet(this, cmp, val);
}
/* ---------------- Nodes -------------- */
@ -444,14 +446,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* compareAndSet value field.
*/
boolean casValue(Object cmp, Object val) {
return U.compareAndSwapObject(this, VALUE, cmp, val);
return VALUE.compareAndSet(this, cmp, val);
}
/**
* compareAndSet next field.
*/
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
return U.compareAndSwapObject(this, NEXT, cmp, val);
return NEXT.compareAndSet(this, cmp, val);
}
/**
@ -532,18 +534,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
private static final long NEXT;
// VarHandle mechanics
private static final VarHandle VALUE;
private static final VarHandle NEXT;
static {
try {
VALUE = U.objectFieldOffset
(Node.class.getDeclaredField("value"));
NEXT = U.objectFieldOffset
(Node.class.getDeclaredField("next"));
MethodHandles.Lookup l = MethodHandles.lookup();
VALUE = l.findVarHandle(Node.class, "value", Object.class);
NEXT = l.findVarHandle(Node.class, "next", Node.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -577,7 +575,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
* compareAndSet right field.
*/
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
return U.compareAndSwapObject(this, RIGHT, cmp, val);
return RIGHT.compareAndSet(this, cmp, val);
}
/**
@ -613,13 +611,12 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
return node.value != null && casRight(succ, succ.right);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long RIGHT;
// VarHandle mechanics
private static final VarHandle RIGHT;
static {
try {
RIGHT = U.objectFieldOffset
(Index.class.getDeclaredField("right"));
MethodHandles.Lookup l = MethodHandles.lookup();
RIGHT = l.findVarHandle(Index.class, "right", Index.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -3607,13 +3604,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
}
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long HEAD;
// VarHandle mechanics
private static final VarHandle HEAD;
static {
try {
HEAD = U.objectFieldOffset
(ConcurrentSkipListMap.class.getDeclaredField("head"));
MethodHandles.Lookup l = MethodHandles.lookup();
HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head",
HeadIndex.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
@ -507,15 +509,16 @@ public class ConcurrentSkipListSet<E>
// Support for resetting map in clone
private void setMap(ConcurrentNavigableMap<E,Object> map) {
U.putObjectVolatile(this, MAP, map);
MAP.setVolatile(this, map);
}
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long MAP;
// VarHandle mechanics
private static final VarHandle MAP;
static {
try {
MAP = U.objectFieldOffset
(ConcurrentSkipListSet.class.getDeclaredField("m"));
MethodHandles.Lookup l = MethodHandles.lookup();
MAP = l.findVarHandle(ConcurrentSkipListSet.class, "m",
ConcurrentNavigableMap.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -34,6 +34,7 @@
package java.util.concurrent;
import java.lang.reflect.Field;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
@ -1541,18 +1542,22 @@ public class CopyOnWriteArrayList<E>
}
}
// Support for resetting lock while deserializing
/** Initializes the lock; for use when deserializing or cloning. */
private void resetLock() {
U.putObjectVolatile(this, LOCK, new Object());
}
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long LOCK;
static {
Field lockField = java.security.AccessController.doPrivileged(
(java.security.PrivilegedAction<Field>) () -> {
try {
LOCK = U.objectFieldOffset
(CopyOnWriteArrayList.class.getDeclaredField("lock"));
Field f = CopyOnWriteArrayList.class
.getDeclaredField("lock");
f.setAccessible(true);
return f;
} catch (ReflectiveOperationException e) {
throw new Error(e);
}});
try {
lockField.set(this, new Object());
} catch (IllegalAccessException e) {
throw new Error(e);
}
}
}

View file

@ -35,6 +35,9 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
/**
* A {@link ForkJoinTask} with a completion action performed when
* triggered and there are no remaining pending actions.
@ -524,7 +527,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
* @param delta the value to add
*/
public final void addToPendingCount(int delta) {
U.getAndAddInt(this, PENDING, delta);
PENDING.getAndAdd(this, delta);
}
/**
@ -536,7 +539,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
* @return {@code true} if successful
*/
public final boolean compareAndSetPendingCount(int expected, int count) {
return U.compareAndSwapInt(this, PENDING, expected, count);
return PENDING.compareAndSet(this, expected, count);
}
/**
@ -548,7 +551,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
public final int decrementPendingCountUnlessZero() {
int c;
do {} while ((c = pending) != 0 &&
!U.compareAndSwapInt(this, PENDING, c, c - 1));
!PENDING.weakCompareAndSetVolatile(this, c, c - 1));
return c;
}
@ -581,7 +584,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
return;
}
}
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
return;
}
}
@ -604,7 +607,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
return;
}
}
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
return;
}
}
@ -649,7 +652,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
for (int c;;) {
if ((c = pending) == 0)
return this;
else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1))
return null;
}
}
@ -753,13 +756,13 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
*/
protected void setRawResult(T t) { }
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long PENDING;
// VarHandle mechanics
private static final VarHandle PENDING;
static {
try {
PENDING = U.objectFieldOffset
(CountedCompleter.class.getDeclaredField("pending"));
MethodHandles.Lookup l = MethodHandles.lookup();
PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -36,6 +36,10 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
/**
* A synchronization point at which threads can pair and swap elements
* within pairs. Each thread presents some object on entry to the
@ -155,9 +159,7 @@ public class Exchanger<V> {
* a value that is enough for common platforms. Additionally,
* extra care elsewhere is taken to avoid other false/unintended
* sharing and to enhance locality, including adding padding (via
* @Contended) to Nodes, embedding "bound" as an Exchanger field,
* and reworking some park/unpark mechanics compared to
* LockSupport versions.
* @Contended) to Nodes, embedding "bound" as an Exchanger field.
*
* The arena starts out with only one used slot. We expand the
* effective arena size by tracking collisions; i.e., failed CASes
@ -233,29 +235,23 @@ public class Exchanger<V> {
* As is too common in this sort of code, methods are monolithic
* because most of the logic relies on reads of fields that are
* maintained as local variables so can't be nicely factored --
* mainly, here, bulky spin->yield->block/cancel code), and
* heavily dependent on intrinsics (Unsafe) to use inlined
* embedded CAS and related memory access operations (that tend
* not to be as readily inlined by dynamic compilers when they are
* hidden behind other methods that would more nicely name and
* encapsulate the intended effects). This includes the use of
* putXRelease to clear fields of the per-thread Nodes between
* uses. Note that field Node.item is not declared as volatile
* even though it is read by releasing threads, because they only
* do so after CAS operations that must precede access, and all
* uses by the owning thread are otherwise acceptably ordered by
* other operations. (Because the actual points of atomicity are
* slot CASes, it would also be legal for the write to Node.match
* in a release to be weaker than a full volatile write. However,
* this is not done because it could allow further postponement of
* the write, delaying progress.)
* mainly, here, bulky spin->yield->block/cancel code. Note that
* field Node.item is not declared as volatile even though it is
* read by releasing threads, because they only do so after CAS
* operations that must precede access, and all uses by the owning
* thread are otherwise acceptably ordered by other operations.
* (Because the actual points of atomicity are slot CASes, it
* would also be legal for the write to Node.match in a release to
* be weaker than a full volatile write. However, this is not done
* because it could allow further postponement of the write,
* delaying progress.)
*/
/**
* The byte distance (as a shift value) between any two used slots
* in the arena. 1 << ASHIFT should be at least cacheline size.
* The index distance (as a shift value) between any two used slots
* in the arena, spacing them out to avoid false sharing.
*/
private static final int ASHIFT = 7;
private static final int ASHIFT = 5;
/**
* The maximum supported arena index. The maximum allocatable
@ -356,27 +352,31 @@ public class Exchanger<V> {
*/
private final Object arenaExchange(Object item, boolean timed, long ns) {
Node[] a = arena;
int alen = a.length;
Node p = participant.get();
for (int i = p.index;;) { // access slot at i
int b, m, c; long j; // j is raw array offset
Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
if (q != null && U.compareAndSwapObject(a, j, q, null)) {
int b, m, c;
int j = (i << ASHIFT) + ((1 << ASHIFT) - 1);
if (j < 0 || j >= alen)
j = alen - 1;
Node q = (Node)AA.getAcquire(a, j);
if (q != null && AA.compareAndSet(a, j, q, null)) {
Object v = q.item; // release
q.match = item;
Thread w = q.parked;
if (w != null)
U.unpark(w);
LockSupport.unpark(w);
return v;
}
else if (i <= (m = (b = bound) & MMASK) && q == null) {
p.item = item; // offer
if (U.compareAndSwapObject(a, j, null, p)) {
if (AA.compareAndSet(a, j, null, p)) {
long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
Thread t = Thread.currentThread(); // wait
for (int h = p.hash, spins = SPINS;;) {
Object v = p.match;
if (v != null) {
U.putObjectRelease(p, MATCH, null);
MATCH.setRelease(p, null);
p.item = null; // clear for next use
p.hash = h;
return v;
@ -389,22 +389,24 @@ public class Exchanger<V> {
(--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield(); // two yields per wait
}
else if (U.getObjectVolatile(a, j) != p)
else if (AA.getAcquire(a, j) != p)
spins = SPINS; // releaser hasn't set match yet
else if (!t.isInterrupted() && m == 0 &&
(!timed ||
(ns = end - System.nanoTime()) > 0L)) {
U.putObject(t, BLOCKER, this); // emulate LockSupport
p.parked = t; // minimize window
if (U.getObjectVolatile(a, j) == p)
U.park(false, ns);
p.parked = null;
U.putObject(t, BLOCKER, null);
if (AA.getAcquire(a, j) == p) {
if (ns == 0L)
LockSupport.park(this);
else
LockSupport.parkNanos(this, ns);
}
else if (U.getObjectVolatile(a, j) == p &&
U.compareAndSwapObject(a, j, p, null)) {
p.parked = null;
}
else if (AA.getAcquire(a, j) == p &&
AA.compareAndSet(a, j, p, null)) {
if (m != 0) // try to shrink
U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
BOUND.compareAndSet(this, b, b + SEQ - 1);
p.item = null;
p.hash = h;
i = p.index >>>= 1; // descend
@ -426,7 +428,7 @@ public class Exchanger<V> {
i = (i != m || m == 0) ? m : m - 1;
}
else if ((c = p.collides) < m || m == FULL ||
!U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
!BOUND.compareAndSet(this, b, b + SEQ + 1)) {
p.collides = c + 1;
i = (i == 0) ? m : i - 1; // cyclically traverse
}
@ -455,24 +457,24 @@ public class Exchanger<V> {
for (Node q;;) {
if ((q = slot) != null) {
if (U.compareAndSwapObject(this, SLOT, q, null)) {
if (SLOT.compareAndSet(this, q, null)) {
Object v = q.item;
q.match = item;
Thread w = q.parked;
if (w != null)
U.unpark(w);
LockSupport.unpark(w);
return v;
}
// create arena on contention, but continue until slot null
if (NCPU > 1 && bound == 0 &&
U.compareAndSwapInt(this, BOUND, 0, SEQ))
BOUND.compareAndSet(this, 0, SEQ))
arena = new Node[(FULL + 2) << ASHIFT];
}
else if (arena != null)
return null; // caller must reroute to arenaExchange
else {
p.item = item;
if (U.compareAndSwapObject(this, SLOT, null, p))
if (SLOT.compareAndSet(this, null, p))
break;
p.item = null;
}
@ -495,19 +497,21 @@ public class Exchanger<V> {
spins = SPINS;
else if (!t.isInterrupted() && arena == null &&
(!timed || (ns = end - System.nanoTime()) > 0L)) {
U.putObject(t, BLOCKER, this);
p.parked = t;
if (slot == p)
U.park(false, ns);
p.parked = null;
U.putObject(t, BLOCKER, null);
if (slot == p) {
if (ns == 0L)
LockSupport.park(this);
else
LockSupport.parkNanos(this, ns);
}
else if (U.compareAndSwapObject(this, SLOT, p, null)) {
p.parked = null;
}
else if (SLOT.compareAndSet(this, p, null)) {
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
break;
}
}
U.putObjectRelease(p, MATCH, null);
MATCH.setRelease(p, null);
p.item = null;
p.hash = h;
return v;
@ -556,8 +560,9 @@ public class Exchanger<V> {
@SuppressWarnings("unchecked")
public V exchange(V x) throws InterruptedException {
Object v;
Node[] a;
Object item = (x == null) ? NULL_ITEM : x; // translate null args
if ((arena != null ||
if (((a = arena) != null ||
(v = slotExchange(item, false, 0L)) == null) &&
((Thread.interrupted() || // disambiguates null return
(v = arenaExchange(item, false, 0L)) == null)))
@ -623,31 +628,18 @@ public class Exchanger<V> {
return (v == NULL_ITEM) ? null : (V)v;
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long BOUND;
private static final long SLOT;
private static final long MATCH;
private static final long BLOCKER;
private static final int ABASE;
// VarHandle mechanics
private static final VarHandle BOUND;
private static final VarHandle SLOT;
private static final VarHandle MATCH;
private static final VarHandle AA;
static {
try {
BOUND = U.objectFieldOffset
(Exchanger.class.getDeclaredField("bound"));
SLOT = U.objectFieldOffset
(Exchanger.class.getDeclaredField("slot"));
MATCH = U.objectFieldOffset
(Node.class.getDeclaredField("match"));
BLOCKER = U.objectFieldOffset
(Thread.class.getDeclaredField("parkBlocker"));
int scale = U.arrayIndexScale(Node[].class);
if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
throw new Error("Unsupported array scale");
// ABASE absorbs padding in front of element 0
ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
MethodHandles.Lookup l = MethodHandles.lookup();
BOUND = l.findVarHandle(Exchanger.class, "bound", int.class);
SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class);
MATCH = l.findVarHandle(Node.class, "match", Object.class);
AA = MethodHandles.arrayElementVarHandle(Node[].class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -36,6 +36,8 @@
package java.util.concurrent;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
@ -92,7 +94,7 @@ import java.util.concurrent.locks.ReentrantLock;
* encountering the exception; minimally only the latter.
*
* <p>It is possible to define and use ForkJoinTasks that may block,
* but doing do requires three further considerations: (1) Completion
* but doing so requires three further considerations: (1) Completion
* of few if any <em>other</em> tasks should be dependent on a task
* that blocks on external synchronization or I/O. Event-style async
* tasks that are never joined (for example, those subclassing {@link
@ -259,7 +261,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
for (int s;;) {
if ((s = status) < 0)
return s;
if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
if (STATUS.compareAndSet(this, s, s | completion)) {
if ((s >>> 16) != 0)
synchronized (this) { notifyAll(); }
return completion;
@ -297,7 +299,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
final void internalWait(long timeout) {
int s;
if ((s = status) >= 0 && // force completer to issue notify
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
try { wait(timeout); } catch (InterruptedException ie) { }
@ -319,7 +321,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
if (s >= 0 && (s = status) >= 0) {
boolean interrupted = false;
do {
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0) {
try {
@ -353,7 +355,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
0)) >= 0) {
while ((s = status) >= 0) {
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(0L);
@ -400,22 +402,24 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
// Exception table support
/**
* Table of exceptions thrown by tasks, to enable reporting by
* callers. Because exceptions are rare, we don't directly keep
* Hash table of exceptions thrown by tasks, to enable reporting
* by callers. Because exceptions are rare, we don't directly keep
* them with task objects, but instead use a weak ref table. Note
* that cancellation exceptions don't appear in the table, but are
* instead recorded as status values.
*
* Note: These statics are initialized below in static block.
* The exception table has a fixed capacity.
*/
private static final ExceptionNode[] exceptionTable;
private static final ReentrantLock exceptionTableLock;
private static final ReferenceQueue<Object> exceptionTableRefQueue;
private static final ExceptionNode[] exceptionTable
= new ExceptionNode[32];
/**
* Fixed capacity for exceptionTable.
*/
private static final int EXCEPTION_MAP_CAPACITY = 32;
/** Lock protecting access to exceptionTable. */
private static final ReentrantLock exceptionTableLock
= new ReentrantLock();
/** Reference queue of stale exceptionally completed tasks. */
private static final ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue
= new ReferenceQueue<ForkJoinTask<?>>();
/**
* Key-value nodes for exception table. The chained hash table
@ -435,7 +439,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
final long thrower; // use id not ref to avoid weak cycles
final int hashCode; // store task hashCode before weak ref disappears
ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next,
ReferenceQueue<Object> exceptionTableRefQueue) {
ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue) {
super(task, exceptionTableRefQueue);
this.ex = ex;
this.next = next;
@ -599,9 +603,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
private static void expungeStaleExceptions() {
for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
if (x instanceof ExceptionNode) {
int hashCode = ((ExceptionNode)x).hashCode;
ExceptionNode[] t = exceptionTable;
int i = hashCode & (t.length - 1);
int i = ((ExceptionNode)x).hashCode & (t.length - 1);
ExceptionNode e = t[i];
ExceptionNode pred = null;
while (e != null) {
@ -1031,7 +1034,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
while ((s = status) >= 0 &&
(ns = deadline - System.nanoTime()) > 0L) {
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(ms); // OK to throw InterruptedException
@ -1324,7 +1327,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
*/
public final short setForkJoinTaskTag(short newValue) {
for (int s;;) {
if (U.compareAndSwapInt(this, STATUS, s = status,
if (STATUS.compareAndSet(this, s = status,
(s & ~SMASK) | (newValue & SMASK)))
return (short)s;
}
@ -1348,7 +1351,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
for (int s;;) {
if ((short)(s = status) != expect)
return false;
if (U.compareAndSwapInt(this, STATUS, s,
if (STATUS.compareAndSet(this, s,
(s & ~SMASK) | (update & SMASK)))
return true;
}
@ -1510,17 +1513,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
setExceptionalCompletion((Throwable)ex);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long STATUS;
// VarHandle mechanics
private static final VarHandle STATUS;
static {
exceptionTableLock = new ReentrantLock();
exceptionTableRefQueue = new ReferenceQueue<Object>();
exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
try {
STATUS = U.objectFieldOffset
(ForkJoinTask.class.getDeclaredField("status"));
MethodHandles.Lookup l = MethodHandles.lookup();
STATUS = l.findVarHandle(ForkJoinTask.class, "status", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -66,8 +66,9 @@ public class ForkJoinWorkerThread extends Thread {
* owning thread.
*
* Support for (non-public) subclass InnocuousForkJoinWorkerThread
* requires that we break quite a lot of encapsulation (via Unsafe)
* both here and in the subclass to access and set Thread fields.
* requires that we break quite a lot of encapsulation (via helper
* methods in ThreadLocalRandom) both here and in the subclass to
* access and set Thread fields.
*/
final ForkJoinPool pool; // the pool this thread works in
@ -92,8 +93,8 @@ public class ForkJoinWorkerThread extends Thread {
ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
AccessControlContext acc) {
super(threadGroup, null, "aForkJoinWorkerThread");
U.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, acc);
eraseThreadLocals(); // clear before registering
ThreadLocalRandom.setInheritedAccessControlContext(this, acc);
ThreadLocalRandom.eraseThreadLocals(this); // clear before registering
this.pool = pool;
this.workQueue = pool.registerWorker(this);
}
@ -170,38 +171,12 @@ public class ForkJoinWorkerThread extends Thread {
}
}
/**
* Erases ThreadLocals by nulling out Thread maps.
*/
final void eraseThreadLocals() {
U.putObject(this, THREADLOCALS, null);
U.putObject(this, INHERITABLETHREADLOCALS, null);
}
/**
* Non-public hook method for InnocuousForkJoinWorkerThread.
*/
void afterTopLevelExec() {
}
// Set up to allow setting thread fields in constructor
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long THREADLOCALS;
private static final long INHERITABLETHREADLOCALS;
private static final long INHERITEDACCESSCONTROLCONTEXT;
static {
try {
THREADLOCALS = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocals"));
INHERITABLETHREADLOCALS = U.objectFieldOffset
(Thread.class.getDeclaredField("inheritableThreadLocals"));
INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
(Thread.class.getDeclaredField("inheritedAccessControlContext"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
/**
* A worker thread that has no permissions, is not a member of any
* user-defined ThreadGroup, and erases all ThreadLocals after
@ -210,7 +185,7 @@ public class ForkJoinWorkerThread extends Thread {
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
private static final ThreadGroup innocuousThreadGroup =
createThreadGroup();
ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup");
/** An AccessControlContext supporting no privileges */
private static final AccessControlContext INNOCUOUS_ACC =
@ -225,7 +200,7 @@ public class ForkJoinWorkerThread extends Thread {
@Override // to erase ThreadLocals
void afterTopLevelExec() {
eraseThreadLocals();
ThreadLocalRandom.eraseThreadLocals(this);
}
@Override // to always report system loader
@ -241,33 +216,5 @@ public class ForkJoinWorkerThread extends Thread {
throw new SecurityException("setContextClassLoader");
}
/**
* Returns a new group with the system ThreadGroup (the
* topmost, parent-less group) as parent. Uses Unsafe to
* traverse Thread.group and ThreadGroup.parent fields.
*/
private static ThreadGroup createThreadGroup() {
try {
jdk.internal.misc.Unsafe u = jdk.internal.misc.Unsafe.getUnsafe();
long tg = u.objectFieldOffset
(Thread.class.getDeclaredField("group"));
long gp = u.objectFieldOffset
(ThreadGroup.class.getDeclaredField("parent"));
ThreadGroup group = (ThreadGroup)
u.getObject(Thread.currentThread(), tg);
while (group != null) {
ThreadGroup parent = (ThreadGroup)u.getObject(group, gp);
if (parent == null)
return new ThreadGroup(group,
"InnocuousForkJoinWorkerThreadGroup");
group = parent;
}
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
// fall through if null as cannot-happen safeguard
throw new Error("Cannot create ThreadGroup");
}
}
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
/**
@ -69,9 +71,6 @@ public class FutureTask<V> implements RunnableFuture<V> {
* cancellation races. Sync control in the current design relies
* on a "state" field updated via CAS to track completion, along
* with a simple Treiber stack to hold waiting threads.
*
* Style note: As usual, we bypass overhead of using
* AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
*/
/**
@ -163,9 +162,8 @@ public class FutureTask<V> implements RunnableFuture<V> {
}
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
U.compareAndSwapInt(this, STATE, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
@ -174,7 +172,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
if (t != null)
t.interrupt();
} finally { // final state
U.putIntRelease(this, STATE, INTERRUPTED);
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
@ -228,9 +226,9 @@ public class FutureTask<V> implements RunnableFuture<V> {
* @param v the value
*/
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
U.putIntRelease(this, STATE, NORMAL); // final state
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
@ -246,16 +244,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
* @param t the cause of failure
*/
protected void setException(Throwable t) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = t;
U.putIntRelease(this, STATE, EXCEPTIONAL); // final state
STATE.setRelease(this, EXCEPTIONAL); // final state
finishCompletion();
}
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
@ -296,7 +294,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
*/
protected boolean runAndReset() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
@ -363,7 +361,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
if (WAITERS.weakCompareAndSetVolatile(this, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
@ -425,8 +423,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
q = new WaitNode();
}
else if (!queued)
queued = U.compareAndSwapObject(this, WAITERS,
q.next = waiters, q);
queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q);
else if (timed) {
final long parkNanos;
if (startTime == 0L) { // first time
@ -475,7 +472,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
if (pred.thread == null) // check for race
continue retry;
}
else if (!U.compareAndSwapObject(this, WAITERS, q, s))
else if (!WAITERS.compareAndSet(this, q, s))
continue retry;
}
break;
@ -483,19 +480,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
}
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long STATE;
private static final long RUNNER;
private static final long WAITERS;
// VarHandle mechanics
private static final VarHandle STATE;
private static final VarHandle RUNNER;
private static final VarHandle WAITERS;
static {
try {
STATE = U.objectFieldOffset
(FutureTask.class.getDeclaredField("state"));
RUNNER = U.objectFieldOffset
(FutureTask.class.getDeclaredField("runner"));
WAITERS = U.objectFieldOffset
(FutureTask.class.getDeclaredField("waiters"));
MethodHandles.Lookup l = MethodHandles.lookup();
STATE = l.findVarHandle(FutureTask.class, "state", int.class);
RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@ -444,7 +446,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
/**
* Queue nodes. Uses Object, not E, for items to allow forgetting
* them after use. Relies heavily on Unsafe mechanics to minimize
* them after use. Relies heavily on VarHandles to minimize
* unnecessary ordering constraints: Writes that are intrinsically
* ordered wrt other accesses or CASes use simple relaxed forms.
*/
@ -456,12 +458,12 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
// CAS methods for fields
final boolean casNext(Node cmp, Node val) {
return U.compareAndSwapObject(this, NEXT, cmp, val);
return NEXT.compareAndSet(this, cmp, val);
}
final boolean casItem(Object cmp, Object val) {
// assert cmp == null || cmp.getClass() != Node.class;
return U.compareAndSwapObject(this, ITEM, cmp, val);
return ITEM.compareAndSet(this, cmp, val);
}
/**
@ -469,7 +471,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
* only be seen after publication via casNext.
*/
Node(Object item, boolean isData) {
U.putObject(this, ITEM, item); // relaxed write
ITEM.set(this, item); // relaxed write
this.isData = isData;
}
@ -478,7 +480,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
* only after CASing head field, so uses relaxed write.
*/
final void forgetNext() {
U.putObject(this, NEXT, this);
NEXT.set(this, this);
}
/**
@ -491,8 +493,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
* else we don't care).
*/
final void forgetContents() {
U.putObject(this, ITEM, this);
U.putObject(this, WAITER, null);
ITEM.set(this, this);
WAITER.set(this, null);
}
/**
@ -537,19 +539,16 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
private static final long serialVersionUID = -3375979862319811754L;
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long ITEM;
private static final long NEXT;
private static final long WAITER;
// VarHandle mechanics
private static final VarHandle ITEM;
private static final VarHandle NEXT;
private static final VarHandle WAITER;
static {
try {
ITEM = U.objectFieldOffset
(Node.class.getDeclaredField("item"));
NEXT = U.objectFieldOffset
(Node.class.getDeclaredField("next"));
WAITER = U.objectFieldOffset
(Node.class.getDeclaredField("waiter"));
MethodHandles.Lookup l = MethodHandles.lookup();
ITEM = l.findVarHandle(Node.class, "item", Object.class);
NEXT = l.findVarHandle(Node.class, "next", Node.class);
WAITER = l.findVarHandle(Node.class, "waiter", Thread.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -567,15 +566,15 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
// CAS methods for fields
private boolean casTail(Node cmp, Node val) {
return U.compareAndSwapObject(this, TAIL, cmp, val);
return TAIL.compareAndSet(this, cmp, val);
}
private boolean casHead(Node cmp, Node val) {
return U.compareAndSwapObject(this, HEAD, cmp, val);
return HEAD.compareAndSet(this, cmp, val);
}
private boolean casSweepVotes(int cmp, int val) {
return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val);
return SWEEPVOTES.compareAndSet(this, cmp, val);
}
/*
@ -1562,20 +1561,19 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
}
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long HEAD;
private static final long TAIL;
private static final long SWEEPVOTES;
// VarHandle mechanics
private static final VarHandle HEAD;
private static final VarHandle TAIL;
private static final VarHandle SWEEPVOTES;
static {
try {
HEAD = U.objectFieldOffset
(LinkedTransferQueue.class.getDeclaredField("head"));
TAIL = U.objectFieldOffset
(LinkedTransferQueue.class.getDeclaredField("tail"));
SWEEPVOTES = U.objectFieldOffset
(LinkedTransferQueue.class.getDeclaredField("sweepVotes"));
MethodHandles.Lookup l = MethodHandles.lookup();
HEAD = l.findVarHandle(LinkedTransferQueue.class, "head",
Node.class);
TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail",
Node.class);
SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes",
int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
@ -221,7 +223,6 @@ import java.util.concurrent.locks.LockSupport;
* phaser.arriveAndDeregister();
* }}</pre>
*
*
* <p>To create a set of {@code n} tasks using a tree of phasers, you
* could use code of the following form, assuming a Task class with a
* constructor accepting a {@code Phaser} that it registers with upon
@ -384,7 +385,7 @@ public class Phaser {
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
if (STATE.compareAndSet(this, s, s-=adjust)) {
if (unarrived == 1) {
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
@ -397,12 +398,12 @@ public class Phaser {
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
U.compareAndSwapLong(this, STATE, s, n);
STATE.compareAndSet(this, s, n);
releaseWaiters(phase);
}
else if (nextUnarrived == 0) { // propagate deregistration
phase = parent.doArrive(ONE_DEREGISTER);
U.compareAndSwapLong(this, STATE, s, s | EMPTY);
STATE.compareAndSet(this, s, s | EMPTY);
}
else
phase = parent.doArrive(ONE_ARRIVAL);
@ -437,13 +438,13 @@ public class Phaser {
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
else if (STATE.compareAndSet(this, s, s + adjust))
break;
}
}
else if (parent == null) { // 1st root registration
long next = ((long)phase << PHASE_SHIFT) | adjust;
if (U.compareAndSwapLong(this, STATE, s, next))
if (STATE.compareAndSet(this, s, next))
break;
}
else {
@ -455,8 +456,8 @@ public class Phaser {
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
while (!U.compareAndSwapLong
(this, STATE, s,
while (!STATE.weakCompareAndSetVolatile
(this, s,
((long)phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
@ -487,8 +488,8 @@ public class Phaser {
// CAS to root phase with current parties, tripping unarrived
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
(int)(s >>> PHASE_SHIFT) &&
!U.compareAndSwapLong
(this, STATE, s,
!STATE.weakCompareAndSetVolatile
(this, s,
s = (((long)phase << PHASE_SHIFT) |
((phase < 0) ? (s & COUNTS_MASK) :
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
@ -677,7 +678,7 @@ public class Phaser {
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) {
if (unarrived > 1)
return root.internalAwaitAdvance(phase, null);
if (root != this)
@ -692,7 +693,7 @@ public class Phaser {
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
if (!U.compareAndSwapLong(this, STATE, s, n))
if (!STATE.compareAndSet(this, s, n))
return (int)(state >>> PHASE_SHIFT); // terminated
releaseWaiters(phase);
return nextPhase;
@ -808,7 +809,7 @@ public class Phaser {
final Phaser root = this.root;
long s;
while ((s = root.state) >= 0) {
if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) {
// signal all threads
releaseWaiters(0); // Waiters on evenQ
releaseWaiters(1); // Waiters on oddQ
@ -1043,6 +1044,8 @@ public class Phaser {
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
else
Thread.onSpinWait();
}
else if (node.isReleasable()) // done or aborted
break;
@ -1131,14 +1134,12 @@ public class Phaser {
}
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long STATE;
// VarHandle mechanics
private static final VarHandle STATE;
static {
try {
STATE = U.objectFieldOffset
(Phaser.class.getDeclaredField("state"));
MethodHandles.Lookup l = MethodHandles.lookup();
STATE = l.findVarHandle(Phaser.class, "state", long.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@ -289,7 +291,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
lock.unlock(); // must release and then re-acquire main lock
Object[] newArray = null;
if (allocationSpinLock == 0 &&
U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) {
try {
int newCap = oldCap + ((oldCap < 64) ?
(oldCap + 2) : // grow faster if small
@ -1009,13 +1011,14 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
return new PBQSpliterator<E>(this, null, 0, -1);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long ALLOCATIONSPINLOCK;
// VarHandle mechanics
private static final VarHandle ALLOCATIONSPINLOCK;
static {
try {
ALLOCATIONSPINLOCK = U.objectFieldOffset
(PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock"));
MethodHandles.Lookup l = MethodHandles.lookup();
ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class,
"allocationSpinLock",
int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,6 +35,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.LockSupport;
@ -906,7 +908,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
*/
@SuppressWarnings("serial")
static final class ConsumerTask<T> extends ForkJoinTask<Void>
implements Runnable {
implements Runnable, CompletableFuture.AsynchronousCompletionTask {
final BufferedSubscription<T> consumer;
ConsumerTask(BufferedSubscription<T> consumer) {
this.consumer = consumer;
@ -959,11 +961,9 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
* Blocking control relies on the "waiter" field. Producers set
* the field before trying to block, but must then recheck (via
* offer) before parking. Signalling then just unparks and clears
* waiter field. If the producer and consumer are both in the same
* ForkJoinPool, or consumers are running in commonPool, the
* producer attempts to help run consumer tasks that it forked
* before blocking. To avoid potential cycles, only one level of
* helping is currently supported.
* waiter field. If the producer and/or consumer are using a
* ForkJoinPool, the producer attempts to help run consumer tasks
* via ForkJoinPool.helpAsyncBlocker before blocking.
*
* This class uses @Contended and heuristic field declaration
* ordering to reduce false-sharing-based memory contention among
@ -983,7 +983,6 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
volatile long demand; // # unfilled requests
int maxCapacity; // reduced on OOME
int putStat; // offer result for ManagedBlocker
int helpDepth; // nested helping depth (at most 1)
volatile int ctl; // atomic run state flags
volatile int head; // next position to take
int tail; // next position to put
@ -1077,7 +1076,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
alloc = true;
}
else {
U.fullFence(); // recheck
VarHandle.fullFence(); // recheck
int h = head, t = tail, size = t + 1 - h;
if (cap >= size) {
a[(cap - 1) & t] = item;
@ -1116,10 +1115,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
if (a != null && cap > 0) {
int mask = cap - 1;
for (int j = head; j != t; ++j) {
long k = ((long)(j & mask) << ASHIFT) + ABASE;
Object x = U.getObjectVolatile(a, k);
int k = j & mask;
Object x = QA.getAcquire(a, k);
if (x != null && // races with consumer
U.compareAndSwapObject(a, k, x, null))
QA.compareAndSet(a, k, x, null))
newArray[j & newMask] = x;
}
}
@ -1136,100 +1135,43 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
* initial offer return 0.
*/
final int submit(T item) {
int stat; Executor e; ForkJoinWorkerThread w;
if ((stat = offer(item)) == 0 && helpDepth == 0 &&
((e = executor) instanceof ForkJoinPool)) {
helpDepth = 1;
Thread thread = Thread.currentThread();
if ((thread instanceof ForkJoinWorkerThread) &&
((w = (ForkJoinWorkerThread)thread)).getPool() == e)
stat = internalHelpConsume(w.workQueue, item);
else if (e == ForkJoinPool.commonPool())
stat = externalHelpConsume
(ForkJoinPool.commonSubmitterQueue(), item);
helpDepth = 0;
}
if (stat == 0 && (stat = offer(item)) == 0) {
int stat;
if ((stat = offer(item)) == 0) {
putItem = item;
timeout = 0L;
putStat = 0;
ForkJoinPool.helpAsyncBlocker(executor, this);
if ((stat = putStat) == 0) {
try {
ForkJoinPool.managedBlock(this);
} catch (InterruptedException ie) {
timeout = INTERRUPTED;
}
stat = putStat;
}
if (timeout < 0L)
Thread.currentThread().interrupt();
}
return stat;
}
/**
* Tries helping for FJ submitter.
*/
private int internalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
int stat = 0;
if (w != null) {
ForkJoinTask<?> t;
while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
if ((stat = offer(item)) != 0 || !w.tryUnpush(t))
break;
((ConsumerTask<?>)t).consumer.consume();
}
}
return stat;
}
/**
* Tries helping for non-FJ submitter.
*/
private int externalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
int stat = 0;
if (w != null) {
ForkJoinTask<?> t;
while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
if ((stat = offer(item)) != 0 || !w.trySharedUnpush(t))
break;
((ConsumerTask<?>)t).consumer.consume();
}
}
return stat;
}
/**
* Timeout version; similar to submit.
*/
final int timedOffer(T item, long nanos) {
int stat; Executor e;
if ((stat = offer(item)) == 0 && helpDepth == 0 &&
((e = executor) instanceof ForkJoinPool)) {
Thread thread = Thread.currentThread();
if (((thread instanceof ForkJoinWorkerThread) &&
((ForkJoinWorkerThread)thread).getPool() == e) ||
e == ForkJoinPool.commonPool()) {
helpDepth = 1;
ForkJoinTask<?> t;
long deadline = System.nanoTime() + nanos;
while ((t = ForkJoinTask.peekNextLocalTask()) != null &&
(t instanceof ConsumerTask)) {
if ((stat = offer(item)) != 0 ||
(nanos = deadline - System.nanoTime()) <= 0L ||
!t.tryUnfork())
break;
((ConsumerTask<?>)t).consumer.consume();
}
helpDepth = 0;
}
}
if (stat == 0 && (stat = offer(item)) == 0 &&
(timeout = nanos) > 0L) {
int stat;
if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) {
putItem = item;
putStat = 0;
ForkJoinPool.helpAsyncBlocker(executor, this);
if ((stat = putStat) == 0) {
try {
ForkJoinPool.managedBlock(this);
} catch (InterruptedException ie) {
timeout = INTERRUPTED;
}
stat = putStat;
}
if (timeout < 0L)
Thread.currentThread().interrupt();
}
@ -1249,22 +1191,20 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
}
else if ((c & ACTIVE) != 0) { // ensure keep-alive
if ((c & CONSUME) != 0 ||
U.compareAndSwapInt(this, CTL, c,
c | CONSUME))
CTL.compareAndSet(this, c, c | CONSUME))
break;
}
else if (demand == 0L || tail == head)
break;
else if (U.compareAndSwapInt(this, CTL, c,
c | (ACTIVE | CONSUME))) {
else if (CTL.compareAndSet(this, c, c | (ACTIVE | CONSUME))) {
try {
e.execute(new ConsumerTask<T>(this));
break;
} catch (RuntimeException | Error ex) { // back out
do {} while (((c = ctl) & DISABLED) == 0 &&
(c & ACTIVE) != 0 &&
!U.compareAndSwapInt(this, CTL, c,
c & ~ACTIVE));
!CTL.weakCompareAndSetVolatile
(this, c, c & ~ACTIVE));
throw ex;
}
}
@ -1300,10 +1240,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
break;
else if ((c & ACTIVE) != 0) {
pendingError = ex;
if (U.compareAndSwapInt(this, CTL, c, c | ERROR))
if (CTL.compareAndSet(this, c, c | ERROR))
break; // cause consumer task to exit
}
else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
else if (CTL.compareAndSet(this, c, DISABLED)) {
Flow.Subscriber<? super T> s = subscriber;
if (s != null && ex != null) {
try {
@ -1330,7 +1270,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
for (int c;;) {
if ((c = ctl) == DISABLED || (c & ACTIVE) == 0)
break;
if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
if (CTL.compareAndSet(this, c, c & ~ACTIVE)) {
onError(ex);
break;
}
@ -1343,7 +1283,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
for (int c;;) {
if ((c = ctl) == DISABLED)
break;
if (U.compareAndSwapInt(this, CTL, c,
if (CTL.compareAndSet(this, c,
c | (ACTIVE | CONSUME | COMPLETE))) {
if ((c & ACTIVE) == 0)
startOrDisable();
@ -1356,7 +1296,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
for (int c;;) {
if ((c = ctl) == DISABLED)
break;
if (U.compareAndSwapInt(this, CTL, c,
if (CTL.compareAndSet(this, c,
c | (ACTIVE | CONSUME | SUBSCRIBE))) {
if ((c & ACTIVE) == 0)
startOrDisable();
@ -1375,11 +1315,11 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
if ((c = ctl) == DISABLED)
break;
else if ((c & ACTIVE) != 0) {
if (U.compareAndSwapInt(this, CTL, c,
if (CTL.compareAndSet(this, c,
c | (CONSUME | ERROR)))
break;
}
else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
else if (CTL.compareAndSet(this, c, DISABLED)) {
detach();
break;
}
@ -1395,18 +1335,17 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
long prev = demand, d;
if ((d = prev + n) < prev) // saturate
d = Long.MAX_VALUE;
if (U.compareAndSwapLong(this, DEMAND, prev, d)) {
if (DEMAND.compareAndSet(this, prev, d)) {
for (int c, h;;) {
if ((c = ctl) == DISABLED)
break;
else if ((c & ACTIVE) != 0) {
if ((c & CONSUME) != 0 ||
U.compareAndSwapInt(this, CTL, c,
c | CONSUME))
CTL.compareAndSet(this, c, c | CONSUME))
break;
}
else if ((h = head) != tail) {
if (U.compareAndSwapInt(this, CTL, c,
if (CTL.compareAndSet(this, c,
c | (ACTIVE|CONSUME))) {
startOrDisable();
break;
@ -1476,16 +1415,14 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
if ((s = subscriber) != null) { // else disabled
for (;;) {
long d = demand;
int c; Object[] a; int n; long i; Object x; Thread w;
int c; Object[] a; int n, i; Object x; Thread w;
if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) {
if (!checkControl(s, c))
break;
}
else if ((a = array) == null || h == tail ||
(n = a.length) == 0 ||
(x = U.getObjectVolatile
(a, (i = ((long)((n - 1) & h) << ASHIFT) + ABASE)))
== null) {
(x = QA.getAcquire(a, i = (n - 1) & h)) == null) {
if (!checkEmpty(s, c))
break;
}
@ -1494,10 +1431,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
break;
}
else if (((c & CONSUME) != 0 ||
U.compareAndSwapInt(this, CTL, c, c | CONSUME)) &&
U.compareAndSwapObject(a, i, x, null)) {
U.putIntRelease(this, HEAD, ++h);
U.getAndAddLong(this, DEMAND, -1L);
CTL.compareAndSet(this, c, c | CONSUME)) &&
QA.compareAndSet(a, i, x, null)) {
HEAD.setRelease(this, ++h);
DEMAND.getAndAdd(this, -1L);
if ((w = waiter) != null)
signalWaiter(w);
try {
@ -1528,7 +1465,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
}
}
else if ((c & SUBSCRIBE) != 0) {
if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) {
if (CTL.compareAndSet(this, c, c & ~SUBSCRIBE)) {
try {
if (s != null)
s.onSubscribe(this);
@ -1551,9 +1488,9 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
boolean stat = true;
if (head == tail) {
if ((c & CONSUME) != 0)
U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
CTL.compareAndSet(this, c, c & ~CONSUME);
else if ((c & COMPLETE) != 0) {
if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
if (CTL.compareAndSet(this, c, DISABLED)) {
try {
if (s != null)
s.onComplete();
@ -1561,7 +1498,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
}
}
}
else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
stat = false;
}
return stat;
@ -1574,8 +1511,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
boolean stat = true;
if (demand == 0L) {
if ((c & CONSUME) != 0)
U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
CTL.compareAndSet(this, c, c & ~CONSUME);
else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
stat = false;
}
return stat;
@ -1595,31 +1532,25 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
onError(ex);
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long CTL;
private static final long TAIL;
private static final long HEAD;
private static final long DEMAND;
private static final int ABASE;
private static final int ASHIFT;
// VarHandle mechanics
private static final VarHandle CTL;
private static final VarHandle TAIL;
private static final VarHandle HEAD;
private static final VarHandle DEMAND;
private static final VarHandle QA;
static {
try {
CTL = U.objectFieldOffset
(BufferedSubscription.class.getDeclaredField("ctl"));
TAIL = U.objectFieldOffset
(BufferedSubscription.class.getDeclaredField("tail"));
HEAD = U.objectFieldOffset
(BufferedSubscription.class.getDeclaredField("head"));
DEMAND = U.objectFieldOffset
(BufferedSubscription.class.getDeclaredField("demand"));
ABASE = U.arrayBaseOffset(Object[].class);
int scale = U.arrayIndexScale(Object[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
MethodHandles.Lookup l = MethodHandles.lookup();
CTL = l.findVarHandle(BufferedSubscription.class, "ctl",
int.class);
TAIL = l.findVarHandle(BufferedSubscription.class, "tail",
int.class);
HEAD = l.findVarHandle(BufferedSubscription.class, "head",
int.class);
DEMAND = l.findVarHandle(BufferedSubscription.class, "demand",
long.class);
QA = MethodHandles.arrayElementVarHandle(Object[].class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -36,6 +36,8 @@
package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Collections;
@ -247,7 +249,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
U.compareAndSwapObject(this, NEXT, cmp, val);
SNEXT.compareAndSet(this, cmp, val);
}
/**
@ -260,7 +262,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
*/
boolean tryMatch(SNode s) {
if (match == null &&
U.compareAndSwapObject(this, MATCH, null, s)) {
SMATCH.compareAndSet(this, null, s)) {
Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
@ -275,24 +277,21 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
* Tries to cancel a wait by matching node to itself.
*/
void tryCancel() {
U.compareAndSwapObject(this, MATCH, null, this);
SMATCH.compareAndSet(this, null, this);
}
boolean isCancelled() {
return match == this;
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long MATCH;
private static final long NEXT;
// VarHandle mechanics
private static final VarHandle SMATCH;
private static final VarHandle SNEXT;
static {
try {
MATCH = U.objectFieldOffset
(SNode.class.getDeclaredField("match"));
NEXT = U.objectFieldOffset
(SNode.class.getDeclaredField("next"));
MethodHandles.Lookup l = MethodHandles.lookup();
SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -304,7 +303,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
boolean casHead(SNode h, SNode nh) {
return h == head &&
U.compareAndSwapObject(this, HEAD, h, nh);
SHEAD.compareAndSet(this, h, nh);
}
/**
@ -451,8 +450,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
continue;
}
}
if (spins > 0)
if (spins > 0) {
Thread.onSpinWait();
spins = shouldSpin(s) ? (spins - 1) : 0;
}
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
else if (!timed)
@ -508,13 +509,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long HEAD;
// VarHandle mechanics
private static final VarHandle SHEAD;
static {
try {
HEAD = U.objectFieldOffset
(TransferStack.class.getDeclaredField("head"));
MethodHandles.Lookup l = MethodHandles.lookup();
SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -546,19 +546,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
U.compareAndSwapObject(this, NEXT, cmp, val);
QNEXT.compareAndSet(this, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp &&
U.compareAndSwapObject(this, ITEM, cmp, val);
QITEM.compareAndSet(this, cmp, val);
}
/**
* Tries to cancel by CAS'ing ref to this as item.
*/
void tryCancel(Object cmp) {
U.compareAndSwapObject(this, ITEM, cmp, this);
QITEM.compareAndSet(this, cmp, this);
}
boolean isCancelled() {
@ -574,17 +574,14 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
return next == this;
}
// Unsafe mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long ITEM;
private static final long NEXT;
// VarHandle mechanics
private static final VarHandle QITEM;
private static final VarHandle QNEXT;
static {
try {
ITEM = U.objectFieldOffset
(QNode.class.getDeclaredField("item"));
NEXT = U.objectFieldOffset
(QNode.class.getDeclaredField("next"));
MethodHandles.Lookup l = MethodHandles.lookup();
QITEM = l.findVarHandle(QNode.class, "item", Object.class);
QNEXT = l.findVarHandle(QNode.class, "next", QNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -614,7 +611,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
*/
void advanceHead(QNode h, QNode nh) {
if (h == head &&
U.compareAndSwapObject(this, HEAD, h, nh))
QHEAD.compareAndSet(this, h, nh))
h.next = h; // forget old next
}
@ -623,7 +620,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
*/
void advanceTail(QNode t, QNode nt) {
if (tail == t)
U.compareAndSwapObject(this, TAIL, t, nt);
QTAIL.compareAndSet(this, t, nt);
}
/**
@ -631,7 +628,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
*/
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp &&
U.compareAndSwapObject(this, CLEANME, cmp, val);
QCLEANME.compareAndSet(this, cmp, val);
}
/**
@ -752,8 +749,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
continue;
}
}
if (spins > 0)
if (spins > 0) {
--spins;
Thread.onSpinWait();
}
else if (s.waiter == null)
s.waiter = w;
else if (!timed)
@ -817,18 +816,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
}
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long HEAD;
private static final long TAIL;
private static final long CLEANME;
// VarHandle mechanics
private static final VarHandle QHEAD;
private static final VarHandle QTAIL;
private static final VarHandle QCLEANME;
static {
try {
HEAD = U.objectFieldOffset
(TransferQueue.class.getDeclaredField("head"));
TAIL = U.objectFieldOffset
(TransferQueue.class.getDeclaredField("tail"));
CLEANME = U.objectFieldOffset
(TransferQueue.class.getDeclaredField("cleanMe"));
MethodHandles.Lookup l = MethodHandles.lookup();
QHEAD = l.findVarHandle(TransferQueue.class, "head",
QNode.class);
QTAIL = l.findVarHandle(TransferQueue.class, "tail",
QNode.class);
QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe",
QNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -36,6 +36,7 @@
package java.util.concurrent;
import java.io.ObjectStreamField;
import java.security.AccessControlContext;
import java.util.Random;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger;
@ -47,6 +48,7 @@ import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.StreamSupport;
import jdk.internal.misc.Unsafe;
/**
* A random number generator isolated to the current thread. Like the
@ -95,7 +97,9 @@ public class ThreadLocalRandom extends Random {
* ThreadLocalRandom sequence. The dual use is a marriage of
* convenience, but is a simple and efficient way of reducing
* application-level overhead and footprint of most concurrent
* programs.
* programs. Even more opportunistically, we also define here
* other package-private utilities that access Thread class
* fields.
*
* Even though this class subclasses java.util.Random, it uses the
* same basic algorithm as java.util.SplittableRandom. (See its
@ -958,6 +962,49 @@ public class ThreadLocalRandom extends Random {
return r;
}
// Support for other package-private ThreadLocal access
/**
* Erases ThreadLocals by nulling out Thread maps.
*/
static final void eraseThreadLocals(Thread thread) {
U.putObject(thread, THREADLOCALS, null);
U.putObject(thread, INHERITABLETHREADLOCALS, null);
}
static final void setInheritedAccessControlContext(Thread thread,
AccessControlContext acc) {
U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
}
/**
* Returns a new group with the system ThreadGroup (the
* topmost, parent-less group) as parent. Uses Unsafe to
* traverse Thread.group and ThreadGroup.parent fields.
*/
static final ThreadGroup createThreadGroup(String name) {
if (name == null)
throw new NullPointerException();
try {
long tg = U.objectFieldOffset
(Thread.class.getDeclaredField("group"));
long gp = U.objectFieldOffset
(ThreadGroup.class.getDeclaredField("parent"));
ThreadGroup group = (ThreadGroup)
U.getObject(Thread.currentThread(), tg);
while (group != null) {
ThreadGroup parent = (ThreadGroup)U.getObject(group, gp);
if (parent == null)
return new ThreadGroup(group, name);
group = parent;
}
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
// fall through if null as cannot-happen safeguard
throw new Error("Cannot create ThreadGroup");
}
// Serialization support
private static final long serialVersionUID = -5851777807851030925L;
@ -1022,10 +1069,13 @@ public class ThreadLocalRandom extends Random {
static final String BAD_SIZE = "size must be non-negative";
// 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 SEED;
private static final long PROBE;
private static final long SECONDARY;
private static final long THREADLOCALS;
private static final long INHERITABLETHREADLOCALS;
private static final long INHERITEDACCESSCONTROLCONTEXT;
static {
try {
SEED = U.objectFieldOffset
@ -1034,6 +1084,12 @@ public class ThreadLocalRandom extends Random {
(Thread.class.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
THREADLOCALS = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocals"));
INHERITABLETHREADLOCALS = U.objectFieldOffset
(Thread.class.getDeclaredField("inheritableThreadLocals"));
INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
(Thread.class.getDeclaredField("inheritedAccessControlContext"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}

View file

@ -35,27 +35,26 @@
package java.util.concurrent.atomic;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
/**
* A {@code boolean} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicBoolean} is used in applications such as atomically
* updated flags, and cannot be used as a replacement for a
* {@link java.lang.Boolean}.
* {@link VarHandle} specification for descriptions of the properties
* of atomic accesses. An {@code AtomicBoolean} is used in
* applications such as atomically updated flags, and cannot be used
* as a replacement for a {@link java.lang.Boolean}.
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L;
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
private static final VarHandle VALUE;
static {
try {
VALUE = U.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
MethodHandles.Lookup l = MethodHandles.lookup();
VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@ -79,7 +78,8 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
* Returns the current value.
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@ -88,40 +88,39 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* Atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
return U.compareAndSwapInt(this, VALUE,
(expect ? 1 : 0),
(update ? 1 : 0));
public final boolean compareAndSet(boolean expectedValue, boolean newValue) {
return VALUE.compareAndSet(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0));
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
*/
public boolean weakCompareAndSet(boolean expect, boolean update) {
return U.compareAndSwapInt(this, VALUE,
(expect ? 1 : 0),
(update ? 1 : 0));
public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) {
return VALUE.weakCompareAndSet(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0));
}
/**
* Unconditionally sets to the given value.
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@ -130,17 +129,19 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
* Eventually sets to the given value.
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(boolean newValue) {
U.putIntRelease(this, VALUE, (newValue ? 1 : 0));
VALUE.setRelease(this, (newValue ? 1 : 0));
}
/**
* Atomically sets to the given value and returns the previous value.
* Atomically sets the value to {@code newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param newValue the new value
* @return the previous value
@ -161,4 +162,178 @@ public class AtomicBoolean implements java.io.Serializable {
return Boolean.toString(get());
}
// jdk9
/**
* Returns the current value, with memory semantics of reading as
* if the variable was declared non-{@code volatile}.
*
* @return the value
* @since 9
*/
public final boolean getPlain() {
return (int)VALUE.get(this) != 0;
}
/**
* Sets the value to {@code newValue}, with memory semantics
* of setting as if the variable was declared non-{@code volatile}
* and non-{@code final}.
*
* @param newValue the new value
* @since 9
*/
public final void setPlain(boolean newValue) {
VALUE.set(this, newValue ? 1 : 0);
}
/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getOpaque}.
*
* @return the value
* @since 9
*/
public final boolean getOpaque() {
return (int)VALUE.getOpaque(this) != 0;
}
/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setOpaque}.
*
* @param newValue the new value
* @since 9
*/
public final void setOpaque(boolean newValue) {
VALUE.setOpaque(this, newValue ? 1 : 0);
}
/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getAcquire}.
*
* @return the value
* @since 9
*/
public final boolean getAcquire() {
return (int)VALUE.getAcquire(this) != 0;
}
/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 9
*/
public final void setRelease(boolean newValue) {
VALUE.setRelease(this, newValue ? 1 : 0);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchange}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final boolean compareAndExchange(boolean expectedValue, boolean newValue) {
return (int)VALUE.compareAndExchange(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0)) != 0;
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeAcquire}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final boolean compareAndExchangeAcquire(boolean expectedValue, boolean newValue) {
return (int)VALUE.compareAndExchangeAcquire(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0)) != 0;
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeRelease}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final boolean compareAndExchangeRelease(boolean expectedValue, boolean newValue) {
return (int)VALUE.compareAndExchangeRelease(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0)) != 0;
}
/**
* Possibly atomically sets the value to {@code newValue} if the current
* value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetVolatile}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetVolatile(boolean expectedValue, boolean newValue) {
return VALUE.weakCompareAndSetVolatile(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0));
}
/**
* Possibly atomically sets the value to {@code newValue} if the current
* value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetAcquire}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetAcquire(boolean expectedValue, boolean newValue) {
return VALUE.weakCompareAndSetAcquire(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0));
}
/**
* Possibly atomically sets the value to {@code newValue} if the current
* value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetRelease}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetRelease(boolean expectedValue, boolean newValue) {
return VALUE.weakCompareAndSetRelease(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0));
}
}

View file

@ -35,18 +35,18 @@
package java.util.concurrent.atomic;
import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
/**
* An {@code int} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicInteger} is used in applications such as atomically
* incremented counters, and cannot be used as a replacement for an
* {@link java.lang.Integer}. However, this class does extend
* {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
* {@link VarHandle} specification for descriptions of the properties
* of atomic accesses. An {@code AtomicInteger} is used in
* applications such as atomically incremented counters, and cannot be
* used as a replacement for an {@link java.lang.Integer}. However,
* this class does extend {@code Number} to allow uniform access by
* tools and utilities that deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
@ -54,6 +54,10 @@ import java.util.function.IntUnaryOperator;
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
/*
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
*/
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
@ -84,7 +88,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Gets the current value.
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@ -93,7 +98,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Sets to the given value.
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@ -102,7 +108,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Eventually sets to the given value.
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 1.6
@ -112,7 +119,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically sets to the given value and returns the old value.
* Atomically sets the value to {@code newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param newValue the new value
* @return the previous value
@ -122,36 +130,37 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* Atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
public final boolean compareAndSet(int expectedValue, int newValue) {
return U.compareAndSwapInt(this, VALUE, expectedValue, newValue);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
public final boolean weakCompareAndSet(int expectedValue, int newValue) {
return U.weakCompareAndSwapInt(this, VALUE, expectedValue, newValue);
}
/**
* Atomically increments by one the current value.
* Atomically increments the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(1)}.
*
* @return the previous value
*/
@ -160,7 +169,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically decrements by one the current value.
* Atomically decrements the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(-1)}.
*
* @return the previous value
*/
@ -169,7 +181,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically adds the given value to the current value.
* Atomically adds the given value to the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* @param delta the value to add
* @return the previous value
@ -179,7 +192,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically increments by one the current value.
* Atomically increments the current value,
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(1)}.
*
* @return the updated value
*/
@ -188,7 +204,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically decrements by one the current value.
* Atomically decrements the current value,
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(-1)}.
*
* @return the updated value
*/
@ -197,7 +216,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Atomically adds the given value to the current value.
* Atomically adds the given value to the current value,
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* @param delta the value to add
* @return the updated value
@ -217,12 +237,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
* @since 1.8
*/
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
int prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
/**
@ -236,12 +258,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
* @since 1.8
*/
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
int prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return next;
haveNext = (prev == (prev = get()));
}
}
/**
@ -260,12 +284,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
*/
public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
int prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
/**
@ -284,12 +310,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
*/
public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
int prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return next;
haveNext = (prev == (prev = get()));
}
}
/**
@ -301,7 +329,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicInteger} as an {@code int}.
* Returns the current value of this {@code AtomicInteger} as an
* {@code int},
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* Equivalent to {@link #get()}.
*/
public int intValue() {
@ -309,8 +340,9 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicInteger} as a {@code long}
* after a widening primitive conversion.
* Returns the current value of this {@code AtomicInteger} as a
* {@code long} after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public long longValue() {
@ -318,8 +350,9 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicInteger} as a {@code float}
* after a widening primitive conversion.
* Returns the current value of this {@code AtomicInteger} as a
* {@code float} after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
@ -327,12 +360,175 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicInteger} as a {@code double}
* after a widening primitive conversion.
* Returns the current value of this {@code AtomicInteger} as a
* {@code double} after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return (double)get();
}
// jdk9
/**
* Returns the current value, with memory semantics of reading as
* if the variable was declared non-{@code volatile}.
*
* @return the value
* @since 9
*/
public final int getPlain() {
return U.getInt(this, VALUE);
}
/**
* Sets the value to {@code newValue}, with memory semantics
* of setting as if the variable was declared non-{@code volatile}
* and non-{@code final}.
*
* @param newValue the new value
* @since 9
*/
public final void setPlain(int newValue) {
U.putInt(this, VALUE, newValue);
}
/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getOpaque}.
*
* @return the value
* @since 9
*/
public final int getOpaque() {
return U.getIntOpaque(this, VALUE);
}
/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setOpaque}.
*
* @param newValue the new value
* @since 9
*/
public final void setOpaque(int newValue) {
U.putIntOpaque(this, VALUE, newValue);
}
/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getAcquire}.
*
* @return the value
* @since 9
*/
public final int getAcquire() {
return U.getIntAcquire(this, VALUE);
}
/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 9
*/
public final void setRelease(int newValue) {
U.putIntRelease(this, VALUE, newValue);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchange}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final int compareAndExchange(int expectedValue, int newValue) {
return U.compareAndExchangeIntVolatile(this, VALUE, expectedValue, newValue);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeAcquire}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final int compareAndExchangeAcquire(int expectedValue, int newValue) {
return U.compareAndExchangeIntAcquire(this, VALUE, expectedValue, newValue);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeRelease}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final int compareAndExchangeRelease(int expectedValue, int newValue) {
return U.compareAndExchangeIntRelease(this, VALUE, expectedValue, newValue);
}
/**
* Possibly atomically sets the value to {@code newValue} if
* the current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetVolatile}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetVolatile(int expectedValue, int newValue) {
return U.weakCompareAndSwapIntVolatile(this, VALUE, expectedValue, newValue);
}
/**
* Possibly atomically sets the value to {@code newValue} if
* the current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetAcquire}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetAcquire(int expectedValue, int newValue) {
return U.weakCompareAndSwapIntAcquire(this, VALUE, expectedValue, newValue);
}
/**
* Possibly atomically sets the value to {@code newValue} if
* the current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetRelease}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetRelease(int expectedValue, int newValue) {
return U.weakCompareAndSwapIntRelease(this, VALUE, expectedValue, newValue);
}
}

View file

@ -35,44 +35,24 @@
package java.util.concurrent.atomic;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
/**
* An {@code int} array in which elements may be updated atomically.
* See the {@link java.util.concurrent.atomic} package
* specification for description of the properties of atomic
* variables.
* See the {@link VarHandle} specification for descriptions of the
* properties of atomic accesses.
* @since 1.5
* @author Doug Lea
*/
public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L;
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final int ABASE;
private static final int ASHIFT;
private static final VarHandle AA
= MethodHandles.arrayElementVarHandle(int[].class);
private final int[] array;
static {
ABASE = U.arrayBaseOffset(int[].class);
int scale = U.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("array index scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
}
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
private static long byteOffset(int i) {
return ((long) i << ASHIFT) + ABASE;
}
/**
* Creates a new AtomicIntegerArray of the given length, with all
* elements initially zero.
@ -105,147 +85,155 @@ public class AtomicIntegerArray implements java.io.Serializable {
}
/**
* Gets the current value at position {@code i}.
* Returns the current value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @param i the index
* @return the current value
*/
public final int get(int i) {
return getRaw(checkedByteOffset(i));
}
private int getRaw(long offset) {
return U.getIntVolatile(array, offset);
return (int)AA.getVolatile(array, i);
}
/**
* Sets the element at position {@code i} to the given value.
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param i the index
* @param newValue the new value
*/
public final void set(int i, int newValue) {
U.putIntVolatile(array, checkedByteOffset(i), newValue);
AA.setVolatile(array, i, newValue);
}
/**
* Eventually sets the element at position {@code i} to the given value.
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int i, int newValue) {
U.putIntRelease(array, checkedByteOffset(i), newValue);
AA.setRelease(array, i, newValue);
}
/**
* Atomically sets the element at position {@code i} to the given
* value and returns the old value.
* Atomically sets the element at index {@code i} to {@code
* newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param i the index
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int i, int newValue) {
return U.getAndSetInt(array, checkedByteOffset(i), newValue);
return (int)AA.getAndSet(array, i, newValue);
}
/**
* Atomically sets the element at position {@code i} to the given
* updated value if the current value {@code ==} the expected value.
* Atomically sets the element at index {@code i} to {@code
* newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param i the index
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, int expect, int update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
return U.compareAndSwapInt(array, offset, expect, update);
public final boolean compareAndSet(int i, int expectedValue, int newValue) {
return AA.compareAndSet(array, i, expectedValue, newValue);
}
/**
* Atomically sets the element at position {@code i} to the given
* updated value if the current value {@code ==} the expected value.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
* @param i the index
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int i, int expect, int update) {
return compareAndSet(i, expect, update);
public final boolean weakCompareAndSet(int i, int expectedValue, int newValue) {
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
}
/**
* Atomically increments by one the element at index {@code i}.
* Atomically increments the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(i, 1)}.
*
* @param i the index
* @return the previous value
*/
public final int getAndIncrement(int i) {
return getAndAdd(i, 1);
return (int)AA.getAndAdd(array, i, 1);
}
/**
* Atomically decrements by one the element at index {@code i}.
* Atomically decrements the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(i, -1)}.
*
* @param i the index
* @return the previous value
*/
public final int getAndDecrement(int i) {
return getAndAdd(i, -1);
return (int)AA.getAndAdd(array, i, -1);
}
/**
* Atomically adds the given value to the element at index {@code i}.
* Atomically adds the given value to the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* @param i the index
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int i, int delta) {
return U.getAndAddInt(array, checkedByteOffset(i), delta);
return (int)AA.getAndAdd(array, i, delta);
}
/**
* Atomically increments by one the element at index {@code i}.
* Atomically increments the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(i, 1)}.
*
* @param i the index
* @return the updated value
*/
public final int incrementAndGet(int i) {
return getAndAdd(i, 1) + 1;
return (int)AA.addAndGet(array, i, 1);
}
/**
* Atomically decrements by one the element at index {@code i}.
* Atomically decrements the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(i, -1)}.
*
* @param i the index
* @return the updated value
*/
public final int decrementAndGet(int i) {
return getAndAdd(i, -1) - 1;
return (int)AA.addAndGet(array, i, -1);
}
/**
* Atomically adds the given value to the element at index {@code i}.
* Atomically adds the given value to the element at index {@code i},
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* @param i the index
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int i, int delta) {
return getAndAdd(i, delta) + delta;
return (int)AA.addAndGet(array, i, delta);
}
/**
@ -260,13 +248,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
* @since 1.8
*/
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
int prev = get(i), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return prev;
haveNext = (prev == (prev = get(i)));
}
}
/**
@ -281,23 +270,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
* @since 1.8
*/
public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
int prev = get(i), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return next;
haveNext = (prev == (prev = get(i)));
}
}
/**
* Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and
* given values, returning the previous value. The function should
* be side-effect-free, since it may be re-applied when attempted
* results of applying the given function to the current and given
* values, returning the previous value. The function should be
* side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first
* argument, and the given update as the second argument.
* applied with the current value of the element at index {@code i}
* as its first argument, and the given update as the second
* argument.
*
* @param i the index
* @param x the update value
@ -307,23 +298,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
*/
public final int getAndAccumulate(int i, int x,
IntBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
int prev = get(i), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return prev;
haveNext = (prev == (prev = get(i)));
}
}
/**
* Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and
* given values, returning the updated value. The function should
* be side-effect-free, since it may be re-applied when attempted
* results of applying the given function to the current and given
* values, returning the updated value. The function should be
* side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first
* argument, and the given update as the second argument.
* applied with the current value of the element at index {@code i}
* as its first argument, and the given update as the second
* argument.
*
* @param i the index
* @param x the update value
@ -333,13 +326,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
*/
public final int accumulateAndGet(int i, int x,
IntBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
int prev = get(i), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return next;
haveNext = (prev == (prev = get(i)));
}
}
/**
@ -354,11 +348,190 @@ public class AtomicIntegerArray implements java.io.Serializable {
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(getRaw(byteOffset(i)));
b.append(get(i));
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
}
}
// jdk9
/**
* Returns the current value of the element at index {@code i},
* with memory semantics of reading as if the variable was declared
* non-{@code volatile}.
*
* @param i the index
* @return the value
* @since 9
*/
public final int getPlain(int i) {
return (int)AA.get(array, i);
}
/**
* Sets the element at index {@code i} to {@code newValue},
* with memory semantics of setting as if the variable was
* declared non-{@code volatile} and non-{@code final}.
*
* @param i the index
* @param newValue the new value
* @since 9
*/
public final void setPlain(int i, int newValue) {
AA.set(array, i, newValue);
}
/**
* Returns the current value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getOpaque}.
*
* @param i the index
* @return the value
* @since 9
*/
public final int getOpaque(int i) {
return (int)AA.getOpaque(array, i);
}
/**
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setOpaque}.
*
* @param i the index
* @param newValue the new value
* @since 9
*/
public final void setOpaque(int i, int newValue) {
AA.setOpaque(array, i, newValue);
}
/**
* Returns the current value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAcquire}.
*
* @param i the index
* @return the value
* @since 9
*/
public final int getAcquire(int i) {
return (int)AA.getAcquire(array, i);
}
/**
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 9
*/
public final void setRelease(int i, int newValue) {
AA.setRelease(array, i, newValue);
}
/**
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value, referred to as the <em>witness
* value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchange}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final int compareAndExchange(int i, int expectedValue, int newValue) {
return (int)AA.compareAndExchange(array, i, expectedValue, newValue);
}
/**
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value, referred to as the <em>witness
* value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeAcquire}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final int compareAndExchangeAcquire(int i, int expectedValue, int newValue) {
return (int)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
}
/**
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value, referred to as the <em>witness
* value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeRelease}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final int compareAndExchangeRelease(int i, int expectedValue, int newValue) {
return (int)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
}
/**
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetVolatile}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetVolatile(int i, int expectedValue, int newValue) {
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
}
/**
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetAcquire}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetAcquire(int i, int expectedValue, int newValue) {
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
}
/**
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetRelease}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetRelease(int i, int expectedValue, int newValue) {
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
}
}

View file

@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@ -150,8 +151,8 @@ public abstract class AtomicIntegerFieldUpdater<T> {
public abstract void lazySet(T obj, int newValue);
/**
* Gets the current value held in the field of the given object managed
* by this updater.
* Returns the current value held in the field of the given object
* managed by this updater.
*
* @param obj An object whose field to get
* @return the current value
@ -367,7 +368,7 @@ public abstract class AtomicIntegerFieldUpdater<T> {
*/
private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<T> {
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final Unsafe U = Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else

View file

@ -35,18 +35,18 @@
package java.util.concurrent.atomic;
import java.lang.invoke.VarHandle;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
/**
* A {@code long} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicLong} is used in applications such as atomically
* incremented sequence numbers, and cannot be used as a replacement
* for a {@link java.lang.Long}. However, this class does extend
* {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
* {@link VarHandle} specification for descriptions of the properties
* of atomic accesses. An {@code AtomicLong} is used in applications
* such as atomically incremented sequence numbers, and cannot be used
* as a replacement for a {@link java.lang.Long}. However, this class
* does extend {@code Number} to allow uniform access by tools and
* utilities that deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
@ -54,12 +54,9 @@ import java.util.function.LongUnaryOperator;
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
/**
* Records whether the underlying JVM supports lockless
* compareAndSwap for longs. While the Unsafe.compareAndSwapLong
* compareAndSwap for longs. While the intrinsic compareAndSwapLong
* method works in either case, some constructions should be
* handled at Java level to avoid locking user-visible locks.
*/
@ -71,6 +68,13 @@ public class AtomicLong extends Number implements java.io.Serializable {
*/
private static native boolean VMSupportsCS8();
/*
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
*/
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
static {
try {
VALUE = U.objectFieldOffset
@ -98,7 +102,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Gets the current value.
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@ -107,7 +112,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Sets to the given value.
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@ -118,7 +124,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Eventually sets to the given value.
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 1.6
@ -128,7 +135,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically sets to the given value and returns the old value.
* Atomically sets the value to {@code newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param newValue the new value
* @return the previous value
@ -138,36 +146,37 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* Atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(long expect, long update) {
return U.compareAndSwapLong(this, VALUE, expect, update);
public final boolean compareAndSet(long expectedValue, long newValue) {
return U.compareAndSwapLong(this, VALUE, expectedValue, newValue);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
*/
public final boolean weakCompareAndSet(long expect, long update) {
return U.compareAndSwapLong(this, VALUE, expect, update);
public final boolean weakCompareAndSet(long expectedValue, long newValue) {
return U.weakCompareAndSwapLong(this, VALUE, expectedValue, newValue);
}
/**
* Atomically increments by one the current value.
* Atomically increments the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(1)}.
*
* @return the previous value
*/
@ -176,7 +185,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically decrements by one the current value.
* Atomically decrements the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(-1)}.
*
* @return the previous value
*/
@ -185,7 +197,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically adds the given value to the current value.
* Atomically adds the given value to the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* @param delta the value to add
* @return the previous value
@ -195,7 +208,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically increments by one the current value.
* Atomically increments the current value,
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(1)}.
*
* @return the updated value
*/
@ -204,7 +220,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically decrements by one the current value.
* Atomically decrements the current value,
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(-1)}.
*
* @return the updated value
*/
@ -213,7 +232,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Atomically adds the given value to the current value.
* Atomically adds the given value to the current value,
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* @param delta the value to add
* @return the updated value
@ -233,12 +253,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
* @since 1.8
*/
public final long getAndUpdate(LongUnaryOperator updateFunction) {
long prev, next;
do {
prev = get();
long prev = get(), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsLong(prev);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
/**
@ -252,12 +274,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
* @since 1.8
*/
public final long updateAndGet(LongUnaryOperator updateFunction) {
long prev, next;
do {
prev = get();
long prev = get(), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsLong(prev);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return next;
haveNext = (prev == (prev = get()));
}
}
/**
@ -276,12 +300,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
*/
public final long getAndAccumulate(long x,
LongBinaryOperator accumulatorFunction) {
long prev, next;
do {
prev = get();
long prev = get(), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
/**
@ -300,12 +326,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
*/
public final long accumulateAndGet(long x,
LongBinaryOperator accumulatorFunction) {
long prev, next;
do {
prev = get();
long prev = get(), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSet(prev, next));
if (weakCompareAndSetVolatile(prev, next))
return next;
haveNext = (prev == (prev = get()));
}
}
/**
@ -317,8 +345,9 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicLong} as an {@code int}
* after a narrowing primitive conversion.
* Returns the current value of this {@code AtomicLong} as an {@code int}
* after a narrowing primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.3 Narrowing Primitive Conversions
*/
public int intValue() {
@ -326,7 +355,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicLong} as a {@code long}.
* Returns the current value of this {@code AtomicLong} as a {@code long},
* with memory effects as specified by {@link VarHandle#getVolatile}.
* Equivalent to {@link #get()}.
*/
public long longValue() {
@ -334,8 +364,9 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicLong} as a {@code float}
* after a widening primitive conversion.
* Returns the current value of this {@code AtomicLong} as a {@code float}
* after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
@ -343,12 +374,175 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
* Returns the value of this {@code AtomicLong} as a {@code double}
* after a widening primitive conversion.
* Returns the current value of this {@code AtomicLong} as a {@code double}
* after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return (double)get();
}
// jdk9
/**
* Returns the current value, with memory semantics of reading as if the
* variable was declared non-{@code volatile}.
*
* @return the value
* @since 9
*/
public final long getPlain() {
return U.getLong(this, VALUE);
}
/**
* Sets the value to {@code newValue}, with memory semantics
* of setting as if the variable was declared non-{@code volatile}
* and non-{@code final}.
*
* @param newValue the new value
* @since 9
*/
public final void setPlain(long newValue) {
U.putLong(this, VALUE, newValue);
}
/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getOpaque}.
*
* @return the value
* @since 9
*/
public final long getOpaque() {
return U.getLongOpaque(this, VALUE);
}
/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setOpaque}.
*
* @param newValue the new value
* @since 9
*/
public final void setOpaque(long newValue) {
U.putLongOpaque(this, VALUE, newValue);
}
/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getAcquire}.
*
* @return the value
* @since 9
*/
public final long getAcquire() {
return U.getLongAcquire(this, VALUE);
}
/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 9
*/
public final void setRelease(long newValue) {
U.putLongRelease(this, VALUE, newValue);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchange}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final long compareAndExchange(long expectedValue, long newValue) {
return U.compareAndExchangeLongVolatile(this, VALUE, expectedValue, newValue);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeAcquire}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final long compareAndExchangeAcquire(long expectedValue, long newValue) {
return U.compareAndExchangeLongAcquire(this, VALUE, expectedValue, newValue);
}
/**
* Atomically sets the value to {@code newValue} if the current value,
* referred to as the <em>witness value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeRelease}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final long compareAndExchangeRelease(long expectedValue, long newValue) {
return U.compareAndExchangeLongRelease(this, VALUE, expectedValue, newValue);
}
/**
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetVolatile}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetVolatile(long expectedValue, long newValue) {
return U.weakCompareAndSwapLongVolatile(this, VALUE, expectedValue, newValue);
}
/**
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetAcquire}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetAcquire(long expectedValue, long newValue) {
return U.weakCompareAndSwapLongAcquire(this, VALUE, expectedValue, newValue);
}
/**
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetRelease}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetRelease(long expectedValue, long newValue) {
return U.weakCompareAndSwapLongRelease(this, VALUE, expectedValue, newValue);
}
}

View file

@ -35,43 +35,24 @@
package java.util.concurrent.atomic;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
/**
* A {@code long} array in which elements may be updated atomically.
* See the {@link java.util.concurrent.atomic} package specification
* for description of the properties of atomic variables.
* See the {@link VarHandle} specification for descriptions of the
* properties of atomic accesses.
* @since 1.5
* @author Doug Lea
*/
public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final int ABASE;
private static final int ASHIFT;
private static final VarHandle AA
= MethodHandles.arrayElementVarHandle(long[].class);
private final long[] array;
static {
ABASE = U.arrayBaseOffset(long[].class);
int scale = U.arrayIndexScale(long[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("array index scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
}
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
private static long byteOffset(int i) {
return ((long) i << ASHIFT) + ABASE;
}
/**
* Creates a new AtomicLongArray of the given length, with all
* elements initially zero.
@ -104,147 +85,155 @@ public class AtomicLongArray implements java.io.Serializable {
}
/**
* Gets the current value at position {@code i}.
* Returns the current value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @param i the index
* @return the current value
*/
public final long get(int i) {
return getRaw(checkedByteOffset(i));
}
private long getRaw(long offset) {
return U.getLongVolatile(array, offset);
return (long)AA.getVolatile(array, i);
}
/**
* Sets the element at position {@code i} to the given value.
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param i the index
* @param newValue the new value
*/
public final void set(int i, long newValue) {
U.putLongVolatile(array, checkedByteOffset(i), newValue);
AA.setVolatile(array, i, newValue);
}
/**
* Eventually sets the element at position {@code i} to the given value.
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int i, long newValue) {
U.putLongRelease(array, checkedByteOffset(i), newValue);
AA.setRelease(array, i, newValue);
}
/**
* Atomically sets the element at position {@code i} to the given value
* and returns the old value.
* Atomically sets the element at index {@code i} to {@code
* newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param i the index
* @param newValue the new value
* @return the previous value
*/
public final long getAndSet(int i, long newValue) {
return U.getAndSetLong(array, checkedByteOffset(i), newValue);
return (long)AA.getAndSet(array, i, newValue);
}
/**
* Atomically sets the element at position {@code i} to the given
* updated value if the current value {@code ==} the expected value.
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param i the index
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, long expect, long update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
private boolean compareAndSetRaw(long offset, long expect, long update) {
return U.compareAndSwapLong(array, offset, expect, update);
public final boolean compareAndSet(int i, long expectedValue, long newValue) {
return AA.compareAndSet(array, i, expectedValue, newValue);
}
/**
* Atomically sets the element at position {@code i} to the given
* updated value if the current value {@code ==} the expected value.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
* @param i the index
* @param expect the expected value
* @param update the new value
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int i, long expect, long update) {
return compareAndSet(i, expect, update);
public final boolean weakCompareAndSet(int i, long expectedValue, long newValue) {
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
}
/**
* Atomically increments by one the element at index {@code i}.
* Atomically increments the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(i, 1)}.
*
* @param i the index
* @return the previous value
*/
public final long getAndIncrement(int i) {
return getAndAdd(i, 1);
return (long)AA.getAndAdd(array, i, 1L);
}
/**
* Atomically decrements by one the element at index {@code i}.
* Atomically decrements the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* <p>Equivalent to {@code getAndAdd(i, -1)}.
*
* @param i the index
* @return the previous value
*/
public final long getAndDecrement(int i) {
return getAndAdd(i, -1);
return (long)AA.getAndAdd(array, i, -1L);
}
/**
* Atomically adds the given value to the element at index {@code i}.
* Atomically adds the given value to the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* @param i the index
* @param delta the value to add
* @return the previous value
*/
public final long getAndAdd(int i, long delta) {
return U.getAndAddLong(array, checkedByteOffset(i), delta);
return (long)AA.getAndAdd(array, i, delta);
}
/**
* Atomically increments by one the element at index {@code i}.
* Atomically increments the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(i, 1)}.
*
* @param i the index
* @return the updated value
*/
public final long incrementAndGet(int i) {
return getAndAdd(i, 1) + 1;
return (long)AA.addAndGet(array, i, 1L);
}
/**
* Atomically decrements by one the element at index {@code i}.
* Atomically decrements the value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* <p>Equivalent to {@code addAndGet(i, -1)}.
*
* @param i the index
* @return the updated value
*/
public final long decrementAndGet(int i) {
return getAndAdd(i, -1) - 1;
return (long)AA.addAndGet(array, i, -1L);
}
/**
* Atomically adds the given value to the element at index {@code i}.
* Atomically adds the given value to the element at index {@code i},
* with memory effects as specified by {@link VarHandle#addAndGet}.
*
* @param i the index
* @param delta the value to add
* @return the updated value
*/
public long addAndGet(int i, long delta) {
return getAndAdd(i, delta) + delta;
return (long)AA.addAndGet(array, i, delta);
}
/**
@ -259,13 +248,14 @@ public class AtomicLongArray implements java.io.Serializable {
* @since 1.8
*/
public final long getAndUpdate(int i, LongUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
long prev, next;
do {
prev = getRaw(offset);
long prev = get(i), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsLong(prev);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return prev;
haveNext = (prev == (prev = get(i)));
}
}
/**
@ -280,23 +270,25 @@ public class AtomicLongArray implements java.io.Serializable {
* @since 1.8
*/
public final long updateAndGet(int i, LongUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
long prev, next;
do {
prev = getRaw(offset);
long prev = get(i), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsLong(prev);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return next;
haveNext = (prev == (prev = get(i)));
}
}
/**
* Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and
* given values, returning the previous value. The function should
* be side-effect-free, since it may be re-applied when attempted
* results of applying the given function to the current and given
* values, returning the previous value. The function should be
* side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first
* argument, and the given update as the second argument.
* applied with the current value of the element at index {@code i}
* as its first argument, and the given update as the second
* argument.
*
* @param i the index
* @param x the update value
@ -306,23 +298,25 @@ public class AtomicLongArray implements java.io.Serializable {
*/
public final long getAndAccumulate(int i, long x,
LongBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
long prev, next;
do {
prev = getRaw(offset);
long prev = get(i), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return prev;
haveNext = (prev == (prev = get(i)));
}
}
/**
* Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and
* given values, returning the updated value. The function should
* be side-effect-free, since it may be re-applied when attempted
* results of applying the given function to the current and given
* values, returning the updated value. The function should be
* side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first
* argument, and the given update as the second argument.
* applied with the current value of the element at index {@code i}
* as its first argument, and the given update as the second
* argument.
*
* @param i the index
* @param x the update value
@ -332,13 +326,14 @@ public class AtomicLongArray implements java.io.Serializable {
*/
public final long accumulateAndGet(int i, long x,
LongBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
long prev, next;
do {
prev = getRaw(offset);
long prev = get(i), next = 0L;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSetRaw(offset, prev, next));
if (weakCompareAndSetVolatile(i, prev, next))
return next;
haveNext = (prev == (prev = get(i)));
}
}
/**
@ -353,11 +348,189 @@ public class AtomicLongArray implements java.io.Serializable {
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(getRaw(byteOffset(i)));
b.append(get(i));
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
}
}
// jdk9
/**
* Returns the current value of the element at index {@code i},
* with memory semantics of reading as if the variable was declared
* non-{@code volatile}.
*
* @param i the index
* @return the value
* @since 9
*/
public final long getPlain(int i) {
return (long)AA.get(array, i);
}
/**
* Sets the element at index {@code i} to {@code newValue},
* with memory semantics of setting as if the variable was
* declared non-{@code volatile} and non-{@code final}.
*
* @param i the index
* @param newValue the new value
* @since 9
*/
public final void setPlain(int i, long newValue) {
AA.set(array, i, newValue);
}
/**
* Returns the current value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getOpaque}.
*
* @param i the index
* @return the value
* @since 9
*/
public final long getOpaque(int i) {
return (long)AA.getOpaque(array, i);
}
/**
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setOpaque}.
*
* @param i the index
* @param newValue the new value
* @since 9
*/
public final void setOpaque(int i, long newValue) {
AA.setOpaque(array, i, newValue);
}
/**
* Returns the current value of the element at index {@code i},
* with memory effects as specified by {@link VarHandle#getAcquire}.
*
* @param i the index
* @return the value
* @since 9
*/
public final long getAcquire(int i) {
return (long)AA.getAcquire(array, i);
}
/**
* Sets the element at index {@code i} to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 9
*/
public final void setRelease(int i, long newValue) {
AA.setRelease(array, i, newValue);
}
/**
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value, referred to as the <em>witness
* value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchange}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final long compareAndExchange(int i, long expectedValue, long newValue) {
return (long)AA.compareAndExchange(array, i, expectedValue, newValue);
}
/**
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value, referred to as the <em>witness
* value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeAcquire}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final long compareAndExchangeAcquire(int i, long expectedValue, long newValue) {
return (long)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
}
/**
* Atomically sets the element at index {@code i} to {@code newValue}
* if the element's current value, referred to as the <em>witness
* value</em>, {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#compareAndExchangeRelease}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return the witness value, which will be the same as the
* expected value if successful
* @since 9
*/
public final long compareAndExchangeRelease(int i, long expectedValue, long newValue) {
return (long)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
}
/**
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetVolatile}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetVolatile(int i, long expectedValue, long newValue) {
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
}
/**
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetAcquire}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetAcquire(int i, long expectedValue, long newValue) {
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
}
/**
* Possibly atomically sets the element at index {@code i} to
* {@code newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by
* {@link VarHandle#weakCompareAndSetRelease}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @since 9
*/
public final boolean weakCompareAndSetRelease(int i, long expectedValue, long newValue) {
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
}
}

View file

@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@ -153,8 +154,8 @@ public abstract class AtomicLongFieldUpdater<T> {
public abstract void lazySet(T obj, long newValue);
/**
* Gets the current value held in the field of the given object managed
* by this updater.
* Returns the current value held in the field of the given object
* managed by this updater.
*
* @param obj An object whose field to get
* @return the current value
@ -366,7 +367,7 @@ public abstract class AtomicLongFieldUpdater<T> {
}
private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final Unsafe U = Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
@ -497,7 +498,7 @@ public abstract class AtomicLongFieldUpdater<T> {
}
private static final class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final Unsafe U = Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else

Some files were not shown because too many files have changed in this diff Show more