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 3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125
03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126 03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126
8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127 8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127
5bf88dce615f6804f9e101a96ffa7c9dfb4fbbbe jdk-9+128

View file

@ -370,3 +370,4 @@ f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125 9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126 3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
8fa686245bd2a072ece3392743460030f0854520 jdk-9+127 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... # 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]) 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. # Apply user provided options.
ADD_JVM_ARG_IF_OK([$with_boot_jdk_jvmargs],boot_jdk_jvmargs,[$JAVA]) 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 #CUSTOM_AUTOCONF_INCLUDE
# Do not change or remove the following line, it is needed for consistency checks: # 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 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. # Apply user provided options.
$ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5 $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 1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126 c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127 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. * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * 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 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*
*/ */
package com.sun.corba.se.impl.activation; package com.sun.corba.se.impl.activation;

View file

@ -34,21 +34,13 @@ import java.security.PermissionCollection;
import java.security.Policy; import java.security.Policy;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.util.ArrayList; import java.security.PrivilegedActionException;
import java.util.Arrays; import java.security.PrivilegedExceptionAction;
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.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Properties;
import java.util.IdentityHashMap;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -165,8 +157,18 @@ public final class ORBUtility {
* Return default ValueHandler * Return default ValueHandler
*/ */
public static ValueHandler createValueHandler() { public static ValueHandler createValueHandler() {
ValueHandler vh;
try {
vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() {
public ValueHandler run() throws Exception {
return Util.createValueHandler(); 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 * Returns true if it was accurately determined that the remote ORB is
@ -664,7 +666,16 @@ public final class ORBUtility {
* ValueHandler. * ValueHandler.
*/ */
public static byte getMaxStreamFormatVersion() { 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)) if (!(vh instanceof javax.rmi.CORBA.ValueHandlerMultiFormat))
return ORBConstants.STREAM_FORMAT_VERSION_1; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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.rmi.Remote;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.SerializablePermission;
import java.net.MalformedURLException ; import java.net.MalformedURLException ;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
@ -195,6 +196,8 @@ public class Util {
*/ */
public static ValueHandler createValueHandler() { public static ValueHandler createValueHandler() {
isCustomSerializationPermitted();
if (utilDelegate != null) { if (utilDelegate != null) {
return utilDelegate.createValueHandler(); return utilDelegate.createValueHandler();
} }
@ -337,6 +340,7 @@ public class Util {
// security reasons. If you know a better solution how to share this code // security reasons. If you know a better solution how to share this code
// then remove it from PortableRemoteObject. Also in Stub.java // then remove it from PortableRemoteObject. Also in Stub.java
private static Object createDelegate(String classKey) { private static Object createDelegate(String classKey) {
String className = (String) String className = (String)
AccessController.doPrivileged(new GetPropertyAction(classKey)); AccessController.doPrivileged(new GetPropertyAction(classKey));
if (className == null) { if (className == null) {
@ -345,7 +349,6 @@ public class Util {
className = props.getProperty(classKey); className = props.getProperty(classKey);
} }
} }
if (className == null) { if (className == null) {
return new com.sun.corba.se.impl.javax.rmi.CORBA.Util(); return new com.sun.corba.se.impl.javax.rmi.CORBA.Util();
} }
@ -389,4 +392,14 @@ public class Util {
new GetORBPropertiesFileAction()); 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 bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126 adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127 352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
22bf6db9767b1b3a1994cbf32eb3331f31ae2093 jdk-9+128

View file

@ -270,6 +270,10 @@ public:
// For reading from/writing to the CDS archive // For reading from/writing to the CDS archive
void serialize(SerializeClosure* soc); 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. // Pick hashing algorithm.
unsigned int SymbolTable::hash_symbol(const char* s, int len) { unsigned int SymbolTable::hash_symbol(const char* s, int len) {
return use_alternate_hashcode() ? return use_alternate_hashcode() ?

View file

@ -253,6 +253,8 @@ public:
// Sharing // Sharing
static void serialize(SerializeClosure* soc); 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 // Rehash the symbol table if it gets out of balance
static void rehash_table(); static void rehash_table();

View file

@ -78,7 +78,19 @@ public:
TRAPS) { TRAPS) {
return NULL; return NULL;
} }
static void serialize(SerializeClosure* soc) {} 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 #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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -24,6 +24,7 @@
#include "precompiled.hpp" #include "precompiled.hpp"
#include "classfile/symbolTable.hpp" #include "classfile/symbolTable.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verificationType.hpp" #include "classfile/verificationType.hpp"
#include "classfile/verifier.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( bool VerificationType::is_reference_assignable_from(
const VerificationType& from, ClassVerifier* context, const VerificationType& from, ClassVerifier* context,
bool from_field_is_protected, TRAPS) const { 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 // any object or array is assignable to java.lang.Object
return true; return true;
} }
Klass* obj = SystemDictionary::resolve_or_fail(
name(), Handle(THREAD, klass->class_loader()), if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass(),
Handle(THREAD, klass->protection_domain()), true, CHECK_false); name(), from.name(), from_field_is_protected, from.is_array(),
if (log_is_enabled(Debug, class, resolve)) { from.is_object())) {
Verifier::trace_class_resolution(obj, klass()); // If add_verification_constraint() returns true, the resolution/check should be
// delayed until runtime.
return true;
} }
KlassHandle this_class(THREAD, obj); return resolve_and_check_assignability(klass(), name(), from.name(),
from_field_is_protected, from.is_array(), from.is_object(), THREAD);
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());
}
} else if (is_array() && from.is_array()) { } else if (is_array() && from.is_array()) {
VerificationType comp_this = get_component(context, CHECK_false); VerificationType comp_this = get_component(context, CHECK_false);
VerificationType comp_from = from.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( bool is_reference_assignable_from(
const VerificationType&, ClassVerifier*, bool from_field_is_protected, const VerificationType&, ClassVerifier*, bool from_field_is_protected,
TRAPS) const; 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 #endif // SHARE_VM_CLASSFILE_VERIFICATIONTYPE_HPP

View file

@ -2377,9 +2377,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
case Bytecodes::_ifnonnull: case Bytecodes::_ifnonnull:
target = bcs.dest(); target = bcs.dest();
if (visited_branches->contains(bci)) { 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. // Pop a bytecode starting offset and scan from there.
bcs.set_start(bci_stack->pop()); bcs.set_start(bci_stack->pop());
}
} else { } else {
if (target > bci) { // forward branch if (target > bci) { // forward branch
if (target >= code_length) return false; if (target >= code_length) return false;
@ -2402,9 +2410,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
case Bytecodes::_goto_w: case Bytecodes::_goto_w:
target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
if (visited_branches->contains(bci)) { 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. // Been here before, pop new starting offset from stack.
bcs.set_start(bci_stack->pop()); bcs.set_start(bci_stack->pop());
}
} else { } else {
if (target >= code_length) return false; if (target >= code_length) return false;
// Continue scanning from the target onward. // 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 // set next bytecode position
address bcp = RawBytecodeStream::bcp(); address bcp = RawBytecodeStream::bcp();
address end = method()->code_base() + end_bci(); address end = method()->code_base() + end_bci();
int l = Bytecodes::raw_special_length_at(bcp, end); int len = Bytecodes::raw_special_length_at(bcp, end);
if (l <= 0 || (_bci + l) > _end_bci) { // 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; code = Bytecodes::_illegal;
} else { } else {
_next_bci += l; _next_bci += len;
assert(_bci < _next_bci, "length must be > 0");
// set attributes // set attributes
_is_wide = false; _is_wide = false;
// check for special (uncommon) cases // 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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); code = Bytecodes::code_or_bp_at(bcp);
// set next bytecode position // set next bytecode position
int l = Bytecodes::length_for(code); int len = Bytecodes::length_for(code);
if (l > 0 && (_bci + l) <= _end_bci) { if (len > 0 && (_bci <= _end_bci - len)) {
assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch
&& code != Bytecodes::_lookupswitch, "can't be special bytecode"); && code != Bytecodes::_lookupswitch, "can't be special bytecode");
_is_wide = false; _is_wide = false;
_next_bci += l; _next_bci += len;
if (_next_bci <= _bci) { // Check for integer overflow
code = Bytecodes::_illegal;
}
_raw_code = code; _raw_code = code;
return code; return code;
} else { } else {
@ -189,9 +192,12 @@ class BytecodeStream: public BaseBytecodeStream {
// note that we cannot advance before having the // note that we cannot advance before having the
// tty bytecode otherwise the stepping is wrong! // tty bytecode otherwise the stepping is wrong!
// (carefull: length_for(...) must be used first!) // (carefull: length_for(...) must be used first!)
int l = Bytecodes::length_for(code); int len = Bytecodes::length_for(code);
if (l == 0) l = Bytecodes::length_at(_method(), bcp); if (len == 0) len = Bytecodes::length_at(_method(), bcp);
_next_bci += l; 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"); assert(_bci < _next_bci, "length must be > 0");
// set attributes // set attributes
_is_wide = false; _is_wide = false;
@ -203,6 +209,7 @@ class BytecodeStream: public BaseBytecodeStream {
} }
assert(Bytecodes::is_java_code(code), "sanity check"); assert(Bytecodes::is_java_code(code), "sanity check");
} }
}
_raw_code = raw_code; _raw_code = raw_code;
_code = code; _code = code;
return _code; return _code;

View file

@ -60,6 +60,7 @@ bool MetaspaceShared::_link_classes_made_progress;
bool MetaspaceShared::_check_classes_made_progress; bool MetaspaceShared::_check_classes_made_progress;
bool MetaspaceShared::_has_error_classes; bool MetaspaceShared::_has_error_classes;
bool MetaspaceShared::_archive_loading_failed = false; bool MetaspaceShared::_archive_loading_failed = false;
bool MetaspaceShared::_remapped_readwrite = false;
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL; address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0; size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
SharedMiscRegion MetaspaceShared::_mc; SharedMiscRegion MetaspaceShared::_mc;
@ -806,6 +807,10 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
exit(1); exit(1);
} }
} }
// Copy the verification constraints from C_HEAP-alloced GrowableArrays to RO-alloced
// Arrays
SystemDictionaryShared::finalize_verification_constraints();
} }
void MetaspaceShared::prepare_for_dumping() { void MetaspaceShared::prepare_for_dumping() {
@ -1181,6 +1186,7 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
if (!mapinfo->remap_shared_readonly_as_readwrite()) { if (!mapinfo->remap_shared_readonly_as_readwrite()) {
return false; return false;
} }
_remapped_readwrite = true;
} }
return true; return true;
} }

View file

@ -125,6 +125,7 @@ class MetaspaceShared : AllStatic {
static bool _check_classes_made_progress; static bool _check_classes_made_progress;
static bool _has_error_classes; static bool _has_error_classes;
static bool _archive_loading_failed; static bool _archive_loading_failed;
static bool _remapped_readwrite;
static address _cds_i2i_entry_code_buffers; static address _cds_i2i_entry_code_buffers;
static size_t _cds_i2i_entry_code_buffers_size; 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 // sharing is enabled. Simply returns true if sharing is not enabled
// or if the remapping has already been done by a prior call. // 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 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(); static void print_shared_spaces();

View file

@ -27,6 +27,7 @@
#include "classfile/classFileStream.hpp" #include "classfile/classFileStream.hpp"
#include "classfile/javaClasses.hpp" #include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp" #include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verifier.hpp" #include "classfile/verifier.hpp"
#include "classfile/vmSymbols.hpp" #include "classfile/vmSymbols.hpp"
#include "code/dependencyContext.hpp" #include "code/dependencyContext.hpp"
@ -597,6 +598,8 @@ bool InstanceKlass::link_class_impl(
// also sets rewritten // also sets rewritten
this_k->rewrite_class(CHECK_false); 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 // 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 // methods have been rewritten since rewrite may
// fabricate new Method*s. // fabricate new Method*s.
// also does loader constraint checking // 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); ResourceMark rm(THREAD);
this_k->vtable()->initialize_vtable(true, CHECK_false); this_k->vtable()->initialize_vtable(true, CHECK_false);
this_k->itable()->initialize_itable(true, CHECK_false); this_k->itable()->initialize_itable(true, CHECK_false);

View file

@ -27,6 +27,7 @@
#include "classfile/vmSymbols.hpp" #include "classfile/vmSymbols.hpp"
#include "gc/shared/gcLocker.hpp" #include "gc/shared/gcLocker.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "memory/universe.inline.hpp" #include "memory/universe.inline.hpp"
#include "oops/instanceKlass.hpp" #include "oops/instanceKlass.hpp"
@ -42,6 +43,10 @@ inline InstanceKlass* klassVtable::ik() const {
return InstanceKlass::cast(_klass()); 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 // this function computes the vtable size (including the size needed for miranda
// methods) and the number of miranda methods in this class. // 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) { int klassVtable::initialize_from_super(KlassHandle super) {
if (super.is_null()) { if (super.is_null()) {
return 0; 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 { } else {
// copy methods from superKlass // copy methods from superKlass
klassVtable* superVtable = super->vtable(); klassVtable* superVtable = super->vtable();
@ -152,6 +163,8 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
KlassHandle super (THREAD, klass()->java_super()); KlassHandle super (THREAD, klass()->java_super());
int nofNewEntries = 0; int nofNewEntries = 0;
bool is_shared = _klass->is_shared();
if (!klass()->is_array_klass()) { if (!klass()->is_array_klass()) {
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string()); log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string());
@ -164,6 +177,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
#endif #endif
if (Universe::is_bootstrapping()) { if (Universe::is_bootstrapping()) {
assert(!is_shared, "sanity");
// just clear everything // just clear everything
for (int i = 0; i < _length; i++) table()[i].clear(); for (int i = 0; i < _length; i++) table()[i].clear();
return; return;
@ -203,6 +217,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
if (len > 0) { if (len > 0) {
Array<int>* def_vtable_indices = NULL; Array<int>* def_vtable_indices = NULL;
if ((def_vtable_indices = ik()->default_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); def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK);
} else { } else {
assert(def_vtable_indices->length() == len, "reinit vtable len?"); assert(def_vtable_indices->length() == len, "reinit vtable len?");
@ -217,7 +232,15 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
// needs new entry // needs new entry
if (needs_new_entry) { if (needs_new_entry) {
put_method_at(mh(), initialized); 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 def_vtable_indices->at_put(i, initialized); //set vtable index
}
initialized++; initialized++;
} }
} }
@ -378,7 +401,8 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
} }
// we need a new entry if there is no superclass // we need a new entry if there is no superclass
if (klass->super() == NULL) { Klass* super = klass->super();
if (super == NULL) {
return allocate_new; return allocate_new;
} }
@ -407,7 +431,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
Symbol* target_classname = target_klass->name(); Symbol* target_classname = target_klass->name();
for(int i = 0; i < super_vtable_len; i++) { 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 // Check if method name matches
if (super_method->name() == name && super_method->signature() == signature) { 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); target_method()->set_vtable_index(i);
} else { } else {
if (def_vtable_indices != NULL) { 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); def_vtable_indices->at_put(default_index, i);
} }
}
assert(super_method->is_default_method() || super_method->is_overpass() assert(super_method->is_default_method() || super_method->is_overpass()
|| super_method->is_abstract(), "default override error"); || 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) { 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)) { if (log_develop_is_enabled(Trace, vtables)) {
ResourceMark rm; ResourceMark rm;
outputStream* logst = Log(vtables)::trace_stream(); outputStream* logst = Log(vtables)::trace_stream();
@ -502,6 +550,7 @@ void klassVtable::put_method_at(Method* m, int index) {
} }
table()[index].set(m); table()[index].set(m);
} }
}
// Find out if a method "m" with superclass "super", loader "classloader" and // 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 // 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) { void itableMethodEntry::initialize(Method* m) {
if (m == NULL) return; 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; _method = m;
} }
}
klassItable::klassItable(instanceKlassHandle klass) { klassItable::klassItable(instanceKlassHandle klass) {
_klass = klass; _klass = klass;
@ -1054,7 +1111,11 @@ int klassItable::assign_itable_indices_for_interface(Klass* klass) {
logst->cr(); logst->cr();
} }
if (!m->has_vtable_index()) { 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); m->set_itable_index(ime_num);
// Progress to next itable entry // Progress to next itable entry
ime_num++; ime_num++;
@ -1248,7 +1309,6 @@ void klassItable::dump_itable() {
} }
#endif // INCLUDE_JVMTI #endif // INCLUDE_JVMTI
// Setup // Setup
class InterfaceVisiterClosure : public StackObj { class InterfaceVisiterClosure : public StackObj {
public: public:

View file

@ -153,6 +153,19 @@ class klassVtable : public ResourceObj {
Array<Klass*>* local_interfaces); Array<Klass*>* local_interfaces);
void verify_against(outputStream* st, klassVtable* vt, int index); void verify_against(outputStream* st, klassVtable* vt, int index);
inline InstanceKlass* ik() const; 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(); 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) { bool Method::was_executed_more_than(int n) {
// Invocation counter is reset when the Method* is compiled. // 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; }) DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; })
bool has_vtable_index() const { return _vtable_index >= 0; } bool has_vtable_index() const { return _vtable_index >= 0; }
int vtable_index() const { return _vtable_index; } 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; }) DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; })
bool has_itable_index() const { return _vtable_index <= itable_index_max; } bool has_itable_index() const { return _vtable_index <= itable_index_max; }
int itable_index() const { assert(valid_itable_index(), ""); int itable_index() const { assert(valid_itable_index(), "");
return itable_index_max - _vtable_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 // interpreter entry
address interpreter_entry() const { return _i2i_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); Compile::AliasType* alias_type = C->alias_type(adr_type);
assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here"); assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here");
assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM || // Only field, array element or unknown locations are supported.
alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown"); if (alias_type->adr_type() != TypeRawPtr::BOTTOM &&
alias_type->adr_type() != TypeOopPtr::BOTTOM &&
alias_type->basic_type() == T_ILLEGAL) {
return false;
}
bool mismatched = false; bool mismatched = false;
BasicType bt = alias_type->basic_type(); BasicType bt = alias_type->basic_type();
if (bt != T_ILLEGAL) { if (bt != T_ILLEGAL) {
@ -2782,12 +2787,6 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
ShouldNotReachHere(); ShouldNotReachHere();
} }
// Null check receiver.
receiver = null_check(receiver);
if (stopped()) {
return true;
}
// Build field offset expression. // Build field offset expression.
// We currently rely on the cookies produced by Unsafe.xxxFieldOffset // We currently rely on the cookies produced by Unsafe.xxxFieldOffset
// to be plain byte offsets, which are also the same as those accepted // 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(); const TypePtr *adr_type = _gvn.type(adr)->isa_ptr();
Compile::AliasType* alias_type = C->alias_type(adr_type); 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(); BasicType bt = alias_type->basic_type();
if (bt != T_ILLEGAL && if (bt != T_ILLEGAL &&
((bt == T_OBJECT || bt == T_ARRAY) != (type == T_OBJECT))) { ((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(); ShouldNotReachHere();
} }
// Null check receiver.
receiver = null_check(receiver);
if (stopped()) {
return true;
}
int alias_idx = C->get_alias_index(adr_type); int alias_idx = C->get_alias_index(adr_type);
// Memory-model-wise, a LoadStore acts like a little synchronized // Memory-model-wise, a LoadStore acts like a little synchronized

View file

@ -25,6 +25,7 @@
* @test * @test
* @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64") * @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64")
* @library ../../../../../ * @library ../../../../../
* @ignore 8161550
* @modules java.base/jdk.internal.reflect * @modules java.base/jdk.internal.reflect
* jdk.vm.ci/jdk.vm.ci.meta * jdk.vm.ci/jdk.vm.ci.meta
* jdk.vm.ci/jdk.vm.ci.runtime * 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" * @requires vm.gc=="null"
* @summary Verify that starting the VM with a small heap works * @summary Verify that starting the VM with a small heap works
* @library /testlibrary /test/lib /test/lib/share/classes * @library /testlibrary /test/lib /test/lib/share/classes
* @ignore 8161552
* @modules java.base/jdk.internal.misc * @modules java.base/jdk.internal.misc
* @modules java.management/sun.management * @modules java.management/sun.management
* @build TestSmallHeap * @build TestSmallHeap

View file

@ -29,6 +29,7 @@
* parallel collectors. * parallel collectors.
* @requires vm.gc=="null" * @requires vm.gc=="null"
* @library /testlibrary /test/lib * @library /testlibrary /test/lib
* @ignore 8161552
* @modules java.base/jdk.internal.misc * @modules java.base/jdk.internal.misc
* java.management * java.management
* @build TestParallelHeapSizeFlags TestMaxHeapSizeTools * @build TestParallelHeapSizeFlags TestMaxHeapSizeTools

View file

@ -370,3 +370,4 @@ e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124
493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125 493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125
15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126 15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126
bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127 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 = public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT =
ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit"; 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 * JDK maximum parameter entity size limit
*/ */
@ -136,6 +144,13 @@ public final class XalanConstants {
* JDK maximum general entity size limit * JDK maximum general entity size limit
*/ */
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit"; 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 * 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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, MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH,
XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0), XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0),
MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT, 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 key;
final String apiProperty; final String apiProperty;

View file

@ -1,13 +1,13 @@
/* /*
* reserved comment block * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT REMOVE OR ALTER!
*/ */
/* /*
* Copyright 2001-2004 The Apache Software Foundation. * Licensed to the Apache Software Foundation (ASF) under one or more
* * contributor license agreements. See the NOTICE file distributed with
* Licensed under the Apache License, Version 2.0 (the "License"); * this work for additional information regarding copyright ownership.
* you may not use this file except in compliance with the License. * The ASF licenses this file to You under the Apache License, Version 2.0
* You may obtain a copy of the License at * (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
@ -98,6 +98,10 @@ public interface Constants extends InstructionConstants {
public static final int ACC_STATIC public static final int ACC_STATIC
= com.sun.org.apache.bcel.internal.Constants.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 public static final String STRING_SIG
= "Ljava/lang/String;"; = "Ljava/lang/String;";
public static final String STRING_BUFFER_SIG 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"; = "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl";
public static final String SAX_IMPL public static final String SAX_IMPL
= "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl"; = "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 public static final String STRING_CLASS
= "java.lang.String"; = "java.lang.String";
public static final String OBJECT_CLASS public static final String OBJECT_CLASS
@ -335,6 +343,19 @@ public interface Constants extends InstructionConstants {
= "setStartNode"; = "setStartNode";
public static final String RESET public static final String RESET
= "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 public static final String ATTR_SET_SIG
= "(" + DOM_INTF_SIG + NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + "I)V"; = "(" + 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 * 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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; 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.InstructionConstants;
import com.sun.org.apache.bcel.internal.generic.InstructionList; 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.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.LocalVariableGen;
import com.sun.org.apache.bcel.internal.generic.NEW; import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.bcel.internal.generic.PUSH; import com.sun.org.apache.bcel.internal.generic.PUSH;
@ -792,6 +790,11 @@ class FunctionCall extends Expression {
final String clazz = final String clazz =
_chosenConstructor.getDeclaringClass().getName(); _chosenConstructor.getDeclaringClass().getName();
// Generate call to Module.addReads:
// <TransletClass>.class.getModule().addReads(
generateAddReads(classGen, methodGen, clazz);
Class[] paramTypes = _chosenConstructor.getParameterTypes(); Class[] paramTypes = _chosenConstructor.getParameterTypes();
LocalVariableGen[] paramTemp = new LocalVariableGen[n]; LocalVariableGen[] paramTemp = new LocalVariableGen[n];
@ -855,6 +858,12 @@ class FunctionCall extends Expression {
final String clazz = _chosenMethod.getDeclaringClass().getName(); final String clazz = _chosenMethod.getDeclaringClass().getName();
Class[] paramTypes = _chosenMethod.getParameterTypes(); 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 // Push "this" if it is an instance method
if (_thisArgument != null) { if (_thisArgument != null) {
_thisArgument.translate(classGen, methodGen); _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 @Override
public String toString() { public String toString() {
return "funcall(" + _fname + ", " + _arguments + ')'; return "funcall(" + _fname + ", " + _arguments + ')';

View file

@ -58,7 +58,6 @@ import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.URIResolver; import javax.xml.transform.URIResolver;
import jdk.internal.module.Modules;
/** /**
* @author Morten Jorgensen * @author Morten Jorgensen
@ -486,10 +485,6 @@ public final class TemplatesImpl implements Templates, Serializable {
thisModule.addExports(p, m); 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 // java.xml needs to instanitate the translet class
thisModule.addReads(m); thisModule.addReads(m);
@ -513,7 +508,7 @@ public final class TemplatesImpl implements Templates, Serializable {
} }
catch (ClassFormatError e) { catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name); ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
throw new TransformerConfigurationException(err.toString()); throw new TransformerConfigurationException(err.toString(), e);
} }
catch (LinkageError e) { catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name); 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 * 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 = public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT =
ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit"; 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 * JDK maximum parameter entity size limit
*/ */
@ -292,6 +300,13 @@ public final class Constants {
* JDK maximum general entity size limit * JDK maximum general entity size limit
*/ */
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit"; 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 * JDK maximum parameter entity size limit
*/ */

View file

@ -1,62 +1,21 @@
/* /*
* reserved comment block * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT REMOVE OR ALTER!
*/ */
/* /*
* 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. * Unless required by applicable law or agreed to in writing, software
* All rights reserved. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Redistribution and use in source and binary forms, with or without * See the License for the specific language governing permissions and
* modification, are permitted provided that the following conditions * limitations under the License.
* 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/>.
*/ */
package com.sun.org.apache.xerces.internal.impl; package com.sun.org.apache.xerces.internal.impl;
@ -146,7 +105,7 @@ public class XML11DTDScannerImpl
protected boolean scanPubidLiteral(XMLString literal) protected boolean scanPubidLiteral(XMLString literal)
throws IOException, XNIException throws IOException, XNIException
{ {
int quote = fEntityScanner.scanChar(); int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') { if (quote != '\'' && quote != '"') {
reportFatalError("QuoteRequiredInPublicID", null); reportFatalError("QuoteRequiredInPublicID", null);
return false; return false;
@ -157,7 +116,7 @@ public class XML11DTDScannerImpl
boolean skipSpace = true; boolean skipSpace = true;
boolean dataok = true; boolean dataok = true;
while (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 // 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 (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
if (!skipSpace) { if (!skipSpace) {

View file

@ -1,62 +1,21 @@
/* /*
* reserved comment block * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT REMOVE OR ALTER!
*/ */
/* /*
* 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. * Unless required by applicable law or agreed to in writing, software
* All rights reserved. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Redistribution and use in source and binary forms, with or without * See the License for the specific language governing permissions and
* modification, are permitted provided that the following conditions * limitations under the License.
* 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/>.
*/ */
package com.sun.org.apache.xerces.internal.impl; package com.sun.org.apache.xerces.internal.impl;
@ -134,7 +93,7 @@ public class XML11DocumentScannerImpl
// happens when there is the character reference &#13; // happens when there is the character reference &#13;
// but scanContent doesn't do entity expansions... // but scanContent doesn't do entity expansions...
// is this *really* necessary??? - NG // is this *really* necessary??? - NG
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
content.append((char)c); content.append((char)c);
c = -1; c = -1;
} }
@ -143,7 +102,7 @@ public class XML11DocumentScannerImpl
} */ } */
if (c == ']') { 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 // remember where we are in case we get an endEntity before we
// could flush the buffer out - this happens when we're parsing an // could flush the buffer out - this happens when we're parsing an
// entity which ends with a ] // entity which ends with a ]
@ -152,12 +111,12 @@ public class XML11DocumentScannerImpl
// We work on a single character basis to handle cases such as: // We work on a single character basis to handle cases such as:
// ']]]>' which we might otherwise miss. // ']]]>' which we might otherwise miss.
// //
if (fEntityScanner.skipChar(']')) { if (fEntityScanner.skipChar(']', null)) {
content.append(']'); content.append(']');
while (fEntityScanner.skipChar(']')) { while (fEntityScanner.skipChar(']', null)) {
content.append(']'); content.append(']');
} }
if (fEntityScanner.skipChar('>')) { if (fEntityScanner.skipChar('>', null)) {
reportFatalError("CDEndInContent", null); reportFatalError("CDEndInContent", null);
} }
} }
@ -184,6 +143,7 @@ public class XML11DocumentScannerImpl
* @param checkEntities true if undeclared entities should be reported as VC violation, * @param checkEntities true if undeclared entities should be reported as VC violation,
* false if undeclared entities should be reported as WFC violation. * false if undeclared entities should be reported as WFC violation.
* @param eleName The name of element to which this attribute belongs. * @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 * @return true if the non-normalized and normalized value are the same
* *
@ -193,7 +153,7 @@ public class XML11DocumentScannerImpl
protected boolean scanAttributeValue(XMLString value, protected boolean scanAttributeValue(XMLString value,
XMLString nonNormalizedValue, XMLString nonNormalizedValue,
String atName, String atName,
boolean checkEntities,String eleName) boolean checkEntities,String eleName, boolean isNSURI)
throws IOException, XNIException throws IOException, XNIException
{ {
// quote // quote
@ -202,10 +162,10 @@ public class XML11DocumentScannerImpl
reportFatalError("OpenQuoteExpected", new Object[]{eleName,atName}); reportFatalError("OpenQuoteExpected", new Object[]{eleName,atName});
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.ATTRIBUTE);
int entityDepth = fEntityDepth; int entityDepth = fEntityDepth;
int c = fEntityScanner.scanLiteral(quote, value); int c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (DEBUG_ATTR_NORMALIZATION) { if (DEBUG_ATTR_NORMALIZATION) {
System.out.println("** scanLiteral -> \"" System.out.println("** scanLiteral -> \""
+ value.toString() + "\""); + value.toString() + "\"");
@ -215,7 +175,7 @@ public class XML11DocumentScannerImpl
if (c == quote && (fromIndex = isUnchangedByNormalization(value)) == -1) { if (c == quote && (fromIndex = isUnchangedByNormalization(value)) == -1) {
/** Both the non-normalized and normalized attribute values are equal. **/ /** Both the non-normalized and normalized attribute values are equal. **/
nonNormalizedValue.setValues(value); nonNormalizedValue.setValues(value);
int cquote = fEntityScanner.scanChar(); int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
if (cquote != quote) { if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName}); reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName});
} }
@ -238,11 +198,11 @@ public class XML11DocumentScannerImpl
+ fStringBuffer.toString() + "\""); + fStringBuffer.toString() + "\"");
} }
if (c == '&') { if (c == '&') {
fEntityScanner.skipChar('&'); fEntityScanner.skipChar('&', NameType.REFERENCE);
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append('&'); fStringBuffer2.append('&');
} }
if (fEntityScanner.skipChar('#')) { if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append('#'); fStringBuffer2.append('#');
} }
@ -256,59 +216,22 @@ public class XML11DocumentScannerImpl
} }
} }
else { else {
String entityName = fEntityScanner.scanName(); String entityName = fEntityScanner.scanName(NameType.REFERENCE);
if (entityName == null) { if (entityName == null) {
reportFatalError("NameRequiredInReference", null); reportFatalError("NameRequiredInReference", null);
} }
else if (entityDepth == fEntityDepth) { else if (entityDepth == fEntityDepth) {
fStringBuffer2.append(entityName); fStringBuffer2.append(entityName);
} }
if (!fEntityScanner.skipChar(';')) { if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference", reportFatalError("SemicolonRequiredInReference",
new Object []{entityName}); new Object []{entityName});
} }
else if (entityDepth == fEntityDepth) { else if (entityDepth == fEntityDepth) {
fStringBuffer2.append(';'); fStringBuffer2.append(';');
} }
if (entityName == fAmpSymbol) { if (resolveCharacter(entityName, fStringBuffer)) {
fStringBuffer.append('&'); checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
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()
+ "\"");
}
} }
else { else {
if (fEntityManager.isExternalEntity(entityName)) { if (fEntityManager.isExternalEntity(entityName)) {
@ -339,13 +262,13 @@ public class XML11DocumentScannerImpl
else if (c == '<') { else if (c == '<') {
reportFatalError("LessthanInAttValue", reportFatalError("LessthanInAttValue",
new Object[] { eleName, atName }); new Object[] { eleName, atName });
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append((char)c); fStringBuffer2.append((char)c);
} }
} }
else if (c == '%' || c == ']') { else if (c == '%' || c == ']') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fStringBuffer.append((char)c); fStringBuffer.append((char)c);
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append((char)c); fStringBuffer2.append((char)c);
@ -359,7 +282,7 @@ public class XML11DocumentScannerImpl
// XML11EntityScanner. Not sure why // XML11EntityScanner. Not sure why
// this check was originally necessary. - NG // this check was originally necessary. - NG
else if (c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) { else if (c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fStringBuffer.append(' '); fStringBuffer.append(' ');
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append('\n'); fStringBuffer2.append('\n');
@ -382,12 +305,12 @@ public class XML11DocumentScannerImpl
else if (c != -1 && isInvalidLiteral(c)) { else if (c != -1 && isInvalidLiteral(c)) {
reportFatalError("InvalidCharInAttValue", reportFatalError("InvalidCharInAttValue",
new Object[] {eleName, atName, Integer.toString(c, 16)}); new Object[] {eleName, atName, Integer.toString(c, 16)});
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append((char)c); fStringBuffer2.append((char)c);
} }
} }
c = fEntityScanner.scanLiteral(quote, value); c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (entityDepth == fEntityDepth) { if (entityDepth == fEntityDepth) {
fStringBuffer2.append(value); fStringBuffer2.append(value);
} }
@ -404,7 +327,7 @@ public class XML11DocumentScannerImpl
nonNormalizedValue.setValues(fStringBuffer2); nonNormalizedValue.setValues(fStringBuffer2);
// quote // quote
int cquote = fEntityScanner.scanChar(); int cquote = fEntityScanner.scanChar(null);
if (cquote != quote) { if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName}); reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName});
} }
@ -439,7 +362,7 @@ public class XML11DocumentScannerImpl
protected boolean scanPubidLiteral(XMLString literal) protected boolean scanPubidLiteral(XMLString literal)
throws IOException, XNIException throws IOException, XNIException
{ {
int quote = fEntityScanner.scanChar(); int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') { if (quote != '\'' && quote != '"') {
reportFatalError("QuoteRequiredInPublicID", null); reportFatalError("QuoteRequiredInPublicID", null);
return false; return false;
@ -450,7 +373,7 @@ public class XML11DocumentScannerImpl
boolean skipSpace = true; boolean skipSpace = true;
boolean dataok = true; boolean dataok = true;
while (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 // 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 (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
if (!skipSpace) { if (!skipSpace) {

View file

@ -21,6 +21,7 @@
package com.sun.org.apache.xerces.internal.impl; 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.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.XML11Char; import com.sun.org.apache.xerces.internal.util.XML11Char;
import com.sun.org.apache.xerces.internal.util.XMLChar; 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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @throws EOFException Thrown on end of file.
*/ */
public int scanChar() throws IOException { protected int scanChar(NameType nt) throws IOException {
// load more characters, if needed // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -100,6 +101,7 @@ public class XML11EntityScanner
} }
// scan character // scan character
int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[fCurrentEntity.position++]; int c = fCurrentEntity.ch[fCurrentEntity.position++];
boolean external = false; boolean external = false;
if (c == '\n' || if (c == '\n' ||
@ -110,6 +112,7 @@ public class XML11EntityScanner
invokeListeners(1); invokeListeners(1);
fCurrentEntity.ch[0] = (char)c; fCurrentEntity.ch[0] = (char)c;
load(1, false, false); load(1, false, false);
offset = 0;
} }
if (c == '\r' && external) { if (c == '\r' && external) {
int cc = fCurrentEntity.ch[fCurrentEntity.position++]; int cc = fCurrentEntity.ch[fCurrentEntity.position++];
@ -122,6 +125,9 @@ public class XML11EntityScanner
// return character that was scanned // return character that was scanned
fCurrentEntity.columnNumber++; fCurrentEntity.columnNumber++;
if (!detectingVersion) {
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
}
return c; return c;
} // scanChar():int } // 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.SymbolTable
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
load(0, true, true); load(0, true, true);
@ -248,6 +254,8 @@ public class XML11EntityScanner
* <strong>Note:</strong> The string returned must be a symbol. The * <strong>Note:</strong> The string returned must be a symbol. The
* SymbolTable can be used for this purpose. * 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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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#isXML11Name
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
load(0, true, true); load(0, true, true);
@ -310,23 +318,11 @@ public class XML11EntityScanner
return null; return null;
} }
int length = 0;
do { do {
ch = fCurrentEntity.ch[fCurrentEntity.position]; ch = fCurrentEntity.ch[fCurrentEntity.position];
if (XML11Char.isXML11Name(ch)) { if (XML11Char.isXML11Name(ch)) {
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
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);
}
offset = 0; offset = 0;
if (load(length, false, false)) { if (load(length, false, false)) {
break; break;
@ -334,20 +330,7 @@ public class XML11EntityScanner
} }
} }
else if (XML11Char.isXML11NameHighSurrogate(ch)) { else if (XML11Char.isXML11NameHighSurrogate(ch)) {
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
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);
}
offset = 0; offset = 0;
if (load(length, false, false)) { if (load(length, false, false)) {
--fCurrentEntity.position; --fCurrentEntity.position;
@ -361,20 +344,7 @@ public class XML11EntityScanner
--fCurrentEntity.position; --fCurrentEntity.position;
break; break;
} }
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
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);
}
offset = 0; offset = 0;
if (load(length, false, false)) { if (load(length, false, false)) {
break; break;
@ -387,12 +357,14 @@ public class XML11EntityScanner
} }
while (true); while (true);
int length = fCurrentEntity.position - offset; length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
// return name // return name
String symbol = null; String symbol = null;
if (length > 0) { if (length > 0) {
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
checkEntityLimit(nt, fCurrentEntity, offset, length);
symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length); symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
} }
return symbol; 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#isXML11NCName
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCNameStart * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -571,6 +543,7 @@ public class XML11EntityScanner
* this purpose. * this purpose.
* *
* @param qname The qualified name structure to fill. * @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 * @return Returns true if a qualified name appeared immediately on
* the input and was scanned, false otherwise. * 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#isXML11Name
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -602,6 +575,7 @@ public class XML11EntityScanner
fCurrentEntity.columnNumber++; fCurrentEntity.columnNumber++;
String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 1); String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 1);
qname.setValues(null, name, name, null); qname.setValues(null, name, name, null);
checkEntityLimit(nt, fCurrentEntity, 0, 1);
return true; return true;
} }
} }
@ -632,6 +606,7 @@ public class XML11EntityScanner
fCurrentEntity.columnNumber += 2; fCurrentEntity.columnNumber += 2;
String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 2); String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 2);
qname.setValues(null, name, name, null); qname.setValues(null, name, name, null);
checkEntityLimit(nt, fCurrentEntity, 0, 2);
return true; return true;
} }
} }
@ -641,6 +616,7 @@ public class XML11EntityScanner
} }
int index = -1; int index = -1;
int length = 0;
boolean sawIncompleteSurrogatePair = false; boolean sawIncompleteSurrogatePair = false;
do { do {
ch = fCurrentEntity.ch[fCurrentEntity.position]; ch = fCurrentEntity.ch[fCurrentEntity.position];
@ -653,22 +629,7 @@ public class XML11EntityScanner
//check prefix before further read //check prefix before further read
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset); checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset);
} }
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
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 (index != -1) { if (index != -1) {
index = index - offset; index = index - offset;
} }
@ -679,20 +640,7 @@ public class XML11EntityScanner
} }
} }
else if (XML11Char.isXML11NameHighSurrogate(ch)) { else if (XML11Char.isXML11NameHighSurrogate(ch)) {
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
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 (index != -1) { if (index != -1) {
index = index - offset; index = index - offset;
} }
@ -711,20 +659,7 @@ public class XML11EntityScanner
--fCurrentEntity.position; --fCurrentEntity.position;
break; break;
} }
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
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 (index != -1) { if (index != -1) {
index = index - offset; index = index - offset;
} }
@ -740,7 +675,7 @@ public class XML11EntityScanner
} }
while (true); while (true);
int length = fCurrentEntity.position - offset; length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
if (length > 0) { if (length > 0) {
@ -776,6 +711,7 @@ public class XML11EntityScanner
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length); checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
} }
qname.setValues(prefix, localpart, rawname, null); qname.setValues(prefix, localpart, rawname, null);
checkEntityLimit(nt, fCurrentEntity, offset, length);
return true; return true;
} }
return false; return false;
@ -808,7 +744,7 @@ public class XML11EntityScanner
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -826,6 +762,7 @@ public class XML11EntityScanner
int offset = fCurrentEntity.position; int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[offset]; int c = fCurrentEntity.ch[offset];
int newlines = 0; int newlines = 0;
boolean counted = false;
boolean external = fCurrentEntity.isExternal(); boolean external = fCurrentEntity.isExternal();
if (c == '\n' || ((c == '\r' || c == 0x85 || c == 0x2028) && external)) { if (c == '\n' || ((c == '\r' || c == 0x85 || c == 0x2028) && external)) {
do { do {
@ -835,11 +772,13 @@ public class XML11EntityScanner
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1; fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0; offset = 0;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
fCurrentEntity.position = newlines; fCurrentEntity.position = newlines;
fCurrentEntity.startPosition = newlines; fCurrentEntity.startPosition = newlines;
if (load(newlines, false, true)) { if (load(newlines, false, true)) {
counted = true;
break; break;
} }
} }
@ -858,11 +797,13 @@ public class XML11EntityScanner
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1; fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0; offset = 0;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
fCurrentEntity.position = newlines; fCurrentEntity.position = newlines;
fCurrentEntity.startPosition = newlines; fCurrentEntity.startPosition = newlines;
if (load(newlines, false, true)) { if (load(newlines, false, true)) {
counted = true;
break; break;
} }
} }
@ -877,6 +818,7 @@ public class XML11EntityScanner
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) { if (fCurrentEntity.position == fCurrentEntity.count - 1) {
checkEntityLimit(null, fCurrentEntity, offset, length);
content.setValues(fCurrentEntity.ch, offset, length); content.setValues(fCurrentEntity.ch, offset, length);
return -1; return -1;
} }
@ -904,8 +846,8 @@ public class XML11EntityScanner
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
if (fCurrentEntity.isGE) { if (!counted) {
checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length); checkEntityLimit(null, fCurrentEntity, offset, length);
} }
content.setValues(fCurrentEntity.ch, 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 * @param quote The quote character that signifies the end of the
* attribute value data. * attribute value data.
* @param content The content structure to fill. * @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 * @return Returns the next character on the input, if known. This
* value may be -1 but this does <em>note</em> designate * 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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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 { throws IOException {
// load more characters, if needed // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -1051,8 +994,10 @@ public class XML11EntityScanner
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; 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); content.setValues(fCurrentEntity.ch, offset, length);
@ -1103,7 +1048,7 @@ public class XML11EntityScanner
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @throws EOFException Thrown on end of file.
*/ */
public boolean scanData(String delimiter, XMLStringBuffer buffer) protected boolean scanData(String delimiter, XMLStringBuffer buffer)
throws IOException { throws IOException {
boolean done = false; boolean done = false;
@ -1135,6 +1080,7 @@ public class XML11EntityScanner
if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) { if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
// something must be wrong with the input: e.g., file ends an unterminated comment // something must be wrong with the input: e.g., file ends an unterminated comment
int length = fCurrentEntity.count - fCurrentEntity.position; int length = fCurrentEntity.count - fCurrentEntity.position;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length);
buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length); buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length);
fCurrentEntity.columnNumber += fCurrentEntity.count; fCurrentEntity.columnNumber += fCurrentEntity.count;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
@ -1199,6 +1145,7 @@ public class XML11EntityScanner
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) { if (fCurrentEntity.position == fCurrentEntity.count - 1) {
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length); buffer.append(fCurrentEntity.ch, offset, length);
return true; return true;
} }
@ -1237,6 +1184,7 @@ public class XML11EntityScanner
fCurrentEntity.position--; fCurrentEntity.position--;
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length); buffer.append(fCurrentEntity.ch, offset, length);
return true; return true;
} }
@ -1274,6 +1222,7 @@ public class XML11EntityScanner
fCurrentEntity.position--; fCurrentEntity.position--;
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length); buffer.append(fCurrentEntity.ch, offset, length);
return true; return true;
} }
@ -1281,6 +1230,7 @@ public class XML11EntityScanner
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
if (done) { if (done) {
length -= delimLen; length -= delimLen;
} }
@ -1305,7 +1255,7 @@ public class XML11EntityScanner
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -1313,6 +1263,7 @@ public class XML11EntityScanner
} }
// skip character // skip character
int offset = fCurrentEntity.position;
int cc = fCurrentEntity.ch[fCurrentEntity.position]; int cc = fCurrentEntity.ch[fCurrentEntity.position];
if (cc == c) { if (cc == c) {
fCurrentEntity.position++; fCurrentEntity.position++;
@ -1323,12 +1274,14 @@ public class XML11EntityScanner
else { else {
fCurrentEntity.columnNumber++; fCurrentEntity.columnNumber++;
} }
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true; return true;
} }
else if (c == '\n' && ((cc == 0x2028 || cc == 0x85) && fCurrentEntity.isExternal())) { else if (c == '\n' && ((cc == 0x2028 || cc == 0x85) && fCurrentEntity.isExternal())) {
fCurrentEntity.position++; fCurrentEntity.position++;
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1; fCurrentEntity.columnNumber = 1;
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true; return true;
} }
else if (c == '\n' && (cc == '\r' ) && fCurrentEntity.isExternal()) { else if (c == '\n' && (cc == '\r' ) && fCurrentEntity.isExternal()) {
@ -1344,6 +1297,7 @@ public class XML11EntityScanner
} }
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1; fCurrentEntity.columnNumber = 1;
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true; 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.XMLChar#isSpace
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Space * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -1386,7 +1340,7 @@ public class XML11EntityScanner
// skip spaces // skip spaces
int c = fCurrentEntity.ch[fCurrentEntity.position]; int c = fCurrentEntity.ch[fCurrentEntity.position];
int offset = fCurrentEntity.position - 1;
// External -- Match: S + 0x85 + 0x2028, and perform end of line normalization // External -- Match: S + 0x85 + 0x2028, and perform end of line normalization
if (fCurrentEntity.isExternal()) { if (fCurrentEntity.isExternal()) {
if (XML11Char.isXML11Space(c)) { if (XML11Char.isXML11Space(c)) {
@ -1422,6 +1376,11 @@ public class XML11EntityScanner
else { else {
fCurrentEntity.columnNumber++; 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 // load more characters, if needed
if (!entityChanged) if (!entityChanged)
fCurrentEntity.position++; fCurrentEntity.position++;
@ -1462,6 +1421,11 @@ public class XML11EntityScanner
else { else {
fCurrentEntity.columnNumber++; 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 // load more characters, if needed
if (!entityChanged) if (!entityChanged)
fCurrentEntity.position++; fCurrentEntity.position++;
@ -1495,7 +1459,7 @@ public class XML11EntityScanner
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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 // load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
@ -1504,6 +1468,7 @@ public class XML11EntityScanner
// skip string // skip string
final int length = s.length(); final int length = s.length();
final int beforeSkip = fCurrentEntity.position ;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
char c = fCurrentEntity.ch[fCurrentEntity.position++]; char c = fCurrentEntity.ch[fCurrentEntity.position++];
if (c != s.charAt(i)) { if (c != s.charAt(i)) {
@ -1523,6 +1488,9 @@ public class XML11EntityScanner
} }
} }
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
if (!detectingVersion) {
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
}
return true; return true;
} // skipString(String):boolean } // 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) if (DEBUG_START_END_ELEMENT)
System.out.println(">>> scanStartElementNS()"); System.out.println(">>> scanStartElementNS()");
// Note: namespace processing is on by default // 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 // REVISIT - [Q] Why do we need this local variable? -- mrglavas
String rawname = fElementQName.rawname; String rawname = fElementQName.rawname;
if (fBindNamespaces) { if (fBindNamespaces) {
@ -173,11 +173,11 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// end tag? // end tag?
int c = fEntityScanner.peekChar(); int c = fEntityScanner.peekChar();
if (c == '>') { if (c == '>') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
break; break;
} else if (c == '/') { } else if (c == '/') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError( reportFatalError(
"ElementUnterminated", "ElementUnterminated",
new Object[] { rawname }); new Object[] { rawname });
@ -345,7 +345,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
protected void scanStartElementName () protected void scanStartElementName ()
throws IOException, XNIException { throws IOException, XNIException {
// Note: namespace processing is on by default // Note: namespace processing is on by default
fEntityScanner.scanQName(fElementQName); fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE);
// Must skip spaces here because the DTD scanner // Must skip spaces here because the DTD scanner
// would consume them at the end of the external subset. // would consume them at the end of the external subset.
fSawSpace = fEntityScanner.skipSpaces(); fSawSpace = fEntityScanner.skipSpaces();
@ -395,11 +395,11 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// end tag? // end tag?
int c = fEntityScanner.peekChar(); int c = fEntityScanner.peekChar();
if (c == '>') { if (c == '>') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
break; break;
} else if (c == '/') { } else if (c == '/') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError( reportFatalError(
"ElementUnterminated", "ElementUnterminated",
new Object[] { rawname }); new Object[] { rawname });
@ -571,11 +571,11 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
System.out.println(">>> scanAttribute()"); System.out.println(">>> scanAttribute()");
// name // name
fEntityScanner.scanQName(fAttributeQName); fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE);
// equals // equals
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('=')) { if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
reportFatalError( reportFatalError(
"EqRequiredInAttribute", "EqRequiredInAttribute",
new Object[] { 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 //REVISIT: one more case needs to be included: external PE and standalone is no
boolean isVC = fHasExternalDTD && !fStandalone; boolean isVC = fHasExternalDTD && !fStandalone;
// REVISIT: it seems that this function should not take attributes, and length /**
scanAttributeValue( * Determine whether this is a namespace declaration that will be subject
this.fTempString, * to the name limit check in the scanAttributeValue operation.
fTempString2, * Namespace declaration format: xmlns="..." or xmlns:prefix="..."
fAttributeQName.rawname, * Note that prefix:xmlns="..." isn't a namespace.
isVC, */
fCurrentElement.rawname); 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(); String value = fTempString.toString();
attributes.setValue(attrIndex, value); attributes.setValue(attrIndex, value);
attributes.setNonNormalizedValue(attrIndex, fTempString2.toString()); attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
@ -628,17 +635,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// record namespace declarations if any. // record namespace declarations if any.
if (fBindNamespaces) { if (fBindNamespaces) {
if (isNSDecl) {
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 (value.length() > fXMLNameLimit) { if (value.length() > fXMLNameLimit) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MaxXMLNameLimit", "MaxXMLNameLimit",
@ -758,7 +755,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
// end // end
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError( reportFatalError(
"ETagUnterminated", "ETagUnterminated",
new Object[] { endElementName.rawname }); new Object[] { endElementName.rawname });

View file

@ -21,10 +21,7 @@
package com.sun.org.apache.xerces.internal.impl; 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.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.SymbolTable;
import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl; import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
import com.sun.org.apache.xerces.internal.util.XMLChar; 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 // we're done, set starting state for external subset
setScannerState(SCANNER_STATE_TEXT_DECL); setScannerState(SCANNER_STATE_TEXT_DECL);
// we're done scanning DTD. // we're done scanning DTD.
fLimitAnalyzer.reset(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT);
fLimitAnalyzer.reset(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT); fLimitAnalyzer.reset(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT);
return false; return false;
} }
@ -399,7 +397,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
if (isInvalidLiteral(c)) { if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInDTD", reportFatalError("InvalidCharInDTD",
new Object[] { Integer.toHexString(c) }); new Object[] { Integer.toHexString(c) });
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
} }
} }
} }
@ -767,7 +765,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fStringBuffer.clear(); fStringBuffer.clear();
fStringBuffer.append("xml"); fStringBuffer.append("xml");
while (isValidNameChar(fEntityScanner.peekChar())) { while (isValidNameChar(fEntityScanner.peekChar())) {
fStringBuffer.append((char)fEntityScanner.scanChar()); fStringBuffer.append((char)fEntityScanner.scanChar(null));
} }
String target = String target =
fSymbolTable.addSymbol(fStringBuffer.ch, fSymbolTable.addSymbol(fStringBuffer.ch,
@ -867,7 +865,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
// element name // element name
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
if (name == null) { if (name == null) {
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL", reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL",
null); null);
@ -900,7 +898,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
} }
else { else {
if (!fEntityScanner.skipChar('(')) { if (!fEntityScanner.skipChar('(', null)) {
reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
new Object[]{name}); new Object[]{name});
} }
@ -930,7 +928,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fReportEntity = false; fReportEntity = false;
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
// end // end
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("ElementDeclUnterminated", new Object[]{name}); reportFatalError("ElementDeclUnterminated", new Object[]{name});
} }
fReportEntity = true; fReportEntity = true;
@ -967,7 +965,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDContentModelHandler.pcdata(null); fDTDContentModelHandler.pcdata(null);
} }
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
while (fEntityScanner.skipChar('|')) { while (fEntityScanner.skipChar('|', null)) {
fStringBuffer.append('|'); fStringBuffer.append('|');
// call handler // call handler
if (fDTDContentModelHandler != null) { if (fDTDContentModelHandler != null) {
@ -976,7 +974,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
childName = fEntityScanner.scanName(); childName = fEntityScanner.scanName(NameType.ENTITY);
if (childName == null) { if (childName == null) {
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT", reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
new Object[]{elName}); new Object[]{elName});
@ -1005,7 +1003,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
reportFatalError("MixedContentUnterminated", reportFatalError("MixedContentUnterminated",
new Object[]{elName}); new Object[]{elName});
} }
else if (fEntityScanner.skipChar(')')){ else if (fEntityScanner.skipChar(')', null)){
fStringBuffer.append(')'); fStringBuffer.append(')');
// call handler // call handler
if (fDTDContentModelHandler != null) { if (fDTDContentModelHandler != null) {
@ -1043,7 +1041,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
int currentOp = 0; int currentOp = 0;
int c; int c;
while (true) { while (true) {
if (fEntityScanner.skipChar('(')) { if (fEntityScanner.skipChar('(', null)) {
fMarkUpDepth++; fMarkUpDepth++;
fStringBuffer.append('('); fStringBuffer.append('(');
// call handler // call handler
@ -1057,7 +1055,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
continue; continue;
} }
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
String childName = fEntityScanner.scanName(); String childName = fEntityScanner.scanName(NameType.ELEMENTSTART);
if (childName == null) { if (childName == null) {
reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
new Object[]{elName}); new Object[]{elName});
@ -1084,7 +1082,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
fDTDContentModelHandler.occurrence(oc, null); fDTDContentModelHandler.occurrence(oc, null);
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fStringBuffer.append((char)c); fStringBuffer.append((char)c);
} }
while (true) { while (true) {
@ -1097,7 +1095,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE, fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
null); null);
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fStringBuffer.append(','); fStringBuffer.append(',');
break; break;
} }
@ -1108,7 +1106,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE, fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
null); null);
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fStringBuffer.append('|'); fStringBuffer.append('|');
break; break;
} }
@ -1154,7 +1152,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
else { else {
// no occurrence specified // no occurrence specified
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fStringBuffer.append(')'); fStringBuffer.append(')');
} }
fMarkUpDepth--; fMarkUpDepth--;
@ -1186,7 +1184,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
// element name // element name
String elName = fEntityScanner.scanName(); String elName = fEntityScanner.scanName(NameType.ELEMENTSTART);
if (elName == null) { if (elName == null) {
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL", reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL",
null); null);
@ -1200,7 +1198,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// spaces // spaces
if (!skipSeparator(true, !scanningInternalSubset())) { if (!skipSeparator(true, !scanningInternalSubset())) {
// no space, is it the end yet? // no space, is it the end yet?
if (fEntityScanner.skipChar('>')) { if (fEntityScanner.skipChar('>', null)) {
// yes, stop here // yes, stop here
// call handler // call handler
if (fDTDHandler != null) { if (fDTDHandler != null) {
@ -1216,8 +1214,8 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
// definitions // definitions
while (!fEntityScanner.skipChar('>')) { while (!fEntityScanner.skipChar('>', null)) {
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ATTRIBUTE);
if (name == null) { if (name == null) {
reportFatalError("AttNameRequiredInAttDef", reportFatalError("AttNameRequiredInAttDef",
new Object[]{elName}); new Object[]{elName});
@ -1353,7 +1351,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
new Object[]{elName, atName}); new Object[]{elName, atName});
} }
// open paren // open paren
int c = fEntityScanner.scanChar(); int c = fEntityScanner.scanChar(null);
if (c != '(') { if (c != '(') {
reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE", reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
new Object[]{elName, atName}); new Object[]{elName, atName});
@ -1361,7 +1359,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fMarkUpDepth++; fMarkUpDepth++;
do { do {
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
String aName = fEntityScanner.scanName(); String aName = fEntityScanner.scanName(NameType.ATTRIBUTE);
if (aName == null) { if (aName == null) {
reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE", reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
new Object[]{elName, atName}); new Object[]{elName, atName});
@ -1369,7 +1367,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
ensureEnumerationSize(fEnumerationCount + 1); ensureEnumerationSize(fEnumerationCount + 1);
fEnumeration[fEnumerationCount++] = aName; fEnumeration[fEnumerationCount++] = aName;
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
c = fEntityScanner.scanChar(); c = fEntityScanner.scanChar(null);
} while (c == '|'); } while (c == '|');
if (c != ')') { if (c != ')') {
reportFatalError("NotationTypeUnterminated", reportFatalError("NotationTypeUnterminated",
@ -1380,7 +1378,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
else { // Enumeration else { // Enumeration
type = "ENUMERATION"; type = "ENUMERATION";
// open paren // open paren
int c = fEntityScanner.scanChar(); int c = fEntityScanner.scanChar(null);
if (c != '(') { if (c != '(') {
// "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL", // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
reportFatalError("AttTypeRequiredInAttDef", reportFatalError("AttTypeRequiredInAttDef",
@ -1397,7 +1395,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
ensureEnumerationSize(fEnumerationCount + 1); ensureEnumerationSize(fEnumerationCount + 1);
fEnumeration[fEnumerationCount++] = token; fEnumeration[fEnumerationCount++] = token;
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
c = fEntityScanner.scanChar(); c = fEntityScanner.scanChar(null);
} while (c == '|'); } while (c == '|');
if (c != ')') { if (c != ')') {
reportFatalError("EnumerationUnterminated", reportFatalError("EnumerationUnterminated",
@ -1447,7 +1445,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// AttValue // AttValue
boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ; boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ;
scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName, scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName,
fAttributes, 0, isVC, elName); fAttributes, 0, isVC, elName, false);
} }
return defaultType; return defaultType;
@ -1475,7 +1473,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
boolean sawPERef = false; boolean sawPERef = false;
fReportEntity = false; fReportEntity = false;
if (fEntityScanner.skipSpaces()) { if (fEntityScanner.skipSpaces()) {
if (!fEntityScanner.skipChar('%')) { if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) {
isPEDecl = false; // <!ENTITY x "x"> isPEDecl = false; // <!ENTITY x "x">
} }
else if (skipSeparator(true, !scanningInternalSubset())) { else if (skipSeparator(true, !scanningInternalSubset())) {
@ -1496,7 +1494,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
sawPERef = true; sawPERef = true;
} }
} }
else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) { else if (scanningInternalSubset() || !fEntityScanner.skipChar('%', NameType.REFERENCE)) {
// <!ENTITY[^ ]...> or <!ENTITY[^ %]...> // <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
null); null);
@ -1513,11 +1511,11 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
if (sawPERef) { if (sawPERef) {
while (true) { while (true) {
String peName = fEntityScanner.scanName(); String peName = fEntityScanner.scanName(NameType.REFERENCE);
if (peName == null) { if (peName == null) {
reportFatalError("NameRequiredInPEReference", null); reportFatalError("NameRequiredInPEReference", null);
} }
else if (!fEntityScanner.skipChar(';')) { else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInPEReference", reportFatalError("SemicolonRequiredInPEReference",
new Object[]{peName}); new Object[]{peName});
} }
@ -1525,20 +1523,20 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
startPE(peName, false); startPE(peName, false);
} }
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('%')) if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
break; break;
if (!isPEDecl) { if (!isPEDecl) {
if (skipSeparator(true, !scanningInternalSubset())) { if (skipSeparator(true, !scanningInternalSubset())) {
isPEDecl = true; isPEDecl = true;
break; break;
} }
isPEDecl = fEntityScanner.skipChar('%'); isPEDecl = fEntityScanner.skipChar('%', NameType.REFERENCE);
} }
} }
} }
// name // name
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ENTITY);
if (name == null) { if (name == null) {
reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", 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", reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
new Object[]{name}); new Object[]{name});
} }
notation = fEntityScanner.scanName(); notation = fEntityScanner.scanName(NameType.NOTATION);
if (notation == null) { if (notation == null) {
reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL", reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
new Object[]{name}); new Object[]{name});
@ -1595,7 +1593,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
// end // end
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("EntityDeclUnterminated", new Object[]{name}); reportFatalError("EntityDeclUnterminated", new Object[]{name});
} }
fMarkUpDepth--; fMarkUpDepth--;
@ -1650,7 +1648,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value, protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value,
XMLString nonNormalizedValue) XMLString nonNormalizedValue)
throws IOException, XNIException { throws IOException, XNIException {
int quote = fEntityScanner.scanChar(); int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') { if (quote != '\'' && quote != '"') {
reportFatalError("OpenQuoteMissingInDecl", null); reportFatalError("OpenQuoteMissingInDecl", null);
} }
@ -1665,23 +1663,24 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
fLimitAnalyzer.startEntity(entityName); fLimitAnalyzer.startEntity(entityName);
if (fEntityScanner.scanLiteral(quote, fString) != quote) { if (fEntityScanner.scanLiteral(quote, fString, false) != quote) {
fStringBuffer.clear(); fStringBuffer.clear();
fStringBuffer2.clear(); fStringBuffer2.clear();
int offset;
do { do {
checkEntityLimit(isPEDecl, entityName, fString.length + countChar);
countChar = 0; countChar = 0;
offset = fStringBuffer.length;
fStringBuffer.append(fString); fStringBuffer.append(fString);
fStringBuffer2.append(fString); fStringBuffer2.append(fString);
if (fEntityScanner.skipChar('&')) { if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
if (fEntityScanner.skipChar('#')) { if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
fStringBuffer2.append("&#"); fStringBuffer2.append("&#");
scanCharReferenceValue(fStringBuffer, fStringBuffer2); scanCharReferenceValue(fStringBuffer, fStringBuffer2);
} }
else { else {
fStringBuffer.append('&'); fStringBuffer.append('&');
fStringBuffer2.append('&'); fStringBuffer2.append('&');
String eName = fEntityScanner.scanName(); String eName = fEntityScanner.scanName(NameType.REFERENCE);
if (eName == null) { if (eName == null) {
reportFatalError("NameRequiredInReference", reportFatalError("NameRequiredInReference",
null); null);
@ -1690,7 +1689,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fStringBuffer.append(eName); fStringBuffer.append(eName);
fStringBuffer2.append(eName); fStringBuffer2.append(eName);
} }
if (!fEntityScanner.skipChar(';')) { if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference", reportFatalError("SemicolonRequiredInReference",
new Object[]{eName}); new Object[]{eName});
} }
@ -1700,15 +1699,15 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
} }
} }
else if (fEntityScanner.skipChar('%')) { else if (fEntityScanner.skipChar('%', NameType.REFERENCE)) {
while (true) { while (true) {
fStringBuffer2.append('%'); fStringBuffer2.append('%');
String peName = fEntityScanner.scanName(); String peName = fEntityScanner.scanName(NameType.REFERENCE);
if (peName == null) { if (peName == null) {
reportFatalError("NameRequiredInPEReference", reportFatalError("NameRequiredInPEReference",
null); null);
} }
else if (!fEntityScanner.skipChar(';')) { else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInPEReference", reportFatalError("SemicolonRequiredInPEReference",
new Object[]{peName}); new Object[]{peName});
} }
@ -1725,20 +1724,20 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// REVISIT: This will make returning the non- // REVISIT: This will make returning the non-
// normalized value harder. -Ac // normalized value harder. -Ac
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('%')) if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
break; break;
} }
} }
else { else {
countChar++;
int c = fEntityScanner.peekChar(); int c = fEntityScanner.peekChar();
if (XMLChar.isHighSurrogate(c)) { if (XMLChar.isHighSurrogate(c)) {
countChar++;
scanSurrogates(fStringBuffer2); scanSurrogates(fStringBuffer2);
} }
else if (isInvalidLiteral(c)) { else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInLiteral", reportFatalError("InvalidCharInLiteral",
new Object[]{Integer.toHexString(c)}); new Object[]{Integer.toHexString(c)});
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
} }
// if it's not the delimiting quote or if it is but from a // if it's not the delimiting quote or if it is but from a
// different entity than the one this literal started from, // different entity than the one this literal started from,
@ -1746,10 +1745,12 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
else if (c != quote || entityDepth != fEntityDepth) { else if (c != quote || entityDepth != fEntityDepth) {
fStringBuffer.append((char)c); fStringBuffer.append((char)c);
fStringBuffer2.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); fStringBuffer.append(fString);
fStringBuffer2.append(fString); fStringBuffer2.append(fString);
literal = fStringBuffer; literal = fStringBuffer;
@ -1760,10 +1761,14 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
value.setValues(literal); value.setValues(literal);
nonNormalizedValue.setValues(literal2); nonNormalizedValue.setValues(literal2);
if (fLimitAnalyzer != null) { if (fLimitAnalyzer != null) {
if (isPEDecl) {
fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName); 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); reportFatalError("CloseQuoteMissingInDecl", null);
} }
} // scanEntityValue(XMLString,XMLString):void } // scanEntityValue(XMLString,XMLString):void
@ -1788,7 +1793,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
// notation name // notation name
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.NOTATION);
if (name == null) { if (name == null) {
reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL", reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL",
null); null);
@ -1815,7 +1820,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
skipSeparator(false, !scanningInternalSubset()); skipSeparator(false, !scanningInternalSubset());
// end // end
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("NotationDeclUnterminated", new Object[]{name}); reportFatalError("NotationDeclUnterminated", new Object[]{name});
} }
fMarkUpDepth--; fMarkUpDepth--;
@ -1863,7 +1868,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
XMLErrorReporter.SEVERITY_ERROR); XMLErrorReporter.SEVERITY_ERROR);
} }
// call handler // call handler
if (!fEntityScanner.skipChar('[')) { if (!fEntityScanner.skipChar('[', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
} }
@ -1888,7 +1893,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE, fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE,
null); null);
} }
if (!fEntityScanner.skipChar('[')) { if (!fEntityScanner.skipChar('[', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
} }
fReportEntity = true; fReportEntity = true;
@ -1897,7 +1902,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fIgnoreConditionalBuffer.clear(); fIgnoreConditionalBuffer.clear();
} }
while (true) { while (true) {
if (fEntityScanner.skipChar('<')) { if (fEntityScanner.skipChar('<', null)) {
if (fDTDHandler != null) { if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append('<'); fIgnoreConditionalBuffer.append('<');
} }
@ -1905,8 +1910,8 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
// These tests are split so that we handle cases like // These tests are split so that we handle cases like
// '<<![' and '<!<![' which we might otherwise miss. // '<<![' and '<!<![' which we might otherwise miss.
// //
if (fEntityScanner.skipChar('!')) { if (fEntityScanner.skipChar('!', null)) {
if(fEntityScanner.skipChar('[')) { if(fEntityScanner.skipChar('[', null)) {
if (fDTDHandler != null) { if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append("!["); fIgnoreConditionalBuffer.append("![");
} }
@ -1918,24 +1923,24 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
} }
} }
else if (fEntityScanner.skipChar(']')) { else if (fEntityScanner.skipChar(']', null)) {
if (fDTDHandler != null) { if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append(']'); fIgnoreConditionalBuffer.append(']');
} }
// //
// The same thing goes for ']<![' and '<]]>', etc. // The same thing goes for ']<![' and '<]]>', etc.
// //
if (fEntityScanner.skipChar(']')) { if (fEntityScanner.skipChar(']', null)) {
if (fDTDHandler != null) { if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append(']'); fIgnoreConditionalBuffer.append(']');
} }
while (fEntityScanner.skipChar(']')) { while (fEntityScanner.skipChar(']', null)) {
/* empty loop body */ /* empty loop body */
if (fDTDHandler != null) { if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append(']'); fIgnoreConditionalBuffer.append(']');
} }
} }
if (fEntityScanner.skipChar('>')) { if (fEntityScanner.skipChar('>', null)) {
if (fIncludeSectDepth-- == initialDepth) { if (fIncludeSectDepth-- == initialDepth) {
fMarkUpDepth--; fMarkUpDepth--;
// call handler // call handler
@ -1953,7 +1958,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
} }
} }
else { else {
int c = fEntityScanner.scanChar(); int c = fEntityScanner.scanChar(null);
if (fScannerState == SCANNER_STATE_END_OF_INPUT) { if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
reportFatalError("IgnoreSectUnterminated", null); reportFatalError("IgnoreSectUnterminated", null);
return; return;
@ -1990,16 +1995,16 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
//System.out.println("scanDecls"+fScannerState); //System.out.println("scanDecls"+fScannerState);
while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) { while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
again = complete; again = complete;
if (fEntityScanner.skipChar('<')) { if (fEntityScanner.skipChar('<', null)) {
fMarkUpDepth++; fMarkUpDepth++;
if (fEntityScanner.skipChar('?')) { if (fEntityScanner.skipChar('?', null)) {
fStringBuffer.clear(); fStringBuffer.clear();
scanPI(fStringBuffer); scanPI(fStringBuffer);
fMarkUpDepth--; // we're done with this decl fMarkUpDepth--; // we're done with this decl
} }
else if (fEntityScanner.skipChar('!')) { else if (fEntityScanner.skipChar('!', null)) {
if (fEntityScanner.skipChar('-')) { if (fEntityScanner.skipChar('-', null)) {
if (!fEntityScanner.skipChar('-')) { if (!fEntityScanner.skipChar('-', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
null); null);
} else { } else {
@ -2018,7 +2023,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
else if (fEntityScanner.skipString("NOTATION")) { else if (fEntityScanner.skipString("NOTATION")) {
scanNotationDecl(); scanNotationDecl();
} }
else if (fEntityScanner.skipChar('[') && else if (fEntityScanner.skipChar('[', null) &&
!scanningInternalSubset()) { !scanningInternalSubset()) {
scanConditionalSect(fPEDepth); scanConditionalSect(fPEDepth);
} }
@ -2033,10 +2038,10 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); 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? // end of conditional section?
if (!fEntityScanner.skipChar(']') if (!fEntityScanner.skipChar(']', null)
|| !fEntityScanner.skipChar('>')) { || !fEntityScanner.skipChar('>', null)) {
reportFatalError("IncludeSectUnterminated", null); reportFatalError("IncludeSectUnterminated", null);
} }
// call handler // call handler
@ -2083,21 +2088,21 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
throws IOException, XNIException { throws IOException, XNIException {
int depth = fPEDepth; int depth = fPEDepth;
boolean sawSpace = fEntityScanner.skipSpaces(); boolean sawSpace = fEntityScanner.skipSpaces();
if (!lookForPERefs || !fEntityScanner.skipChar('%')) { if (!lookForPERefs || !fEntityScanner.skipChar('%', NameType.REFERENCE)) {
return !spaceRequired || sawSpace || (depth != fPEDepth); return !spaceRequired || sawSpace || (depth != fPEDepth);
} }
while (true) { while (true) {
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ENTITY);
if (name == null) { if (name == null) {
reportFatalError("NameRequiredInPEReference", null); reportFatalError("NameRequiredInPEReference", null);
} }
else if (!fEntityScanner.skipChar(';')) { else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInPEReference", reportFatalError("SemicolonRequiredInPEReference",
new Object[]{name}); new Object[]{name});
} }
startPE(name, false); startPE(name, false);
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('%')) if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
return true; return true;
} }
} }
@ -2181,56 +2186,6 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
fSecurityManager = fEntityManager.fSecurityManager; 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(){ public DTDGrammar getGrammar(){
return nvGrammarInfo; return nvGrammarInfo;
} }

View file

@ -21,14 +21,6 @@
package com.sun.org.apache.xerces.internal.impl; 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.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.AugmentationsImpl; import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl; 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.XMLDocumentScanner;
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; 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.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.SecuritySupport;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; 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.XMLSecurityManager.Limit;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; 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.XMLStreamConstants;
import javax.xml.stream.events.XMLEvent;
/** /**
* *
@ -454,6 +451,7 @@ public class XMLDocumentFragmentScannerImpl
//fDocumentHandler.startElement(getElementQName(),fAttributes,null); //fDocumentHandler.startElement(getElementQName(),fAttributes,null);
break; break;
case XMLStreamConstants.CHARACTERS : case XMLStreamConstants.CHARACTERS :
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.characters(getCharacterData(),null); fDocumentHandler.characters(getCharacterData(),null);
break; break;
case XMLStreamConstants.SPACE: case XMLStreamConstants.SPACE:
@ -462,13 +460,15 @@ public class XMLDocumentFragmentScannerImpl
//fDocumentHandler.ignorableWhitespace(getCharacterData(), null); //fDocumentHandler.ignorableWhitespace(getCharacterData(), null);
break; break;
case XMLStreamConstants.ENTITY_REFERENCE : case XMLStreamConstants.ENTITY_REFERENCE :
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
//entity reference callback are given in startEntity //entity reference callback are given in startEntity
break; break;
case XMLStreamConstants.PROCESSING_INSTRUCTION : case XMLStreamConstants.PROCESSING_INSTRUCTION :
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null); fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null);
break; break;
case XMLStreamConstants.COMMENT : case XMLStreamConstants.COMMENT :
//System.out.println(" in COMMENT of the XMLNSDocumentScannerImpl"); fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.comment(getCharacterData(),null); fDocumentHandler.comment(getCharacterData(),null);
break; break;
case XMLStreamConstants.DTD : case XMLStreamConstants.DTD :
@ -477,6 +477,7 @@ public class XMLDocumentFragmentScannerImpl
//therefore we don't need to take care of anything here. So Just break; //therefore we don't need to take care of anything here. So Just break;
break; break;
case XMLStreamConstants.CDATA: case XMLStreamConstants.CDATA:
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.startCDATA(null); fDocumentHandler.startCDATA(null);
//xxx: check if CDATA values comes from getCharacterData() function //xxx: check if CDATA values comes from getCharacterData() function
fDocumentHandler.characters(getCharacterData(),null); fDocumentHandler.characters(getCharacterData(),null);
@ -1273,9 +1274,9 @@ public class XMLDocumentFragmentScannerImpl
fElementQName = fElementStack.nextElement(); fElementQName = fElementStack.nextElement();
// name // name
if (fNamespaces) { if (fNamespaces) {
fEntityScanner.scanQName(fElementQName); fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
} else { } else {
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
fElementQName.setValues(null, name, name, null); fElementQName.setValues(null, name, name, null);
} }
@ -1376,11 +1377,11 @@ public class XMLDocumentFragmentScannerImpl
// end tag? // end tag?
final int c = fEntityScanner.peekChar(); final int c = fEntityScanner.peekChar();
if (c == '>') { if (c == '>') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
return true; return true;
} else if (c == '/') { } else if (c == '/') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError("ElementUnterminated", reportFatalError("ElementUnterminated",
new Object[]{fElementQName.rawname}); new Object[]{fElementQName.rawname});
} }
@ -1518,15 +1519,15 @@ public class XMLDocumentFragmentScannerImpl
// name // name
if (fNamespaces) { if (fNamespaces) {
fEntityScanner.scanQName(fAttributeQName); fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME);
} else { } else {
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ATTRIBUTENAME);
fAttributeQName.setValues(null, name, name, null); fAttributeQName.setValues(null, name, name, null);
} }
// equals // equals
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('=')) { if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
reportFatalError("EqRequiredInAttribute", reportFatalError("EqRequiredInAttribute",
new Object[] {fCurrentElement.rawname, fAttributeQName.rawname}); new Object[] {fCurrentElement.rawname, fAttributeQName.rawname});
} }
@ -1544,9 +1545,8 @@ public class XMLDocumentFragmentScannerImpl
//can safely add the attribute later.. //can safely add the attribute later..
XMLString tmpStr = getString(); XMLString tmpStr = getString();
scanAttributeValue(tmpStr, fTempString2, scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes,
fAttributeQName.rawname, attributes, attIndex, isVC, fCurrentElement.rawname, false);
attIndex, isVC, fCurrentElement.rawname);
// content // content
int oldLen = attributes.getLength(); int oldLen = attributes.getLength();
@ -1594,13 +1594,13 @@ public class XMLDocumentFragmentScannerImpl
if (c == '\r') { if (c == '\r') {
// happens when there is the character reference &#13; // happens when there is the character reference &#13;
//xxx: We know the next chracter.. we should just skip it and add ']' directlry //xxx: We know the next chracter.. we should just skip it and add ']' directlry
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
content.append((char)c); content.append((char)c);
c = -1; c = -1;
} else if (c == ']') { } else if (c == ']') {
//fStringBuffer.clear(); //fStringBuffer.clear();
//xxx: We know the next chracter.. we should just skip it and add ']' directlry //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 // remember where we are in case we get an endEntity before we
// could flush the buffer out - this happens when we're parsing an // could flush the buffer out - this happens when we're parsing an
// entity which ends with a ] // entity which ends with a ]
@ -1609,12 +1609,12 @@ public class XMLDocumentFragmentScannerImpl
// We work on a single character basis to handle cases such as: // We work on a single character basis to handle cases such as:
// ']]]>' which we might otherwise miss. // ']]]>' which we might otherwise miss.
// //
if (fEntityScanner.skipChar(']')) { if (fEntityScanner.skipChar(']', null)) {
content.append(']'); content.append(']');
while (fEntityScanner.skipChar(']')) { while (fEntityScanner.skipChar(']', null)) {
content.append(']'); content.append(']');
} }
if (fEntityScanner.skipChar('>')) { if (fEntityScanner.skipChar('>', null)) {
reportFatalError("CDEndInContent", null); reportFatalError("CDEndInContent", null);
} }
} }
@ -1689,7 +1689,7 @@ public class XMLDocumentFragmentScannerImpl
} else { } else {
reportFatalError("InvalidCharInCDSect", reportFatalError("InvalidCharInCDSect",
new Object[]{Integer.toString(c,16)}); new Object[]{Integer.toString(c,16)});
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
} }
} }
//by this time we have also read surrogate contents if any... //by this time we have also read surrogate contents if any...
@ -1751,7 +1751,7 @@ public class XMLDocumentFragmentScannerImpl
// end // end
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError("ETagUnterminated", reportFatalError("ETagUnterminated",
new Object[]{rawname}); new Object[]{rawname});
} }
@ -1841,12 +1841,12 @@ public class XMLDocumentFragmentScannerImpl
* notification. * notification.
*/ */
protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException { protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException {
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.REFERENCE);
if (name == null) { if (name == null) {
reportFatalError("NameRequiredInReference", null); reportFatalError("NameRequiredInReference", null);
return; return;
} }
if (!fEntityScanner.skipChar(';')) { if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference", new Object []{name}); reportFatalError("SemicolonRequiredInReference", new Object []{name});
} }
if (fEntityStore.isUnparsedEntity(name)) { if (fEntityStore.isUnparsedEntity(name)) {
@ -1943,6 +1943,7 @@ public class XMLDocumentFragmentScannerImpl
*/ */
private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException { private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException {
foundBuiltInRefs = true; foundBuiltInRefs = true;
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
content.append(c); content.append(c);
if (fDocumentHandler != null) { if (fDocumentHandler != null) {
fSingleChar[0] = c; fSingleChar[0] = c;
@ -2608,13 +2609,13 @@ public class XMLDocumentFragmentScannerImpl
switch(ch){ switch(ch){
case '?' :{ case '?' :{
setScannerState(SCANNER_STATE_PI); setScannerState(SCANNER_STATE_PI);
fEntityScanner.skipChar(ch); fEntityScanner.skipChar(ch, null);
break; break;
} }
case '!' :{ case '!' :{
fEntityScanner.skipChar(ch); fEntityScanner.skipChar(ch, null);
if (fEntityScanner.skipChar('-')) { if (fEntityScanner.skipChar('-', null)) {
if (!fEntityScanner.skipChar('-')) { if (!fEntityScanner.skipChar('-', NameType.COMMENT)) {
reportFatalError("InvalidCommentStart", reportFatalError("InvalidCommentStart",
null); null);
} }
@ -2629,7 +2630,7 @@ public class XMLDocumentFragmentScannerImpl
} }
case '/' :{ case '/' :{
setScannerState(SCANNER_STATE_END_ELEMENT_TAG); setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
fEntityScanner.skipChar(ch); fEntityScanner.skipChar(ch, NameType.ELEMENTEND);
break; break;
} }
default :{ default :{
@ -2641,9 +2642,9 @@ public class XMLDocumentFragmentScannerImpl
}//startOfMarkup }//startOfMarkup
private void startOfContent() throws IOException { private void startOfContent() throws IOException {
if (fEntityScanner.skipChar('<')) { if (fEntityScanner.skipChar('<', null)) {
setScannerState(SCANNER_STATE_START_OF_MARKUP); 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 setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
} else { } else {
//element content is there.. //element content is there..
@ -2716,10 +2717,10 @@ public class XMLDocumentFragmentScannerImpl
case SCANNER_STATE_CONTENT: { case SCANNER_STATE_CONTENT: {
final int ch = fEntityScanner.peekChar(); final int ch = fEntityScanner.peekChar();
if (ch == '<') { if (ch == '<') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_START_OF_MARKUP); setScannerState(SCANNER_STATE_START_OF_MARKUP);
} else if (ch == '&') { } else if (ch == '&') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.REFERENCE);
setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
break; break;
} else { } else {
@ -2819,9 +2820,9 @@ public class XMLDocumentFragmentScannerImpl
if(DEBUG){ if(DEBUG){
System.out.println("fTempString = " + fTempString); System.out.println("fTempString = " + fTempString);
} }
if(fEntityScanner.skipChar('<')){ if(fEntityScanner.skipChar('<', null)){
//check if we have reached end of element //check if we have reached end of element
if(fEntityScanner.skipChar('/')){ if(fEntityScanner.skipChar('/', NameType.ELEMENTEND)){
//increase the mark up depth //increase the mark up depth
fMarkupDepth++; fMarkupDepth++;
fLastSectionWasCharacterData = false; fLastSectionWasCharacterData = false;
@ -2871,7 +2872,7 @@ public class XMLDocumentFragmentScannerImpl
} }
// happens when there is the character reference &#13; // happens when there is the character reference &#13;
//xxx: We know the next chracter.. we should just skip it and add ']' directlry //xxx: We know the next chracter.. we should just skip it and add ']' directlry
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
fUsebuffer = true; fUsebuffer = true;
fContentBuffer.append((char)c); fContentBuffer.append((char)c);
c = -1 ; c = -1 ;
@ -2879,7 +2880,7 @@ public class XMLDocumentFragmentScannerImpl
//fStringBuffer.clear(); //fStringBuffer.clear();
//xxx: We know the next chracter.. we should just skip it and add ']' directlry //xxx: We know the next chracter.. we should just skip it and add ']' directlry
fUsebuffer = true; 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 // remember where we are in case we get an endEntity before we
// could flush the buffer out - this happens when we're parsing an // could flush the buffer out - this happens when we're parsing an
// entity which ends with a ] // entity which ends with a ]
@ -2888,12 +2889,12 @@ public class XMLDocumentFragmentScannerImpl
// We work on a single character basis to handle cases such as: // We work on a single character basis to handle cases such as:
// ']]]>' which we might otherwise miss. // ']]]>' which we might otherwise miss.
// //
if (fEntityScanner.skipChar(']')) { if (fEntityScanner.skipChar(']', null)) {
fContentBuffer.append(']'); fContentBuffer.append(']');
while (fEntityScanner.skipChar(']')) { while (fEntityScanner.skipChar(']', null)) {
fContentBuffer.append(']'); fContentBuffer.append(']');
} }
if (fEntityScanner.skipChar('>')) { if (fEntityScanner.skipChar('>', null)) {
reportFatalError("CDEndInContent", null); reportFatalError("CDEndInContent", null);
} }
} }
@ -2906,12 +2907,12 @@ public class XMLDocumentFragmentScannerImpl
// we need not to grow the buffer only when isCoalesce() is not true; // we need not to grow the buffer only when isCoalesce() is not true;
if (c == '<') { if (c == '<') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_START_OF_MARKUP); setScannerState(SCANNER_STATE_START_OF_MARKUP);
break; break;
}//xxx what should be the behavior if entity reference is present in the content ? }//xxx what should be the behavior if entity reference is present in the content ?
else if (c == '&') { else if (c == '&') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.REFERENCE);
setScannerState(SCANNER_STATE_REFERENCE); setScannerState(SCANNER_STATE_REFERENCE);
break; break;
}///xxx since this part is also characters, it should be merged... }///xxx since this part is also characters, it should be merged...
@ -2924,7 +2925,7 @@ public class XMLDocumentFragmentScannerImpl
reportFatalError("InvalidCharInContent", reportFatalError("InvalidCharInContent",
new Object[] { new Object[] {
Integer.toString(c, 16)}); Integer.toString(c, 16)});
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
} }
break; break;
} }
@ -3050,7 +3051,7 @@ public class XMLDocumentFragmentScannerImpl
} }
fUsebuffer = true ; fUsebuffer = true ;
//take care of character reference //take care of character reference
if (fEntityScanner.skipChar('#')) { if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
scanCharReferenceValue(fContentBuffer, null); scanCharReferenceValue(fContentBuffer, null);
fMarkupDepth--; fMarkupDepth--;
if(!fIsCoalesce){ if(!fIsCoalesce){
@ -3106,11 +3107,11 @@ public class XMLDocumentFragmentScannerImpl
if (fNamespaces) { if (fNamespaces) {
while (isValidNCName(fEntityScanner.peekChar())) { while (isValidNCName(fEntityScanner.peekChar())) {
fStringBuffer.append((char)fEntityScanner.scanChar()); fStringBuffer.append((char)fEntityScanner.scanChar(null));
} }
} else { } else {
while (isValidNameChar(fEntityScanner.peekChar())) { 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); String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);

View file

@ -631,7 +631,7 @@ public class XMLDocumentScannerImpl
} }
// root element name // root element name
fDoctypeName = fEntityScanner.scanName(); fDoctypeName = fEntityScanner.scanName(NameType.DOCTYPE);
if (fDoctypeName == null) { if (fDoctypeName == null) {
reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null); reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null);
} }
@ -671,10 +671,10 @@ public class XMLDocumentScannerImpl
// is there an internal subset? // is there an internal subset?
boolean internalSubset = true; boolean internalSubset = true;
if (!fEntityScanner.skipChar('[')) { if (!fEntityScanner.skipChar('[', null)) {
internalSubset = false; internalSubset = false;
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName}); reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
} }
fMarkupDepth--; fMarkupDepth--;
@ -753,7 +753,7 @@ public class XMLDocumentScannerImpl
fStringBuffer.clear(); fStringBuffer.clear();
fStringBuffer.append("xml"); fStringBuffer.append("xml");
while (XMLChar.isName(fEntityScanner.peekChar())) { 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); String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
//this function should fill the data.. and set the fEvent object to this event. //this function should fill the data.. and set the fEvent object to this event.
@ -831,9 +831,9 @@ public class XMLDocumentScannerImpl
switch (fScannerState) { switch (fScannerState) {
case SCANNER_STATE_PROLOG: { case SCANNER_STATE_PROLOG: {
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (fEntityScanner.skipChar('<')) { if (fEntityScanner.skipChar('<', null)) {
setScannerState(SCANNER_STATE_START_OF_MARKUP); setScannerState(SCANNER_STATE_START_OF_MARKUP);
} else if (fEntityScanner.skipChar('&')) { } else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
setScannerState(SCANNER_STATE_REFERENCE); setScannerState(SCANNER_STATE_REFERENCE);
} else { } else {
setScannerState(SCANNER_STATE_CONTENT); setScannerState(SCANNER_STATE_CONTENT);
@ -849,9 +849,9 @@ public class XMLDocumentScannerImpl
setDriver(fContentDriver); setDriver(fContentDriver);
//from now onwards this would be handled by fContentDriver,in the same next() call //from now onwards this would be handled by fContentDriver,in the same next() call
return fContentDriver.next(); return fContentDriver.next();
} else if (fEntityScanner.skipChar('!')) { } else if (fEntityScanner.skipChar('!', null)) {
if (fEntityScanner.skipChar('-')) { if (fEntityScanner.skipChar('-', null)) {
if (!fEntityScanner.skipChar('-')) { if (!fEntityScanner.skipChar('-', null)) {
reportFatalError("InvalidCommentStart", reportFatalError("InvalidCommentStart",
null); null);
} }
@ -871,7 +871,7 @@ public class XMLDocumentScannerImpl
reportFatalError("MarkupNotRecognizedInProlog", reportFatalError("MarkupNotRecognizedInProlog",
null); null);
} }
} else if (fEntityScanner.skipChar('?')) { } else if (fEntityScanner.skipChar('?', null)) {
setScannerState(SCANNER_STATE_PI); setScannerState(SCANNER_STATE_PI);
} else { } else {
reportFatalError("MarkupNotRecognizedInProlog", reportFatalError("MarkupNotRecognizedInProlog",
@ -991,7 +991,7 @@ public class XMLDocumentScannerImpl
case SCANNER_STATE_CONTENT: { case SCANNER_STATE_CONTENT: {
reportFatalError("ContentIllegalInProlog", null); reportFatalError("ContentIllegalInProlog", null);
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
} }
case SCANNER_STATE_REFERENCE: { case SCANNER_STATE_REFERENCE: {
reportFatalError("ReferenceIllegalInProlog", null); reportFatalError("ReferenceIllegalInProlog", null);
@ -1105,11 +1105,11 @@ public class XMLDocumentScannerImpl
fReadingDTD=false; fReadingDTD=false;
if (!moreToScan) { if (!moreToScan) {
// end doctype declaration // end doctype declaration
if (!fEntityScanner.skipChar(']')) { if (!fEntityScanner.skipChar(']', null)) {
reportFatalError("DoctypedeclNotClosed", new Object[]{fDoctypeName}); reportFatalError("DoctypedeclNotClosed", new Object[]{fDoctypeName});
} }
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName}); reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
} }
fMarkupDepth--; fMarkupDepth--;
@ -1373,7 +1373,7 @@ public class XMLDocumentScannerImpl
if(fScannerState == SCANNER_STATE_TERMINATED ){ if(fScannerState == SCANNER_STATE_TERMINATED ){
return XMLEvent.END_DOCUMENT ; return XMLEvent.END_DOCUMENT ;
} }
if (fEntityScanner.skipChar('<')) { if (fEntityScanner.skipChar('<', null)) {
setScannerState(SCANNER_STATE_START_OF_MARKUP); setScannerState(SCANNER_STATE_START_OF_MARKUP);
} else { } else {
setScannerState(SCANNER_STATE_CONTENT); setScannerState(SCANNER_STATE_CONTENT);
@ -1382,11 +1382,11 @@ public class XMLDocumentScannerImpl
} }
case SCANNER_STATE_START_OF_MARKUP: { case SCANNER_STATE_START_OF_MARKUP: {
fMarkupDepth++; fMarkupDepth++;
if (fEntityScanner.skipChar('?')) { if (fEntityScanner.skipChar('?', null)) {
setScannerState(SCANNER_STATE_PI); setScannerState(SCANNER_STATE_PI);
} else if (fEntityScanner.skipChar('!')) { } else if (fEntityScanner.skipChar('!', null)) {
setScannerState(SCANNER_STATE_COMMENT); setScannerState(SCANNER_STATE_COMMENT);
} else if (fEntityScanner.skipChar('/')) { } else if (fEntityScanner.skipChar('/', null)) {
reportFatalError("MarkupNotRecognizedInMisc", reportFatalError("MarkupNotRecognizedInMisc",
null); null);
} else if (isValidNameStartChar(fEntityScanner.peekChar()) || } else if (isValidNameStartChar(fEntityScanner.peekChar()) ||
@ -1429,7 +1429,7 @@ public class XMLDocumentScannerImpl
} else{ } else{
reportFatalError("ContentIllegalInTrailingMisc", reportFatalError("ContentIllegalInTrailingMisc",
null); null);
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_TRAILING_MISC); setScannerState(SCANNER_STATE_TRAILING_MISC);
return XMLEvent.CHARACTERS; return XMLEvent.CHARACTERS;
} }

View file

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

View file

@ -21,6 +21,7 @@
package com.sun.org.apache.xerces.internal.impl; 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.ASCIIReader;
import com.sun.org.apache.xerces.internal.impl.io.UCSReader; import com.sun.org.apache.xerces.internal.impl.io.UCSReader;
import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader; 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. // so that XMLStreamReader.getVersion() can find that out.
protected boolean xmlVersionSetExplicitly = false; protected boolean xmlVersionSetExplicitly = false;
// indicates that the operation is for detecting XML version
boolean detectingVersion = false;
// //
// Constructors // Constructors
// //
@ -530,10 +534,12 @@ public class XMLEntityScanner implements XMLLocator {
* <p> * <p>
* <strong>Note:</strong> The character is consumed. * <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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @throws EOFException Thrown on end of file.
*/ */
public int scanChar() throws IOException { protected int scanChar(NameType nt) throws IOException {
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("(scanChar: "); System.out.print("(scanChar: ");
print(); print();
@ -546,6 +552,7 @@ public class XMLEntityScanner implements XMLLocator {
} }
// scan character // scan character
int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[fCurrentEntity.position++]; int c = fCurrentEntity.ch[fCurrentEntity.position++];
if (c == '\n' || (c == '\r' && isExternal)) { if (c == '\n' || (c == '\r' && isExternal)) {
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
@ -554,6 +561,7 @@ public class XMLEntityScanner implements XMLLocator {
invokeListeners(1); invokeListeners(1);
fCurrentEntity.ch[0] = (char)c; fCurrentEntity.ch[0] = (char)c;
load(1, false, false); load(1, false, false);
offset = 0;
} }
if (c == '\r' && isExternal) { if (c == '\r' && isExternal) {
if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') { if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') {
@ -570,6 +578,9 @@ public class XMLEntityScanner implements XMLLocator {
System.out.println(" -> '"+(char)c+"'"); System.out.println(" -> '"+(char)c+"'");
} }
fCurrentEntity.columnNumber++; fCurrentEntity.columnNumber++;
if (!detectingVersion) {
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
}
return c; return c;
} // scanChar():int } // 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.SymbolTable
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName * @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
*/ */
public String scanNmtoken() throws IOException { protected String scanNmtoken() throws IOException {
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("(scanNmtoken: "); System.out.print("(scanNmtoken: ");
print(); print();
@ -661,6 +672,8 @@ public class XMLEntityScanner implements XMLLocator {
* <strong>Note:</strong> The string returned must be a symbol. The * <strong>Note:</strong> The string returned must be a symbol. The
* SymbolTable can be used for this purpose. * 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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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#isName
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart * @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) { if (DEBUG_BUFFER) {
System.out.print("(scanName: "); System.out.print("(scanName: ");
print(); print();
@ -682,6 +695,7 @@ public class XMLEntityScanner implements XMLLocator {
// scan name // scan name
int offset = fCurrentEntity.position; int offset = fCurrentEntity.position;
int length;
if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) { if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) {
if (++fCurrentEntity.position == fCurrentEntity.count) { if (++fCurrentEntity.position == fCurrentEntity.count) {
invokeListeners(1); invokeListeners(1);
@ -709,20 +723,7 @@ public class XMLEntityScanner implements XMLLocator {
vc = XMLChar.isName(c); vc = XMLChar.isName(c);
} }
if(!vc)break; if(!vc)break;
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
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);
}
offset = 0; offset = 0;
if (load(length, false, false)) { if (load(length, false, false)) {
break; break;
@ -730,12 +731,14 @@ public class XMLEntityScanner implements XMLLocator {
} }
} }
} }
int length = fCurrentEntity.position - offset; length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
// return name // return name
String symbol; String symbol;
if (length > 0) { if (length > 0) {
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
checkEntityLimit(nt, fCurrentEntity, offset, length);
symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length); symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
} else } else
symbol = null; symbol = null;
@ -759,6 +762,7 @@ public class XMLEntityScanner implements XMLLocator {
* this purpose. * this purpose.
* *
* @param qname The qualified name structure to fill. * @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 * @return Returns true if a qualified name appeared immediately on
* the input and was scanned, false otherwise. * 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#isName
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart * @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) { if (DEBUG_BUFFER) {
System.out.print("(scanQName, "+qname+": "); System.out.print("(scanQName, "+qname+": ");
print(); print();
@ -806,11 +810,13 @@ public class XMLEntityScanner implements XMLLocator {
print(); print();
System.out.println(" -> true"); System.out.println(" -> true");
} }
checkEntityLimit(nt, fCurrentEntity, 0, 1);
return true; return true;
} }
} }
int index = -1; int index = -1;
boolean vc = false; boolean vc = false;
int length;
while ( true){ while ( true){
//XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) ; //XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) ;
@ -829,22 +835,7 @@ public class XMLEntityScanner implements XMLLocator {
//check prefix before further read //check prefix before further read
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset); checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset);
} }
if (++fCurrentEntity.position == fCurrentEntity.count) { if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
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 (index != -1) { if (index != -1) {
index = index - offset; index = index - offset;
} }
@ -854,7 +845,7 @@ public class XMLEntityScanner implements XMLLocator {
} }
} }
} }
int length = fCurrentEntity.position - offset; length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
if (length > 0) { if (length > 0) {
String prefix = null; String prefix = null;
@ -885,6 +876,7 @@ public class XMLEntityScanner implements XMLLocator {
print(); print();
System.out.println(" -> true"); System.out.println(" -> true");
} }
checkEntityLimit(nt, fCurrentEntity, offset, length);
return true; return true;
} }
} }
@ -899,23 +891,105 @@ public class XMLEntityScanner implements XMLLocator {
} // scanQName(QName):boolean } // 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 * Checks whether the value of the specified Limit exceeds its limit
* *
* @param limit The Limit to be checked. * @param limit The Limit to be checked
* @param entity The current entity. * @param entity The current entity
* @param offset The index of the first byte * @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) { 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)) { if (fSecurityManager.isOverLimit(limit, fLimitAnalyzer)) {
fSecurityManager.debugPrint(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(), fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, limit.key(),
new Object[]{new String(entity.ch, offset, length), e, XMLErrorReporter.SEVERITY_FATAL_ERROR);
fLimitAnalyzer.getTotalValue(limit), }
fSecurityManager.getLimit(limit), if (fSecurityManager.isOverLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
fSecurityManager.getStateLiteral(limit)}, 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); XMLErrorReporter.SEVERITY_FATAL_ERROR);
} }
} }
@ -942,7 +1016,7 @@ public class XMLEntityScanner implements XMLLocator {
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @throws EOFException Thrown on end of file.
*/ */
public int scanContent(XMLString content) throws IOException { protected int scanContent(XMLString content) throws IOException {
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("(scanContent: "); System.out.print("(scanContent: ");
print(); print();
@ -963,6 +1037,7 @@ public class XMLEntityScanner implements XMLLocator {
int offset = fCurrentEntity.position; int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[offset]; int c = fCurrentEntity.ch[offset];
int newlines = 0; int newlines = 0;
boolean counted = false;
if (c == '\n' || (c == '\r' && isExternal)) { if (c == '\n' || (c == '\r' && isExternal)) {
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("[newline, "+offset+", "+fCurrentEntity.position+": "); System.out.print("[newline, "+offset+", "+fCurrentEntity.position+": ");
@ -976,9 +1051,11 @@ public class XMLEntityScanner implements XMLLocator {
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1; fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0; offset = 0;
fCurrentEntity.position = newlines; fCurrentEntity.position = newlines;
if (load(newlines, false, true)) { if (load(newlines, false, true)) {
counted = true;
break; break;
} }
} }
@ -995,9 +1072,11 @@ public class XMLEntityScanner implements XMLLocator {
fCurrentEntity.lineNumber++; fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1; fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) { if (fCurrentEntity.position == fCurrentEntity.count) {
checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0; offset = 0;
fCurrentEntity.position = newlines; fCurrentEntity.position = newlines;
if (load(newlines, false, true)) { if (load(newlines, false, true)) {
counted = true;
break; break;
} }
} }
@ -1011,6 +1090,7 @@ public class XMLEntityScanner implements XMLLocator {
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) { 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 //CHANGED: dont replace the value.. append to the buffer. This gives control to the callee
//on buffering the data.. //on buffering the data..
content.setValues(fCurrentEntity.ch, offset, length); content.setValues(fCurrentEntity.ch, offset, length);
@ -1038,8 +1118,8 @@ public class XMLEntityScanner implements XMLLocator {
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
if (fCurrentEntity.isGE) { if (!counted) {
checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length); checkEntityLimit(null, fCurrentEntity, offset, length);
} }
//CHANGED: dont replace the value.. append to the buffer. This gives control to the callee //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 * @param quote The quote character that signifies the end of the
* attribute value data. * attribute value data.
* @param content The content structure to fill. * @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 * @return Returns the next character on the input, if known. This
* value may be -1 but this does <em>note</em> designate * 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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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 { throws IOException {
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("(scanLiteral, '"+(char)quote+"': "); System.out.print("(scanLiteral, '"+(char)quote+"': ");
@ -1205,8 +1286,10 @@ public class XMLEntityScanner implements XMLLocator {
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; 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); content.setValues(fCurrentEntity.ch, offset, length);
@ -1273,7 +1356,7 @@ public class XMLEntityScanner implements XMLLocator {
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @throws EOFException Thrown on end of file.
*/ */
public boolean scanData(String delimiter, XMLStringBuffer buffer) protected boolean scanData(String delimiter, XMLStringBuffer buffer)
throws IOException { throws IOException {
boolean done = false; boolean done = false;
@ -1311,6 +1394,7 @@ public class XMLEntityScanner implements XMLLocator {
if (fCurrentEntity.position > fCurrentEntity.count - delimLen) { if (fCurrentEntity.position > fCurrentEntity.count - delimLen) {
// something must be wrong with the input: e.g., file ends in an unterminated comment // something must be wrong with the input: e.g., file ends in an unterminated comment
int length = fCurrentEntity.count - fCurrentEntity.position; int length = fCurrentEntity.count - fCurrentEntity.position;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length);
buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length); buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length);
fCurrentEntity.columnNumber += fCurrentEntity.count; fCurrentEntity.columnNumber += fCurrentEntity.count;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition); fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
@ -1373,6 +1457,7 @@ public class XMLEntityScanner implements XMLLocator {
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) { if (fCurrentEntity.position == fCurrentEntity.count - 1) {
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length); buffer.append(fCurrentEntity.ch, offset, length);
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("]newline, "+offset+", "+fCurrentEntity.position+": "); System.out.print("]newline, "+offset+", "+fCurrentEntity.position+": ");
@ -1416,12 +1501,14 @@ public class XMLEntityScanner implements XMLLocator {
fCurrentEntity.position--; fCurrentEntity.position--;
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length); buffer.append(fCurrentEntity.ch, offset, length);
return true; return true;
} }
} }
int length = fCurrentEntity.position - offset; int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines; fCurrentEntity.columnNumber += length - newlines;
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
if (done) { if (done) {
length -= delimLen; length -= delimLen;
} }
@ -1445,13 +1532,14 @@ public class XMLEntityScanner implements XMLLocator {
* the specified character. * the specified character.
* *
* @param c The character to skip. * @param c The character to skip.
* @param nt The type of the name (element or attribute)
* *
* @return Returns true if the character was skipped. * @return Returns true if the character was skipped.
* *
* @throws IOException Thrown if i/o error occurs. * @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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) { if (DEBUG_BUFFER) {
System.out.print("(skipChar, '"+(char)c+"': "); System.out.print("(skipChar, '"+(char)c+"': ");
print(); print();
@ -1464,6 +1552,7 @@ public class XMLEntityScanner implements XMLLocator {
} }
// skip character // skip character
int offset = fCurrentEntity.position;
int cc = fCurrentEntity.ch[fCurrentEntity.position]; int cc = fCurrentEntity.ch[fCurrentEntity.position];
if (cc == c) { if (cc == c) {
fCurrentEntity.position++; fCurrentEntity.position++;
@ -1478,6 +1567,7 @@ public class XMLEntityScanner implements XMLLocator {
print(); print();
System.out.println(" -> true"); System.out.println(" -> true");
} }
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true; return true;
} else if (c == '\n' && cc == '\r' && isExternal) { } else if (c == '\n' && cc == '\r' && isExternal) {
// handle newlines // handle newlines
@ -1497,6 +1587,7 @@ public class XMLEntityScanner implements XMLLocator {
print(); print();
System.out.println(" -> true"); System.out.println(" -> true");
} }
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true; return true;
} }
@ -1526,7 +1617,7 @@ public class XMLEntityScanner implements XMLLocator {
* *
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace * @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
*/ */
public boolean skipSpaces() throws IOException { protected boolean skipSpaces() throws IOException {
if (DEBUG_BUFFER) { if (DEBUG_BUFFER) {
System.out.print("(skipSpaces: "); System.out.print("(skipSpaces: ");
print(); print();
@ -1550,6 +1641,7 @@ public class XMLEntityScanner implements XMLLocator {
// skip spaces // skip spaces
int c = fCurrentEntity.ch[fCurrentEntity.position]; int c = fCurrentEntity.ch[fCurrentEntity.position];
int offset = fCurrentEntity.position - 1;
if (XMLChar.isSpace(c)) { if (XMLChar.isSpace(c)) {
do { do {
boolean entityChanged = false; boolean entityChanged = false;
@ -1579,6 +1671,11 @@ public class XMLEntityScanner implements XMLLocator {
} else { } else {
fCurrentEntity.columnNumber++; 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 // load more characters, if needed
if (!entityChanged){ if (!entityChanged){
fCurrentEntity.position++; 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. * to the underlying buffer.
* @return This function returns true if capacity asked is available. * @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. * 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. * @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 IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file. * @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(); final int length = s.length();
@ -1714,6 +1811,9 @@ public class XMLEntityScanner implements XMLLocator {
if(afterSkip-- == beforeSkip){ if(afterSkip-- == beforeSkip){
fCurrentEntity.position = fCurrentEntity.position + length ; fCurrentEntity.position = fCurrentEntity.position + length ;
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
if (!detectingVersion) {
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
}
return true; return true;
} }
} }
@ -1722,7 +1822,7 @@ public class XMLEntityScanner implements XMLLocator {
return false; return false;
} // skipString(String):boolean } // skipString(String):boolean
public boolean skipString(char [] s) throws IOException { protected boolean skipString(char [] s) throws IOException {
final int length = s.length; final int length = s.length;
//first make sure that required capacity is avaible //first make sure that required capacity is avaible
@ -1741,6 +1841,9 @@ public class XMLEntityScanner implements XMLLocator {
} }
fCurrentEntity.position = fCurrentEntity.position + length ; fCurrentEntity.position = fCurrentEntity.position + length ;
fCurrentEntity.columnNumber += length; fCurrentEntity.columnNumber += length;
if (!detectingVersion) {
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
}
return true; return true;
} }
@ -2138,7 +2241,7 @@ public class XMLEntityScanner implements XMLLocator {
* *
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace * @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) { if (DEBUG_BUFFER) {
System.out.print("(skipDeclSpaces: "); System.out.print("(skipDeclSpaces: ");
//XMLEntityManager.print(fCurrentEntity); //XMLEntityManager.print(fCurrentEntity);

View file

@ -189,9 +189,9 @@ public class XMLNSDocumentScannerImpl
// There are two variables,fNamespaces and fBindNamespaces // There are two variables,fNamespaces and fBindNamespaces
//StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained //StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained
if (fNamespaces) { if (fNamespaces) {
fEntityScanner.scanQName(fElementQName); fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
} else { } else {
String name = fEntityScanner.scanName(); String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
fElementQName.setValues(null, name, name, null); 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()"); if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()");
// name // name
fEntityScanner.scanQName(fAttributeQName); fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE);
// equals // equals
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('=')) { if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
reportFatalError("EqRequiredInAttribute", reportFatalError("EqRequiredInAttribute",
new Object[]{fCurrentElement.rawname,fAttributeQName.rawname}); new Object[]{fCurrentElement.rawname,fAttributeQName.rawname});
} }
@ -430,23 +430,28 @@ public class XMLNSDocumentScannerImpl
//since scanAttributeValue doesn't use attIndex parameter therefore we //since scanAttributeValue doesn't use attIndex parameter therefore we
//can safely add the attribute later.. //can safely add the attribute later..
XMLString tmpStr = getString(); 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; String value = null;
//fTempString.toString(); //fTempString.toString();
// record namespace declarations if any. // record namespace declarations if any.
if (fBindNamespaces) { if (fBindNamespaces) {
if (isNSDecl) {
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) {
//check the length of URI //check the length of URI
if (tmpStr.length > fXMLNameLimit) { if (tmpStr.length > fXMLNameLimit) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,

View file

@ -114,6 +114,30 @@ public abstract class XMLScanner
/** Debug attribute normalization. */ /** Debug attribute normalization. */
protected static final boolean DEBUG_ATTR_NORMALIZATION = false; 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 //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 //we should have a feature when set to true computes this value
@ -173,13 +197,13 @@ public abstract class XMLScanner
/** event type */ /** event type */
protected XMLEvent fEvent ; 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; protected XMLEntityScanner fEntityScanner = null;
/** Entity depth. */ /** Entity depth. */
protected int fEntityDepth; protected int fEntityDepth;
/** Literal value of the last character refence scanned. */ /** Literal value of the last character reference scanned. */
protected String fCharRefLiteral = null; protected String fCharRefLiteral = null;
/** Scanning attribute. */ /** Scanning attribute. */
@ -547,10 +571,10 @@ public abstract class XMLScanner
} }
// end // end
if (!fEntityScanner.skipChar('?')) { if (!fEntityScanner.skipChar('?', null)) {
reportFatalError("XMLDeclUnterminated", null); reportFatalError("XMLDeclUnterminated", null);
} }
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("XMLDeclUnterminated", null); reportFatalError("XMLDeclUnterminated", null);
} }
@ -577,7 +601,7 @@ public abstract class XMLScanner
* <strong>Note:</strong> This method uses fStringBuffer2, anything in it * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
* at the time of calling is lost. * at the time of calling is lost.
*/ */
public String scanPseudoAttribute(boolean scanningTextDecl, protected String scanPseudoAttribute(boolean scanningTextDecl,
XMLString value) XMLString value)
throws IOException, XNIException { throws IOException, XNIException {
@ -588,7 +612,7 @@ public abstract class XMLScanner
reportFatalError("PseudoAttrNameExpected", null); reportFatalError("PseudoAttrNameExpected", null);
} }
fEntityScanner.skipSpaces(); fEntityScanner.skipSpaces();
if (!fEntityScanner.skipChar('=')) { if (!fEntityScanner.skipChar('=', null)) {
reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl" reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl"
: "EqRequiredInXMLDecl", new Object[]{name}); : "EqRequiredInXMLDecl", new Object[]{name});
} }
@ -598,15 +622,15 @@ public abstract class XMLScanner
reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl" reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl"
: "QuoteRequiredInXMLDecl" , new Object[]{name}); : "QuoteRequiredInXMLDecl" , new Object[]{name});
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.ATTRIBUTE);
int c = fEntityScanner.scanLiteral(quote, value); int c = fEntityScanner.scanLiteral(quote, value, false);
if (c != quote) { if (c != quote) {
fStringBuffer2.clear(); fStringBuffer2.clear();
do { do {
fStringBuffer2.append(value); fStringBuffer2.append(value);
if (c != -1) { if (c != -1) {
if (c == '&' || c == '%' || c == '<' || c == ']') { if (c == '&' || c == '%' || c == '<' || c == ']') {
fStringBuffer2.append((char)fEntityScanner.scanChar()); fStringBuffer2.append((char)fEntityScanner.scanChar(NameType.ATTRIBUTE));
} else if (XMLChar.isHighSurrogate(c)) { } else if (XMLChar.isHighSurrogate(c)) {
scanSurrogates(fStringBuffer2); scanSurrogates(fStringBuffer2);
} else if (isInvalidLiteral(c)) { } else if (isInvalidLiteral(c)) {
@ -614,15 +638,15 @@ public abstract class XMLScanner
? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl"; ? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl";
reportFatalError(key, reportFatalError(key,
new Object[] {Integer.toString(c, 16)}); 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); } while (c != quote);
fStringBuffer2.append(value); fStringBuffer2.append(value);
value.setValues(fStringBuffer2); value.setValues(fStringBuffer2);
} }
if (!fEntityScanner.skipChar(quote)) { if (!fEntityScanner.skipChar(quote, null)) {
reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl" reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl"
: "CloseQuoteMissingInXMLDecl", : "CloseQuoteMissingInXMLDecl",
new Object[]{name}); new Object[]{name});
@ -680,7 +704,7 @@ public abstract class XMLScanner
// target // target
fReportEntity = false; fReportEntity = false;
String target = fEntityScanner.scanName(); String target = fEntityScanner.scanName(NameType.PI);
if (target == null) { if (target == null) {
reportFatalError("PITargetRequired", null); reportFatalError("PITargetRequired", null);
} }
@ -745,7 +769,7 @@ public abstract class XMLScanner
} else if (isInvalidLiteral(c)) { } else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInPI", reportFatalError("InvalidCharInPI",
new Object[]{Integer.toHexString(c)}); new Object[]{Integer.toHexString(c)});
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
} }
} }
} while (fEntityScanner.scanData("?>", data)); } while (fEntityScanner.scanData("?>", data));
@ -786,11 +810,11 @@ public abstract class XMLScanner
else if (isInvalidLiteral(c)) { else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInComment", reportFatalError("InvalidCharInComment",
new Object[] { Integer.toHexString(c) }); new Object[] { Integer.toHexString(c) });
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.COMMENT);
} }
} }
} }
if (!fEntityScanner.skipChar('>')) { if (!fEntityScanner.skipChar('>', NameType.COMMENT)) {
reportFatalError("DashDashInComment", null); reportFatalError("DashDashInComment", null);
} }
@ -811,15 +835,14 @@ public abstract class XMLScanner
* @param checkEntities true if undeclared entities should be reported as VC violation, * @param checkEntities true if undeclared entities should be reported as VC violation,
* false if undeclared entities should be reported as WFC violation. * false if undeclared entities should be reported as WFC violation.
* @param eleName The name of element to which this attribute belongs. * @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 * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
* at the time of calling is lost. * at the time of calling is lost.
**/ **/
protected void scanAttributeValue(XMLString value, protected void scanAttributeValue(XMLString value, XMLString nonNormalizedValue,
XMLString nonNormalizedValue, String atName, XMLAttributes attributes, int attrIndex, boolean checkEntities,
String atName, String eleName, boolean isNSURI)
XMLAttributes attributes, int attrIndex,
boolean checkEntities, String eleName)
throws IOException, XNIException { throws IOException, XNIException {
XMLStringBuffer stringBuffer = null; XMLStringBuffer stringBuffer = null;
// quote // quote
@ -828,10 +851,10 @@ public abstract class XMLScanner
reportFatalError("OpenQuoteExpected", new Object[]{eleName, atName}); reportFatalError("OpenQuoteExpected", new Object[]{eleName, atName});
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.ATTRIBUTE);
int entityDepth = fEntityDepth; int entityDepth = fEntityDepth;
int c = fEntityScanner.scanLiteral(quote, value); int c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (DEBUG_ATTR_NORMALIZATION) { if (DEBUG_ATTR_NORMALIZATION) {
System.out.println("** scanLiteral -> \"" System.out.println("** scanLiteral -> \""
+ value.toString() + "\""); + value.toString() + "\"");
@ -857,11 +880,11 @@ public abstract class XMLScanner
+ stringBuffer.toString() + "\""); + stringBuffer.toString() + "\"");
} }
if (c == '&') { if (c == '&') {
fEntityScanner.skipChar('&'); fEntityScanner.skipChar('&', NameType.REFERENCE);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
fStringBuffer2.append('&'); fStringBuffer2.append('&');
} }
if (fEntityScanner.skipChar('#')) { if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
fStringBuffer2.append('#'); fStringBuffer2.append('#');
} }
@ -879,53 +902,20 @@ public abstract class XMLScanner
} }
} }
} else { } else {
String entityName = fEntityScanner.scanName(); String entityName = fEntityScanner.scanName(NameType.ENTITY);
if (entityName == null) { if (entityName == null) {
reportFatalError("NameRequiredInReference", null); reportFatalError("NameRequiredInReference", null);
} else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append(entityName); fStringBuffer2.append(entityName);
} }
if (!fEntityScanner.skipChar(';')) { if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference", reportFatalError("SemicolonRequiredInReference",
new Object []{entityName}); new Object []{entityName});
} else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append(';'); fStringBuffer2.append(';');
} }
if (entityName == fAmpSymbol) { if (resolveCharacter(entityName, stringBuffer)) {
stringBuffer.append('&'); checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
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()
+ "\"");
}
} else { } else {
if (fEntityStore.isExternalEntity(entityName)) { if (fEntityStore.isExternalEntity(entityName)) {
reportFatalError("ReferenceToExternalEntity", reportFatalError("ReferenceToExternalEntity",
@ -952,12 +942,12 @@ public abstract class XMLScanner
} else if (c == '<') { } else if (c == '<') {
reportFatalError("LessthanInAttValue", reportFatalError("LessthanInAttValue",
new Object[] { eleName, atName }); new Object[] { eleName, atName });
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append((char)c); fStringBuffer2.append((char)c);
} }
} else if (c == '%' || c == ']') { } else if (c == '%' || c == ']') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
stringBuffer.append((char)c); stringBuffer.append((char)c);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append((char)c); fStringBuffer2.append((char)c);
@ -967,7 +957,7 @@ public abstract class XMLScanner
+ stringBuffer.toString() + "\""); + stringBuffer.toString() + "\"");
} }
} else if (c == '\n' || c == '\r') { } else if (c == '\n' || c == '\r') {
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
stringBuffer.append(' '); stringBuffer.append(' ');
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append('\n'); fStringBuffer2.append('\n');
@ -988,12 +978,12 @@ public abstract class XMLScanner
} else if (c != -1 && isInvalidLiteral(c)) { } else if (c != -1 && isInvalidLiteral(c)) {
reportFatalError("InvalidCharInAttValue", reportFatalError("InvalidCharInAttValue",
new Object[] {eleName, atName, Integer.toString(c, 16)}); new Object[] {eleName, atName, Integer.toString(c, 16)});
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append((char)c); fStringBuffer2.append((char)c);
} }
} }
c = fEntityScanner.scanLiteral(quote, value); c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append(value); fStringBuffer2.append(value);
} }
@ -1014,13 +1004,46 @@ public abstract class XMLScanner
nonNormalizedValue.setValues(fStringBuffer2); nonNormalizedValue.setValues(fStringBuffer2);
// quote // quote
int cquote = fEntityScanner.scanChar(); int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
if (cquote != quote) { if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName, atName}); reportFatalError("CloseQuoteExpected", new Object[]{eleName, atName});
} }
} // scanAttributeValue() } // 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. * Scans External ID and return the public and system IDs.
* *
@ -1064,25 +1087,25 @@ public abstract class XMLScanner
} }
reportFatalError("QuoteRequiredInSystemID", null); reportFatalError("QuoteRequiredInSystemID", null);
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
XMLString ident = fString; XMLString ident = fString;
if (fEntityScanner.scanLiteral(quote, ident) != quote) { if (fEntityScanner.scanLiteral(quote, ident, false) != quote) {
fStringBuffer.clear(); fStringBuffer.clear();
do { do {
fStringBuffer.append(ident); fStringBuffer.append(ident);
int c = fEntityScanner.peekChar(); int c = fEntityScanner.peekChar();
if (XMLChar.isMarkup(c) || c == ']') { if (XMLChar.isMarkup(c) || c == ']') {
fStringBuffer.append((char)fEntityScanner.scanChar()); fStringBuffer.append((char)fEntityScanner.scanChar(null));
} else if (c != -1 && isInvalidLiteral(c)) { } else if (c != -1 && isInvalidLiteral(c)) {
reportFatalError("InvalidCharInSystemID", reportFatalError("InvalidCharInSystemID",
new Object[] {Integer.toString(c, 16)}); new Object[] {Integer.toString(c, 16)});
} }
} while (fEntityScanner.scanLiteral(quote, ident) != quote); } while (fEntityScanner.scanLiteral(quote, ident, false) != quote);
fStringBuffer.append(ident); fStringBuffer.append(ident);
ident = fStringBuffer; ident = fStringBuffer;
} }
systemId = ident.toString(); systemId = ident.toString();
if (!fEntityScanner.skipChar(quote)) { if (!fEntityScanner.skipChar(quote, null)) {
reportFatalError("SystemIDUnterminated", null); reportFatalError("SystemIDUnterminated", null);
} }
} }
@ -1114,7 +1137,7 @@ public abstract class XMLScanner
*/ */
protected boolean scanPubidLiteral(XMLString literal) protected boolean scanPubidLiteral(XMLString literal)
throws IOException, XNIException { throws IOException, XNIException {
int quote = fEntityScanner.scanChar(); int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') { if (quote != '\'' && quote != '"') {
reportFatalError("QuoteRequiredInPublicID", null); reportFatalError("QuoteRequiredInPublicID", null);
return false; return false;
@ -1125,7 +1148,7 @@ public abstract class XMLScanner
boolean skipSpace = true; boolean skipSpace = true;
boolean dataok = true; boolean dataok = true;
while (true) { while (true) {
int c = fEntityScanner.scanChar(); int c = fEntityScanner.scanChar(null);
if (c == ' ' || c == '\n' || c == '\r') { if (c == ' ' || c == '\n' || c == '\r') {
if (!skipSpace) { if (!skipSpace) {
// take the first whitespace as a space and skip the others // 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) protected int scanCharReferenceValue(XMLStringBuffer buf, XMLStringBuffer buf2)
throws IOException, XNIException { throws IOException, XNIException {
int initLen = buf.length;
// scan hexadecimal value // scan hexadecimal value
boolean hex = false; boolean hex = false;
if (fEntityScanner.skipChar('x')) { if (fEntityScanner.skipChar('x', NameType.REFERENCE)) {
if (buf2 != null) { buf2.append('x'); } if (buf2 != null) { buf2.append('x'); }
hex = true; hex = true;
fStringBuffer3.clear(); fStringBuffer3.clear();
@ -1255,7 +1279,7 @@ public abstract class XMLScanner
(c >= 'A' && c <= 'F'); (c >= 'A' && c <= 'F');
if (digit) { if (digit) {
if (buf2 != null) { buf2.append((char)c); } if (buf2 != null) { buf2.append((char)c); }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c); fStringBuffer3.append((char)c);
do { do {
@ -1265,7 +1289,7 @@ public abstract class XMLScanner
(c >= 'A' && c <= 'F'); (c >= 'A' && c <= 'F');
if (digit) { if (digit) {
if (buf2 != null) { buf2.append((char)c); } if (buf2 != null) { buf2.append((char)c); }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c); fStringBuffer3.append((char)c);
} }
} while (digit); } while (digit);
@ -1283,7 +1307,7 @@ public abstract class XMLScanner
digit = c >= '0' && c <= '9'; digit = c >= '0' && c <= '9';
if (digit) { if (digit) {
if (buf2 != null) { buf2.append((char)c); } if (buf2 != null) { buf2.append((char)c); }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c); fStringBuffer3.append((char)c);
do { do {
@ -1291,7 +1315,7 @@ public abstract class XMLScanner
digit = c >= '0' && c <= '9'; digit = c >= '0' && c <= '9';
if (digit) { if (digit) {
if (buf2 != null) { buf2.append((char)c); } if (buf2 != null) { buf2.append((char)c); }
fEntityScanner.scanChar(); fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c); fStringBuffer3.append((char)c);
} }
} while (digit); } while (digit);
@ -1301,7 +1325,7 @@ public abstract class XMLScanner
} }
// end // end
if (!fEntityScanner.skipChar(';')) { if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInCharRef", null); reportFatalError("SemicolonRequiredInCharRef", null);
} }
if (buf2 != null) { buf2.append(';'); } 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; return value;
} }
// returns true if the given character is not // returns true if the given character is not
@ -1408,14 +1435,14 @@ public abstract class XMLScanner
protected boolean scanSurrogates(XMLStringBuffer buf) protected boolean scanSurrogates(XMLStringBuffer buf)
throws IOException, XNIException { throws IOException, XNIException {
int high = fEntityScanner.scanChar(); int high = fEntityScanner.scanChar(null);
int low = fEntityScanner.peekChar(); int low = fEntityScanner.peekChar();
if (!XMLChar.isLowSurrogate(low)) { if (!XMLChar.isLowSurrogate(low)) {
reportFatalError("InvalidCharInContent", reportFatalError("InvalidCharInContent",
new Object[] {Integer.toString(high, 16)}); new Object[] {Integer.toString(high, 16)});
return false; return false;
} }
fEntityScanner.scanChar(); fEntityScanner.scanChar(null);
// convert surrogates to supplemental character // convert surrogates to supplemental character
int c = XMLChar.supplemental((char)high, (char)low); 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 } // class XMLScanner

View file

@ -1,62 +1,21 @@
/* /*
* reserved comment block * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT REMOVE OR ALTER!
*/ */
/* /*
* 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. * Unless required by applicable law or agreed to in writing, software
* All rights reserved. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Redistribution and use in source and binary forms, with or without * See the License for the specific language governing permissions and
* modification, are permitted provided that the following conditions * limitations under the License.
* 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/>.
*/ */
package com.sun.org.apache.xerces.internal.impl; package com.sun.org.apache.xerces.internal.impl;
@ -192,40 +151,46 @@ public class XMLVersionDetector {
// in the XML declaration. // in the XML declaration.
fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0); fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
XMLEntityScanner scanner = fEntityManager.getEntityScanner(); XMLEntityScanner scanner = fEntityManager.getEntityScanner();
scanner.detectingVersion = true;
try { try {
if (!scanner.skipString("<?xml")) { if (!scanner.skipString("<?xml")) {
// definitely not a well-formed 1.1 doc! // definitely not a well-formed 1.1 doc!
scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0; return Constants.XML_VERSION_1_0;
} }
if (!scanner.skipDeclSpaces()) { if (!scanner.skipDeclSpaces()) {
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5); fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5);
scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0; return Constants.XML_VERSION_1_0;
} }
if (!scanner.skipString("version")) { if (!scanner.skipString("version")) {
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6); fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6);
scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0; return Constants.XML_VERSION_1_0;
} }
scanner.skipDeclSpaces(); scanner.skipDeclSpaces();
// Check if the next character is '='. If it is then consume it. // Check if the next character is '='. If it is then consume it.
if (scanner.peekChar() != '=') { if (scanner.peekChar() != '=') {
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13); fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13);
scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0; return Constants.XML_VERSION_1_0;
} }
scanner.scanChar(); scanner.scanChar(null);
scanner.skipDeclSpaces(); scanner.skipDeclSpaces();
int quoteChar = scanner.scanChar(); int quoteChar = scanner.scanChar(null);
fExpectedVersionString[14] = (char) quoteChar; fExpectedVersionString[14] = (char) quoteChar;
for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) { 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? // REVISIT: should we check whether this equals quoteChar?
fExpectedVersionString[18] = (char) scanner.scanChar(); fExpectedVersionString[18] = (char) scanner.scanChar(null);
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19); fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19);
int matched = 0; int matched = 0;
for (; matched < XML11_VERSION.length; matched++) { for (; matched < XML11_VERSION.length; matched++) {
if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched]) if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched])
break; break;
} }
scanner.detectingVersion = false;
if (matched == XML11_VERSION.length) if (matched == XML11_VERSION.length)
return Constants.XML_VERSION_1_1; return Constants.XML_VERSION_1_1;
return Constants.XML_VERSION_1_0; return Constants.XML_VERSION_1_0;
@ -237,10 +202,9 @@ public class XMLVersionDetector {
"PrematureEOF", "PrematureEOF",
null, null,
XMLErrorReporter.SEVERITY_FATAL_ERROR); XMLErrorReporter.SEVERITY_FATAL_ERROR);
scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0; return Constants.XML_VERSION_1_0;
} }
} }
// This method prepends "length" chars from the char array, // 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. 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. 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}\". 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}\". 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}\". 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. * 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 * 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 * 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() || if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() ||
index == Limit.ELEMENT_ATTRIBUTE_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; totalValue[index] += value;
return; return;
} }
if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
index == Limit.MAX_NAME_LIMIT.ordinal()) { index == Limit.MAX_NAME_LIMIT.ordinal()) {
values[index] = value;
totalValue[index] = value; totalValue[index] = value;
return; return;
} }
@ -175,10 +177,13 @@ public final class XMLLimitAnalyzer {
* @return the value of the property * @return the value of the property
*/ */
public int getValue(Limit limit) { public int getValue(Limit limit) {
return values[limit.ordinal()]; return getValue(limit.ordinal());
} }
public int getValue(int index) { public int getValue(int index) {
if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) {
return totalValue[index];
}
return values[index]; return values[index];
} }
/** /**
@ -233,6 +238,11 @@ public final class XMLLimitAnalyzer {
public void reset(Limit limit) { public void reset(Limit limit) {
if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) {
totalValue[limit.ordinal()] = 0; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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", MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit",
Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0), Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0),
MAX_NAME_LIMIT("MaxXMLNameLimit", 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 key;
final String apiProperty; final String apiProperty;
@ -450,6 +452,7 @@ public final class XMLSecurityManager {
if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
index == Limit.TOTAL_ENTITY_SIZE_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_ELEMENT_DEPTH_LIMIT.ordinal() ||
index == Limit.MAX_NAME_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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 { public class XSLTFunctionsTest {
/** /**
* @bug 8062518 * @bug 8062518 8153082
* Verifies that a reference to the DTM created by XSLT document function is * Verifies that a reference to the DTM created by XSLT document function is
* actually read from the DTM by an extension function. * actually read from the DTM by an extension function.
* @param xml Content of xml file to process * @param xml Content of xml file to process

View file

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

View file

@ -370,3 +370,4 @@ c40c8739bcdc88892ff58ebee3fd8a3f287be94d jdk-9+123
073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125 073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125
6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126 6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126
7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127 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 * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package java.lang; package java.lang;
import java.io.File; 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 (*procInfoFn)(id rec, SEL sel) = (OSVerStruct(*)(id, SEL))objc_msgSend_stret;
OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo], OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo],
@selector(operatingSystemVersion)); @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]; (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
}
// Copy out the char* // Copy out the char*
osVersionCStr = strdup([nsVerStr UTF8String]); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 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 * Creates an instance of AES cipher with default ECB mode and
* PKCS5Padding. * PKCS5Padding.
@ -304,6 +309,7 @@ abstract class AESCipher extends CipherSpi {
protected void engineInit(int opmode, Key key, SecureRandom random) protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException { throws InvalidKeyException {
checkKeySize(key, fixedKeySize); checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, random); core.init(opmode, key, random);
} }
@ -336,6 +342,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random) SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException { throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize); checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, params, random); core.init(opmode, key, params, random);
} }
@ -344,6 +351,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random) SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException { throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize); checkKeySize(key, fixedKeySize);
updateCalled = false;
core.init(opmode, key, params, random); core.init(opmode, key, params, random);
} }
@ -368,6 +376,7 @@ abstract class AESCipher extends CipherSpi {
*/ */
protected byte[] engineUpdate(byte[] input, int inputOffset, protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) { int inputLen) {
updateCalled = true;
return core.update(input, inputOffset, inputLen); return core.update(input, inputOffset, inputLen);
} }
@ -397,6 +406,7 @@ abstract class AESCipher extends CipherSpi {
protected int engineUpdate(byte[] input, int inputOffset, int inputLen, protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset) byte[] output, int outputOffset)
throws ShortBufferException { throws ShortBufferException {
updateCalled = true;
return core.update(input, inputOffset, inputLen, output, return core.update(input, inputOffset, inputLen, output,
outputOffset); outputOffset);
} }
@ -433,7 +443,9 @@ abstract class AESCipher extends CipherSpi {
*/ */
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException { 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) byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException, throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException { BadPaddingException {
return core.doFinal(input, inputOffset, inputLen, output, int outLen = core.doFinal(input, inputOffset, inputLen, output,
outputOffset); outputOffset);
updateCalled = false;
return outLen;
} }
/** /**
@ -574,6 +588,9 @@ abstract class AESCipher extends CipherSpi {
*/ */
@Override @Override
protected void engineUpdateAAD(byte[] src, int offset, int len) { 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); core.updateAAD(src, offset, len);
} }
@ -606,6 +623,9 @@ abstract class AESCipher extends CipherSpi {
*/ */
@Override @Override
protected void engineUpdateAAD(ByteBuffer src) { 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) { if (src != null) {
int aadLen = src.limit() - src.position(); int aadLen = src.limit() - src.position();
if (aadLen != 0) { 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 PCBC_MODE = 4;
private static final int CTR_MODE = 5; private static final int CTR_MODE = 5;
private static final int CTS_MODE = 6; 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. * variables used for performing the GCM (key+iv) uniqueness check.
@ -196,7 +196,7 @@ final class CipherCore {
cipher = new CounterMode(rawImpl); cipher = new CounterMode(rawImpl);
unitBytes = 1; unitBytes = 1;
padding = null; padding = null;
} else if (modeUpperCase.startsWith("GCM")) { } else if (modeUpperCase.equals("GCM")) {
// can only be used for block ciphers w/ 128-bit block size // can only be used for block ciphers w/ 128-bit block size
if (blockSize != 16) { if (blockSize != 16) {
throw new NoSuchAlgorithmException 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) private static int getNumOfUnit(String mode, int offset, int blockSize)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
int result = blockSize; // use blockSize as default value 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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_TAG_LEN = AES_BLOCK_SIZE;
static int DEFAULT_IV_LEN = 12; // in bytes 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 // buffer for AAD data; if null, meaning update has been called
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream(); private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0; private int sizeOfAAD = 0;
@ -89,9 +99,13 @@ final class GaloisCounterMode extends FeedbackCipher {
} }
} }
// ivLen in bits private static byte[] getLengthBlock(int ivLenInBytes) {
private static byte[] getLengthBlock(int ivLen) { long ivLen = ((long)ivLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE]; 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[12] = (byte)(ivLen >>> 24);
out[13] = (byte)(ivLen >>> 16); out[13] = (byte)(ivLen >>> 16);
out[14] = (byte)(ivLen >>> 8); out[14] = (byte)(ivLen >>> 8);
@ -99,13 +113,22 @@ final class GaloisCounterMode extends FeedbackCipher {
return out; return out;
} }
// aLen and cLen both in bits private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
private static byte[] getLengthBlock(int aLen, int cLen) { long aLen = ((long)aLenInBytes) << 3;
long cLen = ((long)cLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE]; 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[4] = (byte)(aLen >>> 24);
out[5] = (byte)(aLen >>> 16); out[5] = (byte)(aLen >>> 16);
out[6] = (byte)(aLen >>> 8); out[6] = (byte)(aLen >>> 8);
out[7] = (byte)aLen; 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[12] = (byte)(cLen >>> 24);
out[13] = (byte)(cLen >>> 16); out[13] = (byte)(cLen >>> 16);
out[14] = (byte)(cLen >>> 8); out[14] = (byte)(cLen >>> 8);
@ -142,13 +165,20 @@ final class GaloisCounterMode extends FeedbackCipher {
} else { } else {
g.update(iv); g.update(iv);
} }
byte[] lengthBlock = getLengthBlock(iv.length*8); byte[] lengthBlock = getLengthBlock(iv.length);
g.update(lengthBlock); g.update(lengthBlock);
j0 = g.digest(); j0 = g.digest();
} }
return j0; 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) { GaloisCounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher); super(embeddedCipher);
aadBuffer = new ByteArrayOutputStream(); aadBuffer = new ByteArrayOutputStream();
@ -319,10 +349,10 @@ final class GaloisCounterMode extends FeedbackCipher {
// Feed the AAD data to GHASH, pad if necessary // Feed the AAD data to GHASH, pad if necessary
void processAAD() { void processAAD() {
if (aadBuffer != null && aadBuffer.size() > 0) { if (aadBuffer != null) {
if (aadBuffer.size() > 0) {
byte[] aad = aadBuffer.toByteArray(); byte[] aad = aadBuffer.toByteArray();
sizeOfAAD = aad.length; sizeOfAAD = aad.length;
aadBuffer = null;
int lastLen = aad.length % AES_BLOCK_SIZE; int lastLen = aad.length % AES_BLOCK_SIZE;
if (lastLen != 0) { if (lastLen != 0) {
@ -334,6 +364,8 @@ final class GaloisCounterMode extends FeedbackCipher {
ghashAllToS.update(aad); ghashAllToS.update(aad);
} }
} }
aadBuffer = null;
}
} }
// Utility to process the last block; used by encryptFinal and decryptFinal // Utility to process the last block; used by encryptFinal and decryptFinal
@ -384,6 +416,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) { if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering"); throw new ProviderException("Internal error in input buffering");
} }
checkDataLength(processed, len);
processAAD(); processAAD();
if (len > 0) { if (len > 0) {
gctrPAndC.update(in, inOfs, len, out, outOfs); 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) int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException, ShortBufferException { 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)) { if (out.length - outOfs < (len + tagLenBytes)) {
throw new ShortBufferException("Output buffer too small"); throw new ShortBufferException("Output buffer too small");
} }
checkDataLength(processed, len);
processAAD(); processAAD();
if (len > 0) { if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, true); doLastBlock(in, inOfs, len, out, outOfs, true);
} }
byte[] lengthBlock = byte[] lengthBlock =
getLengthBlock(sizeOfAAD*8, processed*8); getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock); ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest(); byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length]; byte[] sOut = new byte[s.length];
@ -447,6 +488,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) { if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering"); throw new ProviderException("Internal error in input buffering");
} }
checkDataLength(ibuffer.size(), len);
processAAD(); processAAD();
if (len > 0) { if (len > 0) {
@ -481,10 +525,21 @@ final class GaloisCounterMode extends FeedbackCipher {
if (len < tagLenBytes) { if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag"); 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)) { if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
throw new ShortBufferException("Output buffer too small"); throw new ShortBufferException("Output buffer too small");
} }
processAAD(); 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) { if (len != 0) {
ibuffer.write(in, inOfs, len); ibuffer.write(in, inOfs, len);
} }
@ -495,17 +550,12 @@ final class GaloisCounterMode extends FeedbackCipher {
len = in.length; len = in.length;
ibuffer.reset(); 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) { if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, false); doLastBlock(in, inOfs, len, out, outOfs, false);
} }
byte[] lengthBlock = byte[] lengthBlock =
getLengthBlock(sizeOfAAD*8, processed*8); getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock); ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest(); byte[] s = ghashAllToS.digest();

View file

@ -1,4 +1,3 @@
/* /*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * 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(); TypeVariable<?>[] typeparms = component.getTypeParameters();
if (typeparms.length > 0) { if (typeparms.length > 0) {
boolean first = true; StringJoiner sj = new StringJoiner(",", "<", ">");
sb.append('<');
for(TypeVariable<?> typeparm: typeparms) { for(TypeVariable<?> typeparm: typeparms) {
if (!first) sj.add(typeparm.getTypeName());
sb.append(',');
sb.append(typeparm.getTypeName());
first = false;
} }
sb.append('>'); sb.append(sj.toString());
} }
for (int i = 0; i < arrayDepth; i++) 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 * Java&nbsp;SE Platform. A version string contains a version number
* optionally followed by pre-release and build information. * 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 * <p> When comparing two version strings, the value of {@code $OPT}, if
* present, may or may not be significant depending on the chosen * present, may or may not be significant depending on the chosen
* comparison method. The comparison methods {@link #compareTo(Version) * comparison method. The comparison methods {@link #compareTo(Version)
* compareTo()} and {@link #compareToIgnoreOpt(Version) * compareTo()} and {@link #compareToIgnoreOptional(Version)
* compareToIgnoreOpt()} should be used consistently with the * compareToIgnoreOptional()} should be used consistently with the
* corresponding methods {@link #equals(Object) equals()} and {@link * 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 * <p> A <em>short version string</em>, {@code $SVSTR}, often useful in
* less formal contexts, is a version number optionally followed by a * less formal contexts, is a version number optionally followed by a
@ -1249,7 +1249,7 @@ public class Runtime {
* @throws NullPointerException * @throws NullPointerException
* If the given object is {@code null} * If the given object is {@code null}
*/ */
public int compareToIgnoreOpt(Version ob) { public int compareToIgnoreOptional(Version ob) {
return compare(ob, true); return compare(ob, true);
} }
@ -1270,7 +1270,7 @@ public class Runtime {
return ret; return ret;
if (!ignoreOpt) if (!ignoreOpt)
return compareOpt(ob); return compareOptional(ob);
return 0; return 0;
} }
@ -1325,7 +1325,7 @@ public class Runtime {
return 0; return 0;
} }
private int compareOpt(Version ob) { private int compareOptional(Version ob) {
Optional<String> oOpt = ob.optional(); Optional<String> oOpt = ob.optional();
if (!optional.isPresent()) { if (!optional.isPresent()) {
if (oOpt.isPresent()) if (oOpt.isPresent())
@ -1384,7 +1384,7 @@ public class Runtime {
*/ */
@Override @Override
public boolean equals(Object ob) { public boolean equals(Object ob) {
boolean ret = equalsIgnoreOpt(ob); boolean ret = equalsIgnoreOptional(ob);
if (!ret) if (!ret)
return false; return false;
@ -1407,7 +1407,7 @@ public class Runtime {
* ignoring the optinal build information * ignoring the optinal build information
* *
*/ */
public boolean equalsIgnoreOpt(Object ob) { public boolean equalsIgnoreOptional(Object ob) {
if (this == ob) if (this == ob)
return true; return true;
if (!(ob instanceof Version)) if (!(ob instanceof Version))

View file

@ -155,7 +155,7 @@ class DirectMethodHandle extends MethodHandle {
private static LambdaForm preparedLambdaForm(MemberName m) { private static LambdaForm preparedLambdaForm(MemberName m) {
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
MethodType mtype = m.getInvocationType().basicType(); MethodType mtype = m.getInvocationType().basicType();
assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m; assert(!m.isMethodHandleInvoke()) : m;
int which; int which;
switch (m.getReferenceKind()) { switch (m.getReferenceKind()) {
case REF_invokeVirtual: which = LF_INVVIRTUAL; break; case REF_invokeVirtual: which = LF_INVVIRTUAL; break;

View file

@ -1049,7 +1049,7 @@ class LambdaForm {
this.member = member; this.member = member;
this.resolvedHandle = resolvedHandle; this.resolvedHandle = resolvedHandle;
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest. // The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
//assert(!isInvokeBasic()); //assert(!isInvokeBasic(member));
} }
NamedFunction(MethodType basicInvokerType) { NamedFunction(MethodType basicInvokerType) {
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType; assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
@ -1060,12 +1060,12 @@ class LambdaForm {
// necessary to pass BigArityTest // necessary to pass BigArityTest
this.member = Invokers.invokeBasicMethod(basicInvokerType); this.member = Invokers.invokeBasicMethod(basicInvokerType);
} }
assert(isInvokeBasic()); assert(isInvokeBasic(member));
} }
private boolean isInvokeBasic() { private static boolean isInvokeBasic(MemberName member) {
return member != null && return member != null &&
member.isMethodHandleInvoke() && member.getDeclaringClass() == MethodHandle.class &&
"invokeBasic".equals(member.getName()); "invokeBasic".equals(member.getName());
} }
@ -1204,7 +1204,7 @@ class LambdaForm {
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype)) assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
: Arrays.asList(mh, rtype, arity); : Arrays.asList(mh, rtype, arity);
MemberName member = mh.internalMemberName(); MemberName member = mh.internalMemberName();
if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) { if (isInvokeBasic(member)) {
assert(arity > 0); assert(arity > 0);
assert(a[0] instanceof MethodHandle); assert(a[0] instanceof MethodHandle);
MethodHandle mh2 = (MethodHandle) a[0]; 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). /** 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() { public boolean isMethodHandleInvoke() {
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC; final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
@ -361,7 +360,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
switch (name) { switch (name) {
case "invoke": case "invoke":
case "invokeExact": case "invokeExact":
case "invokeBasic": // internal sig-poly method
return true; return true;
default: default:
return false; return false;

View file

@ -951,8 +951,6 @@ assertEquals("", (String) MH_newString.invokeExact());
return invoker(type); return invoker(type);
if ("invokeExact".equals(name)) if ("invokeExact".equals(name))
return exactInvoker(type); return exactInvoker(type);
if ("invokeBasic".equals(name))
return basicInvoker(type);
assert(!MemberName.isMethodHandleInvokeName(name)); assert(!MemberName.isMethodHandleInvokeName(name));
return null; return null;
} }
@ -3268,6 +3266,16 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
*/ */
public static public static
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) { 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 MethodType oldType = target.type(); // get NPE
int dropped = dropArgumentChecks(oldType, pos, valueTypes); int dropped = dropArgumentChecks(oldType, pos, valueTypes);
MethodType newType = oldType.insertParameterTypes(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 version which allows caller some freedom with error handling
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos, private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos,
boolean nullOnFailure) { boolean nullOnFailure) {
newTypes = copyTypes(newTypes);
List<Class<?>> oldTypes = target.type().parameterList(); List<Class<?>> oldTypes = target.type().parameterList();
int match = oldTypes.size(); int match = oldTypes.size();
if (skip != 0) { if (skip != 0) {
@ -3379,11 +3388,11 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// target: ( S*[skip], M*[match] ) // target: ( S*[skip], M*[match] )
MethodHandle adapter = target; MethodHandle adapter = target;
if (add > 0) { if (add > 0) {
adapter = dropArguments(adapter, skip+ match, addTypes); adapter = dropArguments0(adapter, skip+ match, addTypes);
} }
// adapter: (S*[skip], M*[match], A*[add] ) // adapter: (S*[skip], M*[match], A*[add] )
if (pos > 0) { 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] ) // adapter: (S*[skip], P*[pos], M*[match], A*[add] )
return adapter; return adapter;
@ -3787,7 +3796,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
int filterValues = filterType.parameterCount(); int filterValues = filterType.parameterCount();
if (filterValues == 0 if (filterValues == 0
? (rtype != void.class) ? (rtype != void.class)
: (rtype != filterType.parameterType(0))) : (rtype != filterType.parameterType(0) || filterValues != 1))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); 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)); step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
} }
if (pred.get(i) == null) { 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) { if (fini.get(i) == null) {
fini.set(i, empty(methodType(t, commonParameterSequence))); fini.set(i, empty(methodType(t, commonParameterSequence)));
@ -4315,7 +4324,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return hs.stream().map(h -> { return hs.stream().map(h -> {
int pc = h.type().parameterCount(); int pc = h.type().parameterCount();
int tpsize = targetParams.size(); 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()); }).collect(Collectors.toList());
} }

View file

@ -52,6 +52,7 @@ import java.util.jar.Manifest;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; 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 // scan the entries in the JAR file to locate the .class and service
// configuration file // configuration file
Map<Boolean, Set<String>> map = Map<Boolean, Set<String>> map =
jf.stream() versionedStream(jf)
.map(JarEntry::getName) .map(JarEntry::getName)
.filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX))) .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
.collect(Collectors.partitioningBy(s -> s.endsWith(".class"), .collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
@ -503,8 +504,21 @@ class ModulePath implements ConfigurableModuleFinder {
return mn; 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) { private Set<String> jarPackages(JarFile jf) {
return jf.stream() return versionedStream(jf)
.filter(e -> e.getName().endsWith(".class")) .filter(e -> e.getName().endsWith(".class"))
.map(e -> toPackageName(e.getName())) .map(e -> toPackageName(e.getName()))
.filter(pkg -> pkg.length() > 0) // module-info .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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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.lang.annotation.*;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.StringJoiner;
import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.SharedSecrets;
import sun.reflect.annotation.AnnotationParser; import sun.reflect.annotation.AnnotationParser;
@ -86,15 +87,6 @@ public abstract class Executable extends AccessibleObject
getDeclaringClass()); 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) { void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) {
int mod = getModifiers() & mask; int mod = getModifiers() & mask;
@ -121,13 +113,20 @@ public abstract class Executable extends AccessibleObject
printModifiersIfNonzero(sb, modifierMask, isDefault); printModifiersIfNonzero(sb, modifierMask, isDefault);
specificToStringHeader(sb); specificToStringHeader(sb);
sb.append('('); sb.append('(');
separateWithCommas(parameterTypes, sb); StringJoiner sj = new StringJoiner(",");
for (Class<?> parameterType : parameterTypes) {
sj.add(parameterType.getTypeName());
}
sb.append(sj.toString());
sb.append(')'); sb.append(')');
if (exceptionTypes.length > 0) { if (exceptionTypes.length > 0) {
sb.append(" throws "); StringJoiner joiner = new StringJoiner(",", "throws ", "");
separateWithCommas(exceptionTypes, sb); for (Class<?> exceptionType : exceptionTypes) {
joiner.add(exceptionType.getTypeName());
}
sb.append(joiner.toString());
} }
return sb.toString(); return sb.toString();
} catch (Exception e) { } catch (Exception e) {
@ -149,42 +148,34 @@ public abstract class Executable extends AccessibleObject
TypeVariable<?>[] typeparms = getTypeParameters(); TypeVariable<?>[] typeparms = getTypeParameters();
if (typeparms.length > 0) { if (typeparms.length > 0) {
boolean first = true; StringJoiner sj = new StringJoiner(",", "<", "> ");
sb.append('<');
for(TypeVariable<?> typeparm: typeparms) { for(TypeVariable<?> typeparm: typeparms) {
if (!first) sj.add(typeparm.getTypeName());
sb.append(',');
// Class objects can't occur here; no need to test
// and call Class.getName().
sb.append(typeparm.toString());
first = false;
} }
sb.append("> "); sb.append(sj.toString());
} }
specificToGenericStringHeader(sb); specificToGenericStringHeader(sb);
sb.append('('); sb.append('(');
StringJoiner sj = new StringJoiner(",");
Type[] params = getGenericParameterTypes(); Type[] params = getGenericParameterTypes();
for (int j = 0; j < params.length; j++) { for (int j = 0; j < params.length; j++) {
String param = params[j].getTypeName(); String param = params[j].getTypeName();
if (isVarArgs() && (j == params.length - 1)) // replace T[] with T... if (isVarArgs() && (j == params.length - 1)) // replace T[] with T...
param = param.replaceFirst("\\[\\]$", "..."); param = param.replaceFirst("\\[\\]$", "...");
sb.append(param); sj.add(param);
if (j < (params.length - 1))
sb.append(',');
} }
sb.append(sj.toString());
sb.append(')'); sb.append(')');
Type[] exceptions = getGenericExceptionTypes();
if (exceptions.length > 0) { Type[] exceptionTypes = getGenericExceptionTypes();
sb.append(" throws "); if (exceptionTypes.length > 0) {
for (int k = 0; k < exceptions.length; k++) { StringJoiner joiner = new StringJoiner(",", " throws ", "");
sb.append((exceptions[k] instanceof Class)? for (Type exceptionType : exceptionTypes) {
((Class)exceptions[k]).getName(): joiner.add(exceptionType.getTypeName());
exceptions[k].toString());
if (k < (exceptions.length - 1))
sb.append(',');
} }
sb.append(joiner.toString());
} }
return sb.toString(); return sb.toString();
} catch (Exception e) { } catch (Exception e) {

View file

@ -461,11 +461,10 @@ public final class URLPermission extends Permission {
} }
private String actions() { private String actions() {
String b = String.join(",", methods); // The colon separator is optional when the request headers list is
if (!requestHeaders.isEmpty()) { // empty.This implementation chooses to include it even when the request
b += ":" + String.join(",", requestHeaders); // headers list is empty.
} return String.join(",", methods) + ":" + String.join(",", requestHeaders);
return b;
} }
/** /**

View file

@ -132,7 +132,7 @@ public class ProtectionDomain {
/* the PermissionCollection is static (pre 1.4 constructor) /* the PermissionCollection is static (pre 1.4 constructor)
or dynamic (via a policy refresh) */ 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. * 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 * Creates a new ProtectionDomain with the given CodeSource and
* Permissions. If the permissions object is not null, then * Permissions. If the permissions object is not null, then
* {@code setReadOnly()} will be called on the passed in * {@code setReadOnly()} will be called on the passed in
* Permissions object. The only permissions granted to this domain * Permissions object.
* are the ones specified; the current Policy will not be consulted. * <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 codesource the codesource associated with this domain
* @param permissions the permissions granted to 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, ClassLoader and array of Principals. If the
* permissions object is not null, then {@code setReadOnly()} * permissions object is not null, then {@code setReadOnly()}
* will be called on the passed in Permissions object. * will be called on the passed in Permissions object.
* The permissions granted to this domain are dynamic; they include * <p>
* both the static permissions passed to this constructor, and any * The permissions granted to this domain are dynamic, i.e.
* permissions granted to this domain by the current Policy at the * 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. * time a permission is checked.
* <p> * <p>
* This constructor is typically used by * This constructor is typically used by
@ -255,6 +261,19 @@ public class ProtectionDomain {
return permissions; 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 * Check and see if this ProtectionDomain implies the permissions
* expressed in the Permission object. * expressed in the Permission object.
@ -263,25 +282,19 @@ public class ProtectionDomain {
* ProtectionDomain was constructed with a static set of permissions * ProtectionDomain was constructed with a static set of permissions
* or it was bound to a dynamically mapped set of permissions. * or it was bound to a dynamically mapped set of permissions.
* <p> * <p>
* If the ProtectionDomain was constructed to a * If the {@link #staticPermissionsOnly()} method returns
* {@link #ProtectionDomain(CodeSource, PermissionCollection) * true, then the permission will only be checked against the
* statically bound} PermissionCollection then the permission will * PermissionCollection supplied at construction.
* only be checked against the PermissionCollection supplied at
* construction.
* <p> * <p>
* However, if the ProtectionDomain was constructed with * Otherwise, the permission will be checked against the combination
* the constructor variant which supports * of the PermissionCollection supplied at construction and
* {@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
* the current Policy binding. * 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) { if (hasAllPerm) {
// internal permission collection already has AllPermission - // internal permission collection already has AllPermission -
@ -290,10 +303,10 @@ public class ProtectionDomain {
} }
if (!staticPermissions && if (!staticPermissions &&
Policy.getPolicyNoCheck().implies(this, permission)) Policy.getPolicyNoCheck().implies(this, perm))
return true; return true;
if (permissions != null) if (permissions != null)
return permissions.implies(permission); return permissions.implies(perm);
return false; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 * always well-defined for queues with the same elements but different
* ordering properties. * ordering properties.
* *
*
* <p>This interface is a member of the * <p>This interface is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html"> * <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>. * Java Collections Framework</a>.

View file

@ -660,6 +660,7 @@ public abstract class ResourceBundle {
// ResourceBundleProviders for loading ResourceBundles // ResourceBundleProviders for loading ResourceBundles
private ServiceLoader<ResourceBundleProvider> providers; private ServiceLoader<ResourceBundleProvider> providers;
private boolean providersChecked;
// Boolean.TRUE if the factory method caller provides a ResourceBundleProvier. // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
private Boolean callerHasProvider; private Boolean callerHasProvider;
@ -675,7 +676,6 @@ public abstract class ResourceBundle {
this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this); this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
} }
this.moduleRef = new KeyElementReference<>(module, referenceQueue, this); this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
this.providers = getServiceLoader(module, baseName);
calculateHashCode(); calculateHashCode();
} }
@ -712,11 +712,15 @@ public abstract class ResourceBundle {
} }
ServiceLoader<ResourceBundleProvider> getProviders() { ServiceLoader<ResourceBundleProvider> getProviders() {
if (!providersChecked) {
providers = getServiceLoader(getModule(), name);
providersChecked = true;
}
return providers; return providers;
} }
boolean hasProviders() { boolean hasProviders() {
return providers != null; return getProviders() != null;
} }
boolean callerHasProvider() { boolean callerHasProvider() {
@ -789,8 +793,9 @@ public abstract class ResourceBundle {
} }
clone.moduleRef = new KeyElementReference<>(getModule(), clone.moduleRef = new KeyElementReference<>(getModule(),
referenceQueue, clone); referenceQueue, clone);
// Clear the reference to ResourceBundleProviders // Clear the reference to ResourceBundleProviders and the flag
clone.providers = null; clone.providers = null;
clone.providersChecked = false;
// Clear the reference to a Throwable // Clear the reference to a Throwable
clone.cause = null; clone.cause = null;
// Clear callerHasProvider // Clear callerHasProvider
@ -1841,6 +1846,9 @@ public abstract class ResourceBundle {
private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module, private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,
String baseName) { String baseName) {
if (!module.isNamed()) {
return null;
}
PrivilegedAction<ClassLoader> pa = module::getClassLoader; PrivilegedAction<ClassLoader> pa = module::getClassLoader;
ClassLoader loader = AccessController.doPrivileged(pa); ClassLoader loader = AccessController.doPrivileged(pa);
return getServiceLoader(module, loader, baseName); 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.ToLongBiFunction;
import java.util.function.ToLongFunction; import java.util.function.ToLongFunction;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.internal.misc.Unsafe;
/** /**
* A hash table supporting full concurrency of retrievals and * 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 -------------- */ /* ---------------- 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 * elements of in-progress next table while resizing. All uses of
* the tab arguments must be null checked by callers. All callers * the tab arguments must be null checked by callers. All callers
* also paranoically precheck that tab's length is not zero (or an * 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, * errors by users, these checks must operate on local variables,
* which accounts for some odd-looking inline assignments below. * which accounts for some odd-looking inline assignments below.
* Note that calls to setTabAt always occur within locked regions, * Note that calls to setTabAt always occur within locked regions,
* and so in principle require only release ordering, not * and so require only release ordering.
* full volatile semantics, but are currently coded as volatile
* writes to be conservative.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) { 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, 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) { 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 -------------- */ /* ---------------- Fields -------------- */
@ -1024,7 +1023,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
int hash = spread(key.hashCode()); int hash = spread(key.hashCode());
int binCount = 0; int binCount = 0;
for (Node<K,V>[] tab = table;;) { 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) if (tab == null || (n = tab.length) == 0)
tab = initTable(); tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { 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) else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f); 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 { else {
V oldVal = null; V oldVal = null;
synchronized (f) { synchronized (f) {
@ -1703,7 +1706,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
V val = null; V val = null;
int binCount = 0; int binCount = 0;
for (Node<K,V>[] tab = table;;) { 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) if (tab == null || (n = tab.length) == 0)
tab = initTable(); tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { 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) else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f); 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 { else {
boolean added = false; boolean added = false;
synchronized (f) { synchronized (f) {
@ -3298,7 +3305,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
return true; 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; private static final long LOCKSTATE;
static { static {
try { try {
@ -4554,15 +4561,22 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
return true; return true;
} }
public final boolean removeAll(Collection<?> c) { public boolean removeAll(Collection<?> c) {
if (c == null) throw new NullPointerException(); if (c == null) throw new NullPointerException();
boolean modified = false; 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())) { if (c.contains(it.next())) {
it.remove(); it.remove();
modified = true; modified = true;
} }
} }
} else {
for (Object e : c)
modified |= remove(e);
}
return modified; return modified;
} }
@ -4748,6 +4762,18 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
throw new UnsupportedOperationException(); 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) { public boolean removeIf(Predicate<? super V> filter) {
return map.removeValueIf(filter); return map.removeValueIf(filter);
} }
@ -6341,7 +6367,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
} }
// Unsafe mechanics // 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 SIZECTL;
private static final long TRANSFERINDEX; private static final long TRANSFERINDEX;
private static final long BASECOUNT; private static final long BASECOUNT;

View file

@ -35,6 +35,8 @@
package java.util.concurrent; package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractCollection; import java.util.AbstractCollection;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -292,64 +294,23 @@ public class ConcurrentLinkedDeque<E>
volatile Node<E> prev; volatile Node<E> prev;
volatile E item; volatile E item;
volatile Node<E> next; volatile Node<E> next;
Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
} }
/** /**
* Constructs a new node. Uses relaxed write because item can * Returns a new node holding item. Uses relaxed write because item
* only be seen after publication via casNext or casPrev. * can only be seen after piggy-backing publication via CAS.
*/ */
Node(E item) { static <E> Node<E> newNode(E item) {
U.putObject(this, ITEM, item); Node<E> node = new Node<E>();
} ITEM.set(node, item);
return node;
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);
}
}
} }
/** /**
* Links e as first element. * Links e as first element.
*/ */
private void linkFirst(E e) { private void linkFirst(E e) {
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); final Node<E> newNode = newNode(Objects.requireNonNull(e));
restartFromHead: restartFromHead:
for (;;) for (;;)
@ -363,13 +324,13 @@ public class ConcurrentLinkedDeque<E>
continue restartFromHead; continue restartFromHead;
else { else {
// p is first node // p is first node
newNode.lazySetNext(p); // CAS piggyback NEXT.set(newNode, p); // CAS piggyback
if (p.casPrev(null, newNode)) { if (PREV.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point // Successful CAS is the linearization point
// for e to become an element of this deque, // for e to become an element of this deque,
// and for newNode to become "live". // and for newNode to become "live".
if (p != h) // hop two nodes at a time if (p != h) // hop two nodes at a time; failure is OK
casHead(h, newNode); // Failure is OK. HEAD.weakCompareAndSetVolatile(this, h, newNode);
return; return;
} }
// Lost CAS race to another thread; re-read prev // Lost CAS race to another thread; re-read prev
@ -381,7 +342,7 @@ public class ConcurrentLinkedDeque<E>
* Links e as last element. * Links e as last element.
*/ */
private void linkLast(E e) { private void linkLast(E e) {
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); final Node<E> newNode = newNode(Objects.requireNonNull(e));
restartFromTail: restartFromTail:
for (;;) for (;;)
@ -395,13 +356,13 @@ public class ConcurrentLinkedDeque<E>
continue restartFromTail; continue restartFromTail;
else { else {
// p is last node // p is last node
newNode.lazySetPrev(p); // CAS piggyback PREV.set(newNode, p); // CAS piggyback
if (p.casNext(null, newNode)) { if (NEXT.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point // Successful CAS is the linearization point
// for e to become an element of this deque, // for e to become an element of this deque,
// and for newNode to become "live". // and for newNode to become "live".
if (p != t) // hop two nodes at a time if (p != t) // hop two nodes at a time; failure is OK
casTail(t, newNode); // Failure is OK. TAIL.weakCompareAndSetVolatile(this, t, newNode);
return; return;
} }
// Lost CAS race to another thread; re-read next // 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 updateTail(); // Ensure x is not reachable from tail
// Finally, actually gc-unlink // Finally, actually gc-unlink
x.lazySetPrev(isFirst ? prevTerminator() : x); PREV.setRelease(x, isFirst ? prevTerminator() : x);
x.lazySetNext(isLast ? nextTerminator() : x); NEXT.setRelease(x, isLast ? nextTerminator() : x);
} }
} }
} }
@ -531,7 +492,8 @@ public class ConcurrentLinkedDeque<E>
// assert first.item == null; // assert first.item == null;
for (Node<E> o = null, p = next, q;;) { for (Node<E> o = null, p = next, q;;) {
if (p.item != null || (q = p.next) == null) { 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); skipDeletedPredecessors(p);
if (first.prev == null && if (first.prev == null &&
(p.next == null || p.item != null) && (p.next == null || p.item != null) &&
@ -541,8 +503,8 @@ public class ConcurrentLinkedDeque<E>
updateTail(); // Ensure o is not reachable from tail updateTail(); // Ensure o is not reachable from tail
// Finally, actually gc-unlink // Finally, actually gc-unlink
o.lazySetNext(o); NEXT.setRelease(o, o);
o.lazySetPrev(prevTerminator()); PREV.setRelease(o, prevTerminator());
} }
} }
return; return;
@ -565,7 +527,8 @@ public class ConcurrentLinkedDeque<E>
// assert last.item == null; // assert last.item == null;
for (Node<E> o = null, p = prev, q;;) { for (Node<E> o = null, p = prev, q;;) {
if (p.item != null || (q = p.prev) == null) { 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); skipDeletedSuccessors(p);
if (last.next == null && if (last.next == null &&
(p.prev == null || p.item != null) && (p.prev == null || p.item != null) &&
@ -575,8 +538,8 @@ public class ConcurrentLinkedDeque<E>
updateTail(); // Ensure o is not reachable from tail updateTail(); // Ensure o is not reachable from tail
// Finally, actually gc-unlink // Finally, actually gc-unlink
o.lazySetPrev(o); PREV.setRelease(o, o);
o.lazySetNext(nextTerminator()); NEXT.setRelease(o, nextTerminator());
} }
} }
return; return;
@ -607,7 +570,7 @@ public class ConcurrentLinkedDeque<E>
(q = (p = q).prev) == null) { (q = (p = q).prev) == null) {
// It is possible that p is PREV_TERMINATOR, // It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail. // but if so, the CAS is guaranteed to fail.
if (casHead(h, p)) if (HEAD.compareAndSet(this, h, p))
return; return;
else else
continue restartFromHead; continue restartFromHead;
@ -637,7 +600,7 @@ public class ConcurrentLinkedDeque<E>
(q = (p = q).next) == null) { (q = (p = q).next) == null) {
// It is possible that p is NEXT_TERMINATOR, // It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail. // but if so, the CAS is guaranteed to fail.
if (casTail(t, p)) if (TAIL.compareAndSet(this, t, p))
return; return;
else else
continue restartFromTail; continue restartFromTail;
@ -675,7 +638,7 @@ public class ConcurrentLinkedDeque<E>
} }
// found active CAS target // found active CAS target
if (prev == p || x.casPrev(prev, p)) if (prev == p || PREV.compareAndSet(x, prev, p))
return; return;
} while (x.item != null || x.next == null); } while (x.item != null || x.next == null);
@ -706,7 +669,7 @@ public class ConcurrentLinkedDeque<E>
} }
// found active CAS target // found active CAS target
if (next == p || x.casNext(next, p)) if (next == p || NEXT.compareAndSet(x, next, p))
return; return;
} while (x.item != null || x.prev == null); } while (x.item != null || x.prev == null);
@ -751,7 +714,7 @@ public class ConcurrentLinkedDeque<E>
else if (p == h else if (p == h
// It is possible that p is PREV_TERMINATOR, // It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail. // but if so, the CAS is guaranteed to fail.
|| casHead(h, p)) || HEAD.compareAndSet(this, h, p))
return p; return p;
else else
continue restartFromHead; continue restartFromHead;
@ -776,7 +739,7 @@ public class ConcurrentLinkedDeque<E>
else if (p == t else if (p == t
// It is possible that p is NEXT_TERMINATOR, // It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail. // but if so, the CAS is guaranteed to fail.
|| casTail(t, p)) || TAIL.compareAndSet(this, t, p))
return p; return p;
else else
continue restartFromTail; continue restartFromTail;
@ -802,7 +765,7 @@ public class ConcurrentLinkedDeque<E>
* Constructs an empty deque. * Constructs an empty deque.
*/ */
public ConcurrentLinkedDeque() { 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 // Copy c into a private chain of Nodes
Node<E> h = null, t = null; Node<E> h = null, t = null;
for (E e : c) { for (E e : c) {
Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); Node<E> newNode = newNode(Objects.requireNonNull(e));
if (h == null) if (h == null)
h = t = newNode; h = t = newNode;
else { else {
t.lazySetNext(newNode); NEXT.set(t, newNode);
newNode.lazySetPrev(t); PREV.set(newNode, t);
t = newNode; t = newNode;
} }
} }
@ -836,12 +799,12 @@ public class ConcurrentLinkedDeque<E>
private void initHeadTail(Node<E> h, Node<E> t) { private void initHeadTail(Node<E> h, Node<E> t) {
if (h == t) { if (h == t) {
if (h == null) if (h == null)
h = t = new Node<E>(null); h = t = new Node<E>();
else { else {
// Avoid edge case of a single Node with non-null item. // Avoid edge case of a single Node with non-null item.
Node<E> newNode = new Node<E>(null); Node<E> newNode = new Node<E>();
t.lazySetNext(newNode); NEXT.set(t, newNode);
newNode.lazySetPrev(t); PREV.set(newNode, t);
t = newNode; t = newNode;
} }
} }
@ -934,7 +897,7 @@ public class ConcurrentLinkedDeque<E>
public E pollFirst() { public E pollFirst() {
for (Node<E> p = first(); p != null; p = succ(p)) { for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item; E item = p.item;
if (item != null && p.casItem(item, null)) { if (item != null && ITEM.compareAndSet(p, item, null)) {
unlink(p); unlink(p);
return item; return item;
} }
@ -945,7 +908,7 @@ public class ConcurrentLinkedDeque<E>
public E pollLast() { public E pollLast() {
for (Node<E> p = last(); p != null; p = pred(p)) { for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item; E item = p.item;
if (item != null && p.casItem(item, null)) { if (item != null && ITEM.compareAndSet(p, item, null)) {
unlink(p); unlink(p);
return item; return item;
} }
@ -1031,7 +994,8 @@ public class ConcurrentLinkedDeque<E>
Objects.requireNonNull(o); Objects.requireNonNull(o);
for (Node<E> p = first(); p != null; p = succ(p)) { for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item; 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); unlink(p);
return true; return true;
} }
@ -1055,7 +1019,8 @@ public class ConcurrentLinkedDeque<E>
Objects.requireNonNull(o); Objects.requireNonNull(o);
for (Node<E> p = last(); p != null; p = pred(p)) { for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item; 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); unlink(p);
return true; return true;
} }
@ -1159,12 +1124,12 @@ public class ConcurrentLinkedDeque<E>
// Copy c into a private chain of Nodes // Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null; Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) { for (E e : c) {
Node<E> newNode = new Node<E>(Objects.requireNonNull(e)); Node<E> newNode = newNode(Objects.requireNonNull(e));
if (beginningOfTheEnd == null) if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode; beginningOfTheEnd = last = newNode;
else { else {
last.lazySetNext(newNode); NEXT.set(last, newNode);
newNode.lazySetPrev(last); PREV.set(newNode, last);
last = newNode; last = newNode;
} }
} }
@ -1184,16 +1149,16 @@ public class ConcurrentLinkedDeque<E>
continue restartFromTail; continue restartFromTail;
else { else {
// p is last node // p is last node
beginningOfTheEnd.lazySetPrev(p); // CAS piggyback PREV.set(beginningOfTheEnd, p); // CAS piggyback
if (p.casNext(null, beginningOfTheEnd)) { if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point // Successful CAS is the linearization point
// for all elements to be added to this deque. // 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, // Try a little harder to update tail,
// since we may be adding many elements. // since we may be adding many elements.
t = tail; t = tail;
if (last.next == null) if (last.next == null)
casTail(t, last); TAIL.weakCompareAndSetVolatile(this, t, last);
} }
return true; return true;
} }
@ -1586,41 +1551,38 @@ public class ConcurrentLinkedDeque<E>
Node<E> h = null, t = null; Node<E> h = null, t = null;
for (Object item; (item = s.readObject()) != null; ) { for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Node<E> newNode = new Node<E>((E) item); Node<E> newNode = newNode((E) item);
if (h == null) if (h == null)
h = t = newNode; h = t = newNode;
else { else {
t.lazySetNext(newNode); NEXT.set(t, newNode);
newNode.lazySetPrev(t); PREV.set(newNode, t);
t = newNode; t = newNode;
} }
} }
initHeadTail(h, t); initHeadTail(h, t);
} }
private boolean casHead(Node<E> cmp, Node<E> val) { // VarHandle mechanics
return U.compareAndSwapObject(this, HEAD, cmp, val); private static final VarHandle HEAD;
} private static final VarHandle TAIL;
private static final VarHandle PREV;
private boolean casTail(Node<E> cmp, Node<E> val) { private static final VarHandle NEXT;
return U.compareAndSwapObject(this, TAIL, cmp, val); private static final VarHandle ITEM;
}
// 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;
static { static {
PREV_TERMINATOR = new Node<Object>(); PREV_TERMINATOR = new Node<Object>();
PREV_TERMINATOR.next = PREV_TERMINATOR; PREV_TERMINATOR.next = PREV_TERMINATOR;
NEXT_TERMINATOR = new Node<Object>(); NEXT_TERMINATOR = new Node<Object>();
NEXT_TERMINATOR.prev = NEXT_TERMINATOR; NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
try { try {
HEAD = U.objectFieldOffset MethodHandles.Lookup l = MethodHandles.lookup();
(ConcurrentLinkedDeque.class.getDeclaredField("head")); HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head",
TAIL = U.objectFieldOffset Node.class);
(ConcurrentLinkedDeque.class.getDeclaredField("tail")); 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) { } catch (ReflectiveOperationException e) {
throw new Error(e); throw new Error(e);
} }

View file

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

View file

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

View file

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

View file

@ -34,6 +34,7 @@
package java.util.concurrent; package java.util.concurrent;
import java.lang.reflect.Field;
import java.util.AbstractList; import java.util.AbstractList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; 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() { private void resetLock() {
U.putObjectVolatile(this, LOCK, new Object()); Field lockField = java.security.AccessController.doPrivileged(
} (java.security.PrivilegedAction<Field>) () -> {
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long LOCK;
static {
try { try {
LOCK = U.objectFieldOffset Field f = CopyOnWriteArrayList.class
(CopyOnWriteArrayList.class.getDeclaredField("lock")); .getDeclaredField("lock");
f.setAccessible(true);
return f;
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new Error(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; package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
/** /**
* A {@link ForkJoinTask} with a completion action performed when * A {@link ForkJoinTask} with a completion action performed when
* triggered and there are no remaining pending actions. * 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 * @param delta the value to add
*/ */
public final void addToPendingCount(int delta) { 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 * @return {@code true} if successful
*/ */
public final boolean compareAndSetPendingCount(int expected, int count) { 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() { public final int decrementPendingCountUnlessZero() {
int c; int c;
do {} while ((c = pending) != 0 && do {} while ((c = pending) != 0 &&
!U.compareAndSwapInt(this, PENDING, c, c - 1)); !PENDING.weakCompareAndSetVolatile(this, c, c - 1));
return c; return c;
} }
@ -581,7 +584,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
return; return;
} }
} }
else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
return; return;
} }
} }
@ -604,7 +607,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
return; return;
} }
} }
else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
return; return;
} }
} }
@ -649,7 +652,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
for (int c;;) { for (int c;;) {
if ((c = pending) == 0) if ((c = pending) == 0)
return this; return this;
else if (U.compareAndSwapInt(this, PENDING, c, c - 1)) else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1))
return null; return null;
} }
} }
@ -753,13 +756,13 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
*/ */
protected void setRawResult(T t) { } protected void setRawResult(T t) { }
// Unsafe mechanics // VarHandle mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); private static final VarHandle PENDING;
private static final long PENDING;
static { static {
try { try {
PENDING = U.objectFieldOffset MethodHandles.Lookup l = MethodHandles.lookup();
(CountedCompleter.class.getDeclaredField("pending")); PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new Error(e); throw new Error(e);
} }

View file

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

View file

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

View file

@ -66,8 +66,9 @@ public class ForkJoinWorkerThread extends Thread {
* owning thread. * owning thread.
* *
* Support for (non-public) subclass InnocuousForkJoinWorkerThread * Support for (non-public) subclass InnocuousForkJoinWorkerThread
* requires that we break quite a lot of encapsulation (via Unsafe) * requires that we break quite a lot of encapsulation (via helper
* both here and in the subclass to access and set Thread fields. * methods in ThreadLocalRandom) both here and in the subclass to
* access and set Thread fields.
*/ */
final ForkJoinPool pool; // the pool this thread works in final ForkJoinPool pool; // the pool this thread works in
@ -92,8 +93,8 @@ public class ForkJoinWorkerThread extends Thread {
ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup, ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
AccessControlContext acc) { AccessControlContext acc) {
super(threadGroup, null, "aForkJoinWorkerThread"); super(threadGroup, null, "aForkJoinWorkerThread");
U.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, acc); ThreadLocalRandom.setInheritedAccessControlContext(this, acc);
eraseThreadLocals(); // clear before registering ThreadLocalRandom.eraseThreadLocals(this); // clear before registering
this.pool = pool; this.pool = pool;
this.workQueue = pool.registerWorker(this); 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. * Non-public hook method for InnocuousForkJoinWorkerThread.
*/ */
void afterTopLevelExec() { 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 * A worker thread that has no permissions, is not a member of any
* user-defined ThreadGroup, and erases all ThreadLocals after * user-defined ThreadGroup, and erases all ThreadLocals after
@ -210,7 +185,7 @@ public class ForkJoinWorkerThread extends Thread {
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread { static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */ /** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
private static final ThreadGroup innocuousThreadGroup = private static final ThreadGroup innocuousThreadGroup =
createThreadGroup(); ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup");
/** An AccessControlContext supporting no privileges */ /** An AccessControlContext supporting no privileges */
private static final AccessControlContext INNOCUOUS_ACC = private static final AccessControlContext INNOCUOUS_ACC =
@ -225,7 +200,7 @@ public class ForkJoinWorkerThread extends Thread {
@Override // to erase ThreadLocals @Override // to erase ThreadLocals
void afterTopLevelExec() { void afterTopLevelExec() {
eraseThreadLocals(); ThreadLocalRandom.eraseThreadLocals(this);
} }
@Override // to always report system loader @Override // to always report system loader
@ -241,33 +216,5 @@ public class ForkJoinWorkerThread extends Thread {
throw new SecurityException("setContextClassLoader"); 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; package java.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport; 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 * cancellation races. Sync control in the current design relies
* on a "state" field updated via CAS to track completion, along * on a "state" field updated via CAS to track completion, along
* with a simple Treiber stack to hold waiting threads. * 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) { public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && if (!(state == NEW && STATE.compareAndSet
U.compareAndSwapInt(this, STATE, NEW, (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false; return false;
try { // in case call to interrupt throws exception try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) { if (mayInterruptIfRunning) {
@ -174,7 +172,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
if (t != null) if (t != null)
t.interrupt(); t.interrupt();
} finally { // final state } finally { // final state
U.putIntRelease(this, STATE, INTERRUPTED); STATE.setRelease(this, INTERRUPTED);
} }
} }
} finally { } finally {
@ -228,9 +226,9 @@ public class FutureTask<V> implements RunnableFuture<V> {
* @param v the value * @param v the value
*/ */
protected void set(V v) { protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v; outcome = v;
U.putIntRelease(this, STATE, NORMAL); // final state STATE.setRelease(this, NORMAL); // final state
finishCompletion(); finishCompletion();
} }
} }
@ -246,16 +244,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
* @param t the cause of failure * @param t the cause of failure
*/ */
protected void setException(Throwable t) { protected void setException(Throwable t) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = t; outcome = t;
U.putIntRelease(this, STATE, EXCEPTIONAL); // final state STATE.setRelease(this, EXCEPTIONAL); // final state
finishCompletion(); finishCompletion();
} }
} }
public void run() { public void run() {
if (state != NEW || if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) !RUNNER.compareAndSet(this, null, Thread.currentThread()))
return; return;
try { try {
Callable<V> c = callable; Callable<V> c = callable;
@ -296,7 +294,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
*/ */
protected boolean runAndReset() { protected boolean runAndReset() {
if (state != NEW || if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) !RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false; return false;
boolean ran = false; boolean ran = false;
int s = state; int s = state;
@ -363,7 +361,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
private void finishCompletion() { private void finishCompletion() {
// assert state > COMPLETING; // assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) { for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) { if (WAITERS.weakCompareAndSetVolatile(this, q, null)) {
for (;;) { for (;;) {
Thread t = q.thread; Thread t = q.thread;
if (t != null) { if (t != null) {
@ -425,8 +423,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
q = new WaitNode(); q = new WaitNode();
} }
else if (!queued) else if (!queued)
queued = U.compareAndSwapObject(this, WAITERS, queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q);
q.next = waiters, q);
else if (timed) { else if (timed) {
final long parkNanos; final long parkNanos;
if (startTime == 0L) { // first time if (startTime == 0L) { // first time
@ -475,7 +472,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
if (pred.thread == null) // check for race if (pred.thread == null) // check for race
continue retry; continue retry;
} }
else if (!U.compareAndSwapObject(this, WAITERS, q, s)) else if (!WAITERS.compareAndSet(this, q, s))
continue retry; continue retry;
} }
break; break;
@ -483,19 +480,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
} }
} }
// Unsafe mechanics // VarHandle mechanics
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); private static final VarHandle STATE;
private static final long STATE; private static final VarHandle RUNNER;
private static final long RUNNER; private static final VarHandle WAITERS;
private static final long WAITERS;
static { static {
try { try {
STATE = U.objectFieldOffset MethodHandles.Lookup l = MethodHandles.lookup();
(FutureTask.class.getDeclaredField("state")); STATE = l.findVarHandle(FutureTask.class, "state", int.class);
RUNNER = U.objectFieldOffset RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
(FutureTask.class.getDeclaredField("runner")); WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
WAITERS = U.objectFieldOffset
(FutureTask.class.getDeclaredField("waiters"));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new Error(e); throw new Error(e);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -36,6 +36,7 @@
package java.util.concurrent; package java.util.concurrent;
import java.io.ObjectStreamField; import java.io.ObjectStreamField;
import java.security.AccessControlContext;
import java.util.Random; import java.util.Random;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -47,6 +48,7 @@ import java.util.stream.DoubleStream;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.LongStream; import java.util.stream.LongStream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import jdk.internal.misc.Unsafe;
/** /**
* A random number generator isolated to the current thread. Like the * 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 * ThreadLocalRandom sequence. The dual use is a marriage of
* convenience, but is a simple and efficient way of reducing * convenience, but is a simple and efficient way of reducing
* application-level overhead and footprint of most concurrent * 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 * Even though this class subclasses java.util.Random, it uses the
* same basic algorithm as java.util.SplittableRandom. (See its * same basic algorithm as java.util.SplittableRandom. (See its
@ -958,6 +962,49 @@ public class ThreadLocalRandom extends Random {
return r; 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 // Serialization support
private static final long serialVersionUID = -5851777807851030925L; 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"; static final String BAD_SIZE = "size must be non-negative";
// Unsafe mechanics // 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 SEED;
private static final long PROBE; private static final long PROBE;
private static final long SECONDARY; private static final long SECONDARY;
private static final long THREADLOCALS;
private static final long INHERITABLETHREADLOCALS;
private static final long INHERITEDACCESSCONTROLCONTEXT;
static { static {
try { try {
SEED = U.objectFieldOffset SEED = U.objectFieldOffset
@ -1034,6 +1084,12 @@ public class ThreadLocalRandom extends Random {
(Thread.class.getDeclaredField("threadLocalRandomProbe")); (Thread.class.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = U.objectFieldOffset SECONDARY = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed")); (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) { } catch (ReflectiveOperationException e) {
throw new Error(e); throw new Error(e);
} }

View file

@ -35,27 +35,26 @@
package java.util.concurrent.atomic; 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 * A {@code boolean} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for * {@link VarHandle} specification for descriptions of the properties
* description of the properties of atomic variables. An * of atomic accesses. An {@code AtomicBoolean} is used in
* {@code AtomicBoolean} is used in applications such as atomically * applications such as atomically updated flags, and cannot be used
* updated flags, and cannot be used as a replacement for a * as a replacement for a {@link java.lang.Boolean}.
* {@link java.lang.Boolean}.
* *
* @since 1.5 * @since 1.5
* @author Doug Lea * @author Doug Lea
*/ */
public class AtomicBoolean implements java.io.Serializable { public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L; private static final long serialVersionUID = 4654671469794556979L;
private static final VarHandle VALUE;
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
static { static {
try { try {
VALUE = U.objectFieldOffset MethodHandles.Lookup l = MethodHandles.lookup();
(AtomicBoolean.class.getDeclaredField("value")); VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new Error(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 * @return the current value
*/ */
@ -88,40 +88,39 @@ public class AtomicBoolean implements java.io.Serializable {
} }
/** /**
* Atomically sets the value to the given updated value * Atomically sets the value to {@code newValue}
* if the current value {@code ==} the expected value. * if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
* *
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful. False return indicates that * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value. * the actual value was not equal to the expected value.
*/ */
public final boolean compareAndSet(boolean expect, boolean update) { public final boolean compareAndSet(boolean expectedValue, boolean newValue) {
return U.compareAndSwapInt(this, VALUE, return VALUE.compareAndSet(this,
(expect ? 1 : 0), (expectedValue ? 1 : 0),
(update ? 1 : 0)); (newValue ? 1 : 0));
} }
/** /**
* Atomically sets the value to the given updated value * Possibly atomically sets the value to {@code newValue}
* if the current value {@code ==} the expected value. * if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
* *
* <p><a href="package-summary.html#weakCompareAndSet">May fail * @param expectedValue the expected value
* spuriously and does not provide ordering guarantees</a>, so is * @param newValue the new value
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful * @return {@code true} if successful
*/ */
public boolean weakCompareAndSet(boolean expect, boolean update) { public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) {
return U.compareAndSwapInt(this, VALUE, return VALUE.weakCompareAndSet(this,
(expect ? 1 : 0), (expectedValue ? 1 : 0),
(update ? 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 * @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 * @param newValue the new value
* @since 1.6 * @since 1.6
*/ */
public final void lazySet(boolean newValue) { 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 * @param newValue the new value
* @return the previous value * @return the previous value
@ -161,4 +162,178 @@ public class AtomicBoolean implements java.io.Serializable {
return Boolean.toString(get()); 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; package java.util.concurrent.atomic;
import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator; import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator; import java.util.function.IntUnaryOperator;
/** /**
* An {@code int} value that may be updated atomically. See the * An {@code int} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for * {@link VarHandle} specification for descriptions of the properties
* description of the properties of atomic variables. An * of atomic accesses. An {@code AtomicInteger} is used in
* {@code AtomicInteger} is used in applications such as atomically * applications such as atomically incremented counters, and cannot be
* incremented counters, and cannot be used as a replacement for an * used as a replacement for an {@link java.lang.Integer}. However,
* {@link java.lang.Integer}. However, this class does extend * this class does extend {@code Number} to allow uniform access by
* {@code Number} to allow uniform access by tools and utilities that * tools and utilities that deal with numerically-based classes.
* deal with numerically-based classes.
* *
* @since 1.5 * @since 1.5
* @author Doug Lea * @author Doug Lea
@ -54,6 +54,10 @@ import java.util.function.IntUnaryOperator;
public class AtomicInteger extends Number implements java.io.Serializable { public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L; 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 jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE; 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 * @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 * @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 * @param newValue the new value
* @since 1.6 * @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 * @param newValue the new value
* @return the previous 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 * Atomically sets the value to {@code newValue}
* if the current value {@code ==} the expected value. * if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
* *
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful. False return indicates that * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value. * the actual value was not equal to the expected value.
*/ */
public final boolean compareAndSet(int expect, int update) { public final boolean compareAndSet(int expectedValue, int newValue) {
return U.compareAndSwapInt(this, VALUE, expect, update); return U.compareAndSwapInt(this, VALUE, expectedValue, newValue);
} }
/** /**
* Atomically sets the value to the given updated value * Possibly atomically sets the value to {@code newValue}
* if the current value {@code ==} the expected value. * if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
* *
* <p><a href="package-summary.html#weakCompareAndSet">May fail * @param expectedValue the expected value
* spuriously and does not provide ordering guarantees</a>, so is * @param newValue the new value
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful * @return {@code true} if successful
*/ */
public final boolean weakCompareAndSet(int expect, int update) { public final boolean weakCompareAndSet(int expectedValue, int newValue) {
return U.compareAndSwapInt(this, VALUE, expect, update); 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 * @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 * @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 * @param delta the value to add
* @return the previous value * @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 * @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 * @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 * @param delta the value to add
* @return the updated value * @return the updated value
@ -217,12 +237,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
* @since 1.8 * @since 1.8
*/ */
public final int getAndUpdate(IntUnaryOperator updateFunction) { public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next; int prev = get(), next = 0;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = updateFunction.applyAsInt(prev); next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return prev; return prev;
haveNext = (prev == (prev = get()));
}
} }
/** /**
@ -236,12 +258,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
* @since 1.8 * @since 1.8
*/ */
public final int updateAndGet(IntUnaryOperator updateFunction) { public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next; int prev = get(), next = 0;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = updateFunction.applyAsInt(prev); next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return 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, public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) { IntBinaryOperator accumulatorFunction) {
int prev, next; int prev = get(), next = 0;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x); next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return prev; 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, public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) { IntBinaryOperator accumulatorFunction) {
int prev, next; int prev = get(), next = 0;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x); next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return 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()}. * Equivalent to {@link #get()}.
*/ */
public int intValue() { 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} * Returns the current value of this {@code AtomicInteger} as a
* after a widening primitive conversion. * {@code long} after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions * @jls 5.1.2 Widening Primitive Conversions
*/ */
public long longValue() { 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} * Returns the current value of this {@code AtomicInteger} as a
* after a widening primitive conversion. * {@code float} after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions * @jls 5.1.2 Widening Primitive Conversions
*/ */
public float floatValue() { 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} * Returns the current value of this {@code AtomicInteger} as a
* after a widening primitive conversion. * {@code double} after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions * @jls 5.1.2 Widening Primitive Conversions
*/ */
public double doubleValue() { public double doubleValue() {
return (double)get(); 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; package java.util.concurrent.atomic;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator; import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator; import java.util.function.IntUnaryOperator;
/** /**
* An {@code int} array in which elements may be updated atomically. * An {@code int} array in which elements may be updated atomically.
* See the {@link java.util.concurrent.atomic} package * See the {@link VarHandle} specification for descriptions of the
* specification for description of the properties of atomic * properties of atomic accesses.
* variables.
* @since 1.5 * @since 1.5
* @author Doug Lea * @author Doug Lea
*/ */
public class AtomicIntegerArray implements java.io.Serializable { public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L; private static final long serialVersionUID = 2862133569453604235L;
private static final VarHandle AA
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); = MethodHandles.arrayElementVarHandle(int[].class);
private static final int ABASE;
private static final int ASHIFT;
private final int[] array; 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 * Creates a new AtomicIntegerArray of the given length, with all
* elements initially zero. * 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 * @param i the index
* @return the current value * @return the current value
*/ */
public final int get(int i) { public final int get(int i) {
return getRaw(checkedByteOffset(i)); return (int)AA.getVolatile(array, i);
}
private int getRaw(long offset) {
return U.getIntVolatile(array, offset);
} }
/** /**
* 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 i the index
* @param newValue the new value * @param newValue the new value
*/ */
public final void set(int i, int newValue) { 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 i the index
* @param newValue the new value * @param newValue the new value
* @since 1.6 * @since 1.6
*/ */
public final void lazySet(int i, int newValue) { 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 * Atomically sets the element at index {@code i} to {@code
* value and returns the old value. * newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
* *
* @param i the index * @param i the index
* @param newValue the new value * @param newValue the new value
* @return the previous value * @return the previous value
*/ */
public final int getAndSet(int i, int newValue) { 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 * Atomically sets the element at index {@code i} to {@code
* updated value if the current value {@code ==} the expected value. * newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
* *
* @param i the index * @param i the index
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful. False return indicates that * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value. * the actual value was not equal to the expected value.
*/ */
public final boolean compareAndSet(int i, int expect, int update) { public final boolean compareAndSet(int i, int expectedValue, int newValue) {
return compareAndSetRaw(checkedByteOffset(i), expect, update); return AA.compareAndSet(array, i, expectedValue, newValue);
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
return U.compareAndSwapInt(array, offset, expect, update);
} }
/** /**
* Atomically sets the element at position {@code i} to the given * Possibly atomically sets the element at index {@code i} to
* updated value if the current value {@code ==} the expected value. * {@code newValue} if the element's 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 i the index * @param i the index
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful * @return {@code true} if successful
*/ */
public final boolean weakCompareAndSet(int i, int expect, int update) { public final boolean weakCompareAndSet(int i, int expectedValue, int newValue) {
return compareAndSet(i, expect, update); 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 * @param i the index
* @return the previous value * @return the previous value
*/ */
public final int getAndIncrement(int i) { 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 * @param i the index
* @return the previous value * @return the previous value
*/ */
public final int getAndDecrement(int i) { 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 i the index
* @param delta the value to add * @param delta the value to add
* @return the previous value * @return the previous value
*/ */
public final int getAndAdd(int i, int delta) { 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 * @param i the index
* @return the updated value * @return the updated value
*/ */
public final int incrementAndGet(int i) { 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 * @param i the index
* @return the updated value * @return the updated value
*/ */
public final int decrementAndGet(int i) { 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 i the index
* @param delta the value to add * @param delta the value to add
* @return the updated value * @return the updated value
*/ */
public final int addAndGet(int i, int delta) { 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 * @since 1.8
*/ */
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) { public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i); int prev = get(i), next = 0;
int prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = updateFunction.applyAsInt(prev); next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return prev; return prev;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
@ -281,23 +270,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
* @since 1.8 * @since 1.8
*/ */
public final int updateAndGet(int i, IntUnaryOperator updateFunction) { public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i); int prev = get(i), next = 0;
int prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = updateFunction.applyAsInt(prev); next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return next; return next;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
* Atomically updates the element at index {@code i} with the * Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and * results of applying the given function to the current and given
* given values, returning the previous value. The function should * values, returning the previous value. The function should be
* be side-effect-free, since it may be re-applied when attempted * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is * updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first * applied with the current value of the element at index {@code i}
* argument, and the given update as the second argument. * as its first argument, and the given update as the second
* argument.
* *
* @param i the index * @param i the index
* @param x the update value * @param x the update value
@ -307,23 +298,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
*/ */
public final int getAndAccumulate(int i, int x, public final int getAndAccumulate(int i, int x,
IntBinaryOperator accumulatorFunction) { IntBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i); int prev = get(i), next = 0;
int prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = accumulatorFunction.applyAsInt(prev, x); next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return prev; return prev;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
* Atomically updates the element at index {@code i} with the * Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and * results of applying the given function to the current and given
* given values, returning the updated value. The function should * values, returning the updated value. The function should be
* be side-effect-free, since it may be re-applied when attempted * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is * updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first * applied with the current value of the element at index {@code i}
* argument, and the given update as the second argument. * as its first argument, and the given update as the second
* argument.
* *
* @param i the index * @param i the index
* @param x the update value * @param x the update value
@ -333,13 +326,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
*/ */
public final int accumulateAndGet(int i, int x, public final int accumulateAndGet(int i, int x,
IntBinaryOperator accumulatorFunction) { IntBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i); int prev = get(i), next = 0;
int prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = accumulatorFunction.applyAsInt(prev, x); next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return next; return next;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
@ -354,11 +348,190 @@ public class AtomicIntegerArray implements java.io.Serializable {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append('['); b.append('[');
for (int i = 0; ; i++) { for (int i = 0; ; i++) {
b.append(getRaw(byteOffset(i))); b.append(get(i));
if (i == iMax) if (i == iMax)
return b.append(']').toString(); return b.append(']').toString();
b.append(',').append(' '); 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.security.PrivilegedExceptionAction;
import java.util.function.IntBinaryOperator; import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator; import java.util.function.IntUnaryOperator;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
@ -150,8 +151,8 @@ public abstract class AtomicIntegerFieldUpdater<T> {
public abstract void lazySet(T obj, int newValue); public abstract void lazySet(T obj, int newValue);
/** /**
* Gets the current value held in the field of the given object managed * Returns the current value held in the field of the given object
* by this updater. * managed by this updater.
* *
* @param obj An object whose field to get * @param obj An object whose field to get
* @return the current value * @return the current value
@ -367,7 +368,7 @@ public abstract class AtomicIntegerFieldUpdater<T> {
*/ */
private static final class AtomicIntegerFieldUpdaterImpl<T> private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<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; private final long offset;
/** /**
* if field is protected, the subclass constructing updater, else * if field is protected, the subclass constructing updater, else

View file

@ -35,18 +35,18 @@
package java.util.concurrent.atomic; package java.util.concurrent.atomic;
import java.lang.invoke.VarHandle;
import java.util.function.LongBinaryOperator; import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator; import java.util.function.LongUnaryOperator;
/** /**
* A {@code long} value that may be updated atomically. See the * A {@code long} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for * {@link VarHandle} specification for descriptions of the properties
* description of the properties of atomic variables. An * of atomic accesses. An {@code AtomicLong} is used in applications
* {@code AtomicLong} is used in applications such as atomically * such as atomically incremented sequence numbers, and cannot be used
* incremented sequence numbers, and cannot be used as a replacement * as a replacement for a {@link java.lang.Long}. However, this class
* for a {@link java.lang.Long}. However, this class does extend * does extend {@code Number} to allow uniform access by tools and
* {@code Number} to allow uniform access by tools and utilities that * utilities that deal with numerically-based classes.
* deal with numerically-based classes.
* *
* @since 1.5 * @since 1.5
* @author Doug Lea * @author Doug Lea
@ -54,12 +54,9 @@ import java.util.function.LongUnaryOperator;
public class AtomicLong extends Number implements java.io.Serializable { public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L; 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 * 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 * method works in either case, some constructions should be
* handled at Java level to avoid locking user-visible locks. * 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(); 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 { static {
try { try {
VALUE = U.objectFieldOffset 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 * @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 * @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 * @param newValue the new value
* @since 1.6 * @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 * @param newValue the new value
* @return the previous 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 * Atomically sets the value to {@code newValue}
* if the current value {@code ==} the expected value. * if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
* *
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful. False return indicates that * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value. * the actual value was not equal to the expected value.
*/ */
public final boolean compareAndSet(long expect, long update) { public final boolean compareAndSet(long expectedValue, long newValue) {
return U.compareAndSwapLong(this, VALUE, expect, update); return U.compareAndSwapLong(this, VALUE, expectedValue, newValue);
} }
/** /**
* Atomically sets the value to the given updated value * Possibly atomically sets the value to {@code newValue}
* if the current value {@code ==} the expected value. * if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
* *
* <p><a href="package-summary.html#weakCompareAndSet">May fail * @param expectedValue the expected value
* spuriously and does not provide ordering guarantees</a>, so is * @param newValue the new value
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful * @return {@code true} if successful
*/ */
public final boolean weakCompareAndSet(long expect, long update) { public final boolean weakCompareAndSet(long expectedValue, long newValue) {
return U.compareAndSwapLong(this, VALUE, expect, update); 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 * @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 * @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 * @param delta the value to add
* @return the previous value * @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 * @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 * @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 * @param delta the value to add
* @return the updated value * @return the updated value
@ -233,12 +253,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
* @since 1.8 * @since 1.8
*/ */
public final long getAndUpdate(LongUnaryOperator updateFunction) { public final long getAndUpdate(LongUnaryOperator updateFunction) {
long prev, next; long prev = get(), next = 0L;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = updateFunction.applyAsLong(prev); next = updateFunction.applyAsLong(prev);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return prev; return prev;
haveNext = (prev == (prev = get()));
}
} }
/** /**
@ -252,12 +274,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
* @since 1.8 * @since 1.8
*/ */
public final long updateAndGet(LongUnaryOperator updateFunction) { public final long updateAndGet(LongUnaryOperator updateFunction) {
long prev, next; long prev = get(), next = 0L;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = updateFunction.applyAsLong(prev); next = updateFunction.applyAsLong(prev);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return 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, public final long getAndAccumulate(long x,
LongBinaryOperator accumulatorFunction) { LongBinaryOperator accumulatorFunction) {
long prev, next; long prev = get(), next = 0L;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = accumulatorFunction.applyAsLong(prev, x); next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return prev; 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, public final long accumulateAndGet(long x,
LongBinaryOperator accumulatorFunction) { LongBinaryOperator accumulatorFunction) {
long prev, next; long prev = get(), next = 0L;
do { for (boolean haveNext = false;;) {
prev = get(); if (!haveNext)
next = accumulatorFunction.applyAsLong(prev, x); next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSet(prev, next)); if (weakCompareAndSetVolatile(prev, next))
return 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} * Returns the current value of this {@code AtomicLong} as an {@code int}
* after a narrowing primitive conversion. * after a narrowing primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.3 Narrowing Primitive Conversions * @jls 5.1.3 Narrowing Primitive Conversions
*/ */
public int intValue() { 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()}. * Equivalent to {@link #get()}.
*/ */
public long longValue() { 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} * Returns the current value of this {@code AtomicLong} as a {@code float}
* after a widening primitive conversion. * after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions * @jls 5.1.2 Widening Primitive Conversions
*/ */
public float floatValue() { 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} * Returns the current value of this {@code AtomicLong} as a {@code double}
* after a widening primitive conversion. * after a widening primitive conversion,
* with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions * @jls 5.1.2 Widening Primitive Conversions
*/ */
public double doubleValue() { public double doubleValue() {
return (double)get(); 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; package java.util.concurrent.atomic;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.function.LongBinaryOperator; import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator; import java.util.function.LongUnaryOperator;
/** /**
* A {@code long} array in which elements may be updated atomically. * A {@code long} array in which elements may be updated atomically.
* See the {@link java.util.concurrent.atomic} package specification * See the {@link VarHandle} specification for descriptions of the
* for description of the properties of atomic variables. * properties of atomic accesses.
* @since 1.5 * @since 1.5
* @author Doug Lea * @author Doug Lea
*/ */
public class AtomicLongArray implements java.io.Serializable { public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L; private static final long serialVersionUID = -2308431214976778248L;
private static final VarHandle AA
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); = MethodHandles.arrayElementVarHandle(long[].class);
private static final int ABASE;
private static final int ASHIFT;
private final long[] array; 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 * Creates a new AtomicLongArray of the given length, with all
* elements initially zero. * 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 * @param i the index
* @return the current value * @return the current value
*/ */
public final long get(int i) { public final long get(int i) {
return getRaw(checkedByteOffset(i)); return (long)AA.getVolatile(array, i);
}
private long getRaw(long offset) {
return U.getLongVolatile(array, offset);
} }
/** /**
* 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 i the index
* @param newValue the new value * @param newValue the new value
*/ */
public final void set(int i, long newValue) { 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 i the index
* @param newValue the new value * @param newValue the new value
* @since 1.6 * @since 1.6
*/ */
public final void lazySet(int i, long newValue) { 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 * Atomically sets the element at index {@code i} to {@code
* and returns the old value. * newValue} and returns the old value,
* with memory effects as specified by {@link VarHandle#getAndSet}.
* *
* @param i the index * @param i the index
* @param newValue the new value * @param newValue the new value
* @return the previous value * @return the previous value
*/ */
public final long getAndSet(int i, long newValue) { 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 * Atomically sets the element at index {@code i} to {@code newValue}
* updated value if the current value {@code ==} the expected value. * if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
* *
* @param i the index * @param i the index
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful. False return indicates that * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value. * the actual value was not equal to the expected value.
*/ */
public final boolean compareAndSet(int i, long expect, long update) { public final boolean compareAndSet(int i, long expectedValue, long newValue) {
return compareAndSetRaw(checkedByteOffset(i), expect, update); return AA.compareAndSet(array, i, expectedValue, newValue);
}
private boolean compareAndSetRaw(long offset, long expect, long update) {
return U.compareAndSwapLong(array, offset, expect, update);
} }
/** /**
* Atomically sets the element at position {@code i} to the given * Possibly atomically sets the element at index {@code i} to
* updated value if the current value {@code ==} the expected value. * {@code newValue} if the element's 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 i the index * @param i the index
* @param expect the expected value * @param expectedValue the expected value
* @param update the new value * @param newValue the new value
* @return {@code true} if successful * @return {@code true} if successful
*/ */
public final boolean weakCompareAndSet(int i, long expect, long update) { public final boolean weakCompareAndSet(int i, long expectedValue, long newValue) {
return compareAndSet(i, expect, update); 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 * @param i the index
* @return the previous value * @return the previous value
*/ */
public final long getAndIncrement(int i) { 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 * @param i the index
* @return the previous value * @return the previous value
*/ */
public final long getAndDecrement(int i) { 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 i the index
* @param delta the value to add * @param delta the value to add
* @return the previous value * @return the previous value
*/ */
public final long getAndAdd(int i, long delta) { 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 * @param i the index
* @return the updated value * @return the updated value
*/ */
public final long incrementAndGet(int i) { 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 * @param i the index
* @return the updated value * @return the updated value
*/ */
public final long decrementAndGet(int i) { 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 i the index
* @param delta the value to add * @param delta the value to add
* @return the updated value * @return the updated value
*/ */
public long addAndGet(int i, long delta) { 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 * @since 1.8
*/ */
public final long getAndUpdate(int i, LongUnaryOperator updateFunction) { public final long getAndUpdate(int i, LongUnaryOperator updateFunction) {
long offset = checkedByteOffset(i); long prev = get(i), next = 0L;
long prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = updateFunction.applyAsLong(prev); next = updateFunction.applyAsLong(prev);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return prev; return prev;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
@ -280,23 +270,25 @@ public class AtomicLongArray implements java.io.Serializable {
* @since 1.8 * @since 1.8
*/ */
public final long updateAndGet(int i, LongUnaryOperator updateFunction) { public final long updateAndGet(int i, LongUnaryOperator updateFunction) {
long offset = checkedByteOffset(i); long prev = get(i), next = 0L;
long prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = updateFunction.applyAsLong(prev); next = updateFunction.applyAsLong(prev);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return next; return next;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
* Atomically updates the element at index {@code i} with the * Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and * results of applying the given function to the current and given
* given values, returning the previous value. The function should * values, returning the previous value. The function should be
* be side-effect-free, since it may be re-applied when attempted * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is * updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first * applied with the current value of the element at index {@code i}
* argument, and the given update as the second argument. * as its first argument, and the given update as the second
* argument.
* *
* @param i the index * @param i the index
* @param x the update value * @param x the update value
@ -306,23 +298,25 @@ public class AtomicLongArray implements java.io.Serializable {
*/ */
public final long getAndAccumulate(int i, long x, public final long getAndAccumulate(int i, long x,
LongBinaryOperator accumulatorFunction) { LongBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i); long prev = get(i), next = 0L;
long prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = accumulatorFunction.applyAsLong(prev, x); next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return prev; return prev;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
* Atomically updates the element at index {@code i} with the * Atomically updates the element at index {@code i} with the
* results of applying the given function to the current and * results of applying the given function to the current and given
* given values, returning the updated value. The function should * values, returning the updated value. The function should be
* be side-effect-free, since it may be re-applied when attempted * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is * updates fail due to contention among threads. The function is
* applied with the current value at index {@code i} as its first * applied with the current value of the element at index {@code i}
* argument, and the given update as the second argument. * as its first argument, and the given update as the second
* argument.
* *
* @param i the index * @param i the index
* @param x the update value * @param x the update value
@ -332,13 +326,14 @@ public class AtomicLongArray implements java.io.Serializable {
*/ */
public final long accumulateAndGet(int i, long x, public final long accumulateAndGet(int i, long x,
LongBinaryOperator accumulatorFunction) { LongBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i); long prev = get(i), next = 0L;
long prev, next; for (boolean haveNext = false;;) {
do { if (!haveNext)
prev = getRaw(offset);
next = accumulatorFunction.applyAsLong(prev, x); next = accumulatorFunction.applyAsLong(prev, x);
} while (!compareAndSetRaw(offset, prev, next)); if (weakCompareAndSetVolatile(i, prev, next))
return next; return next;
haveNext = (prev == (prev = get(i)));
}
} }
/** /**
@ -353,11 +348,189 @@ public class AtomicLongArray implements java.io.Serializable {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append('['); b.append('[');
for (int i = 0; ; i++) { for (int i = 0; ; i++) {
b.append(getRaw(byteOffset(i))); b.append(get(i));
if (i == iMax) if (i == iMax)
return b.append(']').toString(); return b.append(']').toString();
b.append(',').append(' '); 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.security.PrivilegedExceptionAction;
import java.util.function.LongBinaryOperator; import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator; import java.util.function.LongUnaryOperator;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
@ -153,8 +154,8 @@ public abstract class AtomicLongFieldUpdater<T> {
public abstract void lazySet(T obj, long newValue); public abstract void lazySet(T obj, long newValue);
/** /**
* Gets the current value held in the field of the given object managed * Returns the current value held in the field of the given object
* by this updater. * managed by this updater.
* *
* @param obj An object whose field to get * @param obj An object whose field to get
* @return the current value * @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 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; private final long offset;
/** /**
* if field is protected, the subclass constructing updater, else * 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 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; private final long offset;
/** /**
* if field is protected, the subclass constructing updater, else * 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