mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-24 04:18:11 +00:00
Merge
This commit is contained in:
commit
15fafcc1fc
1
.hgtags
1
.hgtags
@ -370,3 +370,4 @@ d53037a90c441cb528dc41c30827985de0e67c62 jdk-9+123
|
||||
3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125
|
||||
03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126
|
||||
8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127
|
||||
5bf88dce615f6804f9e101a96ffa7c9dfb4fbbbe jdk-9+128
|
||||
|
||||
@ -370,3 +370,4 @@ f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124
|
||||
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
|
||||
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
|
||||
8fa686245bd2a072ece3392743460030f0854520 jdk-9+127
|
||||
b30ae794d974d7dd3eb4e84203f70021823fa6c6 jdk-9+128
|
||||
|
||||
@ -345,6 +345,9 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS],
|
||||
# Disable special log output when a debug build is used as Boot JDK...
|
||||
ADD_JVM_ARG_IF_OK([-XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput],boot_jdk_jvmargs,[$JAVA])
|
||||
|
||||
# Force en-US environment
|
||||
ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA])
|
||||
|
||||
# Apply user provided options.
|
||||
ADD_JVM_ARG_IF_OK([$with_boot_jdk_jvmargs],boot_jdk_jvmargs,[$JAVA])
|
||||
|
||||
|
||||
@ -5094,7 +5094,7 @@ VS_SDK_PLATFORM_NAME_2013=
|
||||
#CUSTOM_AUTOCONF_INCLUDE
|
||||
|
||||
# Do not change or remove the following line, it is needed for consistency checks:
|
||||
DATE_WHEN_GENERATED=1467960715
|
||||
DATE_WHEN_GENERATED=1469202305
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
@ -65048,6 +65048,23 @@ $as_echo_n "checking flags for boot jdk java command ... " >&6; }
|
||||
fi
|
||||
|
||||
|
||||
# Force en-US environment
|
||||
|
||||
$ECHO "Check if jvm arg is ok: -Duser.language=en -Duser.country=US" >&5
|
||||
$ECHO "Command: $JAVA -Duser.language=en -Duser.country=US -version" >&5
|
||||
OUTPUT=`$JAVA -Duser.language=en -Duser.country=US -version 2>&1`
|
||||
FOUND_WARN=`$ECHO "$OUTPUT" | $GREP -i warn`
|
||||
FOUND_VERSION=`$ECHO $OUTPUT | $GREP " version \""`
|
||||
if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then
|
||||
boot_jdk_jvmargs="$boot_jdk_jvmargs -Duser.language=en -Duser.country=US"
|
||||
JVM_ARG_OK=true
|
||||
else
|
||||
$ECHO "Arg failed:" >&5
|
||||
$ECHO "$OUTPUT" >&5
|
||||
JVM_ARG_OK=false
|
||||
fi
|
||||
|
||||
|
||||
# Apply user provided options.
|
||||
|
||||
$ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5
|
||||
|
||||
@ -370,3 +370,4 @@ e33a34cc551907617d8129c4faaf1a5a7e61d21c jdk-9+123
|
||||
1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
|
||||
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
|
||||
8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127
|
||||
1f093d3f8cd99cd37c3b0af4cf5c3bffaa9c8b98 jdk-9+128
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -22,7 +21,6 @@
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.sun.corba.se.impl.activation;
|
||||
|
||||
@ -34,21 +34,13 @@ import java.security.PermissionCollection;
|
||||
import java.security.Policy;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Collection;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
@ -165,8 +157,18 @@ public final class ORBUtility {
|
||||
* Return default ValueHandler
|
||||
*/
|
||||
public static ValueHandler createValueHandler() {
|
||||
ValueHandler vh;
|
||||
try {
|
||||
vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() {
|
||||
public ValueHandler run() throws Exception {
|
||||
return Util.createValueHandler();
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw new InternalError(e.getCause());
|
||||
}
|
||||
return vh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it was accurately determined that the remote ORB is
|
||||
@ -664,7 +666,16 @@ public final class ORBUtility {
|
||||
* ValueHandler.
|
||||
*/
|
||||
public static byte getMaxStreamFormatVersion() {
|
||||
ValueHandler vh = Util.createValueHandler();
|
||||
ValueHandler vh;
|
||||
try {
|
||||
vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() {
|
||||
public ValueHandler run() throws Exception {
|
||||
return Util.createValueHandler();
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw new InternalError(e.getCause());
|
||||
}
|
||||
|
||||
if (!(vh instanceof javax.rmi.CORBA.ValueHandlerMultiFormat))
|
||||
return ORBConstants.STREAM_FORMAT_VERSION_1;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -45,6 +45,7 @@ import javax.rmi.CORBA.Tie;
|
||||
import java.rmi.Remote;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.SerializablePermission;
|
||||
import java.net.MalformedURLException ;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
@ -195,6 +196,8 @@ public class Util {
|
||||
*/
|
||||
public static ValueHandler createValueHandler() {
|
||||
|
||||
isCustomSerializationPermitted();
|
||||
|
||||
if (utilDelegate != null) {
|
||||
return utilDelegate.createValueHandler();
|
||||
}
|
||||
@ -337,6 +340,7 @@ public class Util {
|
||||
// security reasons. If you know a better solution how to share this code
|
||||
// then remove it from PortableRemoteObject. Also in Stub.java
|
||||
private static Object createDelegate(String classKey) {
|
||||
|
||||
String className = (String)
|
||||
AccessController.doPrivileged(new GetPropertyAction(classKey));
|
||||
if (className == null) {
|
||||
@ -345,7 +349,6 @@ public class Util {
|
||||
className = props.getProperty(classKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (className == null) {
|
||||
return new com.sun.corba.se.impl.javax.rmi.CORBA.Util();
|
||||
}
|
||||
@ -389,4 +392,14 @@ public class Util {
|
||||
new GetORBPropertiesFileAction());
|
||||
}
|
||||
|
||||
private static void isCustomSerializationPermitted() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if ( sm != null) {
|
||||
// check that a serialization permission has been
|
||||
// set to allow the loading of the Util delegate
|
||||
// which provides access to custom ValueHandler
|
||||
sm.checkPermission(new SerializablePermission(
|
||||
"enableCustomValueHandler"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -530,3 +530,4 @@ af6b4ad908e732d23021f12e8322b204433d5cf6 jdk-9+122
|
||||
bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
|
||||
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
|
||||
352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
|
||||
22bf6db9767b1b3a1994cbf32eb3331f31ae2093 jdk-9+128
|
||||
|
||||
@ -270,6 +270,10 @@ public:
|
||||
|
||||
// For reading from/writing to the CDS archive
|
||||
void serialize(SerializeClosure* soc);
|
||||
|
||||
uintx base_address() {
|
||||
return (uintx) _base_address;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -238,6 +238,29 @@ Symbol* SymbolTable::lookup(int index, const char* name,
|
||||
}
|
||||
}
|
||||
|
||||
u4 SymbolTable::encode_shared(Symbol* sym) {
|
||||
assert(DumpSharedSpaces, "called only during dump time");
|
||||
uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
|
||||
uintx offset = uintx(sym) - base_address;
|
||||
assert(offset < 0x7fffffff, "sanity");
|
||||
return u4(offset);
|
||||
}
|
||||
|
||||
Symbol* SymbolTable::decode_shared(u4 offset) {
|
||||
assert(!DumpSharedSpaces, "called only during runtime");
|
||||
uintx base_address = _shared_table.base_address();
|
||||
Symbol* sym = (Symbol*)(base_address + offset);
|
||||
|
||||
#ifndef PRODUCT
|
||||
const char* s = (const char*)sym->bytes();
|
||||
int len = sym->utf8_length();
|
||||
unsigned int hash = hash_symbol(s, len);
|
||||
assert(sym == lookup_shared(s, len, hash), "must be shared symbol");
|
||||
#endif
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
// Pick hashing algorithm.
|
||||
unsigned int SymbolTable::hash_symbol(const char* s, int len) {
|
||||
return use_alternate_hashcode() ?
|
||||
|
||||
@ -253,6 +253,8 @@ public:
|
||||
|
||||
// Sharing
|
||||
static void serialize(SerializeClosure* soc);
|
||||
static u4 encode_shared(Symbol* sym);
|
||||
static Symbol* decode_shared(u4 offset);
|
||||
|
||||
// Rehash the symbol table if it gets out of balance
|
||||
static void rehash_table();
|
||||
|
||||
@ -78,7 +78,19 @@ public:
|
||||
TRAPS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void serialize(SerializeClosure* soc) {}
|
||||
|
||||
// The (non-application) CDS implementation supports only classes in the boot
|
||||
// class loader, which ensures that the verification constraints are the same
|
||||
// during archive creation time and runtime. Thus we can do the constraint checks
|
||||
// entirely during archive creation time.
|
||||
static bool add_verification_constraint(Klass* k, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected,
|
||||
bool from_is_array, bool from_is_object) {return false;}
|
||||
static void finalize_verification_constraints() {}
|
||||
static void check_verification_constraints(instanceKlassHandle klass,
|
||||
TRAPS) {}
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/verificationType.hpp"
|
||||
#include "classfile/verifier.hpp"
|
||||
|
||||
@ -41,6 +42,39 @@ VerificationType VerificationType::from_tag(u1 tag) {
|
||||
}
|
||||
}
|
||||
|
||||
bool VerificationType::resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, TRAPS) {
|
||||
Klass* obj = SystemDictionary::resolve_or_fail(
|
||||
name, Handle(THREAD, klass->class_loader()),
|
||||
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Verifier::trace_class_resolution(obj, klass());
|
||||
}
|
||||
|
||||
KlassHandle this_class(THREAD, obj);
|
||||
|
||||
if (this_class->is_interface() && (!from_field_is_protected ||
|
||||
from_name != vmSymbols::java_lang_Object())) {
|
||||
// If we are not trying to access a protected field or method in
|
||||
// java.lang.Object then, for arrays, we only allow assignability
|
||||
// to interfaces java.lang.Cloneable and java.io.Serializable.
|
||||
// Otherwise, we treat interfaces as java.lang.Object.
|
||||
return !from_is_array ||
|
||||
this_class == SystemDictionary::Cloneable_klass() ||
|
||||
this_class == SystemDictionary::Serializable_klass();
|
||||
} else if (from_is_object) {
|
||||
Klass* from_class = SystemDictionary::resolve_or_fail(
|
||||
from_name, Handle(THREAD, klass->class_loader()),
|
||||
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Verifier::trace_class_resolution(from_class, klass());
|
||||
}
|
||||
return InstanceKlass::cast(from_class)->is_subclass_of(this_class());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VerificationType::is_reference_assignable_from(
|
||||
const VerificationType& from, ClassVerifier* context,
|
||||
bool from_field_is_protected, TRAPS) const {
|
||||
@ -58,33 +92,17 @@ bool VerificationType::is_reference_assignable_from(
|
||||
// any object or array is assignable to java.lang.Object
|
||||
return true;
|
||||
}
|
||||
Klass* obj = SystemDictionary::resolve_or_fail(
|
||||
name(), Handle(THREAD, klass->class_loader()),
|
||||
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Verifier::trace_class_resolution(obj, klass());
|
||||
|
||||
if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass(),
|
||||
name(), from.name(), from_field_is_protected, from.is_array(),
|
||||
from.is_object())) {
|
||||
// If add_verification_constraint() returns true, the resolution/check should be
|
||||
// delayed until runtime.
|
||||
return true;
|
||||
}
|
||||
|
||||
KlassHandle this_class(THREAD, obj);
|
||||
|
||||
if (this_class->is_interface() && (!from_field_is_protected ||
|
||||
from.name() != vmSymbols::java_lang_Object())) {
|
||||
// If we are not trying to access a protected field or method in
|
||||
// java.lang.Object then, for arrays, we only allow assignability
|
||||
// to interfaces java.lang.Cloneable and java.io.Serializable.
|
||||
// Otherwise, we treat interfaces as java.lang.Object.
|
||||
return !from.is_array() ||
|
||||
this_class == SystemDictionary::Cloneable_klass() ||
|
||||
this_class == SystemDictionary::Serializable_klass();
|
||||
} else if (from.is_object()) {
|
||||
Klass* from_class = SystemDictionary::resolve_or_fail(
|
||||
from.name(), Handle(THREAD, klass->class_loader()),
|
||||
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Verifier::trace_class_resolution(from_class, klass());
|
||||
}
|
||||
return InstanceKlass::cast(from_class)->is_subclass_of(this_class());
|
||||
}
|
||||
return resolve_and_check_assignability(klass(), name(), from.name(),
|
||||
from_field_is_protected, from.is_array(), from.is_object(), THREAD);
|
||||
} else if (is_array() && from.is_array()) {
|
||||
VerificationType comp_this = get_component(context, CHECK_false);
|
||||
VerificationType comp_from = from.get_component(context, CHECK_false);
|
||||
|
||||
@ -333,6 +333,12 @@ class VerificationType VALUE_OBJ_CLASS_SPEC {
|
||||
bool is_reference_assignable_from(
|
||||
const VerificationType&, ClassVerifier*, bool from_field_is_protected,
|
||||
TRAPS) const;
|
||||
|
||||
public:
|
||||
static bool resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected,
|
||||
bool from_is_array, bool from_is_object,
|
||||
TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CLASSFILE_VERIFICATIONTYPE_HPP
|
||||
|
||||
@ -2377,9 +2377,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
|
||||
case Bytecodes::_ifnonnull:
|
||||
target = bcs.dest();
|
||||
if (visited_branches->contains(bci)) {
|
||||
if (bci_stack->is_empty()) return true;
|
||||
// Pop a bytecode starting offset and scan from there.
|
||||
bcs.set_start(bci_stack->pop());
|
||||
if (bci_stack->is_empty()) {
|
||||
if (handler_stack->is_empty()) {
|
||||
return true;
|
||||
} else {
|
||||
// Parse the catch handlers for try blocks containing athrow.
|
||||
bcs.set_start(handler_stack->pop());
|
||||
}
|
||||
} else {
|
||||
// Pop a bytecode starting offset and scan from there.
|
||||
bcs.set_start(bci_stack->pop());
|
||||
}
|
||||
} else {
|
||||
if (target > bci) { // forward branch
|
||||
if (target >= code_length) return false;
|
||||
@ -2402,9 +2410,17 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
|
||||
case Bytecodes::_goto_w:
|
||||
target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
|
||||
if (visited_branches->contains(bci)) {
|
||||
if (bci_stack->is_empty()) return true;
|
||||
// Been here before, pop new starting offset from stack.
|
||||
bcs.set_start(bci_stack->pop());
|
||||
if (bci_stack->is_empty()) {
|
||||
if (handler_stack->is_empty()) {
|
||||
return true;
|
||||
} else {
|
||||
// Parse the catch handlers for try blocks containing athrow.
|
||||
bcs.set_start(handler_stack->pop());
|
||||
}
|
||||
} else {
|
||||
// Been here before, pop new starting offset from stack.
|
||||
bcs.set_start(bci_stack->pop());
|
||||
}
|
||||
} else {
|
||||
if (target >= code_length) return false;
|
||||
// Continue scanning from the target onward.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,12 +31,12 @@ Bytecodes::Code RawBytecodeStream::raw_next_special(Bytecodes::Code code) {
|
||||
// set next bytecode position
|
||||
address bcp = RawBytecodeStream::bcp();
|
||||
address end = method()->code_base() + end_bci();
|
||||
int l = Bytecodes::raw_special_length_at(bcp, end);
|
||||
if (l <= 0 || (_bci + l) > _end_bci) {
|
||||
int len = Bytecodes::raw_special_length_at(bcp, end);
|
||||
// Very large tableswitch or lookupswitch size can cause _next_bci to overflow.
|
||||
if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) {
|
||||
code = Bytecodes::_illegal;
|
||||
} else {
|
||||
_next_bci += l;
|
||||
assert(_bci < _next_bci, "length must be > 0");
|
||||
_next_bci += len;
|
||||
// set attributes
|
||||
_is_wide = false;
|
||||
// check for special (uncommon) cases
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -135,12 +135,15 @@ class RawBytecodeStream: public BaseBytecodeStream {
|
||||
code = Bytecodes::code_or_bp_at(bcp);
|
||||
|
||||
// set next bytecode position
|
||||
int l = Bytecodes::length_for(code);
|
||||
if (l > 0 && (_bci + l) <= _end_bci) {
|
||||
int len = Bytecodes::length_for(code);
|
||||
if (len > 0 && (_bci <= _end_bci - len)) {
|
||||
assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch
|
||||
&& code != Bytecodes::_lookupswitch, "can't be special bytecode");
|
||||
_is_wide = false;
|
||||
_next_bci += l;
|
||||
_next_bci += len;
|
||||
if (_next_bci <= _bci) { // Check for integer overflow
|
||||
code = Bytecodes::_illegal;
|
||||
}
|
||||
_raw_code = code;
|
||||
return code;
|
||||
} else {
|
||||
@ -189,19 +192,23 @@ class BytecodeStream: public BaseBytecodeStream {
|
||||
// note that we cannot advance before having the
|
||||
// tty bytecode otherwise the stepping is wrong!
|
||||
// (carefull: length_for(...) must be used first!)
|
||||
int l = Bytecodes::length_for(code);
|
||||
if (l == 0) l = Bytecodes::length_at(_method(), bcp);
|
||||
_next_bci += l;
|
||||
assert(_bci < _next_bci, "length must be > 0");
|
||||
// set attributes
|
||||
_is_wide = false;
|
||||
// check for special (uncommon) cases
|
||||
if (code == Bytecodes::_wide) {
|
||||
raw_code = (Bytecodes::Code)bcp[1];
|
||||
code = raw_code; // wide BCs are always Java-normal
|
||||
_is_wide = true;
|
||||
int len = Bytecodes::length_for(code);
|
||||
if (len == 0) len = Bytecodes::length_at(_method(), bcp);
|
||||
if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) {
|
||||
raw_code = code = Bytecodes::_illegal;
|
||||
} else {
|
||||
_next_bci += len;
|
||||
assert(_bci < _next_bci, "length must be > 0");
|
||||
// set attributes
|
||||
_is_wide = false;
|
||||
// check for special (uncommon) cases
|
||||
if (code == Bytecodes::_wide) {
|
||||
raw_code = (Bytecodes::Code)bcp[1];
|
||||
code = raw_code; // wide BCs are always Java-normal
|
||||
_is_wide = true;
|
||||
}
|
||||
assert(Bytecodes::is_java_code(code), "sanity check");
|
||||
}
|
||||
assert(Bytecodes::is_java_code(code), "sanity check");
|
||||
}
|
||||
_raw_code = raw_code;
|
||||
_code = code;
|
||||
|
||||
@ -60,6 +60,7 @@ bool MetaspaceShared::_link_classes_made_progress;
|
||||
bool MetaspaceShared::_check_classes_made_progress;
|
||||
bool MetaspaceShared::_has_error_classes;
|
||||
bool MetaspaceShared::_archive_loading_failed = false;
|
||||
bool MetaspaceShared::_remapped_readwrite = false;
|
||||
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
|
||||
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
|
||||
SharedMiscRegion MetaspaceShared::_mc;
|
||||
@ -806,6 +807,10 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the verification constraints from C_HEAP-alloced GrowableArrays to RO-alloced
|
||||
// Arrays
|
||||
SystemDictionaryShared::finalize_verification_constraints();
|
||||
}
|
||||
|
||||
void MetaspaceShared::prepare_for_dumping() {
|
||||
@ -1181,6 +1186,7 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
|
||||
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
|
||||
return false;
|
||||
}
|
||||
_remapped_readwrite = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -125,6 +125,7 @@ class MetaspaceShared : AllStatic {
|
||||
static bool _check_classes_made_progress;
|
||||
static bool _has_error_classes;
|
||||
static bool _archive_loading_failed;
|
||||
static bool _remapped_readwrite;
|
||||
static address _cds_i2i_entry_code_buffers;
|
||||
static size_t _cds_i2i_entry_code_buffers_size;
|
||||
|
||||
@ -205,6 +206,10 @@ class MetaspaceShared : AllStatic {
|
||||
// sharing is enabled. Simply returns true if sharing is not enabled
|
||||
// or if the remapping has already been done by a prior call.
|
||||
static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true);
|
||||
static bool remapped_readwrite() {
|
||||
CDS_ONLY(return _remapped_readwrite);
|
||||
NOT_CDS(return false);
|
||||
}
|
||||
|
||||
static void print_shared_spaces();
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "classfile/classFileStream.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/verifier.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "code/dependencyContext.hpp"
|
||||
@ -597,6 +598,8 @@ bool InstanceKlass::link_class_impl(
|
||||
|
||||
// also sets rewritten
|
||||
this_k->rewrite_class(CHECK_false);
|
||||
} else if (this_k->is_shared()) {
|
||||
SystemDictionaryShared::check_verification_constraints(this_k, CHECK_false);
|
||||
}
|
||||
|
||||
// relocate jsrs and link methods after they are all rewritten
|
||||
@ -606,7 +609,12 @@ bool InstanceKlass::link_class_impl(
|
||||
// methods have been rewritten since rewrite may
|
||||
// fabricate new Method*s.
|
||||
// also does loader constraint checking
|
||||
if (!this_k()->is_shared()) {
|
||||
//
|
||||
// initialize_vtable and initialize_itable need to be rerun for
|
||||
// a shared class if the class is not loaded by the NULL classloader.
|
||||
ClassLoaderData * loader_data = this_k->class_loader_data();
|
||||
if (!(this_k->is_shared() &&
|
||||
loader_data->is_the_null_class_loader_data())) {
|
||||
ResourceMark rm(THREAD);
|
||||
this_k->vtable()->initialize_vtable(true, CHECK_false);
|
||||
this_k->itable()->initialize_itable(true, CHECK_false);
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "gc/shared/gcLocker.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "memory/metaspaceShared.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.inline.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
@ -42,6 +43,10 @@ inline InstanceKlass* klassVtable::ik() const {
|
||||
return InstanceKlass::cast(_klass());
|
||||
}
|
||||
|
||||
bool klassVtable::is_preinitialized_vtable() {
|
||||
return _klass->is_shared() && !MetaspaceShared::remapped_readwrite();
|
||||
}
|
||||
|
||||
|
||||
// this function computes the vtable size (including the size needed for miranda
|
||||
// methods) and the number of miranda methods in this class.
|
||||
@ -126,6 +131,12 @@ int klassVtable::index_of(Method* m, int len) const {
|
||||
int klassVtable::initialize_from_super(KlassHandle super) {
|
||||
if (super.is_null()) {
|
||||
return 0;
|
||||
} else if (is_preinitialized_vtable()) {
|
||||
// A shared class' vtable is preinitialized at dump time. No need to copy
|
||||
// methods from super class for shared class, as that was already done
|
||||
// during archiving time. However, if Jvmti has redefined a class,
|
||||
// copy super class's vtable in case the super class has changed.
|
||||
return super->vtable()->length();
|
||||
} else {
|
||||
// copy methods from superKlass
|
||||
klassVtable* superVtable = super->vtable();
|
||||
@ -152,6 +163,8 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
KlassHandle super (THREAD, klass()->java_super());
|
||||
int nofNewEntries = 0;
|
||||
|
||||
bool is_shared = _klass->is_shared();
|
||||
|
||||
if (!klass()->is_array_klass()) {
|
||||
ResourceMark rm(THREAD);
|
||||
log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string());
|
||||
@ -164,6 +177,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
#endif
|
||||
|
||||
if (Universe::is_bootstrapping()) {
|
||||
assert(!is_shared, "sanity");
|
||||
// just clear everything
|
||||
for (int i = 0; i < _length; i++) table()[i].clear();
|
||||
return;
|
||||
@ -203,6 +217,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
if (len > 0) {
|
||||
Array<int>* def_vtable_indices = NULL;
|
||||
if ((def_vtable_indices = ik()->default_vtable_indices()) == NULL) {
|
||||
assert(!is_shared, "shared class def_vtable_indices does not exist");
|
||||
def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK);
|
||||
} else {
|
||||
assert(def_vtable_indices->length() == len, "reinit vtable len?");
|
||||
@ -217,7 +232,15 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
// needs new entry
|
||||
if (needs_new_entry) {
|
||||
put_method_at(mh(), initialized);
|
||||
def_vtable_indices->at_put(i, initialized); //set vtable index
|
||||
if (is_preinitialized_vtable()) {
|
||||
// At runtime initialize_vtable is rerun for a shared class
|
||||
// (loaded by the non-boot loader) as part of link_class_impl().
|
||||
// The dumptime vtable index should be the same as the runtime index.
|
||||
assert(def_vtable_indices->at(i) == initialized,
|
||||
"dump time vtable index is different from runtime index");
|
||||
} else {
|
||||
def_vtable_indices->at_put(i, initialized); //set vtable index
|
||||
}
|
||||
initialized++;
|
||||
}
|
||||
}
|
||||
@ -378,7 +401,8 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
}
|
||||
|
||||
// we need a new entry if there is no superclass
|
||||
if (klass->super() == NULL) {
|
||||
Klass* super = klass->super();
|
||||
if (super == NULL) {
|
||||
return allocate_new;
|
||||
}
|
||||
|
||||
@ -407,7 +431,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
|
||||
Symbol* target_classname = target_klass->name();
|
||||
for(int i = 0; i < super_vtable_len; i++) {
|
||||
Method* super_method = method_at(i);
|
||||
Method* super_method;
|
||||
if (is_preinitialized_vtable()) {
|
||||
// If this is a shared class, the vtable is already in the final state (fully
|
||||
// initialized). Need to look at the super's vtable.
|
||||
klassVtable* superVtable = super->vtable();
|
||||
super_method = superVtable->method_at(i);
|
||||
} else {
|
||||
super_method = method_at(i);
|
||||
}
|
||||
// Check if method name matches
|
||||
if (super_method->name() == name && super_method->signature() == signature) {
|
||||
|
||||
@ -475,7 +507,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
target_method()->set_vtable_index(i);
|
||||
} else {
|
||||
if (def_vtable_indices != NULL) {
|
||||
def_vtable_indices->at_put(default_index, i);
|
||||
if (is_preinitialized_vtable()) {
|
||||
// At runtime initialize_vtable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader.
|
||||
// The dumptime vtable index should be the same as the runtime index.
|
||||
assert(def_vtable_indices->at(default_index) == i,
|
||||
"dump time vtable index is different from runtime index");
|
||||
} else {
|
||||
def_vtable_indices->at_put(default_index, i);
|
||||
}
|
||||
}
|
||||
assert(super_method->is_default_method() || super_method->is_overpass()
|
||||
|| super_method->is_abstract(), "default override error");
|
||||
@ -490,17 +530,26 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
}
|
||||
|
||||
void klassVtable::put_method_at(Method* m, int index) {
|
||||
if (log_develop_is_enabled(Trace, vtables)) {
|
||||
ResourceMark rm;
|
||||
outputStream* logst = Log(vtables)::trace_stream();
|
||||
const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>";
|
||||
logst->print("adding %s at index %d, flags: ", sig, index);
|
||||
if (m != NULL) {
|
||||
m->print_linkage_flags(logst);
|
||||
if (is_preinitialized_vtable()) {
|
||||
// At runtime initialize_vtable is rerun as part of link_class_impl()
|
||||
// for shared class loaded by the non-boot loader to obtain the loader
|
||||
// constraints based on the runtime classloaders' context. The dumptime
|
||||
// method at the vtable index should be the same as the runtime method.
|
||||
assert(table()[index].method() == m,
|
||||
"archived method is different from the runtime method");
|
||||
} else {
|
||||
if (log_develop_is_enabled(Trace, vtables)) {
|
||||
ResourceMark rm;
|
||||
outputStream* logst = Log(vtables)::trace_stream();
|
||||
const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>";
|
||||
logst->print("adding %s at index %d, flags: ", sig, index);
|
||||
if (m != NULL) {
|
||||
m->print_linkage_flags(logst);
|
||||
}
|
||||
logst->cr();
|
||||
}
|
||||
logst->cr();
|
||||
table()[index].set(m);
|
||||
}
|
||||
table()[index].set(m);
|
||||
}
|
||||
|
||||
// Find out if a method "m" with superclass "super", loader "classloader" and
|
||||
@ -950,7 +999,15 @@ bool klassVtable::is_initialized() {
|
||||
void itableMethodEntry::initialize(Method* m) {
|
||||
if (m == NULL) return;
|
||||
|
||||
_method = m;
|
||||
if (MetaspaceShared::is_in_shared_space((void*)&_method) &&
|
||||
!MetaspaceShared::remapped_readwrite()) {
|
||||
// At runtime initialize_itable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader.
|
||||
// The dumptime itable method entry should be the same as the runtime entry.
|
||||
assert(_method == m, "sanity");
|
||||
} else {
|
||||
_method = m;
|
||||
}
|
||||
}
|
||||
|
||||
klassItable::klassItable(instanceKlassHandle klass) {
|
||||
@ -1054,7 +1111,11 @@ int klassItable::assign_itable_indices_for_interface(Klass* klass) {
|
||||
logst->cr();
|
||||
}
|
||||
if (!m->has_vtable_index()) {
|
||||
assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable");
|
||||
// A shared method could have an initialized itable_index that
|
||||
// is < 0.
|
||||
assert(m->vtable_index() == Method::pending_itable_index ||
|
||||
m->is_shared(),
|
||||
"set by initialize_vtable");
|
||||
m->set_itable_index(ime_num);
|
||||
// Progress to next itable entry
|
||||
ime_num++;
|
||||
@ -1248,7 +1309,6 @@ void klassItable::dump_itable() {
|
||||
}
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
|
||||
// Setup
|
||||
class InterfaceVisiterClosure : public StackObj {
|
||||
public:
|
||||
|
||||
@ -153,6 +153,19 @@ class klassVtable : public ResourceObj {
|
||||
Array<Klass*>* local_interfaces);
|
||||
void verify_against(outputStream* st, klassVtable* vt, int index);
|
||||
inline InstanceKlass* ik() const;
|
||||
// When loading a class from CDS archive at run time, and no class redefintion
|
||||
// has happened, it is expected that the class's itable/vtables are
|
||||
// laid out exactly the same way as they had been during dump time.
|
||||
// Therefore, in klassVtable::initialize_[iv]table, we do not layout the
|
||||
// tables again. Instead, we only rerun the process to create/check
|
||||
// the class loader constraints. In non-product builds, we add asserts to
|
||||
// guarantee that the table's layout would be the same as at dump time.
|
||||
//
|
||||
// If JVMTI redefines any class, the read-only shared memory are remapped
|
||||
// as read-write. A shared class' vtable/itable are re-initialized and
|
||||
// might have different layout due to class redefinition of the shared class
|
||||
// or its super types.
|
||||
bool is_preinitialized_vtable();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -313,6 +313,33 @@ void Method::remove_unshareable_info() {
|
||||
unlink_method();
|
||||
}
|
||||
|
||||
void Method::set_vtable_index(int index) {
|
||||
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
|
||||
// At runtime initialize_vtable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader to obtain the loader
|
||||
// constraints based on the runtime classloaders' context.
|
||||
return; // don't write into the shared class
|
||||
} else {
|
||||
_vtable_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
void Method::set_itable_index(int index) {
|
||||
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
|
||||
// At runtime initialize_itable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader to obtain the loader
|
||||
// constraints based on the runtime classloaders' context. The dumptime
|
||||
// itable index should be the same as the runtime index.
|
||||
assert(_vtable_index == itable_index_max - index,
|
||||
"archived itable index is different from runtime index");
|
||||
return; // don’t write into the shared class
|
||||
} else {
|
||||
_vtable_index = itable_index_max - index;
|
||||
}
|
||||
assert(valid_itable_index(), "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Method::was_executed_more_than(int n) {
|
||||
// Invocation counter is reset when the Method* is compiled.
|
||||
|
||||
@ -470,12 +470,12 @@ class Method : public Metadata {
|
||||
DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; })
|
||||
bool has_vtable_index() const { return _vtable_index >= 0; }
|
||||
int vtable_index() const { return _vtable_index; }
|
||||
void set_vtable_index(int index) { _vtable_index = index; }
|
||||
void set_vtable_index(int index);
|
||||
DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; })
|
||||
bool has_itable_index() const { return _vtable_index <= itable_index_max; }
|
||||
int itable_index() const { assert(valid_itable_index(), "");
|
||||
return itable_index_max - _vtable_index; }
|
||||
void set_itable_index(int index) { _vtable_index = itable_index_max - index; assert(valid_itable_index(), ""); }
|
||||
void set_itable_index(int index);
|
||||
|
||||
// interpreter entry
|
||||
address interpreter_entry() const { return _i2i_entry; }
|
||||
|
||||
@ -2405,8 +2405,13 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c
|
||||
Compile::AliasType* alias_type = C->alias_type(adr_type);
|
||||
assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here");
|
||||
|
||||
assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM ||
|
||||
alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown");
|
||||
// Only field, array element or unknown locations are supported.
|
||||
if (alias_type->adr_type() != TypeRawPtr::BOTTOM &&
|
||||
alias_type->adr_type() != TypeOopPtr::BOTTOM &&
|
||||
alias_type->basic_type() == T_ILLEGAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mismatched = false;
|
||||
BasicType bt = alias_type->basic_type();
|
||||
if (bt != T_ILLEGAL) {
|
||||
@ -2782,12 +2787,6 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
// Null check receiver.
|
||||
receiver = null_check(receiver);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build field offset expression.
|
||||
// We currently rely on the cookies produced by Unsafe.xxxFieldOffset
|
||||
// to be plain byte offsets, which are also the same as those accepted
|
||||
@ -2799,8 +2798,6 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
|
||||
const TypePtr *adr_type = _gvn.type(adr)->isa_ptr();
|
||||
|
||||
Compile::AliasType* alias_type = C->alias_type(adr_type);
|
||||
assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM ||
|
||||
alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown");
|
||||
BasicType bt = alias_type->basic_type();
|
||||
if (bt != T_ILLEGAL &&
|
||||
((bt == T_OBJECT || bt == T_ARRAY) != (type == T_OBJECT))) {
|
||||
@ -2832,6 +2829,12 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
// Null check receiver.
|
||||
receiver = null_check(receiver);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int alias_idx = C->get_alias_index(adr_type);
|
||||
|
||||
// Memory-model-wise, a LoadStore acts like a little synchronized
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64")
|
||||
* @library ../../../../../
|
||||
* @ignore 8161550
|
||||
* @modules java.base/jdk.internal.reflect
|
||||
* jdk.vm.ci/jdk.vm.ci.meta
|
||||
* jdk.vm.ci/jdk.vm.ci.runtime
|
||||
|
||||
134
hotspot/test/compiler/unsafe/OpaqueAccesses.java
Normal file
134
hotspot/test/compiler/unsafe/OpaqueAccesses.java
Normal 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");
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,7 @@
|
||||
* @requires vm.gc=="null"
|
||||
* @summary Verify that starting the VM with a small heap works
|
||||
* @library /testlibrary /test/lib /test/lib/share/classes
|
||||
* @ignore 8161552
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @modules java.management/sun.management
|
||||
* @build TestSmallHeap
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
* parallel collectors.
|
||||
* @requires vm.gc=="null"
|
||||
* @library /testlibrary /test/lib
|
||||
* @ignore 8161552
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @build TestParallelHeapSizeFlags TestMaxHeapSizeTools
|
||||
|
||||
@ -370,3 +370,4 @@ e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124
|
||||
493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125
|
||||
15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126
|
||||
bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127
|
||||
8a7681a9d70640ac7fbf05c28f53c1d51d8d00a1 jdk-9+128
|
||||
|
||||
@ -80,6 +80,14 @@ public final class XalanConstants {
|
||||
*/
|
||||
public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT =
|
||||
ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit";
|
||||
|
||||
/**
|
||||
* JDK node count limit in entities that limits the total number of nodes
|
||||
* in all of entity references.
|
||||
*/
|
||||
public static final String JDK_ENTITY_REPLACEMENT_LIMIT =
|
||||
ORACLE_JAXP_PROPERTY_PREFIX + "entityReplacementLimit";
|
||||
|
||||
/**
|
||||
* JDK maximum parameter entity size limit
|
||||
*/
|
||||
@ -136,6 +144,13 @@ public final class XalanConstants {
|
||||
* JDK maximum general entity size limit
|
||||
*/
|
||||
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit";
|
||||
|
||||
/**
|
||||
* JDK node count limit in entities that limits the total number of nodes
|
||||
* in all of entity references.
|
||||
*/
|
||||
public static final String SP_ENTITY_REPLACEMENT_LIMIT = "jdk.xml.entityReplacementLimit";
|
||||
|
||||
/**
|
||||
* JDK maximum parameter entity size limit
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -82,7 +82,9 @@ public final class XMLSecurityManager {
|
||||
MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH,
|
||||
XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0),
|
||||
MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT,
|
||||
XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000);
|
||||
XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000),
|
||||
ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", XalanConstants.JDK_ENTITY_REPLACEMENT_LIMIT,
|
||||
XalanConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000);
|
||||
|
||||
final String key;
|
||||
final String apiProperty;
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
/*
|
||||
* reserved comment block
|
||||
* DO NOT REMOVE OR ALTER!
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2001-2004 The Apache Software Foundation.
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@ -98,6 +98,10 @@ public interface Constants extends InstructionConstants {
|
||||
public static final int ACC_STATIC
|
||||
= com.sun.org.apache.bcel.internal.Constants.ACC_STATIC;
|
||||
|
||||
public static final String MODULE_SIG
|
||||
= "Ljava/lang/reflect/Module;";
|
||||
public static final String CLASS_SIG
|
||||
= "Ljava/lang/Class;";
|
||||
public static final String STRING_SIG
|
||||
= "Ljava/lang/String;";
|
||||
public static final String STRING_BUFFER_SIG
|
||||
@ -246,8 +250,12 @@ public interface Constants extends InstructionConstants {
|
||||
= "com.sun.org.apache.xalan.internal.xsltc.DOM";
|
||||
public static final String DOM_IMPL
|
||||
= "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl";
|
||||
public static final String SAX_IMPL
|
||||
public static final String SAX_IMPL
|
||||
= "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl";
|
||||
public static final String CLASS_CLASS
|
||||
= "java.lang.Class";
|
||||
public static final String MODULE_CLASS
|
||||
= "java.lang.reflect.Module";
|
||||
public static final String STRING_CLASS
|
||||
= "java.lang.String";
|
||||
public static final String OBJECT_CLASS
|
||||
@ -293,7 +301,7 @@ public interface Constants extends InstructionConstants {
|
||||
= "()D";
|
||||
|
||||
public static final String DOM_PNAME
|
||||
= "dom";
|
||||
= "dom";
|
||||
public static final String NODE_PNAME
|
||||
= "node";
|
||||
public static final String TRANSLET_OUTPUT_PNAME
|
||||
@ -335,6 +343,19 @@ public interface Constants extends InstructionConstants {
|
||||
= "setStartNode";
|
||||
public static final String RESET
|
||||
= "reset";
|
||||
public static final String GET_MODULE
|
||||
= "getModule";
|
||||
public static final String FOR_NAME
|
||||
= "forName";
|
||||
public static final String ADD_READS
|
||||
= "addReads";
|
||||
|
||||
public static final String GET_MODULE_SIG
|
||||
= "()" + MODULE_SIG;
|
||||
public static final String FOR_NAME_SIG
|
||||
= "(" + STRING_SIG + ")" + CLASS_SIG;
|
||||
public static final String ADD_READS_SIG
|
||||
= "(" + MODULE_SIG + ")" + MODULE_SIG;
|
||||
|
||||
public static final String ATTR_SET_SIG
|
||||
= "(" + DOM_INTF_SIG + NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + "I)V";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@ -17,9 +17,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* $Id: FunctionCall.java,v 1.2.4.1 2005/09/12 10:31:32 pvedula Exp $
|
||||
*/
|
||||
|
||||
package com.sun.org.apache.xalan.internal.xsltc.compiler;
|
||||
|
||||
@ -32,6 +29,7 @@ import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
|
||||
import com.sun.org.apache.bcel.internal.generic.InstructionConstants;
|
||||
import com.sun.org.apache.bcel.internal.generic.InstructionList;
|
||||
import com.sun.org.apache.bcel.internal.generic.InvokeInstruction;
|
||||
import com.sun.org.apache.bcel.internal.generic.LDC;
|
||||
import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
|
||||
import com.sun.org.apache.bcel.internal.generic.NEW;
|
||||
import com.sun.org.apache.bcel.internal.generic.PUSH;
|
||||
@ -792,6 +790,11 @@ class FunctionCall extends Expression {
|
||||
|
||||
final String clazz =
|
||||
_chosenConstructor.getDeclaringClass().getName();
|
||||
|
||||
// Generate call to Module.addReads:
|
||||
// <TransletClass>.class.getModule().addReads(
|
||||
generateAddReads(classGen, methodGen, clazz);
|
||||
|
||||
Class[] paramTypes = _chosenConstructor.getParameterTypes();
|
||||
LocalVariableGen[] paramTemp = new LocalVariableGen[n];
|
||||
|
||||
@ -855,6 +858,12 @@ class FunctionCall extends Expression {
|
||||
final String clazz = _chosenMethod.getDeclaringClass().getName();
|
||||
Class[] paramTypes = _chosenMethod.getParameterTypes();
|
||||
|
||||
|
||||
// Generate call to Module.addReads:
|
||||
// <TransletClass>.class.getModule().addReads(
|
||||
// Class.forName(<clazz>).getModule());
|
||||
generateAddReads(classGen, methodGen, clazz);
|
||||
|
||||
// Push "this" if it is an instance method
|
||||
if (_thisArgument != null) {
|
||||
_thisArgument.translate(classGen, methodGen);
|
||||
@ -896,6 +905,41 @@ class FunctionCall extends Expression {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateAddReads(ClassGenerator classGen, MethodGenerator methodGen,
|
||||
String clazz) {
|
||||
final ConstantPoolGen cpg = classGen.getConstantPool();
|
||||
final InstructionList il = methodGen.getInstructionList();
|
||||
|
||||
// Generate call to Module.addReads:
|
||||
// <TransletClass>.class.getModule().addReads(
|
||||
// Class.forName(<clazz>).getModule());
|
||||
// Class.forName may throw ClassNotFoundException.
|
||||
// This is OK as it will caught higher up the stack in
|
||||
// TransformerImpl.transform() and wrapped into a
|
||||
// TransformerException.
|
||||
methodGen.markChunkStart();
|
||||
|
||||
int index = cpg.addMethodref(CLASS_CLASS,
|
||||
GET_MODULE,
|
||||
GET_MODULE_SIG);
|
||||
int index2 = cpg.addMethodref(CLASS_CLASS,
|
||||
FOR_NAME,
|
||||
FOR_NAME_SIG);
|
||||
il.append(new LDC(cpg.addString(classGen.getClassName())));
|
||||
il.append(new INVOKESTATIC(index2));
|
||||
il.append(new INVOKEVIRTUAL(index));
|
||||
il.append(new LDC(cpg.addString(clazz)));
|
||||
il.append(new INVOKESTATIC(index2));
|
||||
il.append(new INVOKEVIRTUAL(index));
|
||||
index = cpg.addMethodref(MODULE_CLASS,
|
||||
ADD_READS,
|
||||
ADD_READS_SIG);
|
||||
il.append(new INVOKEVIRTUAL(index));
|
||||
il.append(InstructionConstants.POP);
|
||||
|
||||
methodGen.markChunkEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "funcall(" + _fname + ", " + _arguments + ')';
|
||||
|
||||
@ -58,7 +58,6 @@ import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.URIResolver;
|
||||
|
||||
import jdk.internal.module.Modules;
|
||||
|
||||
/**
|
||||
* @author Morten Jorgensen
|
||||
@ -486,10 +485,6 @@ public final class TemplatesImpl implements Templates, Serializable {
|
||||
thisModule.addExports(p, m);
|
||||
});
|
||||
|
||||
// For now, the module reads all unnnamed modules. This will be changed once
|
||||
// the XSLT compiler is updated to generate code to invoke addReads.
|
||||
Modules.addReadsAllUnnamed(m);
|
||||
|
||||
// java.xml needs to instanitate the translet class
|
||||
thisModule.addReads(m);
|
||||
|
||||
@ -513,7 +508,7 @@ public final class TemplatesImpl implements Templates, Serializable {
|
||||
}
|
||||
catch (ClassFormatError e) {
|
||||
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
|
||||
throw new TransformerConfigurationException(err.toString());
|
||||
throw new TransformerConfigurationException(err.toString(), e);
|
||||
}
|
||||
catch (LinkageError e) {
|
||||
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@ -239,6 +239,14 @@ public final class Constants {
|
||||
*/
|
||||
public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT =
|
||||
ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit";
|
||||
|
||||
/**
|
||||
* JDK node count limit in entities that limits the total number of nodes
|
||||
* in all of entity references.
|
||||
*/
|
||||
public static final String JDK_ENTITY_REPLACEMENT_LIMIT =
|
||||
ORACLE_JAXP_PROPERTY_PREFIX + "entityReplacementLimit";
|
||||
|
||||
/**
|
||||
* JDK maximum parameter entity size limit
|
||||
*/
|
||||
@ -292,6 +300,13 @@ public final class Constants {
|
||||
* JDK maximum general entity size limit
|
||||
*/
|
||||
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit";
|
||||
|
||||
/**
|
||||
* JDK node count limit in entities that limits the total number of nodes
|
||||
* in all of entity references.
|
||||
*/
|
||||
public static final String SP_ENTITY_REPLACEMENT_LIMIT = "jdk.xml.entityReplacementLimit";
|
||||
|
||||
/**
|
||||
* JDK maximum parameter entity size limit
|
||||
*/
|
||||
|
||||
@ -1,62 +1,21 @@
|
||||
/*
|
||||
* reserved comment block
|
||||
* DO NOT REMOVE OR ALTER!
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* The Apache Software License, Version 1.1
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Copyright (c) 1999-2004 The Apache Software Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by the
|
||||
* Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Xerces" and "Apache Software Foundation" must
|
||||
* not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written
|
||||
* permission, please contact apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache",
|
||||
* nor may "Apache" appear in their name, without prior written
|
||||
* permission of the Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation and was
|
||||
* originally based on software copyright (c) 1999, International
|
||||
* Business Machines, Inc., http://www.apache.org. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
@ -146,7 +105,7 @@ public class XML11DTDScannerImpl
|
||||
protected boolean scanPubidLiteral(XMLString literal)
|
||||
throws IOException, XNIException
|
||||
{
|
||||
int quote = fEntityScanner.scanChar();
|
||||
int quote = fEntityScanner.scanChar(null);
|
||||
if (quote != '\'' && quote != '"') {
|
||||
reportFatalError("QuoteRequiredInPublicID", null);
|
||||
return false;
|
||||
@ -157,7 +116,7 @@ public class XML11DTDScannerImpl
|
||||
boolean skipSpace = true;
|
||||
boolean dataok = true;
|
||||
while (true) {
|
||||
int c = fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanChar(null);
|
||||
// REVISIT: it could really only be \n or 0x20; all else is normalized, no? - neilg
|
||||
if (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
|
||||
if (!skipSpace) {
|
||||
|
||||
@ -1,62 +1,21 @@
|
||||
/*
|
||||
* reserved comment block
|
||||
* DO NOT REMOVE OR ALTER!
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* The Apache Software License, Version 1.1
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Copyright (c) 1999-2004 The Apache Software Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by the
|
||||
* Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Xerces" and "Apache Software Foundation" must
|
||||
* not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written
|
||||
* permission, please contact apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache",
|
||||
* nor may "Apache" appear in their name, without prior written
|
||||
* permission of the Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation and was
|
||||
* originally based on software copyright (c) 1999, International
|
||||
* Business Machines, Inc., http://www.apache.org. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
@ -134,7 +93,7 @@ public class XML11DocumentScannerImpl
|
||||
// happens when there is the character reference
|
||||
// but scanContent doesn't do entity expansions...
|
||||
// is this *really* necessary??? - NG
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
content.append((char)c);
|
||||
c = -1;
|
||||
}
|
||||
@ -143,7 +102,7 @@ public class XML11DocumentScannerImpl
|
||||
} */
|
||||
|
||||
if (c == ']') {
|
||||
content.append((char)fEntityScanner.scanChar());
|
||||
content.append((char)fEntityScanner.scanChar(null));
|
||||
// remember where we are in case we get an endEntity before we
|
||||
// could flush the buffer out - this happens when we're parsing an
|
||||
// entity which ends with a ]
|
||||
@ -152,12 +111,12 @@ public class XML11DocumentScannerImpl
|
||||
// We work on a single character basis to handle cases such as:
|
||||
// ']]]>' which we might otherwise miss.
|
||||
//
|
||||
if (fEntityScanner.skipChar(']')) {
|
||||
if (fEntityScanner.skipChar(']', null)) {
|
||||
content.append(']');
|
||||
while (fEntityScanner.skipChar(']')) {
|
||||
while (fEntityScanner.skipChar(']', null)) {
|
||||
content.append(']');
|
||||
}
|
||||
if (fEntityScanner.skipChar('>')) {
|
||||
if (fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("CDEndInContent", null);
|
||||
}
|
||||
}
|
||||
@ -184,6 +143,7 @@ public class XML11DocumentScannerImpl
|
||||
* @param checkEntities true if undeclared entities should be reported as VC violation,
|
||||
* false if undeclared entities should be reported as WFC violation.
|
||||
* @param eleName The name of element to which this attribute belongs.
|
||||
* @param isNSURI The flag indicating whether the content is a namespace URI
|
||||
*
|
||||
* @return true if the non-normalized and normalized value are the same
|
||||
*
|
||||
@ -193,7 +153,7 @@ public class XML11DocumentScannerImpl
|
||||
protected boolean scanAttributeValue(XMLString value,
|
||||
XMLString nonNormalizedValue,
|
||||
String atName,
|
||||
boolean checkEntities,String eleName)
|
||||
boolean checkEntities,String eleName, boolean isNSURI)
|
||||
throws IOException, XNIException
|
||||
{
|
||||
// quote
|
||||
@ -202,10 +162,10 @@ public class XML11DocumentScannerImpl
|
||||
reportFatalError("OpenQuoteExpected", new Object[]{eleName,atName});
|
||||
}
|
||||
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.ATTRIBUTE);
|
||||
int entityDepth = fEntityDepth;
|
||||
|
||||
int c = fEntityScanner.scanLiteral(quote, value);
|
||||
int c = fEntityScanner.scanLiteral(quote, value, isNSURI);
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** scanLiteral -> \""
|
||||
+ value.toString() + "\"");
|
||||
@ -215,7 +175,7 @@ public class XML11DocumentScannerImpl
|
||||
if (c == quote && (fromIndex = isUnchangedByNormalization(value)) == -1) {
|
||||
/** Both the non-normalized and normalized attribute values are equal. **/
|
||||
nonNormalizedValue.setValues(value);
|
||||
int cquote = fEntityScanner.scanChar();
|
||||
int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
|
||||
if (cquote != quote) {
|
||||
reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName});
|
||||
}
|
||||
@ -238,11 +198,11 @@ public class XML11DocumentScannerImpl
|
||||
+ fStringBuffer.toString() + "\"");
|
||||
}
|
||||
if (c == '&') {
|
||||
fEntityScanner.skipChar('&');
|
||||
fEntityScanner.skipChar('&', NameType.REFERENCE);
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append('&');
|
||||
}
|
||||
if (fEntityScanner.skipChar('#')) {
|
||||
if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append('#');
|
||||
}
|
||||
@ -256,59 +216,22 @@ public class XML11DocumentScannerImpl
|
||||
}
|
||||
}
|
||||
else {
|
||||
String entityName = fEntityScanner.scanName();
|
||||
String entityName = fEntityScanner.scanName(NameType.REFERENCE);
|
||||
if (entityName == null) {
|
||||
reportFatalError("NameRequiredInReference", null);
|
||||
}
|
||||
else if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append(entityName);
|
||||
}
|
||||
if (!fEntityScanner.skipChar(';')) {
|
||||
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInReference",
|
||||
new Object []{entityName});
|
||||
}
|
||||
else if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append(';');
|
||||
}
|
||||
if (entityName == fAmpSymbol) {
|
||||
fStringBuffer.append('&');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** value5: \""
|
||||
+ fStringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
}
|
||||
else if (entityName == fAposSymbol) {
|
||||
fStringBuffer.append('\'');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** value7: \""
|
||||
+ fStringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
}
|
||||
else if (entityName == fLtSymbol) {
|
||||
fStringBuffer.append('<');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** value9: \""
|
||||
+ fStringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
}
|
||||
else if (entityName == fGtSymbol) {
|
||||
fStringBuffer.append('>');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** valueB: \""
|
||||
+ fStringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
}
|
||||
else if (entityName == fQuotSymbol) {
|
||||
fStringBuffer.append('"');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** valueD: \""
|
||||
+ fStringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
if (resolveCharacter(entityName, fStringBuffer)) {
|
||||
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
|
||||
}
|
||||
else {
|
||||
if (fEntityManager.isExternalEntity(entityName)) {
|
||||
@ -339,13 +262,13 @@ public class XML11DocumentScannerImpl
|
||||
else if (c == '<') {
|
||||
reportFatalError("LessthanInAttValue",
|
||||
new Object[] { eleName, atName });
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append((char)c);
|
||||
}
|
||||
}
|
||||
else if (c == '%' || c == ']') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fStringBuffer.append((char)c);
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append((char)c);
|
||||
@ -359,7 +282,7 @@ public class XML11DocumentScannerImpl
|
||||
// XML11EntityScanner. Not sure why
|
||||
// this check was originally necessary. - NG
|
||||
else if (c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fStringBuffer.append(' ');
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append('\n');
|
||||
@ -382,12 +305,12 @@ public class XML11DocumentScannerImpl
|
||||
else if (c != -1 && isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInAttValue",
|
||||
new Object[] {eleName, atName, Integer.toString(c, 16)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append((char)c);
|
||||
}
|
||||
}
|
||||
c = fEntityScanner.scanLiteral(quote, value);
|
||||
c = fEntityScanner.scanLiteral(quote, value, isNSURI);
|
||||
if (entityDepth == fEntityDepth) {
|
||||
fStringBuffer2.append(value);
|
||||
}
|
||||
@ -404,7 +327,7 @@ public class XML11DocumentScannerImpl
|
||||
nonNormalizedValue.setValues(fStringBuffer2);
|
||||
|
||||
// quote
|
||||
int cquote = fEntityScanner.scanChar();
|
||||
int cquote = fEntityScanner.scanChar(null);
|
||||
if (cquote != quote) {
|
||||
reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName});
|
||||
}
|
||||
@ -439,7 +362,7 @@ public class XML11DocumentScannerImpl
|
||||
protected boolean scanPubidLiteral(XMLString literal)
|
||||
throws IOException, XNIException
|
||||
{
|
||||
int quote = fEntityScanner.scanChar();
|
||||
int quote = fEntityScanner.scanChar(null);
|
||||
if (quote != '\'' && quote != '"') {
|
||||
reportFatalError("QuoteRequiredInPublicID", null);
|
||||
return false;
|
||||
@ -450,7 +373,7 @@ public class XML11DocumentScannerImpl
|
||||
boolean skipSpace = true;
|
||||
boolean dataok = true;
|
||||
while (true) {
|
||||
int c = fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanChar(null);
|
||||
// REVISIT: none of these except \n and 0x20 should make it past the entity scanner
|
||||
if (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
|
||||
if (!skipSpace) {
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
|
||||
import com.sun.org.apache.xerces.internal.impl.XMLScanner.NameType;
|
||||
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
|
||||
import com.sun.org.apache.xerces.internal.util.XML11Char;
|
||||
import com.sun.org.apache.xerces.internal.util.XMLChar;
|
||||
@ -92,7 +93,7 @@ public class XML11EntityScanner
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public int scanChar() throws IOException {
|
||||
protected int scanChar(NameType nt) throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -100,6 +101,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
|
||||
// scan character
|
||||
int offset = fCurrentEntity.position;
|
||||
int c = fCurrentEntity.ch[fCurrentEntity.position++];
|
||||
boolean external = false;
|
||||
if (c == '\n' ||
|
||||
@ -110,6 +112,7 @@ public class XML11EntityScanner
|
||||
invokeListeners(1);
|
||||
fCurrentEntity.ch[0] = (char)c;
|
||||
load(1, false, false);
|
||||
offset = 0;
|
||||
}
|
||||
if (c == '\r' && external) {
|
||||
int cc = fCurrentEntity.ch[fCurrentEntity.position++];
|
||||
@ -122,6 +125,9 @@ public class XML11EntityScanner
|
||||
|
||||
// return character that was scanned
|
||||
fCurrentEntity.columnNumber++;
|
||||
if (!detectingVersion) {
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
}
|
||||
return c;
|
||||
|
||||
} // scanChar():int
|
||||
@ -141,7 +147,7 @@ public class XML11EntityScanner
|
||||
* @see com.sun.org.apache.xerces.internal.util.SymbolTable
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name
|
||||
*/
|
||||
public String scanNmtoken() throws IOException {
|
||||
protected String scanNmtoken() throws IOException {
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
load(0, true, true);
|
||||
@ -248,6 +254,8 @@ public class XML11EntityScanner
|
||||
* <strong>Note:</strong> The string returned must be a symbol. The
|
||||
* SymbolTable can be used for this purpose.
|
||||
*
|
||||
* @param nt The type of the name (element or attribute)
|
||||
*
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*
|
||||
@ -255,7 +263,7 @@ public class XML11EntityScanner
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart
|
||||
*/
|
||||
public String scanName() throws IOException {
|
||||
protected String scanName(NameType nt) throws IOException {
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
load(0, true, true);
|
||||
@ -310,23 +318,11 @@ public class XML11EntityScanner
|
||||
return null;
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
do {
|
||||
ch = fCurrentEntity.ch[fCurrentEntity.position];
|
||||
if (XML11Char.isXML11Name(ch)) {
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.ch.length << 1];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
|
||||
offset = 0;
|
||||
if (load(length, false, false)) {
|
||||
break;
|
||||
@ -334,20 +330,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
}
|
||||
else if (XML11Char.isXML11NameHighSurrogate(ch)) {
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.ch.length << 1];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
|
||||
offset = 0;
|
||||
if (load(length, false, false)) {
|
||||
--fCurrentEntity.position;
|
||||
@ -361,20 +344,7 @@ public class XML11EntityScanner
|
||||
--fCurrentEntity.position;
|
||||
break;
|
||||
}
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.ch.length << 1];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
|
||||
offset = 0;
|
||||
if (load(length, false, false)) {
|
||||
break;
|
||||
@ -387,12 +357,14 @@ public class XML11EntityScanner
|
||||
}
|
||||
while (true);
|
||||
|
||||
int length = fCurrentEntity.position - offset;
|
||||
length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length;
|
||||
|
||||
// return name
|
||||
String symbol = null;
|
||||
if (length > 0) {
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, length);
|
||||
symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
|
||||
}
|
||||
return symbol;
|
||||
@ -415,7 +387,7 @@ public class XML11EntityScanner
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCName
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCNameStart
|
||||
*/
|
||||
public String scanNCName() throws IOException {
|
||||
protected String scanNCName() throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -571,6 +543,7 @@ public class XML11EntityScanner
|
||||
* this purpose.
|
||||
*
|
||||
* @param qname The qualified name structure to fill.
|
||||
* @param nt The type of the name (element or attribute)
|
||||
*
|
||||
* @return Returns true if a qualified name appeared immediately on
|
||||
* the input and was scanned, false otherwise.
|
||||
@ -582,7 +555,7 @@ public class XML11EntityScanner
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart
|
||||
*/
|
||||
public boolean scanQName(QName qname) throws IOException {
|
||||
protected boolean scanQName(QName qname, XMLScanner.NameType nt) throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -602,6 +575,7 @@ public class XML11EntityScanner
|
||||
fCurrentEntity.columnNumber++;
|
||||
String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 1);
|
||||
qname.setValues(null, name, name, null);
|
||||
checkEntityLimit(nt, fCurrentEntity, 0, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -632,6 +606,7 @@ public class XML11EntityScanner
|
||||
fCurrentEntity.columnNumber += 2;
|
||||
String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 2);
|
||||
qname.setValues(null, name, name, null);
|
||||
checkEntityLimit(nt, fCurrentEntity, 0, 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -641,6 +616,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
int length = 0;
|
||||
boolean sawIncompleteSurrogatePair = false;
|
||||
do {
|
||||
ch = fCurrentEntity.ch[fCurrentEntity.position];
|
||||
@ -653,22 +629,7 @@ public class XML11EntityScanner
|
||||
//check prefix before further read
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset);
|
||||
}
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
//check localpart before loading more data
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length - index - 1);
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.ch.length << 1];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
|
||||
if (index != -1) {
|
||||
index = index - offset;
|
||||
}
|
||||
@ -679,20 +640,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
}
|
||||
else if (XML11Char.isXML11NameHighSurrogate(ch)) {
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.ch.length << 1];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
|
||||
if (index != -1) {
|
||||
index = index - offset;
|
||||
}
|
||||
@ -711,20 +659,7 @@ public class XML11EntityScanner
|
||||
--fCurrentEntity.position;
|
||||
break;
|
||||
}
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.ch.length << 1];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
|
||||
if (index != -1) {
|
||||
index = index - offset;
|
||||
}
|
||||
@ -740,7 +675,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
while (true);
|
||||
|
||||
int length = fCurrentEntity.position - offset;
|
||||
length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length;
|
||||
|
||||
if (length > 0) {
|
||||
@ -776,6 +711,7 @@ public class XML11EntityScanner
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
|
||||
}
|
||||
qname.setValues(prefix, localpart, rawname, null);
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -808,7 +744,7 @@ public class XML11EntityScanner
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public int scanContent(XMLString content) throws IOException {
|
||||
protected int scanContent(XMLString content) throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -826,6 +762,7 @@ public class XML11EntityScanner
|
||||
int offset = fCurrentEntity.position;
|
||||
int c = fCurrentEntity.ch[offset];
|
||||
int newlines = 0;
|
||||
boolean counted = false;
|
||||
boolean external = fCurrentEntity.isExternal();
|
||||
if (c == '\n' || ((c == '\r' || c == 0x85 || c == 0x2028) && external)) {
|
||||
do {
|
||||
@ -835,11 +772,13 @@ public class XML11EntityScanner
|
||||
fCurrentEntity.lineNumber++;
|
||||
fCurrentEntity.columnNumber = 1;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, newlines);
|
||||
offset = 0;
|
||||
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
|
||||
fCurrentEntity.position = newlines;
|
||||
fCurrentEntity.startPosition = newlines;
|
||||
if (load(newlines, false, true)) {
|
||||
counted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -858,11 +797,13 @@ public class XML11EntityScanner
|
||||
fCurrentEntity.lineNumber++;
|
||||
fCurrentEntity.columnNumber = 1;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, newlines);
|
||||
offset = 0;
|
||||
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
|
||||
fCurrentEntity.position = newlines;
|
||||
fCurrentEntity.startPosition = newlines;
|
||||
if (load(newlines, false, true)) {
|
||||
counted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -877,6 +818,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, length);
|
||||
content.setValues(fCurrentEntity.ch, offset, length);
|
||||
return -1;
|
||||
}
|
||||
@ -904,8 +846,8 @@ public class XML11EntityScanner
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
if (fCurrentEntity.isGE) {
|
||||
checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
|
||||
if (!counted) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, length);
|
||||
}
|
||||
content.setValues(fCurrentEntity.ch, offset, length);
|
||||
|
||||
@ -945,6 +887,7 @@ public class XML11EntityScanner
|
||||
* @param quote The quote character that signifies the end of the
|
||||
* attribute value data.
|
||||
* @param content The content structure to fill.
|
||||
* @param isNSURI a flag indicating whether the content is a Namespace URI
|
||||
*
|
||||
* @return Returns the next character on the input, if known. This
|
||||
* value may be -1 but this does <em>note</em> designate
|
||||
@ -953,7 +896,7 @@ public class XML11EntityScanner
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public int scanLiteral(int quote, XMLString content)
|
||||
protected int scanLiteral(int quote, XMLString content, boolean isNSURI)
|
||||
throws IOException {
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -1051,8 +994,10 @@ public class XML11EntityScanner
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
if (fCurrentEntity.isGE) {
|
||||
checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
|
||||
|
||||
checkEntityLimit(null, fCurrentEntity, offset, length);
|
||||
if (isNSURI) {
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
|
||||
}
|
||||
content.setValues(fCurrentEntity.ch, offset, length);
|
||||
|
||||
@ -1103,7 +1048,7 @@ public class XML11EntityScanner
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public boolean scanData(String delimiter, XMLStringBuffer buffer)
|
||||
protected boolean scanData(String delimiter, XMLStringBuffer buffer)
|
||||
throws IOException {
|
||||
|
||||
boolean done = false;
|
||||
@ -1135,6 +1080,7 @@ public class XML11EntityScanner
|
||||
if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
|
||||
// something must be wrong with the input: e.g., file ends an unterminated comment
|
||||
int length = fCurrentEntity.count - fCurrentEntity.position;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length);
|
||||
buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length);
|
||||
fCurrentEntity.columnNumber += fCurrentEntity.count;
|
||||
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
|
||||
@ -1199,6 +1145,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
buffer.append(fCurrentEntity.ch, offset, length);
|
||||
return true;
|
||||
}
|
||||
@ -1237,6 +1184,7 @@ public class XML11EntityScanner
|
||||
fCurrentEntity.position--;
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
buffer.append(fCurrentEntity.ch, offset, length);
|
||||
return true;
|
||||
}
|
||||
@ -1274,6 +1222,7 @@ public class XML11EntityScanner
|
||||
fCurrentEntity.position--;
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
buffer.append(fCurrentEntity.ch, offset, length);
|
||||
return true;
|
||||
}
|
||||
@ -1281,6 +1230,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
if (done) {
|
||||
length -= delimLen;
|
||||
}
|
||||
@ -1305,7 +1255,7 @@ public class XML11EntityScanner
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public boolean skipChar(int c) throws IOException {
|
||||
protected boolean skipChar(int c, NameType nt) throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -1313,6 +1263,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
|
||||
// skip character
|
||||
int offset = fCurrentEntity.position;
|
||||
int cc = fCurrentEntity.ch[fCurrentEntity.position];
|
||||
if (cc == c) {
|
||||
fCurrentEntity.position++;
|
||||
@ -1323,12 +1274,14 @@ public class XML11EntityScanner
|
||||
else {
|
||||
fCurrentEntity.columnNumber++;
|
||||
}
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
return true;
|
||||
}
|
||||
else if (c == '\n' && ((cc == 0x2028 || cc == 0x85) && fCurrentEntity.isExternal())) {
|
||||
fCurrentEntity.position++;
|
||||
fCurrentEntity.lineNumber++;
|
||||
fCurrentEntity.columnNumber = 1;
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
return true;
|
||||
}
|
||||
else if (c == '\n' && (cc == '\r' ) && fCurrentEntity.isExternal()) {
|
||||
@ -1344,6 +1297,7 @@ public class XML11EntityScanner
|
||||
}
|
||||
fCurrentEntity.lineNumber++;
|
||||
fCurrentEntity.columnNumber = 1;
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1366,7 +1320,7 @@ public class XML11EntityScanner
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
|
||||
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Space
|
||||
*/
|
||||
public boolean skipSpaces() throws IOException {
|
||||
protected boolean skipSpaces() throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -1386,7 +1340,7 @@ public class XML11EntityScanner
|
||||
|
||||
// skip spaces
|
||||
int c = fCurrentEntity.ch[fCurrentEntity.position];
|
||||
|
||||
int offset = fCurrentEntity.position - 1;
|
||||
// External -- Match: S + 0x85 + 0x2028, and perform end of line normalization
|
||||
if (fCurrentEntity.isExternal()) {
|
||||
if (XML11Char.isXML11Space(c)) {
|
||||
@ -1422,6 +1376,11 @@ public class XML11EntityScanner
|
||||
else {
|
||||
fCurrentEntity.columnNumber++;
|
||||
}
|
||||
|
||||
//If this is a general entity, spaces within a start element should be counted
|
||||
checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
offset = fCurrentEntity.position;
|
||||
|
||||
// load more characters, if needed
|
||||
if (!entityChanged)
|
||||
fCurrentEntity.position++;
|
||||
@ -1462,6 +1421,11 @@ public class XML11EntityScanner
|
||||
else {
|
||||
fCurrentEntity.columnNumber++;
|
||||
}
|
||||
|
||||
//If this is a general entity, spaces within a start element should be counted
|
||||
checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
offset = fCurrentEntity.position;
|
||||
|
||||
// load more characters, if needed
|
||||
if (!entityChanged)
|
||||
fCurrentEntity.position++;
|
||||
@ -1495,7 +1459,7 @@ public class XML11EntityScanner
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public boolean skipString(String s) throws IOException {
|
||||
protected boolean skipString(String s) throws IOException {
|
||||
|
||||
// load more characters, if needed
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
@ -1504,6 +1468,7 @@ public class XML11EntityScanner
|
||||
|
||||
// skip string
|
||||
final int length = s.length();
|
||||
final int beforeSkip = fCurrentEntity.position ;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = fCurrentEntity.ch[fCurrentEntity.position++];
|
||||
if (c != s.charAt(i)) {
|
||||
@ -1523,6 +1488,9 @@ public class XML11EntityScanner
|
||||
}
|
||||
}
|
||||
fCurrentEntity.columnNumber += length;
|
||||
if (!detectingVersion) {
|
||||
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
|
||||
}
|
||||
return true;
|
||||
|
||||
} // skipString(String):boolean
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -135,7 +135,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
if (DEBUG_START_END_ELEMENT)
|
||||
System.out.println(">>> scanStartElementNS()");
|
||||
// Note: namespace processing is on by default
|
||||
fEntityScanner.scanQName(fElementQName);
|
||||
fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE);
|
||||
// REVISIT - [Q] Why do we need this local variable? -- mrglavas
|
||||
String rawname = fElementQName.rawname;
|
||||
if (fBindNamespaces) {
|
||||
@ -173,11 +173,11 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
// end tag?
|
||||
int c = fEntityScanner.peekChar();
|
||||
if (c == '>') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
break;
|
||||
} else if (c == '/') {
|
||||
fEntityScanner.scanChar();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
fEntityScanner.scanChar(null);
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError(
|
||||
"ElementUnterminated",
|
||||
new Object[] { rawname });
|
||||
@ -345,7 +345,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
protected void scanStartElementName ()
|
||||
throws IOException, XNIException {
|
||||
// Note: namespace processing is on by default
|
||||
fEntityScanner.scanQName(fElementQName);
|
||||
fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE);
|
||||
// Must skip spaces here because the DTD scanner
|
||||
// would consume them at the end of the external subset.
|
||||
fSawSpace = fEntityScanner.skipSpaces();
|
||||
@ -395,11 +395,11 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
// end tag?
|
||||
int c = fEntityScanner.peekChar();
|
||||
if (c == '>') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
break;
|
||||
} else if (c == '/') {
|
||||
fEntityScanner.scanChar();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
fEntityScanner.scanChar(null);
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError(
|
||||
"ElementUnterminated",
|
||||
new Object[] { rawname });
|
||||
@ -571,11 +571,11 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
System.out.println(">>> scanAttribute()");
|
||||
|
||||
// name
|
||||
fEntityScanner.scanQName(fAttributeQName);
|
||||
fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE);
|
||||
|
||||
// equals
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('=')) {
|
||||
if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
|
||||
reportFatalError(
|
||||
"EqRequiredInAttribute",
|
||||
new Object[] {
|
||||
@ -614,13 +614,20 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
//REVISIT: one more case needs to be included: external PE and standalone is no
|
||||
boolean isVC = fHasExternalDTD && !fStandalone;
|
||||
|
||||
// REVISIT: it seems that this function should not take attributes, and length
|
||||
scanAttributeValue(
|
||||
this.fTempString,
|
||||
fTempString2,
|
||||
fAttributeQName.rawname,
|
||||
isVC,
|
||||
fCurrentElement.rawname);
|
||||
/**
|
||||
* Determine whether this is a namespace declaration that will be subject
|
||||
* to the name limit check in the scanAttributeValue operation.
|
||||
* Namespace declaration format: xmlns="..." or xmlns:prefix="..."
|
||||
* Note that prefix:xmlns="..." isn't a namespace.
|
||||
*/
|
||||
String localpart = fAttributeQName.localpart;
|
||||
String prefix = fAttributeQName.prefix != null
|
||||
? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
|
||||
boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS ||
|
||||
prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS);
|
||||
|
||||
scanAttributeValue(this.fTempString, fTempString2, fAttributeQName.rawname,
|
||||
isVC, fCurrentElement.rawname, isNSDecl);
|
||||
String value = fTempString.toString();
|
||||
attributes.setValue(attrIndex, value);
|
||||
attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
|
||||
@ -628,17 +635,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
|
||||
// record namespace declarations if any.
|
||||
if (fBindNamespaces) {
|
||||
|
||||
String localpart = fAttributeQName.localpart;
|
||||
String prefix =
|
||||
fAttributeQName.prefix != null
|
||||
? fAttributeQName.prefix
|
||||
: XMLSymbols.EMPTY_STRING;
|
||||
// when it's of form xmlns="..." or xmlns:prefix="...",
|
||||
// it's a namespace declaration. but prefix:xmlns="..." isn't.
|
||||
if (prefix == XMLSymbols.PREFIX_XMLNS
|
||||
|| prefix == XMLSymbols.EMPTY_STRING
|
||||
&& localpart == XMLSymbols.PREFIX_XMLNS) {
|
||||
if (isNSDecl) {
|
||||
if (value.length() > fXMLNameLimit) {
|
||||
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
|
||||
"MaxXMLNameLimit",
|
||||
@ -758,7 +755,7 @@ public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
|
||||
|
||||
// end
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
|
||||
reportFatalError(
|
||||
"ETagUnterminated",
|
||||
new Object[] { endElementName.rawname });
|
||||
|
||||
@ -21,10 +21,7 @@
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
|
||||
import com.sun.org.apache.xerces.internal.impl.Constants;
|
||||
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
|
||||
import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
|
||||
import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler;
|
||||
import com.sun.org.apache.xerces.internal.util.SymbolTable;
|
||||
import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
|
||||
import com.sun.org.apache.xerces.internal.util.XMLChar;
|
||||
@ -367,6 +364,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
// we're done, set starting state for external subset
|
||||
setScannerState(SCANNER_STATE_TEXT_DECL);
|
||||
// we're done scanning DTD.
|
||||
fLimitAnalyzer.reset(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT);
|
||||
fLimitAnalyzer.reset(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT);
|
||||
return false;
|
||||
}
|
||||
@ -399,7 +397,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
if (isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInDTD",
|
||||
new Object[] { Integer.toHexString(c) });
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -767,7 +765,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fStringBuffer.clear();
|
||||
fStringBuffer.append("xml");
|
||||
while (isValidNameChar(fEntityScanner.peekChar())) {
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar());
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar(null));
|
||||
}
|
||||
String target =
|
||||
fSymbolTable.addSymbol(fStringBuffer.ch,
|
||||
@ -867,7 +865,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
|
||||
// element name
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
|
||||
if (name == null) {
|
||||
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL",
|
||||
null);
|
||||
@ -900,7 +898,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!fEntityScanner.skipChar('(')) {
|
||||
if (!fEntityScanner.skipChar('(', null)) {
|
||||
reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
|
||||
new Object[]{name});
|
||||
}
|
||||
@ -930,7 +928,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fReportEntity = false;
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
// end
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("ElementDeclUnterminated", new Object[]{name});
|
||||
}
|
||||
fReportEntity = true;
|
||||
@ -967,7 +965,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fDTDContentModelHandler.pcdata(null);
|
||||
}
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
while (fEntityScanner.skipChar('|')) {
|
||||
while (fEntityScanner.skipChar('|', null)) {
|
||||
fStringBuffer.append('|');
|
||||
// call handler
|
||||
if (fDTDContentModelHandler != null) {
|
||||
@ -976,7 +974,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
|
||||
childName = fEntityScanner.scanName();
|
||||
childName = fEntityScanner.scanName(NameType.ENTITY);
|
||||
if (childName == null) {
|
||||
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
|
||||
new Object[]{elName});
|
||||
@ -1005,7 +1003,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
reportFatalError("MixedContentUnterminated",
|
||||
new Object[]{elName});
|
||||
}
|
||||
else if (fEntityScanner.skipChar(')')){
|
||||
else if (fEntityScanner.skipChar(')', null)){
|
||||
fStringBuffer.append(')');
|
||||
// call handler
|
||||
if (fDTDContentModelHandler != null) {
|
||||
@ -1043,7 +1041,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
int currentOp = 0;
|
||||
int c;
|
||||
while (true) {
|
||||
if (fEntityScanner.skipChar('(')) {
|
||||
if (fEntityScanner.skipChar('(', null)) {
|
||||
fMarkUpDepth++;
|
||||
fStringBuffer.append('(');
|
||||
// call handler
|
||||
@ -1057,7 +1055,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
continue;
|
||||
}
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
String childName = fEntityScanner.scanName();
|
||||
String childName = fEntityScanner.scanName(NameType.ELEMENTSTART);
|
||||
if (childName == null) {
|
||||
reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
|
||||
new Object[]{elName});
|
||||
@ -1084,7 +1082,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
fDTDContentModelHandler.occurrence(oc, null);
|
||||
}
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fStringBuffer.append((char)c);
|
||||
}
|
||||
while (true) {
|
||||
@ -1097,7 +1095,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
|
||||
null);
|
||||
}
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fStringBuffer.append(',');
|
||||
break;
|
||||
}
|
||||
@ -1108,7 +1106,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
|
||||
null);
|
||||
}
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fStringBuffer.append('|');
|
||||
break;
|
||||
}
|
||||
@ -1154,7 +1152,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
else {
|
||||
// no occurrence specified
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fStringBuffer.append(')');
|
||||
}
|
||||
fMarkUpDepth--;
|
||||
@ -1186,7 +1184,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
|
||||
// element name
|
||||
String elName = fEntityScanner.scanName();
|
||||
String elName = fEntityScanner.scanName(NameType.ELEMENTSTART);
|
||||
if (elName == null) {
|
||||
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL",
|
||||
null);
|
||||
@ -1200,7 +1198,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
// spaces
|
||||
if (!skipSeparator(true, !scanningInternalSubset())) {
|
||||
// no space, is it the end yet?
|
||||
if (fEntityScanner.skipChar('>')) {
|
||||
if (fEntityScanner.skipChar('>', null)) {
|
||||
// yes, stop here
|
||||
// call handler
|
||||
if (fDTDHandler != null) {
|
||||
@ -1216,8 +1214,8 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
|
||||
// definitions
|
||||
while (!fEntityScanner.skipChar('>')) {
|
||||
String name = fEntityScanner.scanName();
|
||||
while (!fEntityScanner.skipChar('>', null)) {
|
||||
String name = fEntityScanner.scanName(NameType.ATTRIBUTE);
|
||||
if (name == null) {
|
||||
reportFatalError("AttNameRequiredInAttDef",
|
||||
new Object[]{elName});
|
||||
@ -1353,7 +1351,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
new Object[]{elName, atName});
|
||||
}
|
||||
// open paren
|
||||
int c = fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanChar(null);
|
||||
if (c != '(') {
|
||||
reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
|
||||
new Object[]{elName, atName});
|
||||
@ -1361,7 +1359,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fMarkUpDepth++;
|
||||
do {
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
String aName = fEntityScanner.scanName();
|
||||
String aName = fEntityScanner.scanName(NameType.ATTRIBUTE);
|
||||
if (aName == null) {
|
||||
reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
|
||||
new Object[]{elName, atName});
|
||||
@ -1369,7 +1367,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
ensureEnumerationSize(fEnumerationCount + 1);
|
||||
fEnumeration[fEnumerationCount++] = aName;
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
c = fEntityScanner.scanChar();
|
||||
c = fEntityScanner.scanChar(null);
|
||||
} while (c == '|');
|
||||
if (c != ')') {
|
||||
reportFatalError("NotationTypeUnterminated",
|
||||
@ -1380,7 +1378,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
else { // Enumeration
|
||||
type = "ENUMERATION";
|
||||
// open paren
|
||||
int c = fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanChar(null);
|
||||
if (c != '(') {
|
||||
// "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
|
||||
reportFatalError("AttTypeRequiredInAttDef",
|
||||
@ -1397,7 +1395,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
ensureEnumerationSize(fEnumerationCount + 1);
|
||||
fEnumeration[fEnumerationCount++] = token;
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
c = fEntityScanner.scanChar();
|
||||
c = fEntityScanner.scanChar(null);
|
||||
} while (c == '|');
|
||||
if (c != ')') {
|
||||
reportFatalError("EnumerationUnterminated",
|
||||
@ -1447,7 +1445,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
// AttValue
|
||||
boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ;
|
||||
scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName,
|
||||
fAttributes, 0, isVC, elName);
|
||||
fAttributes, 0, isVC, elName, false);
|
||||
}
|
||||
return defaultType;
|
||||
|
||||
@ -1475,7 +1473,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
boolean sawPERef = false;
|
||||
fReportEntity = false;
|
||||
if (fEntityScanner.skipSpaces()) {
|
||||
if (!fEntityScanner.skipChar('%')) {
|
||||
if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) {
|
||||
isPEDecl = false; // <!ENTITY x "x">
|
||||
}
|
||||
else if (skipSeparator(true, !scanningInternalSubset())) {
|
||||
@ -1496,7 +1494,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
sawPERef = true;
|
||||
}
|
||||
}
|
||||
else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) {
|
||||
else if (scanningInternalSubset() || !fEntityScanner.skipChar('%', NameType.REFERENCE)) {
|
||||
// <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
|
||||
reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
|
||||
null);
|
||||
@ -1513,11 +1511,11 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
if (sawPERef) {
|
||||
while (true) {
|
||||
String peName = fEntityScanner.scanName();
|
||||
String peName = fEntityScanner.scanName(NameType.REFERENCE);
|
||||
if (peName == null) {
|
||||
reportFatalError("NameRequiredInPEReference", null);
|
||||
}
|
||||
else if (!fEntityScanner.skipChar(';')) {
|
||||
else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInPEReference",
|
||||
new Object[]{peName});
|
||||
}
|
||||
@ -1525,20 +1523,20 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
startPE(peName, false);
|
||||
}
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('%'))
|
||||
if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
|
||||
break;
|
||||
if (!isPEDecl) {
|
||||
if (skipSeparator(true, !scanningInternalSubset())) {
|
||||
isPEDecl = true;
|
||||
break;
|
||||
}
|
||||
isPEDecl = fEntityScanner.skipChar('%');
|
||||
isPEDecl = fEntityScanner.skipChar('%', NameType.REFERENCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// name
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.ENTITY);
|
||||
if (name == null) {
|
||||
reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null);
|
||||
}
|
||||
@ -1573,7 +1571,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
|
||||
new Object[]{name});
|
||||
}
|
||||
notation = fEntityScanner.scanName();
|
||||
notation = fEntityScanner.scanName(NameType.NOTATION);
|
||||
if (notation == null) {
|
||||
reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
|
||||
new Object[]{name});
|
||||
@ -1595,7 +1593,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
|
||||
// end
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("EntityDeclUnterminated", new Object[]{name});
|
||||
}
|
||||
fMarkUpDepth--;
|
||||
@ -1650,7 +1648,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value,
|
||||
XMLString nonNormalizedValue)
|
||||
throws IOException, XNIException {
|
||||
int quote = fEntityScanner.scanChar();
|
||||
int quote = fEntityScanner.scanChar(null);
|
||||
if (quote != '\'' && quote != '"') {
|
||||
reportFatalError("OpenQuoteMissingInDecl", null);
|
||||
}
|
||||
@ -1665,23 +1663,24 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
fLimitAnalyzer.startEntity(entityName);
|
||||
|
||||
if (fEntityScanner.scanLiteral(quote, fString) != quote) {
|
||||
if (fEntityScanner.scanLiteral(quote, fString, false) != quote) {
|
||||
fStringBuffer.clear();
|
||||
fStringBuffer2.clear();
|
||||
int offset;
|
||||
do {
|
||||
checkEntityLimit(isPEDecl, entityName, fString.length + countChar);
|
||||
countChar = 0;
|
||||
offset = fStringBuffer.length;
|
||||
fStringBuffer.append(fString);
|
||||
fStringBuffer2.append(fString);
|
||||
if (fEntityScanner.skipChar('&')) {
|
||||
if (fEntityScanner.skipChar('#')) {
|
||||
if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
|
||||
if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
|
||||
fStringBuffer2.append("&#");
|
||||
scanCharReferenceValue(fStringBuffer, fStringBuffer2);
|
||||
}
|
||||
else {
|
||||
fStringBuffer.append('&');
|
||||
fStringBuffer2.append('&');
|
||||
String eName = fEntityScanner.scanName();
|
||||
String eName = fEntityScanner.scanName(NameType.REFERENCE);
|
||||
if (eName == null) {
|
||||
reportFatalError("NameRequiredInReference",
|
||||
null);
|
||||
@ -1690,7 +1689,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fStringBuffer.append(eName);
|
||||
fStringBuffer2.append(eName);
|
||||
}
|
||||
if (!fEntityScanner.skipChar(';')) {
|
||||
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInReference",
|
||||
new Object[]{eName});
|
||||
}
|
||||
@ -1700,15 +1699,15 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fEntityScanner.skipChar('%')) {
|
||||
else if (fEntityScanner.skipChar('%', NameType.REFERENCE)) {
|
||||
while (true) {
|
||||
fStringBuffer2.append('%');
|
||||
String peName = fEntityScanner.scanName();
|
||||
String peName = fEntityScanner.scanName(NameType.REFERENCE);
|
||||
if (peName == null) {
|
||||
reportFatalError("NameRequiredInPEReference",
|
||||
null);
|
||||
}
|
||||
else if (!fEntityScanner.skipChar(';')) {
|
||||
else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInPEReference",
|
||||
new Object[]{peName});
|
||||
}
|
||||
@ -1725,20 +1724,20 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
// REVISIT: This will make returning the non-
|
||||
// normalized value harder. -Ac
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('%'))
|
||||
if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
countChar++;
|
||||
int c = fEntityScanner.peekChar();
|
||||
if (XMLChar.isHighSurrogate(c)) {
|
||||
countChar++;
|
||||
scanSurrogates(fStringBuffer2);
|
||||
}
|
||||
else if (isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInLiteral",
|
||||
new Object[]{Integer.toHexString(c)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
// if it's not the delimiting quote or if it is but from a
|
||||
// different entity than the one this literal started from,
|
||||
@ -1746,10 +1745,12 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
else if (c != quote || entityDepth != fEntityDepth) {
|
||||
fStringBuffer.append((char)c);
|
||||
fStringBuffer2.append((char)c);
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
}
|
||||
} while (fEntityScanner.scanLiteral(quote, fString) != quote);
|
||||
checkEntityLimit(isPEDecl, entityName, fStringBuffer.length - offset + countChar);
|
||||
} while (fEntityScanner.scanLiteral(quote, fString, false) != quote);
|
||||
checkEntityLimit(isPEDecl, entityName, fString.length);
|
||||
fStringBuffer.append(fString);
|
||||
fStringBuffer2.append(fString);
|
||||
literal = fStringBuffer;
|
||||
@ -1760,10 +1761,14 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
value.setValues(literal);
|
||||
nonNormalizedValue.setValues(literal2);
|
||||
if (fLimitAnalyzer != null) {
|
||||
fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName);
|
||||
if (isPEDecl) {
|
||||
fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName);
|
||||
} else {
|
||||
fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fEntityScanner.skipChar(quote)) {
|
||||
if (!fEntityScanner.skipChar(quote, null)) {
|
||||
reportFatalError("CloseQuoteMissingInDecl", null);
|
||||
}
|
||||
} // scanEntityValue(XMLString,XMLString):void
|
||||
@ -1788,7 +1793,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
|
||||
// notation name
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.NOTATION);
|
||||
if (name == null) {
|
||||
reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL",
|
||||
null);
|
||||
@ -1815,7 +1820,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
skipSeparator(false, !scanningInternalSubset());
|
||||
|
||||
// end
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("NotationDeclUnterminated", new Object[]{name});
|
||||
}
|
||||
fMarkUpDepth--;
|
||||
@ -1863,7 +1868,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
XMLErrorReporter.SEVERITY_ERROR);
|
||||
}
|
||||
// call handler
|
||||
if (!fEntityScanner.skipChar('[')) {
|
||||
if (!fEntityScanner.skipChar('[', null)) {
|
||||
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
|
||||
}
|
||||
|
||||
@ -1888,7 +1893,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE,
|
||||
null);
|
||||
}
|
||||
if (!fEntityScanner.skipChar('[')) {
|
||||
if (!fEntityScanner.skipChar('[', null)) {
|
||||
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
|
||||
}
|
||||
fReportEntity = true;
|
||||
@ -1897,7 +1902,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fIgnoreConditionalBuffer.clear();
|
||||
}
|
||||
while (true) {
|
||||
if (fEntityScanner.skipChar('<')) {
|
||||
if (fEntityScanner.skipChar('<', null)) {
|
||||
if (fDTDHandler != null) {
|
||||
fIgnoreConditionalBuffer.append('<');
|
||||
}
|
||||
@ -1905,8 +1910,8 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
// These tests are split so that we handle cases like
|
||||
// '<<![' and '<!<![' which we might otherwise miss.
|
||||
//
|
||||
if (fEntityScanner.skipChar('!')) {
|
||||
if(fEntityScanner.skipChar('[')) {
|
||||
if (fEntityScanner.skipChar('!', null)) {
|
||||
if(fEntityScanner.skipChar('[', null)) {
|
||||
if (fDTDHandler != null) {
|
||||
fIgnoreConditionalBuffer.append("![");
|
||||
}
|
||||
@ -1918,24 +1923,24 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fEntityScanner.skipChar(']')) {
|
||||
else if (fEntityScanner.skipChar(']', null)) {
|
||||
if (fDTDHandler != null) {
|
||||
fIgnoreConditionalBuffer.append(']');
|
||||
}
|
||||
//
|
||||
// The same thing goes for ']<![' and '<]]>', etc.
|
||||
//
|
||||
if (fEntityScanner.skipChar(']')) {
|
||||
if (fEntityScanner.skipChar(']', null)) {
|
||||
if (fDTDHandler != null) {
|
||||
fIgnoreConditionalBuffer.append(']');
|
||||
}
|
||||
while (fEntityScanner.skipChar(']')) {
|
||||
while (fEntityScanner.skipChar(']', null)) {
|
||||
/* empty loop body */
|
||||
if (fDTDHandler != null) {
|
||||
fIgnoreConditionalBuffer.append(']');
|
||||
}
|
||||
}
|
||||
if (fEntityScanner.skipChar('>')) {
|
||||
if (fEntityScanner.skipChar('>', null)) {
|
||||
if (fIncludeSectDepth-- == initialDepth) {
|
||||
fMarkUpDepth--;
|
||||
// call handler
|
||||
@ -1953,7 +1958,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
}
|
||||
}
|
||||
else {
|
||||
int c = fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanChar(null);
|
||||
if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
|
||||
reportFatalError("IgnoreSectUnterminated", null);
|
||||
return;
|
||||
@ -1990,16 +1995,16 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
//System.out.println("scanDecls"+fScannerState);
|
||||
while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
|
||||
again = complete;
|
||||
if (fEntityScanner.skipChar('<')) {
|
||||
if (fEntityScanner.skipChar('<', null)) {
|
||||
fMarkUpDepth++;
|
||||
if (fEntityScanner.skipChar('?')) {
|
||||
if (fEntityScanner.skipChar('?', null)) {
|
||||
fStringBuffer.clear();
|
||||
scanPI(fStringBuffer);
|
||||
fMarkUpDepth--; // we're done with this decl
|
||||
}
|
||||
else if (fEntityScanner.skipChar('!')) {
|
||||
if (fEntityScanner.skipChar('-')) {
|
||||
if (!fEntityScanner.skipChar('-')) {
|
||||
else if (fEntityScanner.skipChar('!', null)) {
|
||||
if (fEntityScanner.skipChar('-', null)) {
|
||||
if (!fEntityScanner.skipChar('-', null)) {
|
||||
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
|
||||
null);
|
||||
} else {
|
||||
@ -2018,7 +2023,7 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
else if (fEntityScanner.skipString("NOTATION")) {
|
||||
scanNotationDecl();
|
||||
}
|
||||
else if (fEntityScanner.skipChar('[') &&
|
||||
else if (fEntityScanner.skipChar('[', null) &&
|
||||
!scanningInternalSubset()) {
|
||||
scanConditionalSect(fPEDepth);
|
||||
}
|
||||
@ -2033,10 +2038,10 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
|
||||
}
|
||||
}
|
||||
else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) {
|
||||
else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']', null)) {
|
||||
// end of conditional section?
|
||||
if (!fEntityScanner.skipChar(']')
|
||||
|| !fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar(']', null)
|
||||
|| !fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("IncludeSectUnterminated", null);
|
||||
}
|
||||
// call handler
|
||||
@ -2083,21 +2088,21 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
throws IOException, XNIException {
|
||||
int depth = fPEDepth;
|
||||
boolean sawSpace = fEntityScanner.skipSpaces();
|
||||
if (!lookForPERefs || !fEntityScanner.skipChar('%')) {
|
||||
if (!lookForPERefs || !fEntityScanner.skipChar('%', NameType.REFERENCE)) {
|
||||
return !spaceRequired || sawSpace || (depth != fPEDepth);
|
||||
}
|
||||
while (true) {
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.ENTITY);
|
||||
if (name == null) {
|
||||
reportFatalError("NameRequiredInPEReference", null);
|
||||
}
|
||||
else if (!fEntityScanner.skipChar(';')) {
|
||||
else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInPEReference",
|
||||
new Object[]{name});
|
||||
}
|
||||
startPE(name, false);
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('%'))
|
||||
if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2181,56 +2186,6 @@ implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
|
||||
fSecurityManager = fEntityManager.fSecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the count of the content buffer and check if the accumulated
|
||||
* value exceeds the limit
|
||||
* @param isPEDecl a flag to indicate whether the entity is parameter
|
||||
* @param entityName entity name
|
||||
* @param buffer content buffer
|
||||
*/
|
||||
private void checkEntityLimit(boolean isPEDecl, String entityName, XMLString buffer) {
|
||||
checkEntityLimit(isPEDecl, entityName, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the count and check limit
|
||||
* @param isPEDecl a flag to indicate whether the entity is parameter
|
||||
* @param entityName entity name
|
||||
* @param len length of the buffer
|
||||
*/
|
||||
private void checkEntityLimit(boolean isPEDecl, String entityName, int len) {
|
||||
if (fLimitAnalyzer == null) {
|
||||
fLimitAnalyzer = fEntityManager.fLimitAnalyzer;
|
||||
}
|
||||
if (isPEDecl) {
|
||||
fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, "%" + entityName, len);
|
||||
if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
reportFatalError("MaxEntitySizeLimit", new Object[]{"%" + entityName,
|
||||
fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)});
|
||||
}
|
||||
} else {
|
||||
fLimitAnalyzer.addValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName, len);
|
||||
if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
reportFatalError("MaxEntitySizeLimit", new Object[]{entityName,
|
||||
fLimitAnalyzer.getValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT)});
|
||||
}
|
||||
}
|
||||
if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
reportFatalError("TotalEntitySizeLimit",
|
||||
new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DTDGrammar getGrammar(){
|
||||
return nvGrammarInfo;
|
||||
}
|
||||
|
||||
@ -21,14 +21,6 @@
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
|
||||
import com.sun.xml.internal.stream.XMLBufferListener;
|
||||
import com.sun.xml.internal.stream.XMLEntityStorage;
|
||||
import com.sun.xml.internal.stream.dtd.DTDGrammarUtil;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
|
||||
import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
|
||||
import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl;
|
||||
@ -47,13 +39,18 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
|
||||
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner;
|
||||
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
|
||||
import com.sun.org.apache.xerces.internal.xni.Augmentations;
|
||||
import com.sun.org.apache.xerces.internal.impl.Constants;
|
||||
import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler;
|
||||
import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
|
||||
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
|
||||
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit;
|
||||
import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
|
||||
import com.sun.xml.internal.stream.XMLBufferListener;
|
||||
import com.sun.xml.internal.stream.XMLEntityStorage;
|
||||
import com.sun.xml.internal.stream.dtd.DTDGrammarUtil;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamConstants;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -454,6 +451,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
//fDocumentHandler.startElement(getElementQName(),fAttributes,null);
|
||||
break;
|
||||
case XMLStreamConstants.CHARACTERS :
|
||||
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
|
||||
fDocumentHandler.characters(getCharacterData(),null);
|
||||
break;
|
||||
case XMLStreamConstants.SPACE:
|
||||
@ -462,13 +460,15 @@ public class XMLDocumentFragmentScannerImpl
|
||||
//fDocumentHandler.ignorableWhitespace(getCharacterData(), null);
|
||||
break;
|
||||
case XMLStreamConstants.ENTITY_REFERENCE :
|
||||
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
|
||||
//entity reference callback are given in startEntity
|
||||
break;
|
||||
case XMLStreamConstants.PROCESSING_INSTRUCTION :
|
||||
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
|
||||
fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null);
|
||||
break;
|
||||
case XMLStreamConstants.COMMENT :
|
||||
//System.out.println(" in COMMENT of the XMLNSDocumentScannerImpl");
|
||||
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
|
||||
fDocumentHandler.comment(getCharacterData(),null);
|
||||
break;
|
||||
case XMLStreamConstants.DTD :
|
||||
@ -477,6 +477,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
//therefore we don't need to take care of anything here. So Just break;
|
||||
break;
|
||||
case XMLStreamConstants.CDATA:
|
||||
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
|
||||
fDocumentHandler.startCDATA(null);
|
||||
//xxx: check if CDATA values comes from getCharacterData() function
|
||||
fDocumentHandler.characters(getCharacterData(),null);
|
||||
@ -1273,9 +1274,9 @@ public class XMLDocumentFragmentScannerImpl
|
||||
fElementQName = fElementStack.nextElement();
|
||||
// name
|
||||
if (fNamespaces) {
|
||||
fEntityScanner.scanQName(fElementQName);
|
||||
fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
|
||||
} else {
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
|
||||
fElementQName.setValues(null, name, name, null);
|
||||
}
|
||||
|
||||
@ -1376,11 +1377,11 @@ public class XMLDocumentFragmentScannerImpl
|
||||
// end tag?
|
||||
final int c = fEntityScanner.peekChar();
|
||||
if (c == '>') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
return true;
|
||||
} else if (c == '/') {
|
||||
fEntityScanner.scanChar();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
fEntityScanner.scanChar(null);
|
||||
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
|
||||
reportFatalError("ElementUnterminated",
|
||||
new Object[]{fElementQName.rawname});
|
||||
}
|
||||
@ -1518,15 +1519,15 @@ public class XMLDocumentFragmentScannerImpl
|
||||
|
||||
// name
|
||||
if (fNamespaces) {
|
||||
fEntityScanner.scanQName(fAttributeQName);
|
||||
fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME);
|
||||
} else {
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.ATTRIBUTENAME);
|
||||
fAttributeQName.setValues(null, name, name, null);
|
||||
}
|
||||
|
||||
// equals
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('=')) {
|
||||
if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
|
||||
reportFatalError("EqRequiredInAttribute",
|
||||
new Object[] {fCurrentElement.rawname, fAttributeQName.rawname});
|
||||
}
|
||||
@ -1544,9 +1545,8 @@ public class XMLDocumentFragmentScannerImpl
|
||||
//can safely add the attribute later..
|
||||
XMLString tmpStr = getString();
|
||||
|
||||
scanAttributeValue(tmpStr, fTempString2,
|
||||
fAttributeQName.rawname, attributes,
|
||||
attIndex, isVC, fCurrentElement.rawname);
|
||||
scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes,
|
||||
attIndex, isVC, fCurrentElement.rawname, false);
|
||||
|
||||
// content
|
||||
int oldLen = attributes.getLength();
|
||||
@ -1594,13 +1594,13 @@ public class XMLDocumentFragmentScannerImpl
|
||||
if (c == '\r') {
|
||||
// happens when there is the character reference
|
||||
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
content.append((char)c);
|
||||
c = -1;
|
||||
} else if (c == ']') {
|
||||
//fStringBuffer.clear();
|
||||
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
|
||||
content.append((char)fEntityScanner.scanChar());
|
||||
content.append((char)fEntityScanner.scanChar(null));
|
||||
// remember where we are in case we get an endEntity before we
|
||||
// could flush the buffer out - this happens when we're parsing an
|
||||
// entity which ends with a ]
|
||||
@ -1609,12 +1609,12 @@ public class XMLDocumentFragmentScannerImpl
|
||||
// We work on a single character basis to handle cases such as:
|
||||
// ']]]>' which we might otherwise miss.
|
||||
//
|
||||
if (fEntityScanner.skipChar(']')) {
|
||||
if (fEntityScanner.skipChar(']', null)) {
|
||||
content.append(']');
|
||||
while (fEntityScanner.skipChar(']')) {
|
||||
while (fEntityScanner.skipChar(']', null)) {
|
||||
content.append(']');
|
||||
}
|
||||
if (fEntityScanner.skipChar('>')) {
|
||||
if (fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("CDEndInContent", null);
|
||||
}
|
||||
}
|
||||
@ -1689,7 +1689,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
} else {
|
||||
reportFatalError("InvalidCharInCDSect",
|
||||
new Object[]{Integer.toString(c,16)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
}
|
||||
//by this time we have also read surrogate contents if any...
|
||||
@ -1751,7 +1751,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
|
||||
// end
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
|
||||
reportFatalError("ETagUnterminated",
|
||||
new Object[]{rawname});
|
||||
}
|
||||
@ -1841,12 +1841,12 @@ public class XMLDocumentFragmentScannerImpl
|
||||
* notification.
|
||||
*/
|
||||
protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException {
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.REFERENCE);
|
||||
if (name == null) {
|
||||
reportFatalError("NameRequiredInReference", null);
|
||||
return;
|
||||
}
|
||||
if (!fEntityScanner.skipChar(';')) {
|
||||
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInReference", new Object []{name});
|
||||
}
|
||||
if (fEntityStore.isUnparsedEntity(name)) {
|
||||
@ -1943,6 +1943,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
*/
|
||||
private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException {
|
||||
foundBuiltInRefs = true;
|
||||
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
|
||||
content.append(c);
|
||||
if (fDocumentHandler != null) {
|
||||
fSingleChar[0] = c;
|
||||
@ -2608,13 +2609,13 @@ public class XMLDocumentFragmentScannerImpl
|
||||
switch(ch){
|
||||
case '?' :{
|
||||
setScannerState(SCANNER_STATE_PI);
|
||||
fEntityScanner.skipChar(ch);
|
||||
fEntityScanner.skipChar(ch, null);
|
||||
break;
|
||||
}
|
||||
case '!' :{
|
||||
fEntityScanner.skipChar(ch);
|
||||
if (fEntityScanner.skipChar('-')) {
|
||||
if (!fEntityScanner.skipChar('-')) {
|
||||
fEntityScanner.skipChar(ch, null);
|
||||
if (fEntityScanner.skipChar('-', null)) {
|
||||
if (!fEntityScanner.skipChar('-', NameType.COMMENT)) {
|
||||
reportFatalError("InvalidCommentStart",
|
||||
null);
|
||||
}
|
||||
@ -2629,7 +2630,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
}
|
||||
case '/' :{
|
||||
setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
|
||||
fEntityScanner.skipChar(ch);
|
||||
fEntityScanner.skipChar(ch, NameType.ELEMENTEND);
|
||||
break;
|
||||
}
|
||||
default :{
|
||||
@ -2641,9 +2642,9 @@ public class XMLDocumentFragmentScannerImpl
|
||||
}//startOfMarkup
|
||||
|
||||
private void startOfContent() throws IOException {
|
||||
if (fEntityScanner.skipChar('<')) {
|
||||
if (fEntityScanner.skipChar('<', null)) {
|
||||
setScannerState(SCANNER_STATE_START_OF_MARKUP);
|
||||
} else if (fEntityScanner.skipChar('&')) {
|
||||
} else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
|
||||
setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
|
||||
} else {
|
||||
//element content is there..
|
||||
@ -2716,10 +2717,10 @@ public class XMLDocumentFragmentScannerImpl
|
||||
case SCANNER_STATE_CONTENT: {
|
||||
final int ch = fEntityScanner.peekChar();
|
||||
if (ch == '<') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
setScannerState(SCANNER_STATE_START_OF_MARKUP);
|
||||
} else if (ch == '&') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.REFERENCE);
|
||||
setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
|
||||
break;
|
||||
} else {
|
||||
@ -2819,9 +2820,9 @@ public class XMLDocumentFragmentScannerImpl
|
||||
if(DEBUG){
|
||||
System.out.println("fTempString = " + fTempString);
|
||||
}
|
||||
if(fEntityScanner.skipChar('<')){
|
||||
if(fEntityScanner.skipChar('<', null)){
|
||||
//check if we have reached end of element
|
||||
if(fEntityScanner.skipChar('/')){
|
||||
if(fEntityScanner.skipChar('/', NameType.ELEMENTEND)){
|
||||
//increase the mark up depth
|
||||
fMarkupDepth++;
|
||||
fLastSectionWasCharacterData = false;
|
||||
@ -2871,7 +2872,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
}
|
||||
// happens when there is the character reference
|
||||
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
fUsebuffer = true;
|
||||
fContentBuffer.append((char)c);
|
||||
c = -1 ;
|
||||
@ -2879,7 +2880,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
//fStringBuffer.clear();
|
||||
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
|
||||
fUsebuffer = true;
|
||||
fContentBuffer.append((char)fEntityScanner.scanChar());
|
||||
fContentBuffer.append((char)fEntityScanner.scanChar(null));
|
||||
// remember where we are in case we get an endEntity before we
|
||||
// could flush the buffer out - this happens when we're parsing an
|
||||
// entity which ends with a ]
|
||||
@ -2888,12 +2889,12 @@ public class XMLDocumentFragmentScannerImpl
|
||||
// We work on a single character basis to handle cases such as:
|
||||
// ']]]>' which we might otherwise miss.
|
||||
//
|
||||
if (fEntityScanner.skipChar(']')) {
|
||||
if (fEntityScanner.skipChar(']', null)) {
|
||||
fContentBuffer.append(']');
|
||||
while (fEntityScanner.skipChar(']')) {
|
||||
while (fEntityScanner.skipChar(']', null)) {
|
||||
fContentBuffer.append(']');
|
||||
}
|
||||
if (fEntityScanner.skipChar('>')) {
|
||||
if (fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("CDEndInContent", null);
|
||||
}
|
||||
}
|
||||
@ -2906,12 +2907,12 @@ public class XMLDocumentFragmentScannerImpl
|
||||
// we need not to grow the buffer only when isCoalesce() is not true;
|
||||
|
||||
if (c == '<') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
setScannerState(SCANNER_STATE_START_OF_MARKUP);
|
||||
break;
|
||||
}//xxx what should be the behavior if entity reference is present in the content ?
|
||||
else if (c == '&') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.REFERENCE);
|
||||
setScannerState(SCANNER_STATE_REFERENCE);
|
||||
break;
|
||||
}///xxx since this part is also characters, it should be merged...
|
||||
@ -2924,7 +2925,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
reportFatalError("InvalidCharInContent",
|
||||
new Object[] {
|
||||
Integer.toString(c, 16)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3050,7 +3051,7 @@ public class XMLDocumentFragmentScannerImpl
|
||||
}
|
||||
fUsebuffer = true ;
|
||||
//take care of character reference
|
||||
if (fEntityScanner.skipChar('#')) {
|
||||
if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
|
||||
scanCharReferenceValue(fContentBuffer, null);
|
||||
fMarkupDepth--;
|
||||
if(!fIsCoalesce){
|
||||
@ -3106,11 +3107,11 @@ public class XMLDocumentFragmentScannerImpl
|
||||
|
||||
if (fNamespaces) {
|
||||
while (isValidNCName(fEntityScanner.peekChar())) {
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar());
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar(null));
|
||||
}
|
||||
} else {
|
||||
while (isValidNameChar(fEntityScanner.peekChar())) {
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar());
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar(null));
|
||||
}
|
||||
}
|
||||
String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
|
||||
|
||||
@ -631,7 +631,7 @@ public class XMLDocumentScannerImpl
|
||||
}
|
||||
|
||||
// root element name
|
||||
fDoctypeName = fEntityScanner.scanName();
|
||||
fDoctypeName = fEntityScanner.scanName(NameType.DOCTYPE);
|
||||
if (fDoctypeName == null) {
|
||||
reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null);
|
||||
}
|
||||
@ -671,10 +671,10 @@ public class XMLDocumentScannerImpl
|
||||
|
||||
// is there an internal subset?
|
||||
boolean internalSubset = true;
|
||||
if (!fEntityScanner.skipChar('[')) {
|
||||
if (!fEntityScanner.skipChar('[', null)) {
|
||||
internalSubset = false;
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
|
||||
}
|
||||
fMarkupDepth--;
|
||||
@ -753,7 +753,7 @@ public class XMLDocumentScannerImpl
|
||||
fStringBuffer.clear();
|
||||
fStringBuffer.append("xml");
|
||||
while (XMLChar.isName(fEntityScanner.peekChar())) {
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar());
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar(null));
|
||||
}
|
||||
String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
|
||||
//this function should fill the data.. and set the fEvent object to this event.
|
||||
@ -831,9 +831,9 @@ public class XMLDocumentScannerImpl
|
||||
switch (fScannerState) {
|
||||
case SCANNER_STATE_PROLOG: {
|
||||
fEntityScanner.skipSpaces();
|
||||
if (fEntityScanner.skipChar('<')) {
|
||||
if (fEntityScanner.skipChar('<', null)) {
|
||||
setScannerState(SCANNER_STATE_START_OF_MARKUP);
|
||||
} else if (fEntityScanner.skipChar('&')) {
|
||||
} else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
|
||||
setScannerState(SCANNER_STATE_REFERENCE);
|
||||
} else {
|
||||
setScannerState(SCANNER_STATE_CONTENT);
|
||||
@ -849,9 +849,9 @@ public class XMLDocumentScannerImpl
|
||||
setDriver(fContentDriver);
|
||||
//from now onwards this would be handled by fContentDriver,in the same next() call
|
||||
return fContentDriver.next();
|
||||
} else if (fEntityScanner.skipChar('!')) {
|
||||
if (fEntityScanner.skipChar('-')) {
|
||||
if (!fEntityScanner.skipChar('-')) {
|
||||
} else if (fEntityScanner.skipChar('!', null)) {
|
||||
if (fEntityScanner.skipChar('-', null)) {
|
||||
if (!fEntityScanner.skipChar('-', null)) {
|
||||
reportFatalError("InvalidCommentStart",
|
||||
null);
|
||||
}
|
||||
@ -871,7 +871,7 @@ public class XMLDocumentScannerImpl
|
||||
reportFatalError("MarkupNotRecognizedInProlog",
|
||||
null);
|
||||
}
|
||||
} else if (fEntityScanner.skipChar('?')) {
|
||||
} else if (fEntityScanner.skipChar('?', null)) {
|
||||
setScannerState(SCANNER_STATE_PI);
|
||||
} else {
|
||||
reportFatalError("MarkupNotRecognizedInProlog",
|
||||
@ -991,7 +991,7 @@ public class XMLDocumentScannerImpl
|
||||
|
||||
case SCANNER_STATE_CONTENT: {
|
||||
reportFatalError("ContentIllegalInProlog", null);
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
case SCANNER_STATE_REFERENCE: {
|
||||
reportFatalError("ReferenceIllegalInProlog", null);
|
||||
@ -1105,11 +1105,11 @@ public class XMLDocumentScannerImpl
|
||||
fReadingDTD=false;
|
||||
if (!moreToScan) {
|
||||
// end doctype declaration
|
||||
if (!fEntityScanner.skipChar(']')) {
|
||||
if (!fEntityScanner.skipChar(']', null)) {
|
||||
reportFatalError("DoctypedeclNotClosed", new Object[]{fDoctypeName});
|
||||
}
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
|
||||
}
|
||||
fMarkupDepth--;
|
||||
@ -1373,7 +1373,7 @@ public class XMLDocumentScannerImpl
|
||||
if(fScannerState == SCANNER_STATE_TERMINATED ){
|
||||
return XMLEvent.END_DOCUMENT ;
|
||||
}
|
||||
if (fEntityScanner.skipChar('<')) {
|
||||
if (fEntityScanner.skipChar('<', null)) {
|
||||
setScannerState(SCANNER_STATE_START_OF_MARKUP);
|
||||
} else {
|
||||
setScannerState(SCANNER_STATE_CONTENT);
|
||||
@ -1382,11 +1382,11 @@ public class XMLDocumentScannerImpl
|
||||
}
|
||||
case SCANNER_STATE_START_OF_MARKUP: {
|
||||
fMarkupDepth++;
|
||||
if (fEntityScanner.skipChar('?')) {
|
||||
if (fEntityScanner.skipChar('?', null)) {
|
||||
setScannerState(SCANNER_STATE_PI);
|
||||
} else if (fEntityScanner.skipChar('!')) {
|
||||
} else if (fEntityScanner.skipChar('!', null)) {
|
||||
setScannerState(SCANNER_STATE_COMMENT);
|
||||
} else if (fEntityScanner.skipChar('/')) {
|
||||
} else if (fEntityScanner.skipChar('/', null)) {
|
||||
reportFatalError("MarkupNotRecognizedInMisc",
|
||||
null);
|
||||
} else if (isValidNameStartChar(fEntityScanner.peekChar()) ||
|
||||
@ -1429,7 +1429,7 @@ public class XMLDocumentScannerImpl
|
||||
} else{
|
||||
reportFatalError("ContentIllegalInTrailingMisc",
|
||||
null);
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
setScannerState(SCANNER_STATE_TRAILING_MISC);
|
||||
return XMLEvent.CHARACTERS;
|
||||
}
|
||||
|
||||
@ -2066,6 +2066,7 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
|
||||
|
||||
// system id has to be a valid URI
|
||||
if (strict) {
|
||||
|
||||
try {
|
||||
// if it's already an absolute one, return it
|
||||
new URI(systemId);
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
|
||||
import com.sun.org.apache.xerces.internal.impl.XMLScanner.NameType;
|
||||
import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader;
|
||||
import com.sun.org.apache.xerces.internal.impl.io.UCSReader;
|
||||
import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader;
|
||||
@ -144,6 +145,9 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
// so that XMLStreamReader.getVersion() can find that out.
|
||||
protected boolean xmlVersionSetExplicitly = false;
|
||||
|
||||
// indicates that the operation is for detecting XML version
|
||||
boolean detectingVersion = false;
|
||||
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
@ -530,10 +534,12 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* <p>
|
||||
* <strong>Note:</strong> The character is consumed.
|
||||
*
|
||||
* @param nt The type of the name (element or attribute)
|
||||
*
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public int scanChar() throws IOException {
|
||||
protected int scanChar(NameType nt) throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(scanChar: ");
|
||||
print();
|
||||
@ -546,6 +552,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
|
||||
// scan character
|
||||
int offset = fCurrentEntity.position;
|
||||
int c = fCurrentEntity.ch[fCurrentEntity.position++];
|
||||
if (c == '\n' || (c == '\r' && isExternal)) {
|
||||
fCurrentEntity.lineNumber++;
|
||||
@ -554,6 +561,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
invokeListeners(1);
|
||||
fCurrentEntity.ch[0] = (char)c;
|
||||
load(1, false, false);
|
||||
offset = 0;
|
||||
}
|
||||
if (c == '\r' && isExternal) {
|
||||
if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') {
|
||||
@ -570,6 +578,9 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
System.out.println(" -> '"+(char)c+"'");
|
||||
}
|
||||
fCurrentEntity.columnNumber++;
|
||||
if (!detectingVersion) {
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
}
|
||||
return c;
|
||||
|
||||
} // scanChar():int
|
||||
@ -589,7 +600,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @see com.sun.org.apache.xerces.internal.util.SymbolTable
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
|
||||
*/
|
||||
public String scanNmtoken() throws IOException {
|
||||
protected String scanNmtoken() throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(scanNmtoken: ");
|
||||
print();
|
||||
@ -661,6 +672,8 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* <strong>Note:</strong> The string returned must be a symbol. The
|
||||
* SymbolTable can be used for this purpose.
|
||||
*
|
||||
* @param nt The type of the name (element or attribute)
|
||||
*
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*
|
||||
@ -668,7 +681,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart
|
||||
*/
|
||||
public String scanName() throws IOException {
|
||||
protected String scanName(NameType nt) throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(scanName: ");
|
||||
print();
|
||||
@ -682,6 +695,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
|
||||
// scan name
|
||||
int offset = fCurrentEntity.position;
|
||||
int length;
|
||||
if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) {
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
invokeListeners(1);
|
||||
@ -709,20 +723,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
vc = XMLChar.isName(c);
|
||||
}
|
||||
if(!vc)break;
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.fBufferSize) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.fBufferSize * 2];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
fCurrentEntity.fBufferSize *= 2;
|
||||
} else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
|
||||
offset = 0;
|
||||
if (load(length, false, false)) {
|
||||
break;
|
||||
@ -730,12 +731,14 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
}
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length;
|
||||
|
||||
// return name
|
||||
String symbol;
|
||||
if (length > 0) {
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, length);
|
||||
symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
|
||||
} else
|
||||
symbol = null;
|
||||
@ -759,6 +762,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* this purpose.
|
||||
*
|
||||
* @param qname The qualified name structure to fill.
|
||||
* @param nt The type of the name (element or attribute)
|
||||
*
|
||||
* @return Returns true if a qualified name appeared immediately on
|
||||
* the input and was scanned, false otherwise.
|
||||
@ -770,7 +774,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart
|
||||
*/
|
||||
public boolean scanQName(QName qname) throws IOException {
|
||||
protected boolean scanQName(QName qname, NameType nt) throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(scanQName, "+qname+": ");
|
||||
print();
|
||||
@ -806,11 +810,13 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
print();
|
||||
System.out.println(" -> true");
|
||||
}
|
||||
checkEntityLimit(nt, fCurrentEntity, 0, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
int index = -1;
|
||||
boolean vc = false;
|
||||
int length;
|
||||
while ( true){
|
||||
|
||||
//XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) ;
|
||||
@ -829,22 +835,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
//check prefix before further read
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset);
|
||||
}
|
||||
if (++fCurrentEntity.position == fCurrentEntity.count) {
|
||||
int length = fCurrentEntity.position - offset;
|
||||
//check localpart before loading more data
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length - index - 1);
|
||||
invokeListeners(length);
|
||||
if (length == fCurrentEntity.fBufferSize) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[fCurrentEntity.fBufferSize * 2];
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
tmp, 0, length);
|
||||
fCurrentEntity.ch = tmp;
|
||||
fCurrentEntity.fBufferSize *= 2;
|
||||
} else {
|
||||
System.arraycopy(fCurrentEntity.ch, offset,
|
||||
fCurrentEntity.ch, 0, length);
|
||||
}
|
||||
if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
|
||||
if (index != -1) {
|
||||
index = index - offset;
|
||||
}
|
||||
@ -854,7 +845,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
}
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length;
|
||||
if (length > 0) {
|
||||
String prefix = null;
|
||||
@ -885,6 +876,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
print();
|
||||
System.out.println(" -> true");
|
||||
}
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -899,23 +891,105 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
|
||||
} // scanQName(QName):boolean
|
||||
|
||||
/**
|
||||
* Checks whether the end of the entity buffer has been reached. If yes,
|
||||
* checks against the limit and buffer size before loading more characters.
|
||||
*
|
||||
* @param entity the current entity
|
||||
* @param offset the offset from which the current read was started
|
||||
* @param nameOffset the offset from which the current name starts
|
||||
* @return the length of characters scanned before the end of the buffer,
|
||||
* zero if there is more to be read in the buffer
|
||||
*/
|
||||
protected int checkBeforeLoad(Entity.ScannedEntity entity, int offset,
|
||||
int nameOffset) throws IOException {
|
||||
int length = 0;
|
||||
if (++entity.position == entity.count) {
|
||||
length = entity.position - offset;
|
||||
int nameLength = length;
|
||||
if (nameOffset != -1) {
|
||||
nameOffset = nameOffset - offset;
|
||||
nameLength = length - nameOffset;
|
||||
} else {
|
||||
nameOffset = offset;
|
||||
}
|
||||
//check limit before loading more data
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, entity, nameOffset, nameLength);
|
||||
invokeListeners(length);
|
||||
if (length == entity.ch.length) {
|
||||
// bad luck we have to resize our buffer
|
||||
char[] tmp = new char[entity.fBufferSize * 2];
|
||||
System.arraycopy(entity.ch, offset, tmp, 0, length);
|
||||
entity.ch = tmp;
|
||||
entity.fBufferSize *= 2;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(entity.ch, offset, entity.ch, 0, length);
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current entity is an Entity reference, check the accumulated size
|
||||
* against the limit.
|
||||
*
|
||||
* @param nt type of name (element, attribute or entity)
|
||||
* @param entity The current entity
|
||||
* @param offset The index of the first byte
|
||||
* @param length The length of the entity scanned
|
||||
*/
|
||||
protected void checkEntityLimit(NameType nt, ScannedEntity entity, int offset, int length) {
|
||||
if (entity == null || !entity.isGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nt != NameType.REFERENCE) {
|
||||
checkLimit(Limit.GENERAL_ENTITY_SIZE_LIMIT, entity, offset, length);
|
||||
}
|
||||
if (nt == NameType.ELEMENTSTART || nt == NameType.ATTRIBUTENAME) {
|
||||
checkNodeCount(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current entity is an Entity reference, counts the total nodes in
|
||||
* the entity and checks the accumulated value against the limit.
|
||||
*
|
||||
* @param entity The current entity
|
||||
*/
|
||||
protected void checkNodeCount(ScannedEntity entity) {
|
||||
if (entity != null && entity.isGE) {
|
||||
checkLimit(Limit.ENTITY_REPLACEMENT_LIMIT, entity, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the value of the specified Limit exceeds its limit
|
||||
*
|
||||
* @param limit The Limit to be checked.
|
||||
* @param entity The current entity.
|
||||
* @param limit The Limit to be checked
|
||||
* @param entity The current entity
|
||||
* @param offset The index of the first byte
|
||||
* @param length The length of the entity scanned.
|
||||
* @param length The length of the entity scanned
|
||||
*/
|
||||
protected void checkLimit(Limit limit, ScannedEntity entity, int offset, int length) {
|
||||
fLimitAnalyzer.addValue(limit, null, length);
|
||||
fLimitAnalyzer.addValue(limit, entity.name, length);
|
||||
if (fSecurityManager.isOverLimit(limit, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
Object[] e = (limit == Limit.ENTITY_REPLACEMENT_LIMIT) ?
|
||||
new Object[]{fLimitAnalyzer.getValue(limit),
|
||||
fSecurityManager.getLimit(limit), fSecurityManager.getStateLiteral(limit)} :
|
||||
new Object[]{entity.name, fLimitAnalyzer.getValue(limit),
|
||||
fSecurityManager.getLimit(limit), fSecurityManager.getStateLiteral(limit)};
|
||||
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, limit.key(),
|
||||
new Object[]{new String(entity.ch, offset, length),
|
||||
fLimitAnalyzer.getTotalValue(limit),
|
||||
fSecurityManager.getLimit(limit),
|
||||
fSecurityManager.getStateLiteral(limit)},
|
||||
e, XMLErrorReporter.SEVERITY_FATAL_ERROR);
|
||||
}
|
||||
if (fSecurityManager.isOverLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "TotalEntitySizeLimit",
|
||||
new Object[]{fLimitAnalyzer.getTotalValue(Limit.TOTAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(Limit.TOTAL_ENTITY_SIZE_LIMIT)},
|
||||
XMLErrorReporter.SEVERITY_FATAL_ERROR);
|
||||
}
|
||||
}
|
||||
@ -942,7 +1016,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public int scanContent(XMLString content) throws IOException {
|
||||
protected int scanContent(XMLString content) throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(scanContent: ");
|
||||
print();
|
||||
@ -963,6 +1037,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
int offset = fCurrentEntity.position;
|
||||
int c = fCurrentEntity.ch[offset];
|
||||
int newlines = 0;
|
||||
boolean counted = false;
|
||||
if (c == '\n' || (c == '\r' && isExternal)) {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("[newline, "+offset+", "+fCurrentEntity.position+": ");
|
||||
@ -976,9 +1051,11 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
fCurrentEntity.lineNumber++;
|
||||
fCurrentEntity.columnNumber = 1;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, newlines);
|
||||
offset = 0;
|
||||
fCurrentEntity.position = newlines;
|
||||
if (load(newlines, false, true)) {
|
||||
counted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -995,9 +1072,11 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
fCurrentEntity.lineNumber++;
|
||||
fCurrentEntity.columnNumber = 1;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, newlines);
|
||||
offset = 0;
|
||||
fCurrentEntity.position = newlines;
|
||||
if (load(newlines, false, true)) {
|
||||
counted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1011,6 +1090,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, length);
|
||||
//CHANGED: dont replace the value.. append to the buffer. This gives control to the callee
|
||||
//on buffering the data..
|
||||
content.setValues(fCurrentEntity.ch, offset, length);
|
||||
@ -1038,8 +1118,8 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
if (fCurrentEntity.isGE) {
|
||||
checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
|
||||
if (!counted) {
|
||||
checkEntityLimit(null, fCurrentEntity, offset, length);
|
||||
}
|
||||
|
||||
//CHANGED: dont replace the value.. append to the buffer. This gives control to the callee
|
||||
@ -1086,6 +1166,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @param quote The quote character that signifies the end of the
|
||||
* attribute value data.
|
||||
* @param content The content structure to fill.
|
||||
* @param isNSURI a flag indicating whether the content is a Namespace URI
|
||||
*
|
||||
* @return Returns the next character on the input, if known. This
|
||||
* value may be -1 but this does <em>note</em> designate
|
||||
@ -1094,7 +1175,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public int scanLiteral(int quote, XMLString content)
|
||||
protected int scanLiteral(int quote, XMLString content, boolean isNSURI)
|
||||
throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(scanLiteral, '"+(char)quote+"': ");
|
||||
@ -1205,8 +1286,10 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
if (fCurrentEntity.isGE) {
|
||||
checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
|
||||
|
||||
checkEntityLimit(null, fCurrentEntity, offset, length);
|
||||
if (isNSURI) {
|
||||
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
|
||||
}
|
||||
content.setValues(fCurrentEntity.ch, offset, length);
|
||||
|
||||
@ -1273,7 +1356,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public boolean scanData(String delimiter, XMLStringBuffer buffer)
|
||||
protected boolean scanData(String delimiter, XMLStringBuffer buffer)
|
||||
throws IOException {
|
||||
|
||||
boolean done = false;
|
||||
@ -1311,6 +1394,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
if (fCurrentEntity.position > fCurrentEntity.count - delimLen) {
|
||||
// something must be wrong with the input: e.g., file ends in an unterminated comment
|
||||
int length = fCurrentEntity.count - fCurrentEntity.position;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length);
|
||||
buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length);
|
||||
fCurrentEntity.columnNumber += fCurrentEntity.count;
|
||||
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
|
||||
@ -1373,6 +1457,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
buffer.append(fCurrentEntity.ch, offset, length);
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("]newline, "+offset+", "+fCurrentEntity.position+": ");
|
||||
@ -1416,12 +1501,14 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
fCurrentEntity.position--;
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
buffer.append(fCurrentEntity.ch, offset, length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
int length = fCurrentEntity.position - offset;
|
||||
fCurrentEntity.columnNumber += length - newlines;
|
||||
checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
|
||||
if (done) {
|
||||
length -= delimLen;
|
||||
}
|
||||
@ -1445,13 +1532,14 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* the specified character.
|
||||
*
|
||||
* @param c The character to skip.
|
||||
* @param nt The type of the name (element or attribute)
|
||||
*
|
||||
* @return Returns true if the character was skipped.
|
||||
*
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public boolean skipChar(int c) throws IOException {
|
||||
protected boolean skipChar(int c, NameType nt) throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(skipChar, '"+(char)c+"': ");
|
||||
print();
|
||||
@ -1464,6 +1552,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
|
||||
// skip character
|
||||
int offset = fCurrentEntity.position;
|
||||
int cc = fCurrentEntity.ch[fCurrentEntity.position];
|
||||
if (cc == c) {
|
||||
fCurrentEntity.position++;
|
||||
@ -1478,6 +1567,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
print();
|
||||
System.out.println(" -> true");
|
||||
}
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
return true;
|
||||
} else if (c == '\n' && cc == '\r' && isExternal) {
|
||||
// handle newlines
|
||||
@ -1497,6 +1587,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
print();
|
||||
System.out.println(" -> true");
|
||||
}
|
||||
checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1526,7 +1617,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
*
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
|
||||
*/
|
||||
public boolean skipSpaces() throws IOException {
|
||||
protected boolean skipSpaces() throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(skipSpaces: ");
|
||||
print();
|
||||
@ -1550,6 +1641,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
|
||||
// skip spaces
|
||||
int c = fCurrentEntity.ch[fCurrentEntity.position];
|
||||
int offset = fCurrentEntity.position - 1;
|
||||
if (XMLChar.isSpace(c)) {
|
||||
do {
|
||||
boolean entityChanged = false;
|
||||
@ -1579,6 +1671,11 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
} else {
|
||||
fCurrentEntity.columnNumber++;
|
||||
}
|
||||
|
||||
//If this is a general entity, spaces within a start element should be counted
|
||||
checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset);
|
||||
offset = fCurrentEntity.position;
|
||||
|
||||
// load more characters, if needed
|
||||
if (!entityChanged){
|
||||
fCurrentEntity.position++;
|
||||
@ -1620,7 +1717,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
|
||||
|
||||
/**
|
||||
* @param legnth This function checks that following number of characters are available.
|
||||
* @param length This function checks that following number of characters are available.
|
||||
* to the underlying buffer.
|
||||
* @return This function returns true if capacity asked is available.
|
||||
*/
|
||||
@ -1629,9 +1726,9 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param legnth This function checks that following number of characters are available.
|
||||
* @param length This function checks that following number of characters are available.
|
||||
* to the underlying buffer.
|
||||
* @param if the underlying function should change the entity
|
||||
* @param changeEntity a flag to indicate that the underlying function should change the entity
|
||||
* @return This function returns true if capacity asked is available.
|
||||
*
|
||||
*/
|
||||
@ -1694,7 +1791,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
* @throws IOException Thrown if i/o error occurs.
|
||||
* @throws EOFException Thrown on end of file.
|
||||
*/
|
||||
public boolean skipString(String s) throws IOException {
|
||||
protected boolean skipString(String s) throws IOException {
|
||||
|
||||
final int length = s.length();
|
||||
|
||||
@ -1714,6 +1811,9 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
if(afterSkip-- == beforeSkip){
|
||||
fCurrentEntity.position = fCurrentEntity.position + length ;
|
||||
fCurrentEntity.columnNumber += length;
|
||||
if (!detectingVersion) {
|
||||
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1722,7 +1822,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
return false;
|
||||
} // skipString(String):boolean
|
||||
|
||||
public boolean skipString(char [] s) throws IOException {
|
||||
protected boolean skipString(char [] s) throws IOException {
|
||||
|
||||
final int length = s.length;
|
||||
//first make sure that required capacity is avaible
|
||||
@ -1741,6 +1841,9 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
}
|
||||
fCurrentEntity.position = fCurrentEntity.position + length ;
|
||||
fCurrentEntity.columnNumber += length;
|
||||
if (!detectingVersion) {
|
||||
checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
@ -2138,7 +2241,7 @@ public class XMLEntityScanner implements XMLLocator {
|
||||
*
|
||||
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
|
||||
*/
|
||||
public final boolean skipDeclSpaces() throws IOException {
|
||||
protected final boolean skipDeclSpaces() throws IOException {
|
||||
if (DEBUG_BUFFER) {
|
||||
System.out.print("(skipDeclSpaces: ");
|
||||
//XMLEntityManager.print(fCurrentEntity);
|
||||
|
||||
@ -189,9 +189,9 @@ public class XMLNSDocumentScannerImpl
|
||||
// There are two variables,fNamespaces and fBindNamespaces
|
||||
//StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained
|
||||
if (fNamespaces) {
|
||||
fEntityScanner.scanQName(fElementQName);
|
||||
fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
|
||||
} else {
|
||||
String name = fEntityScanner.scanName();
|
||||
String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
|
||||
fElementQName.setValues(null, name, name, null);
|
||||
}
|
||||
|
||||
@ -404,11 +404,11 @@ public class XMLNSDocumentScannerImpl
|
||||
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()");
|
||||
|
||||
// name
|
||||
fEntityScanner.scanQName(fAttributeQName);
|
||||
fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE);
|
||||
|
||||
// equals
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('=')) {
|
||||
if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
|
||||
reportFatalError("EqRequiredInAttribute",
|
||||
new Object[]{fCurrentElement.rawname,fAttributeQName.rawname});
|
||||
}
|
||||
@ -430,23 +430,28 @@ public class XMLNSDocumentScannerImpl
|
||||
//since scanAttributeValue doesn't use attIndex parameter therefore we
|
||||
//can safely add the attribute later..
|
||||
XMLString tmpStr = getString();
|
||||
scanAttributeValue(tmpStr, fTempString2,
|
||||
fAttributeQName.rawname, attributes,
|
||||
attrIndex, isVC, fCurrentElement.rawname);
|
||||
|
||||
/**
|
||||
* Determine whether this is a namespace declaration that will be subject
|
||||
* to the name limit check in the scanAttributeValue operation.
|
||||
* Namespace declaration format: xmlns="..." or xmlns:prefix="..."
|
||||
* Note that prefix:xmlns="..." isn't a namespace.
|
||||
*/
|
||||
String localpart = fAttributeQName.localpart;
|
||||
String prefix = fAttributeQName.prefix != null
|
||||
? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
|
||||
boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS ||
|
||||
prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS);
|
||||
|
||||
scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes,
|
||||
attrIndex, isVC, fCurrentElement.rawname, isNSDecl);
|
||||
|
||||
String value = null;
|
||||
//fTempString.toString();
|
||||
|
||||
// record namespace declarations if any.
|
||||
if (fBindNamespaces) {
|
||||
|
||||
String localpart = fAttributeQName.localpart;
|
||||
String prefix = fAttributeQName.prefix != null
|
||||
? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
|
||||
// when it's of form xmlns="..." or xmlns:prefix="...",
|
||||
// it's a namespace declaration. but prefix:xmlns="..." isn't.
|
||||
if (prefix == XMLSymbols.PREFIX_XMLNS ||
|
||||
prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) {
|
||||
if (isNSDecl) {
|
||||
//check the length of URI
|
||||
if (tmpStr.length > fXMLNameLimit) {
|
||||
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
|
||||
|
||||
@ -114,6 +114,30 @@ public abstract class XMLScanner
|
||||
/** Debug attribute normalization. */
|
||||
protected static final boolean DEBUG_ATTR_NORMALIZATION = false;
|
||||
|
||||
/**
|
||||
* Type of names
|
||||
*/
|
||||
public static enum NameType {
|
||||
ATTRIBUTE("attribute"),
|
||||
ATTRIBUTENAME("attribute name"),
|
||||
COMMENT("comment"),
|
||||
DOCTYPE("doctype"),
|
||||
ELEMENTSTART("startelement"),
|
||||
ELEMENTEND("endelement"),
|
||||
ENTITY("entity"),
|
||||
NOTATION("notation"),
|
||||
PI("pi"),
|
||||
REFERENCE("reference");
|
||||
|
||||
final String literal;
|
||||
NameType(String literal) {
|
||||
this.literal = literal;
|
||||
}
|
||||
|
||||
String literal() {
|
||||
return literal;
|
||||
}
|
||||
}
|
||||
|
||||
//xxx: setting the default value as false, as we dont need to calculate this value
|
||||
//we should have a feature when set to true computes this value
|
||||
@ -144,7 +168,7 @@ public abstract class XMLScanner
|
||||
protected boolean fNotifyCharRefs = false;
|
||||
|
||||
/** Internal parser-settings feature */
|
||||
protected boolean fParserSettings = true;
|
||||
protected boolean fParserSettings = true;
|
||||
|
||||
// properties
|
||||
|
||||
@ -173,13 +197,13 @@ public abstract class XMLScanner
|
||||
/** event type */
|
||||
protected XMLEvent fEvent ;
|
||||
|
||||
/** Entity scanner, this alwasy works on last entity that was opened. */
|
||||
/** Entity scanner, this always works on last entity that was opened. */
|
||||
protected XMLEntityScanner fEntityScanner = null;
|
||||
|
||||
/** Entity depth. */
|
||||
protected int fEntityDepth;
|
||||
|
||||
/** Literal value of the last character refence scanned. */
|
||||
/** Literal value of the last character reference scanned. */
|
||||
protected String fCharRefLiteral = null;
|
||||
|
||||
/** Scanning attribute. */
|
||||
@ -547,10 +571,10 @@ public abstract class XMLScanner
|
||||
}
|
||||
|
||||
// end
|
||||
if (!fEntityScanner.skipChar('?')) {
|
||||
if (!fEntityScanner.skipChar('?', null)) {
|
||||
reportFatalError("XMLDeclUnterminated", null);
|
||||
}
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', null)) {
|
||||
reportFatalError("XMLDeclUnterminated", null);
|
||||
|
||||
}
|
||||
@ -577,7 +601,7 @@ public abstract class XMLScanner
|
||||
* <strong>Note:</strong> This method uses fStringBuffer2, anything in it
|
||||
* at the time of calling is lost.
|
||||
*/
|
||||
public String scanPseudoAttribute(boolean scanningTextDecl,
|
||||
protected String scanPseudoAttribute(boolean scanningTextDecl,
|
||||
XMLString value)
|
||||
throws IOException, XNIException {
|
||||
|
||||
@ -588,7 +612,7 @@ public abstract class XMLScanner
|
||||
reportFatalError("PseudoAttrNameExpected", null);
|
||||
}
|
||||
fEntityScanner.skipSpaces();
|
||||
if (!fEntityScanner.skipChar('=')) {
|
||||
if (!fEntityScanner.skipChar('=', null)) {
|
||||
reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl"
|
||||
: "EqRequiredInXMLDecl", new Object[]{name});
|
||||
}
|
||||
@ -598,15 +622,15 @@ public abstract class XMLScanner
|
||||
reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl"
|
||||
: "QuoteRequiredInXMLDecl" , new Object[]{name});
|
||||
}
|
||||
fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanLiteral(quote, value);
|
||||
fEntityScanner.scanChar(NameType.ATTRIBUTE);
|
||||
int c = fEntityScanner.scanLiteral(quote, value, false);
|
||||
if (c != quote) {
|
||||
fStringBuffer2.clear();
|
||||
do {
|
||||
fStringBuffer2.append(value);
|
||||
if (c != -1) {
|
||||
if (c == '&' || c == '%' || c == '<' || c == ']') {
|
||||
fStringBuffer2.append((char)fEntityScanner.scanChar());
|
||||
fStringBuffer2.append((char)fEntityScanner.scanChar(NameType.ATTRIBUTE));
|
||||
} else if (XMLChar.isHighSurrogate(c)) {
|
||||
scanSurrogates(fStringBuffer2);
|
||||
} else if (isInvalidLiteral(c)) {
|
||||
@ -614,15 +638,15 @@ public abstract class XMLScanner
|
||||
? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl";
|
||||
reportFatalError(key,
|
||||
new Object[] {Integer.toString(c, 16)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
}
|
||||
c = fEntityScanner.scanLiteral(quote, value);
|
||||
c = fEntityScanner.scanLiteral(quote, value, false);
|
||||
} while (c != quote);
|
||||
fStringBuffer2.append(value);
|
||||
value.setValues(fStringBuffer2);
|
||||
}
|
||||
if (!fEntityScanner.skipChar(quote)) {
|
||||
if (!fEntityScanner.skipChar(quote, null)) {
|
||||
reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl"
|
||||
: "CloseQuoteMissingInXMLDecl",
|
||||
new Object[]{name});
|
||||
@ -680,7 +704,7 @@ public abstract class XMLScanner
|
||||
|
||||
// target
|
||||
fReportEntity = false;
|
||||
String target = fEntityScanner.scanName();
|
||||
String target = fEntityScanner.scanName(NameType.PI);
|
||||
if (target == null) {
|
||||
reportFatalError("PITargetRequired", null);
|
||||
}
|
||||
@ -745,7 +769,7 @@ public abstract class XMLScanner
|
||||
} else if (isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInPI",
|
||||
new Object[]{Integer.toHexString(c)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
}
|
||||
}
|
||||
} while (fEntityScanner.scanData("?>", data));
|
||||
@ -786,11 +810,11 @@ public abstract class XMLScanner
|
||||
else if (isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInComment",
|
||||
new Object[] { Integer.toHexString(c) });
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.COMMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fEntityScanner.skipChar('>')) {
|
||||
if (!fEntityScanner.skipChar('>', NameType.COMMENT)) {
|
||||
reportFatalError("DashDashInComment", null);
|
||||
}
|
||||
|
||||
@ -811,15 +835,14 @@ public abstract class XMLScanner
|
||||
* @param checkEntities true if undeclared entities should be reported as VC violation,
|
||||
* false if undeclared entities should be reported as WFC violation.
|
||||
* @param eleName The name of element to which this attribute belongs.
|
||||
* @param isNSURI a flag indicating whether the content is a Namespace URI
|
||||
*
|
||||
* <strong>Note:</strong> This method uses fStringBuffer2, anything in it
|
||||
* at the time of calling is lost.
|
||||
**/
|
||||
protected void scanAttributeValue(XMLString value,
|
||||
XMLString nonNormalizedValue,
|
||||
String atName,
|
||||
XMLAttributes attributes, int attrIndex,
|
||||
boolean checkEntities, String eleName)
|
||||
protected void scanAttributeValue(XMLString value, XMLString nonNormalizedValue,
|
||||
String atName, XMLAttributes attributes, int attrIndex, boolean checkEntities,
|
||||
String eleName, boolean isNSURI)
|
||||
throws IOException, XNIException {
|
||||
XMLStringBuffer stringBuffer = null;
|
||||
// quote
|
||||
@ -828,10 +851,10 @@ public abstract class XMLScanner
|
||||
reportFatalError("OpenQuoteExpected", new Object[]{eleName, atName});
|
||||
}
|
||||
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.ATTRIBUTE);
|
||||
int entityDepth = fEntityDepth;
|
||||
|
||||
int c = fEntityScanner.scanLiteral(quote, value);
|
||||
int c = fEntityScanner.scanLiteral(quote, value, isNSURI);
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** scanLiteral -> \""
|
||||
+ value.toString() + "\"");
|
||||
@ -857,11 +880,11 @@ public abstract class XMLScanner
|
||||
+ stringBuffer.toString() + "\"");
|
||||
}
|
||||
if (c == '&') {
|
||||
fEntityScanner.skipChar('&');
|
||||
fEntityScanner.skipChar('&', NameType.REFERENCE);
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
|
||||
fStringBuffer2.append('&');
|
||||
}
|
||||
if (fEntityScanner.skipChar('#')) {
|
||||
if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
|
||||
fStringBuffer2.append('#');
|
||||
}
|
||||
@ -879,53 +902,20 @@ public abstract class XMLScanner
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String entityName = fEntityScanner.scanName();
|
||||
String entityName = fEntityScanner.scanName(NameType.ENTITY);
|
||||
if (entityName == null) {
|
||||
reportFatalError("NameRequiredInReference", null);
|
||||
} else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append(entityName);
|
||||
}
|
||||
if (!fEntityScanner.skipChar(';')) {
|
||||
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInReference",
|
||||
new Object []{entityName});
|
||||
} else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append(';');
|
||||
}
|
||||
if (entityName == fAmpSymbol) {
|
||||
stringBuffer.append('&');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** value5: \""
|
||||
+ stringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
} else if (entityName == fAposSymbol) {
|
||||
stringBuffer.append('\'');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** value7: \""
|
||||
+ stringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
} else if (entityName == fLtSymbol) {
|
||||
stringBuffer.append('<');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** value9: \""
|
||||
+ stringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
} else if (entityName == fGtSymbol) {
|
||||
stringBuffer.append('>');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** valueB: \""
|
||||
+ stringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
} else if (entityName == fQuotSymbol) {
|
||||
stringBuffer.append('"');
|
||||
if (DEBUG_ATTR_NORMALIZATION) {
|
||||
System.out.println("** valueD: \""
|
||||
+ stringBuffer.toString()
|
||||
+ "\"");
|
||||
}
|
||||
if (resolveCharacter(entityName, stringBuffer)) {
|
||||
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
|
||||
} else {
|
||||
if (fEntityStore.isExternalEntity(entityName)) {
|
||||
reportFatalError("ReferenceToExternalEntity",
|
||||
@ -952,12 +942,12 @@ public abstract class XMLScanner
|
||||
} else if (c == '<') {
|
||||
reportFatalError("LessthanInAttValue",
|
||||
new Object[] { eleName, atName });
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append((char)c);
|
||||
}
|
||||
} else if (c == '%' || c == ']') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
stringBuffer.append((char)c);
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append((char)c);
|
||||
@ -967,7 +957,7 @@ public abstract class XMLScanner
|
||||
+ stringBuffer.toString() + "\"");
|
||||
}
|
||||
} else if (c == '\n' || c == '\r') {
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
stringBuffer.append(' ');
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append('\n');
|
||||
@ -988,12 +978,12 @@ public abstract class XMLScanner
|
||||
} else if (c != -1 && isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInAttValue",
|
||||
new Object[] {eleName, atName, Integer.toString(c, 16)});
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append((char)c);
|
||||
}
|
||||
}
|
||||
c = fEntityScanner.scanLiteral(quote, value);
|
||||
c = fEntityScanner.scanLiteral(quote, value, isNSURI);
|
||||
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
|
||||
fStringBuffer2.append(value);
|
||||
}
|
||||
@ -1014,13 +1004,46 @@ public abstract class XMLScanner
|
||||
nonNormalizedValue.setValues(fStringBuffer2);
|
||||
|
||||
// quote
|
||||
int cquote = fEntityScanner.scanChar();
|
||||
int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
|
||||
if (cquote != quote) {
|
||||
reportFatalError("CloseQuoteExpected", new Object[]{eleName, atName});
|
||||
}
|
||||
} // scanAttributeValue()
|
||||
|
||||
|
||||
/**
|
||||
* Resolves character entity references.
|
||||
* @param entityName the name of the entity
|
||||
* @param stringBuffer the current XMLStringBuffer to append the character to.
|
||||
* @return true if resolved, false otherwise
|
||||
*/
|
||||
protected boolean resolveCharacter(String entityName, XMLStringBuffer stringBuffer) {
|
||||
/**
|
||||
* entityNames (symbols) are interned. The equals method would do the same,
|
||||
* but I'm leaving it as comparisons by references are common in the impl
|
||||
* and it made it explicit to others who read this code.
|
||||
*/
|
||||
if (entityName == fAmpSymbol) {
|
||||
stringBuffer.append('&');
|
||||
return true;
|
||||
} else if (entityName == fAposSymbol) {
|
||||
stringBuffer.append('\'');
|
||||
return true;
|
||||
} else if (entityName == fLtSymbol) {
|
||||
stringBuffer.append('<');
|
||||
return true;
|
||||
} else if (entityName == fGtSymbol) {
|
||||
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
|
||||
stringBuffer.append('>');
|
||||
return true;
|
||||
} else if (entityName == fQuotSymbol) {
|
||||
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
|
||||
stringBuffer.append('"');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans External ID and return the public and system IDs.
|
||||
*
|
||||
@ -1064,25 +1087,25 @@ public abstract class XMLScanner
|
||||
}
|
||||
reportFatalError("QuoteRequiredInSystemID", null);
|
||||
}
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
XMLString ident = fString;
|
||||
if (fEntityScanner.scanLiteral(quote, ident) != quote) {
|
||||
if (fEntityScanner.scanLiteral(quote, ident, false) != quote) {
|
||||
fStringBuffer.clear();
|
||||
do {
|
||||
fStringBuffer.append(ident);
|
||||
int c = fEntityScanner.peekChar();
|
||||
if (XMLChar.isMarkup(c) || c == ']') {
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar());
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar(null));
|
||||
} else if (c != -1 && isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInSystemID",
|
||||
new Object[] {Integer.toString(c, 16)});
|
||||
}
|
||||
} while (fEntityScanner.scanLiteral(quote, ident) != quote);
|
||||
} while (fEntityScanner.scanLiteral(quote, ident, false) != quote);
|
||||
fStringBuffer.append(ident);
|
||||
ident = fStringBuffer;
|
||||
}
|
||||
systemId = ident.toString();
|
||||
if (!fEntityScanner.skipChar(quote)) {
|
||||
if (!fEntityScanner.skipChar(quote, null)) {
|
||||
reportFatalError("SystemIDUnterminated", null);
|
||||
}
|
||||
}
|
||||
@ -1114,7 +1137,7 @@ public abstract class XMLScanner
|
||||
*/
|
||||
protected boolean scanPubidLiteral(XMLString literal)
|
||||
throws IOException, XNIException {
|
||||
int quote = fEntityScanner.scanChar();
|
||||
int quote = fEntityScanner.scanChar(null);
|
||||
if (quote != '\'' && quote != '"') {
|
||||
reportFatalError("QuoteRequiredInPublicID", null);
|
||||
return false;
|
||||
@ -1125,7 +1148,7 @@ public abstract class XMLScanner
|
||||
boolean skipSpace = true;
|
||||
boolean dataok = true;
|
||||
while (true) {
|
||||
int c = fEntityScanner.scanChar();
|
||||
int c = fEntityScanner.scanChar(null);
|
||||
if (c == ' ' || c == '\n' || c == '\r') {
|
||||
if (!skipSpace) {
|
||||
// take the first whitespace as a space and skip the others
|
||||
@ -1241,9 +1264,10 @@ public abstract class XMLScanner
|
||||
*/
|
||||
protected int scanCharReferenceValue(XMLStringBuffer buf, XMLStringBuffer buf2)
|
||||
throws IOException, XNIException {
|
||||
int initLen = buf.length;
|
||||
// scan hexadecimal value
|
||||
boolean hex = false;
|
||||
if (fEntityScanner.skipChar('x')) {
|
||||
if (fEntityScanner.skipChar('x', NameType.REFERENCE)) {
|
||||
if (buf2 != null) { buf2.append('x'); }
|
||||
hex = true;
|
||||
fStringBuffer3.clear();
|
||||
@ -1255,7 +1279,7 @@ public abstract class XMLScanner
|
||||
(c >= 'A' && c <= 'F');
|
||||
if (digit) {
|
||||
if (buf2 != null) { buf2.append((char)c); }
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.REFERENCE);
|
||||
fStringBuffer3.append((char)c);
|
||||
|
||||
do {
|
||||
@ -1265,7 +1289,7 @@ public abstract class XMLScanner
|
||||
(c >= 'A' && c <= 'F');
|
||||
if (digit) {
|
||||
if (buf2 != null) { buf2.append((char)c); }
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.REFERENCE);
|
||||
fStringBuffer3.append((char)c);
|
||||
}
|
||||
} while (digit);
|
||||
@ -1283,7 +1307,7 @@ public abstract class XMLScanner
|
||||
digit = c >= '0' && c <= '9';
|
||||
if (digit) {
|
||||
if (buf2 != null) { buf2.append((char)c); }
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.REFERENCE);
|
||||
fStringBuffer3.append((char)c);
|
||||
|
||||
do {
|
||||
@ -1291,7 +1315,7 @@ public abstract class XMLScanner
|
||||
digit = c >= '0' && c <= '9';
|
||||
if (digit) {
|
||||
if (buf2 != null) { buf2.append((char)c); }
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(NameType.REFERENCE);
|
||||
fStringBuffer3.append((char)c);
|
||||
}
|
||||
} while (digit);
|
||||
@ -1301,7 +1325,7 @@ public abstract class XMLScanner
|
||||
}
|
||||
|
||||
// end
|
||||
if (!fEntityScanner.skipChar(';')) {
|
||||
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
|
||||
reportFatalError("SemicolonRequiredInCharRef", null);
|
||||
}
|
||||
if (buf2 != null) { buf2.append(';'); }
|
||||
@ -1347,6 +1371,9 @@ public abstract class XMLScanner
|
||||
}
|
||||
}
|
||||
|
||||
if (fEntityScanner.fCurrentEntity.isGE) {
|
||||
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, buf.length - initLen);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
// returns true if the given character is not
|
||||
@ -1408,14 +1435,14 @@ public abstract class XMLScanner
|
||||
protected boolean scanSurrogates(XMLStringBuffer buf)
|
||||
throws IOException, XNIException {
|
||||
|
||||
int high = fEntityScanner.scanChar();
|
||||
int high = fEntityScanner.scanChar(null);
|
||||
int low = fEntityScanner.peekChar();
|
||||
if (!XMLChar.isLowSurrogate(low)) {
|
||||
reportFatalError("InvalidCharInContent",
|
||||
new Object[] {Integer.toString(high, 16)});
|
||||
return false;
|
||||
}
|
||||
fEntityScanner.scanChar();
|
||||
fEntityScanner.scanChar(null);
|
||||
|
||||
// convert surrogates to supplemental character
|
||||
int c = XMLChar.supplemental((char)high, (char)low);
|
||||
@ -1478,5 +1505,52 @@ public abstract class XMLScanner
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the count of the content buffer and check if the accumulated
|
||||
* value exceeds the limit
|
||||
* @param isPEDecl a flag to indicate whether the entity is parameter
|
||||
* @param entityName entity name
|
||||
* @param buffer content buffer
|
||||
*/
|
||||
void checkEntityLimit(boolean isPEDecl, String entityName, XMLString buffer) {
|
||||
checkEntityLimit(isPEDecl, entityName, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the count and check limit
|
||||
* @param isPEDecl a flag to indicate whether the entity is parameter
|
||||
* @param entityName entity name
|
||||
* @param len length of the buffer
|
||||
*/
|
||||
void checkEntityLimit(boolean isPEDecl, String entityName, int len) {
|
||||
if (fLimitAnalyzer == null) {
|
||||
fLimitAnalyzer = fEntityManager.fLimitAnalyzer;
|
||||
}
|
||||
if (isPEDecl) {
|
||||
fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, "%" + entityName, len);
|
||||
if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
reportFatalError("MaxEntitySizeLimit", new Object[]{"%" + entityName,
|
||||
fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)});
|
||||
}
|
||||
} else {
|
||||
fLimitAnalyzer.addValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName, len);
|
||||
if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
reportFatalError("MaxEntitySizeLimit", new Object[]{entityName,
|
||||
fLimitAnalyzer.getValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT)});
|
||||
}
|
||||
}
|
||||
if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
|
||||
fSecurityManager.debugPrint(fLimitAnalyzer);
|
||||
reportFatalError("TotalEntitySizeLimit",
|
||||
new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
|
||||
fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)});
|
||||
}
|
||||
}
|
||||
} // class XMLScanner
|
||||
|
||||
@ -1,62 +1,21 @@
|
||||
/*
|
||||
* reserved comment block
|
||||
* DO NOT REMOVE OR ALTER!
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* The Apache Software License, Version 1.1
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Copyright (c) 1999-2003 The Apache Software Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by the
|
||||
* Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Xerces" and "Apache Software Foundation" must
|
||||
* not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written
|
||||
* permission, please contact apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache",
|
||||
* nor may "Apache" appear in their name, without prior written
|
||||
* permission of the Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation and was
|
||||
* originally based on software copyright (c) 2003, International
|
||||
* Business Machines, Inc., http://www.apache.org. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.sun.org.apache.xerces.internal.impl;
|
||||
@ -192,40 +151,46 @@ public class XMLVersionDetector {
|
||||
// in the XML declaration.
|
||||
fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
|
||||
XMLEntityScanner scanner = fEntityManager.getEntityScanner();
|
||||
scanner.detectingVersion = true;
|
||||
try {
|
||||
if (!scanner.skipString("<?xml")) {
|
||||
// definitely not a well-formed 1.1 doc!
|
||||
scanner.detectingVersion = false;
|
||||
return Constants.XML_VERSION_1_0;
|
||||
}
|
||||
if (!scanner.skipDeclSpaces()) {
|
||||
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5);
|
||||
scanner.detectingVersion = false;
|
||||
return Constants.XML_VERSION_1_0;
|
||||
}
|
||||
if (!scanner.skipString("version")) {
|
||||
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6);
|
||||
scanner.detectingVersion = false;
|
||||
return Constants.XML_VERSION_1_0;
|
||||
}
|
||||
scanner.skipDeclSpaces();
|
||||
// Check if the next character is '='. If it is then consume it.
|
||||
if (scanner.peekChar() != '=') {
|
||||
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13);
|
||||
scanner.detectingVersion = false;
|
||||
return Constants.XML_VERSION_1_0;
|
||||
}
|
||||
scanner.scanChar();
|
||||
scanner.scanChar(null);
|
||||
scanner.skipDeclSpaces();
|
||||
int quoteChar = scanner.scanChar();
|
||||
int quoteChar = scanner.scanChar(null);
|
||||
fExpectedVersionString[14] = (char) quoteChar;
|
||||
for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) {
|
||||
fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar();
|
||||
fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar(null);
|
||||
}
|
||||
// REVISIT: should we check whether this equals quoteChar?
|
||||
fExpectedVersionString[18] = (char) scanner.scanChar();
|
||||
fExpectedVersionString[18] = (char) scanner.scanChar(null);
|
||||
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19);
|
||||
int matched = 0;
|
||||
for (; matched < XML11_VERSION.length; matched++) {
|
||||
if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched])
|
||||
break;
|
||||
}
|
||||
scanner.detectingVersion = false;
|
||||
if (matched == XML11_VERSION.length)
|
||||
return Constants.XML_VERSION_1_1;
|
||||
return Constants.XML_VERSION_1_0;
|
||||
@ -237,10 +202,9 @@ public class XMLVersionDetector {
|
||||
"PrematureEOF",
|
||||
null,
|
||||
XMLErrorReporter.SEVERITY_FATAL_ERROR);
|
||||
scanner.detectingVersion = false;
|
||||
return Constants.XML_VERSION_1_0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This method prepends "length" chars from the char array,
|
||||
|
||||
@ -298,7 +298,8 @@
|
||||
EntityExpansionLimit=JAXP00010001: The parser has encountered more than \"{0}\" entity expansions in this document; this is the limit imposed by the JDK.
|
||||
ElementAttributeLimit=JAXP00010002: Element \"{0}\" has more than \"{1}\" attributes, \"{1}\" is the limit imposed by the JDK.
|
||||
MaxEntitySizeLimit=JAXP00010003: The length of entity \"{0}\" is \"{1}\" that exceeds the \"{2}\" limit set by \"{3}\".
|
||||
TotalEntitySizeLimit=JAXP00010004: The accumulated size of entities is \"{1}\" that exceeded the \"{2}\" limit set by \"{3}\".
|
||||
TotalEntitySizeLimit=JAXP00010004: The accumulated size of entities is \"{0}\" that exceeded the \"{1}\" limit set by \"{2}\".
|
||||
MaxXMLNameLimit=JAXP00010005: The length of entity \"{0}\" is \"{1}\" that exceeds the \"{2}\" limit set by \"{3}\".
|
||||
MaxElementDepthLimit=JAXP00010006: The element \"{0}\" has a depth of \"{1}\" that exceeds the limit \"{2}\" set by \"{3}\".
|
||||
EntityReplacementLimit=JAXP00010007: The total number of nodes in entity references is \"{0}\" that is over the limit \"{1}\" set by \"{2}\".
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
||||
*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* The contents of this file are subject to the terms of either the GNU
|
||||
* General Public License Version 2 only ("GPL") or the Common Development
|
||||
@ -129,13 +129,15 @@ public final class XMLLimitAnalyzer {
|
||||
if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
|
||||
index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() ||
|
||||
index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
|
||||
index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()
|
||||
index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
|
||||
index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()
|
||||
) {
|
||||
totalValue[index] += value;
|
||||
return;
|
||||
}
|
||||
if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
|
||||
index == Limit.MAX_NAME_LIMIT.ordinal()) {
|
||||
values[index] = value;
|
||||
totalValue[index] = value;
|
||||
return;
|
||||
}
|
||||
@ -175,10 +177,13 @@ public final class XMLLimitAnalyzer {
|
||||
* @return the value of the property
|
||||
*/
|
||||
public int getValue(Limit limit) {
|
||||
return values[limit.ordinal()];
|
||||
return getValue(limit.ordinal());
|
||||
}
|
||||
|
||||
public int getValue(int index) {
|
||||
if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) {
|
||||
return totalValue[index];
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
/**
|
||||
@ -233,6 +238,11 @@ public final class XMLLimitAnalyzer {
|
||||
public void reset(Limit limit) {
|
||||
if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) {
|
||||
totalValue[limit.ordinal()] = 0;
|
||||
} else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) {
|
||||
names[limit.ordinal()] = null;
|
||||
values[limit.ordinal()] = 0;
|
||||
caches[limit.ordinal()] = null;
|
||||
totalValue[limit.ordinal()] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -78,7 +78,9 @@ public final class XMLSecurityManager {
|
||||
MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit",
|
||||
Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0),
|
||||
MAX_NAME_LIMIT("MaxXMLNameLimit",
|
||||
Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000);
|
||||
Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000),
|
||||
ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit",
|
||||
Constants.JDK_ENTITY_REPLACEMENT_LIMIT, Constants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000);
|
||||
|
||||
final String key;
|
||||
final String apiProperty;
|
||||
@ -450,6 +452,7 @@ public final class XMLSecurityManager {
|
||||
if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
|
||||
index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
|
||||
index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
|
||||
index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() ||
|
||||
index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
|
||||
index == Limit.MAX_NAME_LIMIT.ordinal()
|
||||
) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -46,7 +46,7 @@ import static org.testng.Assert.assertEquals;
|
||||
public class XSLTFunctionsTest {
|
||||
|
||||
/**
|
||||
* @bug 8062518
|
||||
* @bug 8062518 8153082
|
||||
* Verifies that a reference to the DTM created by XSLT document function is
|
||||
* actually read from the DTM by an extension function.
|
||||
* @param xml Content of xml file to process
|
||||
|
||||
@ -373,3 +373,4 @@ c42decd28bbfa817347112ed6053b5fbd30517a2 jdk-9+123
|
||||
5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125
|
||||
264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126
|
||||
06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127
|
||||
fe4e11bd2423635dc0f5f5cb9a64eb2f2cce7f4c jdk-9+128
|
||||
|
||||
@ -370,3 +370,4 @@ c40c8739bcdc88892ff58ebee3fd8a3f287be94d jdk-9+123
|
||||
073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125
|
||||
6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126
|
||||
7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127
|
||||
9446c534f0222b4eecfd9d9e25ab37c4fd4400a5 jdk-9+128
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@ -177,8 +177,14 @@ void setOSNameAndVersion(java_props_t *sprops) {
|
||||
OSVerStruct (*procInfoFn)(id rec, SEL sel) = (OSVerStruct(*)(id, SEL))objc_msgSend_stret;
|
||||
OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo],
|
||||
@selector(operatingSystemVersion));
|
||||
NSString *nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
|
||||
(long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
|
||||
NSString *nsVerStr;
|
||||
if (osVer.patchVersion == 0) { // Omit trailing ".0"
|
||||
nsVerStr = [NSString stringWithFormat:@"%ld.%ld",
|
||||
(long)osVer.majorVersion, (long)osVer.minorVersion];
|
||||
} else {
|
||||
nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
|
||||
(long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
|
||||
}
|
||||
// Copy out the char*
|
||||
osVersionCStr = strdup([nsVerStr UTF8String]);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -172,6 +172,11 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
private final int fixedKeySize; // in bytes, -1 if no restriction
|
||||
|
||||
/*
|
||||
* needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
|
||||
*/
|
||||
private boolean updateCalled;
|
||||
|
||||
/**
|
||||
* Creates an instance of AES cipher with default ECB mode and
|
||||
* PKCS5Padding.
|
||||
@ -304,6 +309,7 @@ abstract class AESCipher extends CipherSpi {
|
||||
protected void engineInit(int opmode, Key key, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
checkKeySize(key, fixedKeySize);
|
||||
updateCalled = false;
|
||||
core.init(opmode, key, random);
|
||||
}
|
||||
|
||||
@ -336,6 +342,7 @@ abstract class AESCipher extends CipherSpi {
|
||||
SecureRandom random)
|
||||
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
checkKeySize(key, fixedKeySize);
|
||||
updateCalled = false;
|
||||
core.init(opmode, key, params, random);
|
||||
}
|
||||
|
||||
@ -344,6 +351,7 @@ abstract class AESCipher extends CipherSpi {
|
||||
SecureRandom random)
|
||||
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
checkKeySize(key, fixedKeySize);
|
||||
updateCalled = false;
|
||||
core.init(opmode, key, params, random);
|
||||
}
|
||||
|
||||
@ -368,6 +376,7 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
protected byte[] engineUpdate(byte[] input, int inputOffset,
|
||||
int inputLen) {
|
||||
updateCalled = true;
|
||||
return core.update(input, inputOffset, inputLen);
|
||||
}
|
||||
|
||||
@ -397,6 +406,7 @@ abstract class AESCipher extends CipherSpi {
|
||||
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
|
||||
byte[] output, int outputOffset)
|
||||
throws ShortBufferException {
|
||||
updateCalled = true;
|
||||
return core.update(input, inputOffset, inputLen, output,
|
||||
outputOffset);
|
||||
}
|
||||
@ -433,7 +443,9 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
|
||||
throws IllegalBlockSizeException, BadPaddingException {
|
||||
return core.doFinal(input, inputOffset, inputLen);
|
||||
byte[] out = core.doFinal(input, inputOffset, inputLen);
|
||||
updateCalled = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,8 +488,10 @@ abstract class AESCipher extends CipherSpi {
|
||||
byte[] output, int outputOffset)
|
||||
throws IllegalBlockSizeException, ShortBufferException,
|
||||
BadPaddingException {
|
||||
return core.doFinal(input, inputOffset, inputLen, output,
|
||||
outputOffset);
|
||||
int outLen = core.doFinal(input, inputOffset, inputLen, output,
|
||||
outputOffset);
|
||||
updateCalled = false;
|
||||
return outLen;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -574,6 +588,9 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
@Override
|
||||
protected void engineUpdateAAD(byte[] src, int offset, int len) {
|
||||
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
|
||||
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
|
||||
}
|
||||
core.updateAAD(src, offset, len);
|
||||
}
|
||||
|
||||
@ -606,6 +623,9 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
@Override
|
||||
protected void engineUpdateAAD(ByteBuffer src) {
|
||||
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
|
||||
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
|
||||
}
|
||||
if (src != null) {
|
||||
int aadLen = src.limit() - src.position();
|
||||
if (aadLen != 0) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -124,7 +124,7 @@ final class CipherCore {
|
||||
private static final int PCBC_MODE = 4;
|
||||
private static final int CTR_MODE = 5;
|
||||
private static final int CTS_MODE = 6;
|
||||
private static final int GCM_MODE = 7;
|
||||
static final int GCM_MODE = 7;
|
||||
|
||||
/*
|
||||
* variables used for performing the GCM (key+iv) uniqueness check.
|
||||
@ -196,7 +196,7 @@ final class CipherCore {
|
||||
cipher = new CounterMode(rawImpl);
|
||||
unitBytes = 1;
|
||||
padding = null;
|
||||
} else if (modeUpperCase.startsWith("GCM")) {
|
||||
} else if (modeUpperCase.equals("GCM")) {
|
||||
// can only be used for block ciphers w/ 128-bit block size
|
||||
if (blockSize != 16) {
|
||||
throw new NoSuchAlgorithmException
|
||||
@ -223,6 +223,15 @@ final class CipherCore {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode of this cipher.
|
||||
*
|
||||
* @return the parsed cipher mode
|
||||
*/
|
||||
int getMode() {
|
||||
return cipherMode;
|
||||
}
|
||||
|
||||
private static int getNumOfUnit(String mode, int offset, int blockSize)
|
||||
throws NoSuchAlgorithmException {
|
||||
int result = blockSize; // use blockSize as default value
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -49,6 +49,16 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
|
||||
static int DEFAULT_IV_LEN = 12; // in bytes
|
||||
|
||||
// In NIST SP 800-38D, GCM input size is limited to be no longer
|
||||
// than (2^36 - 32) bytes. Otherwise, the counter will wrap
|
||||
// around and lead to a leak of plaintext.
|
||||
// However, given the current GCM spec requirement that recovered
|
||||
// text can only be returned after successful tag verification,
|
||||
// we are bound by limiting the data size to the size limit of
|
||||
// java byte array, e.g. Integer.MAX_VALUE, since all data
|
||||
// can only be returned by the doFinal(...) call.
|
||||
private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
// buffer for AAD data; if null, meaning update has been called
|
||||
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
|
||||
private int sizeOfAAD = 0;
|
||||
@ -89,9 +99,13 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
}
|
||||
}
|
||||
|
||||
// ivLen in bits
|
||||
private static byte[] getLengthBlock(int ivLen) {
|
||||
private static byte[] getLengthBlock(int ivLenInBytes) {
|
||||
long ivLen = ((long)ivLenInBytes) << 3;
|
||||
byte[] out = new byte[AES_BLOCK_SIZE];
|
||||
out[8] = (byte)(ivLen >>> 56);
|
||||
out[9] = (byte)(ivLen >>> 48);
|
||||
out[10] = (byte)(ivLen >>> 40);
|
||||
out[11] = (byte)(ivLen >>> 32);
|
||||
out[12] = (byte)(ivLen >>> 24);
|
||||
out[13] = (byte)(ivLen >>> 16);
|
||||
out[14] = (byte)(ivLen >>> 8);
|
||||
@ -99,13 +113,22 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
return out;
|
||||
}
|
||||
|
||||
// aLen and cLen both in bits
|
||||
private static byte[] getLengthBlock(int aLen, int cLen) {
|
||||
private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
|
||||
long aLen = ((long)aLenInBytes) << 3;
|
||||
long cLen = ((long)cLenInBytes) << 3;
|
||||
byte[] out = new byte[AES_BLOCK_SIZE];
|
||||
out[0] = (byte)(aLen >>> 56);
|
||||
out[1] = (byte)(aLen >>> 48);
|
||||
out[2] = (byte)(aLen >>> 40);
|
||||
out[3] = (byte)(aLen >>> 32);
|
||||
out[4] = (byte)(aLen >>> 24);
|
||||
out[5] = (byte)(aLen >>> 16);
|
||||
out[6] = (byte)(aLen >>> 8);
|
||||
out[7] = (byte)aLen;
|
||||
out[8] = (byte)(cLen >>> 56);
|
||||
out[9] = (byte)(cLen >>> 48);
|
||||
out[10] = (byte)(cLen >>> 40);
|
||||
out[11] = (byte)(cLen >>> 32);
|
||||
out[12] = (byte)(cLen >>> 24);
|
||||
out[13] = (byte)(cLen >>> 16);
|
||||
out[14] = (byte)(cLen >>> 8);
|
||||
@ -142,13 +165,20 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
} else {
|
||||
g.update(iv);
|
||||
}
|
||||
byte[] lengthBlock = getLengthBlock(iv.length*8);
|
||||
byte[] lengthBlock = getLengthBlock(iv.length);
|
||||
g.update(lengthBlock);
|
||||
j0 = g.digest();
|
||||
}
|
||||
return j0;
|
||||
}
|
||||
|
||||
private static void checkDataLength(int processed, int len) {
|
||||
if (processed > MAX_BUF_SIZE - len) {
|
||||
throw new ProviderException("SunJCE provider only supports " +
|
||||
"input size up to " + MAX_BUF_SIZE + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
GaloisCounterMode(SymmetricCipher embeddedCipher) {
|
||||
super(embeddedCipher);
|
||||
aadBuffer = new ByteArrayOutputStream();
|
||||
@ -319,20 +349,22 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
|
||||
// Feed the AAD data to GHASH, pad if necessary
|
||||
void processAAD() {
|
||||
if (aadBuffer != null && aadBuffer.size() > 0) {
|
||||
byte[] aad = aadBuffer.toByteArray();
|
||||
sizeOfAAD = aad.length;
|
||||
aadBuffer = null;
|
||||
if (aadBuffer != null) {
|
||||
if (aadBuffer.size() > 0) {
|
||||
byte[] aad = aadBuffer.toByteArray();
|
||||
sizeOfAAD = aad.length;
|
||||
|
||||
int lastLen = aad.length % AES_BLOCK_SIZE;
|
||||
if (lastLen != 0) {
|
||||
ghashAllToS.update(aad, 0, aad.length - lastLen);
|
||||
byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
|
||||
lastLen);
|
||||
ghashAllToS.update(padded);
|
||||
} else {
|
||||
ghashAllToS.update(aad);
|
||||
int lastLen = aad.length % AES_BLOCK_SIZE;
|
||||
if (lastLen != 0) {
|
||||
ghashAllToS.update(aad, 0, aad.length - lastLen);
|
||||
byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
|
||||
lastLen);
|
||||
ghashAllToS.update(padded);
|
||||
} else {
|
||||
ghashAllToS.update(aad);
|
||||
}
|
||||
}
|
||||
aadBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,6 +416,9 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
if ((len % blockSize) != 0) {
|
||||
throw new ProviderException("Internal error in input buffering");
|
||||
}
|
||||
|
||||
checkDataLength(processed, len);
|
||||
|
||||
processAAD();
|
||||
if (len > 0) {
|
||||
gctrPAndC.update(in, inOfs, len, out, outOfs);
|
||||
@ -405,17 +440,23 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
*/
|
||||
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
|
||||
throws IllegalBlockSizeException, ShortBufferException {
|
||||
if (len > MAX_BUF_SIZE - tagLenBytes) {
|
||||
throw new ShortBufferException
|
||||
("Can't fit both data and tag into one buffer");
|
||||
}
|
||||
if (out.length - outOfs < (len + tagLenBytes)) {
|
||||
throw new ShortBufferException("Output buffer too small");
|
||||
}
|
||||
|
||||
checkDataLength(processed, len);
|
||||
|
||||
processAAD();
|
||||
if (len > 0) {
|
||||
doLastBlock(in, inOfs, len, out, outOfs, true);
|
||||
}
|
||||
|
||||
byte[] lengthBlock =
|
||||
getLengthBlock(sizeOfAAD*8, processed*8);
|
||||
getLengthBlock(sizeOfAAD, processed);
|
||||
ghashAllToS.update(lengthBlock);
|
||||
byte[] s = ghashAllToS.digest();
|
||||
byte[] sOut = new byte[s.length];
|
||||
@ -447,6 +488,9 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
if ((len % blockSize) != 0) {
|
||||
throw new ProviderException("Internal error in input buffering");
|
||||
}
|
||||
|
||||
checkDataLength(ibuffer.size(), len);
|
||||
|
||||
processAAD();
|
||||
|
||||
if (len > 0) {
|
||||
@ -481,10 +525,21 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
if (len < tagLenBytes) {
|
||||
throw new AEADBadTagException("Input too short - need tag");
|
||||
}
|
||||
// do this check here can also catch the potential integer overflow
|
||||
// scenario for the subsequent output buffer capacity check.
|
||||
checkDataLength(ibuffer.size(), (len - tagLenBytes));
|
||||
|
||||
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
|
||||
throw new ShortBufferException("Output buffer too small");
|
||||
}
|
||||
|
||||
processAAD();
|
||||
|
||||
// get the trailing tag bytes from 'in'
|
||||
byte[] tag = new byte[tagLenBytes];
|
||||
System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
|
||||
len -= tagLenBytes;
|
||||
|
||||
if (len != 0) {
|
||||
ibuffer.write(in, inOfs, len);
|
||||
}
|
||||
@ -495,17 +550,12 @@ final class GaloisCounterMode extends FeedbackCipher {
|
||||
len = in.length;
|
||||
ibuffer.reset();
|
||||
|
||||
byte[] tag = new byte[tagLenBytes];
|
||||
// get the trailing tag bytes from 'in'
|
||||
System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
|
||||
len -= tagLenBytes;
|
||||
|
||||
if (len > 0) {
|
||||
doLastBlock(in, inOfs, len, out, outOfs, false);
|
||||
}
|
||||
|
||||
byte[] lengthBlock =
|
||||
getLengthBlock(sizeOfAAD*8, processed*8);
|
||||
getLengthBlock(sizeOfAAD, processed);
|
||||
ghashAllToS.update(lengthBlock);
|
||||
|
||||
byte[] s = ghashAllToS.digest();
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
|
||||
@ -238,15 +238,11 @@ public final class Class<T> implements java.io.Serializable,
|
||||
|
||||
TypeVariable<?>[] typeparms = component.getTypeParameters();
|
||||
if (typeparms.length > 0) {
|
||||
boolean first = true;
|
||||
sb.append('<');
|
||||
StringJoiner sj = new StringJoiner(",", "<", ">");
|
||||
for(TypeVariable<?> typeparm: typeparms) {
|
||||
if (!first)
|
||||
sb.append(',');
|
||||
sb.append(typeparm.getTypeName());
|
||||
first = false;
|
||||
sj.add(typeparm.getTypeName());
|
||||
}
|
||||
sb.append('>');
|
||||
sb.append(sj.toString());
|
||||
}
|
||||
|
||||
for (int i = 0; i < arrayDepth; i++)
|
||||
|
||||
@ -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 SE Platform. A version string contains a version number
|
||||
* optionally followed by pre-release and build information.
|
||||
*
|
||||
@ -1058,10 +1058,10 @@ public class Runtime {
|
||||
* <p> When comparing two version strings, the value of {@code $OPT}, if
|
||||
* present, may or may not be significant depending on the chosen
|
||||
* comparison method. The comparison methods {@link #compareTo(Version)
|
||||
* compareTo()} and {@link #compareToIgnoreOpt(Version)
|
||||
* compareToIgnoreOpt()} should be used consistently with the
|
||||
* compareTo()} and {@link #compareToIgnoreOptional(Version)
|
||||
* compareToIgnoreOptional()} should be used consistently with the
|
||||
* corresponding methods {@link #equals(Object) equals()} and {@link
|
||||
* #equalsIgnoreOpt(Object) equalsIgnoreOpt()}. </p>
|
||||
* #equalsIgnoreOptional(Object) equalsIgnoreOptional()}. </p>
|
||||
*
|
||||
* <p> A <em>short version string</em>, {@code $SVSTR}, often useful in
|
||||
* less formal contexts, is a version number optionally followed by a
|
||||
@ -1249,7 +1249,7 @@ public class Runtime {
|
||||
* @throws NullPointerException
|
||||
* If the given object is {@code null}
|
||||
*/
|
||||
public int compareToIgnoreOpt(Version ob) {
|
||||
public int compareToIgnoreOptional(Version ob) {
|
||||
return compare(ob, true);
|
||||
}
|
||||
|
||||
@ -1270,7 +1270,7 @@ public class Runtime {
|
||||
return ret;
|
||||
|
||||
if (!ignoreOpt)
|
||||
return compareOpt(ob);
|
||||
return compareOptional(ob);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1325,7 +1325,7 @@ public class Runtime {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int compareOpt(Version ob) {
|
||||
private int compareOptional(Version ob) {
|
||||
Optional<String> oOpt = ob.optional();
|
||||
if (!optional.isPresent()) {
|
||||
if (oOpt.isPresent())
|
||||
@ -1384,7 +1384,7 @@ public class Runtime {
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
boolean ret = equalsIgnoreOpt(ob);
|
||||
boolean ret = equalsIgnoreOptional(ob);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
@ -1407,7 +1407,7 @@ public class Runtime {
|
||||
* ignoring the optinal build information
|
||||
*
|
||||
*/
|
||||
public boolean equalsIgnoreOpt(Object ob) {
|
||||
public boolean equalsIgnoreOptional(Object ob) {
|
||||
if (this == ob)
|
||||
return true;
|
||||
if (!(ob instanceof Version))
|
||||
|
||||
@ -155,7 +155,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
private static LambdaForm preparedLambdaForm(MemberName m) {
|
||||
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
|
||||
MethodType mtype = m.getInvocationType().basicType();
|
||||
assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
|
||||
assert(!m.isMethodHandleInvoke()) : m;
|
||||
int which;
|
||||
switch (m.getReferenceKind()) {
|
||||
case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
|
||||
|
||||
@ -1049,7 +1049,7 @@ class LambdaForm {
|
||||
this.member = member;
|
||||
this.resolvedHandle = resolvedHandle;
|
||||
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
|
||||
//assert(!isInvokeBasic());
|
||||
//assert(!isInvokeBasic(member));
|
||||
}
|
||||
NamedFunction(MethodType basicInvokerType) {
|
||||
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
|
||||
@ -1060,13 +1060,13 @@ class LambdaForm {
|
||||
// necessary to pass BigArityTest
|
||||
this.member = Invokers.invokeBasicMethod(basicInvokerType);
|
||||
}
|
||||
assert(isInvokeBasic());
|
||||
assert(isInvokeBasic(member));
|
||||
}
|
||||
|
||||
private boolean isInvokeBasic() {
|
||||
private static boolean isInvokeBasic(MemberName member) {
|
||||
return member != null &&
|
||||
member.isMethodHandleInvoke() &&
|
||||
"invokeBasic".equals(member.getName());
|
||||
member.getDeclaringClass() == MethodHandle.class &&
|
||||
"invokeBasic".equals(member.getName());
|
||||
}
|
||||
|
||||
// The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc.
|
||||
@ -1204,7 +1204,7 @@ class LambdaForm {
|
||||
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
|
||||
: Arrays.asList(mh, rtype, arity);
|
||||
MemberName member = mh.internalMemberName();
|
||||
if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
|
||||
if (isInvokeBasic(member)) {
|
||||
assert(arity > 0);
|
||||
assert(a[0] instanceof MethodHandle);
|
||||
MethodHandle mh2 = (MethodHandle) a[0];
|
||||
|
||||
@ -346,7 +346,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
}
|
||||
|
||||
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
|
||||
* Also returns true for the non-public MH.invokeBasic.
|
||||
*/
|
||||
public boolean isMethodHandleInvoke() {
|
||||
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
|
||||
@ -361,7 +360,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
switch (name) {
|
||||
case "invoke":
|
||||
case "invokeExact":
|
||||
case "invokeBasic": // internal sig-poly method
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@ -951,8 +951,6 @@ assertEquals("", (String) MH_newString.invokeExact());
|
||||
return invoker(type);
|
||||
if ("invokeExact".equals(name))
|
||||
return exactInvoker(type);
|
||||
if ("invokeBasic".equals(name))
|
||||
return basicInvoker(type);
|
||||
assert(!MemberName.isMethodHandleInvokeName(name));
|
||||
return null;
|
||||
}
|
||||
@ -3268,6 +3266,16 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
|
||||
*/
|
||||
public static
|
||||
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
|
||||
return dropArguments0(target, pos, copyTypes(valueTypes));
|
||||
}
|
||||
|
||||
private static List<Class<?>> copyTypes(List<Class<?>> types) {
|
||||
Object[] a = types.toArray();
|
||||
return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
|
||||
}
|
||||
|
||||
private static
|
||||
MethodHandle dropArguments0(MethodHandle target, int pos, List<Class<?>> valueTypes) {
|
||||
MethodType oldType = target.type(); // get NPE
|
||||
int dropped = dropArgumentChecks(oldType, pos, valueTypes);
|
||||
MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
|
||||
@ -3348,6 +3356,7 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
|
||||
// private version which allows caller some freedom with error handling
|
||||
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos,
|
||||
boolean nullOnFailure) {
|
||||
newTypes = copyTypes(newTypes);
|
||||
List<Class<?>> oldTypes = target.type().parameterList();
|
||||
int match = oldTypes.size();
|
||||
if (skip != 0) {
|
||||
@ -3379,11 +3388,11 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
|
||||
// target: ( S*[skip], M*[match] )
|
||||
MethodHandle adapter = target;
|
||||
if (add > 0) {
|
||||
adapter = dropArguments(adapter, skip+ match, addTypes);
|
||||
adapter = dropArguments0(adapter, skip+ match, addTypes);
|
||||
}
|
||||
// adapter: (S*[skip], M*[match], A*[add] )
|
||||
if (pos > 0) {
|
||||
adapter = dropArguments(adapter, skip, newTypes.subList(0, pos));
|
||||
adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos));
|
||||
}
|
||||
// adapter: (S*[skip], P*[pos], M*[match], A*[add] )
|
||||
return adapter;
|
||||
@ -3787,7 +3796,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
int filterValues = filterType.parameterCount();
|
||||
if (filterValues == 0
|
||||
? (rtype != void.class)
|
||||
: (rtype != filterType.parameterType(0)))
|
||||
: (rtype != filterType.parameterType(0) || filterValues != 1))
|
||||
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||
}
|
||||
|
||||
@ -4290,7 +4299,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
|
||||
}
|
||||
if (pred.get(i) == null) {
|
||||
pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence));
|
||||
pred.set(i, dropArguments0(constant(boolean.class, true), 0, commonParameterSequence));
|
||||
}
|
||||
if (fini.get(i) == null) {
|
||||
fini.set(i, empty(methodType(t, commonParameterSequence)));
|
||||
@ -4315,7 +4324,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
return hs.stream().map(h -> {
|
||||
int pc = h.type().parameterCount();
|
||||
int tpsize = targetParams.size();
|
||||
return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
|
||||
return pc < tpsize ? dropArguments0(h, pc, targetParams.subList(pc, tpsize)) : h;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ import java.util.jar.Manifest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@ -420,7 +421,7 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
// scan the entries in the JAR file to locate the .class and service
|
||||
// configuration file
|
||||
Map<Boolean, Set<String>> map =
|
||||
jf.stream()
|
||||
versionedStream(jf)
|
||||
.map(JarEntry::getName)
|
||||
.filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
|
||||
.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
|
||||
@ -503,8 +504,21 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
return mn;
|
||||
}
|
||||
|
||||
private Stream<JarEntry> versionedStream(JarFile jf) {
|
||||
if (jf.isMultiRelease()) {
|
||||
// a stream of JarEntries whose names are base names and whose
|
||||
// contents are from the corresponding versioned entries in
|
||||
// a multi-release jar file
|
||||
return jf.stream().map(JarEntry::getName)
|
||||
.filter(name -> !name.startsWith("META-INF/versions/"))
|
||||
.map(jf::getJarEntry);
|
||||
} else {
|
||||
return jf.stream();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> jarPackages(JarFile jf) {
|
||||
return jf.stream()
|
||||
return versionedStream(jf)
|
||||
.filter(e -> e.getName().endsWith(".class"))
|
||||
.map(e -> toPackageName(e.getName()))
|
||||
.filter(pkg -> pkg.length() > 0) // module-info
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,6 +28,7 @@ package java.lang.reflect;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import sun.reflect.annotation.AnnotationParser;
|
||||
@ -86,15 +87,6 @@ public abstract class Executable extends AccessibleObject
|
||||
getDeclaringClass());
|
||||
}
|
||||
|
||||
void separateWithCommas(Class<?>[] types, StringBuilder sb) {
|
||||
for (int j = 0; j < types.length; j++) {
|
||||
sb.append(types[j].getTypeName());
|
||||
if (j < (types.length - 1))
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) {
|
||||
int mod = getModifiers() & mask;
|
||||
|
||||
@ -121,13 +113,20 @@ public abstract class Executable extends AccessibleObject
|
||||
|
||||
printModifiersIfNonzero(sb, modifierMask, isDefault);
|
||||
specificToStringHeader(sb);
|
||||
|
||||
sb.append('(');
|
||||
separateWithCommas(parameterTypes, sb);
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
sj.add(parameterType.getTypeName());
|
||||
}
|
||||
sb.append(sj.toString());
|
||||
sb.append(')');
|
||||
|
||||
if (exceptionTypes.length > 0) {
|
||||
sb.append(" throws ");
|
||||
separateWithCommas(exceptionTypes, sb);
|
||||
StringJoiner joiner = new StringJoiner(",", "throws ", "");
|
||||
for (Class<?> exceptionType : exceptionTypes) {
|
||||
joiner.add(exceptionType.getTypeName());
|
||||
}
|
||||
sb.append(joiner.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
@ -149,42 +148,34 @@ public abstract class Executable extends AccessibleObject
|
||||
|
||||
TypeVariable<?>[] typeparms = getTypeParameters();
|
||||
if (typeparms.length > 0) {
|
||||
boolean first = true;
|
||||
sb.append('<');
|
||||
StringJoiner sj = new StringJoiner(",", "<", "> ");
|
||||
for(TypeVariable<?> typeparm: typeparms) {
|
||||
if (!first)
|
||||
sb.append(',');
|
||||
// Class objects can't occur here; no need to test
|
||||
// and call Class.getName().
|
||||
sb.append(typeparm.toString());
|
||||
first = false;
|
||||
sj.add(typeparm.getTypeName());
|
||||
}
|
||||
sb.append("> ");
|
||||
sb.append(sj.toString());
|
||||
}
|
||||
|
||||
specificToGenericStringHeader(sb);
|
||||
|
||||
sb.append('(');
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
Type[] params = getGenericParameterTypes();
|
||||
for (int j = 0; j < params.length; j++) {
|
||||
String param = params[j].getTypeName();
|
||||
if (isVarArgs() && (j == params.length - 1)) // replace T[] with T...
|
||||
param = param.replaceFirst("\\[\\]$", "...");
|
||||
sb.append(param);
|
||||
if (j < (params.length - 1))
|
||||
sb.append(',');
|
||||
sj.add(param);
|
||||
}
|
||||
sb.append(sj.toString());
|
||||
sb.append(')');
|
||||
Type[] exceptions = getGenericExceptionTypes();
|
||||
if (exceptions.length > 0) {
|
||||
sb.append(" throws ");
|
||||
for (int k = 0; k < exceptions.length; k++) {
|
||||
sb.append((exceptions[k] instanceof Class)?
|
||||
((Class)exceptions[k]).getName():
|
||||
exceptions[k].toString());
|
||||
if (k < (exceptions.length - 1))
|
||||
sb.append(',');
|
||||
|
||||
Type[] exceptionTypes = getGenericExceptionTypes();
|
||||
if (exceptionTypes.length > 0) {
|
||||
StringJoiner joiner = new StringJoiner(",", " throws ", "");
|
||||
for (Type exceptionType : exceptionTypes) {
|
||||
joiner.add(exceptionType.getTypeName());
|
||||
}
|
||||
sb.append(joiner.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -461,11 +461,10 @@ public final class URLPermission extends Permission {
|
||||
}
|
||||
|
||||
private String actions() {
|
||||
String b = String.join(",", methods);
|
||||
if (!requestHeaders.isEmpty()) {
|
||||
b += ":" + String.join(",", requestHeaders);
|
||||
}
|
||||
return b;
|
||||
// The colon separator is optional when the request headers list is
|
||||
// empty.This implementation chooses to include it even when the request
|
||||
// headers list is empty.
|
||||
return String.join(",", methods) + ":" + String.join(",", requestHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -132,7 +132,7 @@ public class ProtectionDomain {
|
||||
|
||||
/* the PermissionCollection is static (pre 1.4 constructor)
|
||||
or dynamic (via a policy refresh) */
|
||||
private boolean staticPermissions;
|
||||
private final boolean staticPermissions;
|
||||
|
||||
/*
|
||||
* An object used as a key when the ProtectionDomain is stored in a Map.
|
||||
@ -143,8 +143,12 @@ public class ProtectionDomain {
|
||||
* Creates a new ProtectionDomain with the given CodeSource and
|
||||
* Permissions. If the permissions object is not null, then
|
||||
* {@code setReadOnly()} will be called on the passed in
|
||||
* Permissions object. The only permissions granted to this domain
|
||||
* are the ones specified; the current Policy will not be consulted.
|
||||
* Permissions object.
|
||||
* <p>
|
||||
* The permissions granted to this domain are static, i.e.
|
||||
* invoking the {@link #staticPermissionsOnly()} method returns true.
|
||||
* They contain only the ones passed to this constructor and
|
||||
* the current Policy will not be consulted.
|
||||
*
|
||||
* @param codesource the codesource associated with this domain
|
||||
* @param permissions the permissions granted to this domain
|
||||
@ -170,9 +174,11 @@ public class ProtectionDomain {
|
||||
* Permissions, ClassLoader and array of Principals. If the
|
||||
* permissions object is not null, then {@code setReadOnly()}
|
||||
* will be called on the passed in Permissions object.
|
||||
* The permissions granted to this domain are dynamic; they include
|
||||
* both the static permissions passed to this constructor, and any
|
||||
* permissions granted to this domain by the current Policy at the
|
||||
* <p>
|
||||
* The permissions granted to this domain are dynamic, i.e.
|
||||
* invoking the {@link #staticPermissionsOnly()} method returns false.
|
||||
* They include both the static permissions passed to this constructor,
|
||||
* and any permissions granted to this domain by the current Policy at the
|
||||
* time a permission is checked.
|
||||
* <p>
|
||||
* This constructor is typically used by
|
||||
@ -255,6 +261,19 @@ public class ProtectionDomain {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this domain contains only static permissions
|
||||
* and does not check the current {@code Policy} at the time of
|
||||
* permission checking.
|
||||
*
|
||||
* @return true if this domain contains only static permissions.
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean staticPermissionsOnly() {
|
||||
return this.staticPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and see if this ProtectionDomain implies the permissions
|
||||
* expressed in the Permission object.
|
||||
@ -263,25 +282,19 @@ public class ProtectionDomain {
|
||||
* ProtectionDomain was constructed with a static set of permissions
|
||||
* or it was bound to a dynamically mapped set of permissions.
|
||||
* <p>
|
||||
* If the ProtectionDomain was constructed to a
|
||||
* {@link #ProtectionDomain(CodeSource, PermissionCollection)
|
||||
* statically bound} PermissionCollection then the permission will
|
||||
* only be checked against the PermissionCollection supplied at
|
||||
* construction.
|
||||
* If the {@link #staticPermissionsOnly()} method returns
|
||||
* true, then the permission will only be checked against the
|
||||
* PermissionCollection supplied at construction.
|
||||
* <p>
|
||||
* However, if the ProtectionDomain was constructed with
|
||||
* the constructor variant which supports
|
||||
* {@link #ProtectionDomain(CodeSource, PermissionCollection,
|
||||
* ClassLoader, java.security.Principal[]) dynamically binding}
|
||||
* permissions, then the permission will be checked against the
|
||||
* combination of the PermissionCollection supplied at construction and
|
||||
* Otherwise, the permission will be checked against the combination
|
||||
* of the PermissionCollection supplied at construction and
|
||||
* the current Policy binding.
|
||||
*
|
||||
* @param permission the Permission object to check.
|
||||
* @param perm the Permission object to check.
|
||||
*
|
||||
* @return true if "permission" is implicit to this ProtectionDomain.
|
||||
* @return true if {@code perm} is implied by this ProtectionDomain.
|
||||
*/
|
||||
public boolean implies(Permission permission) {
|
||||
public boolean implies(Permission perm) {
|
||||
|
||||
if (hasAllPerm) {
|
||||
// internal permission collection already has AllPermission -
|
||||
@ -290,10 +303,10 @@ public class ProtectionDomain {
|
||||
}
|
||||
|
||||
if (!staticPermissions &&
|
||||
Policy.getPolicyNoCheck().implies(this, permission))
|
||||
Policy.getPolicyNoCheck().implies(this, perm))
|
||||
return true;
|
||||
if (permissions != null)
|
||||
return permissions.implies(permission);
|
||||
return permissions.implies(perm);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved
|
||||
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
||||
@ -124,7 +124,6 @@ package java.util;
|
||||
* always well-defined for queues with the same elements but different
|
||||
* ordering properties.
|
||||
*
|
||||
*
|
||||
* <p>This interface is a member of the
|
||||
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
|
||||
* Java Collections Framework</a>.
|
||||
|
||||
@ -660,6 +660,7 @@ public abstract class ResourceBundle {
|
||||
|
||||
// ResourceBundleProviders for loading ResourceBundles
|
||||
private ServiceLoader<ResourceBundleProvider> providers;
|
||||
private boolean providersChecked;
|
||||
|
||||
// Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
|
||||
private Boolean callerHasProvider;
|
||||
@ -675,7 +676,6 @@ public abstract class ResourceBundle {
|
||||
this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
|
||||
}
|
||||
this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
|
||||
this.providers = getServiceLoader(module, baseName);
|
||||
calculateHashCode();
|
||||
}
|
||||
|
||||
@ -712,11 +712,15 @@ public abstract class ResourceBundle {
|
||||
}
|
||||
|
||||
ServiceLoader<ResourceBundleProvider> getProviders() {
|
||||
if (!providersChecked) {
|
||||
providers = getServiceLoader(getModule(), name);
|
||||
providersChecked = true;
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
boolean hasProviders() {
|
||||
return providers != null;
|
||||
return getProviders() != null;
|
||||
}
|
||||
|
||||
boolean callerHasProvider() {
|
||||
@ -789,8 +793,9 @@ public abstract class ResourceBundle {
|
||||
}
|
||||
clone.moduleRef = new KeyElementReference<>(getModule(),
|
||||
referenceQueue, clone);
|
||||
// Clear the reference to ResourceBundleProviders
|
||||
// Clear the reference to ResourceBundleProviders and the flag
|
||||
clone.providers = null;
|
||||
clone.providersChecked = false;
|
||||
// Clear the reference to a Throwable
|
||||
clone.cause = null;
|
||||
// Clear callerHasProvider
|
||||
@ -1841,6 +1846,9 @@ public abstract class ResourceBundle {
|
||||
|
||||
private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,
|
||||
String baseName) {
|
||||
if (!module.isNamed()) {
|
||||
return null;
|
||||
}
|
||||
PrivilegedAction<ClassLoader> pa = module::getClassLoader;
|
||||
ClassLoader loader = AccessController.doPrivileged(pa);
|
||||
return getServiceLoader(module, loader, baseName);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -68,6 +68,7 @@ import java.util.function.ToIntFunction;
|
||||
import java.util.function.ToLongBiFunction;
|
||||
import java.util.function.ToLongFunction;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* A hash table supporting full concurrency of retrievals and
|
||||
@ -747,7 +748,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
/* ---------------- Table element access -------------- */
|
||||
|
||||
/*
|
||||
* Volatile access methods are used for table elements as well as
|
||||
* Atomic access methods are used for table elements as well as
|
||||
* elements of in-progress next table while resizing. All uses of
|
||||
* the tab arguments must be null checked by callers. All callers
|
||||
* also paranoically precheck that tab's length is not zero (or an
|
||||
@ -757,14 +758,12 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
* errors by users, these checks must operate on local variables,
|
||||
* which accounts for some odd-looking inline assignments below.
|
||||
* Note that calls to setTabAt always occur within locked regions,
|
||||
* and so in principle require only release ordering, not
|
||||
* full volatile semantics, but are currently coded as volatile
|
||||
* writes to be conservative.
|
||||
* and so require only release ordering.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
|
||||
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
|
||||
return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
|
||||
}
|
||||
|
||||
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
|
||||
@ -773,7 +772,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
|
||||
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
|
||||
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
|
||||
U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v);
|
||||
}
|
||||
|
||||
/* ---------------- Fields -------------- */
|
||||
@ -1024,7 +1023,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
int hash = spread(key.hashCode());
|
||||
int binCount = 0;
|
||||
for (Node<K,V>[] tab = table;;) {
|
||||
Node<K,V> f; int n, i, fh;
|
||||
Node<K,V> f; int n, i, fh; K fk; V fv;
|
||||
if (tab == null || (n = tab.length) == 0)
|
||||
tab = initTable();
|
||||
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
|
||||
@ -1033,6 +1032,10 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
else if ((fh = f.hash) == MOVED)
|
||||
tab = helpTransfer(tab, f);
|
||||
else if (onlyIfAbsent && fh == hash && // check first node
|
||||
((fk = f.key) == key || fk != null && key.equals(fk)) &&
|
||||
(fv = f.val) != null)
|
||||
return fv;
|
||||
else {
|
||||
V oldVal = null;
|
||||
synchronized (f) {
|
||||
@ -1703,7 +1706,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
V val = null;
|
||||
int binCount = 0;
|
||||
for (Node<K,V>[] tab = table;;) {
|
||||
Node<K,V> f; int n, i, fh;
|
||||
Node<K,V> f; int n, i, fh; K fk; V fv;
|
||||
if (tab == null || (n = tab.length) == 0)
|
||||
tab = initTable();
|
||||
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
|
||||
@ -1725,6 +1728,10 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
else if ((fh = f.hash) == MOVED)
|
||||
tab = helpTransfer(tab, f);
|
||||
else if (fh == h && // check first node
|
||||
((fk = f.key) == key || fk != null && key.equals(fk)) &&
|
||||
(fv = f.val) != null)
|
||||
return fv;
|
||||
else {
|
||||
boolean added = false;
|
||||
synchronized (f) {
|
||||
@ -3298,7 +3305,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long LOCKSTATE;
|
||||
static {
|
||||
try {
|
||||
@ -4554,14 +4561,21 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean removeAll(Collection<?> c) {
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
if (c == null) throw new NullPointerException();
|
||||
boolean modified = false;
|
||||
for (Iterator<E> it = iterator(); it.hasNext();) {
|
||||
if (c.contains(it.next())) {
|
||||
it.remove();
|
||||
modified = true;
|
||||
// Use (c instanceof Set) as a hint that lookup in c is as
|
||||
// efficient as this view
|
||||
if (c instanceof Set<?> && c.size() > map.table.length) {
|
||||
for (Iterator<?> it = iterator(); it.hasNext(); ) {
|
||||
if (c.contains(it.next())) {
|
||||
it.remove();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Object e : c)
|
||||
modified |= remove(e);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
@ -4748,6 +4762,18 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override public boolean removeAll(Collection<?> c) {
|
||||
if (c == null) throw new NullPointerException();
|
||||
boolean modified = false;
|
||||
for (Iterator<V> it = iterator(); it.hasNext();) {
|
||||
if (c.contains(it.next())) {
|
||||
it.remove();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
public boolean removeIf(Predicate<? super V> filter) {
|
||||
return map.removeValueIf(filter);
|
||||
}
|
||||
@ -6341,7 +6367,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long SIZECTL;
|
||||
private static final long TRANSFERINDEX;
|
||||
private static final long BASECOUNT;
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -292,64 +294,23 @@ public class ConcurrentLinkedDeque<E>
|
||||
volatile Node<E> prev;
|
||||
volatile E item;
|
||||
volatile Node<E> next;
|
||||
}
|
||||
|
||||
Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new node. Uses relaxed write because item can
|
||||
* only be seen after publication via casNext or casPrev.
|
||||
*/
|
||||
Node(E item) {
|
||||
U.putObject(this, ITEM, item);
|
||||
}
|
||||
|
||||
boolean casItem(E cmp, E val) {
|
||||
return U.compareAndSwapObject(this, ITEM, cmp, val);
|
||||
}
|
||||
|
||||
void lazySetNext(Node<E> val) {
|
||||
U.putObjectRelease(this, NEXT, val);
|
||||
}
|
||||
|
||||
boolean casNext(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
}
|
||||
|
||||
void lazySetPrev(Node<E> val) {
|
||||
U.putObjectRelease(this, PREV, val);
|
||||
}
|
||||
|
||||
boolean casPrev(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, PREV, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long PREV;
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
|
||||
static {
|
||||
try {
|
||||
PREV = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("prev"));
|
||||
ITEM = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns a new node holding item. Uses relaxed write because item
|
||||
* can only be seen after piggy-backing publication via CAS.
|
||||
*/
|
||||
static <E> Node<E> newNode(E item) {
|
||||
Node<E> node = new Node<E>();
|
||||
ITEM.set(node, item);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links e as first element.
|
||||
*/
|
||||
private void linkFirst(E e) {
|
||||
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
final Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
|
||||
restartFromHead:
|
||||
for (;;)
|
||||
@ -363,13 +324,13 @@ public class ConcurrentLinkedDeque<E>
|
||||
continue restartFromHead;
|
||||
else {
|
||||
// p is first node
|
||||
newNode.lazySetNext(p); // CAS piggyback
|
||||
if (p.casPrev(null, newNode)) {
|
||||
NEXT.set(newNode, p); // CAS piggyback
|
||||
if (PREV.compareAndSet(p, null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this deque,
|
||||
// and for newNode to become "live".
|
||||
if (p != h) // hop two nodes at a time
|
||||
casHead(h, newNode); // Failure is OK.
|
||||
if (p != h) // hop two nodes at a time; failure is OK
|
||||
HEAD.weakCompareAndSetVolatile(this, h, newNode);
|
||||
return;
|
||||
}
|
||||
// Lost CAS race to another thread; re-read prev
|
||||
@ -381,7 +342,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
* Links e as last element.
|
||||
*/
|
||||
private void linkLast(E e) {
|
||||
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
final Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
|
||||
restartFromTail:
|
||||
for (;;)
|
||||
@ -395,13 +356,13 @@ public class ConcurrentLinkedDeque<E>
|
||||
continue restartFromTail;
|
||||
else {
|
||||
// p is last node
|
||||
newNode.lazySetPrev(p); // CAS piggyback
|
||||
if (p.casNext(null, newNode)) {
|
||||
PREV.set(newNode, p); // CAS piggyback
|
||||
if (NEXT.compareAndSet(p, null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this deque,
|
||||
// and for newNode to become "live".
|
||||
if (p != t) // hop two nodes at a time
|
||||
casTail(t, newNode); // Failure is OK.
|
||||
if (p != t) // hop two nodes at a time; failure is OK
|
||||
TAIL.weakCompareAndSetVolatile(this, t, newNode);
|
||||
return;
|
||||
}
|
||||
// Lost CAS race to another thread; re-read next
|
||||
@ -516,8 +477,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
updateTail(); // Ensure x is not reachable from tail
|
||||
|
||||
// Finally, actually gc-unlink
|
||||
x.lazySetPrev(isFirst ? prevTerminator() : x);
|
||||
x.lazySetNext(isLast ? nextTerminator() : x);
|
||||
PREV.setRelease(x, isFirst ? prevTerminator() : x);
|
||||
NEXT.setRelease(x, isLast ? nextTerminator() : x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -531,7 +492,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
// assert first.item == null;
|
||||
for (Node<E> o = null, p = next, q;;) {
|
||||
if (p.item != null || (q = p.next) == null) {
|
||||
if (o != null && p.prev != p && first.casNext(next, p)) {
|
||||
if (o != null && p.prev != p &&
|
||||
NEXT.compareAndSet(first, next, p)) {
|
||||
skipDeletedPredecessors(p);
|
||||
if (first.prev == null &&
|
||||
(p.next == null || p.item != null) &&
|
||||
@ -541,8 +503,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
updateTail(); // Ensure o is not reachable from tail
|
||||
|
||||
// Finally, actually gc-unlink
|
||||
o.lazySetNext(o);
|
||||
o.lazySetPrev(prevTerminator());
|
||||
NEXT.setRelease(o, o);
|
||||
PREV.setRelease(o, prevTerminator());
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -565,7 +527,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
// assert last.item == null;
|
||||
for (Node<E> o = null, p = prev, q;;) {
|
||||
if (p.item != null || (q = p.prev) == null) {
|
||||
if (o != null && p.next != p && last.casPrev(prev, p)) {
|
||||
if (o != null && p.next != p &&
|
||||
PREV.compareAndSet(last, prev, p)) {
|
||||
skipDeletedSuccessors(p);
|
||||
if (last.next == null &&
|
||||
(p.prev == null || p.item != null) &&
|
||||
@ -575,8 +538,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
updateTail(); // Ensure o is not reachable from tail
|
||||
|
||||
// Finally, actually gc-unlink
|
||||
o.lazySetPrev(o);
|
||||
o.lazySetNext(nextTerminator());
|
||||
PREV.setRelease(o, o);
|
||||
NEXT.setRelease(o, nextTerminator());
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -607,7 +570,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
(q = (p = q).prev) == null) {
|
||||
// It is possible that p is PREV_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
if (casHead(h, p))
|
||||
if (HEAD.compareAndSet(this, h, p))
|
||||
return;
|
||||
else
|
||||
continue restartFromHead;
|
||||
@ -637,7 +600,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
(q = (p = q).next) == null) {
|
||||
// It is possible that p is NEXT_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
if (casTail(t, p))
|
||||
if (TAIL.compareAndSet(this, t, p))
|
||||
return;
|
||||
else
|
||||
continue restartFromTail;
|
||||
@ -675,7 +638,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
}
|
||||
|
||||
// found active CAS target
|
||||
if (prev == p || x.casPrev(prev, p))
|
||||
if (prev == p || PREV.compareAndSet(x, prev, p))
|
||||
return;
|
||||
|
||||
} while (x.item != null || x.next == null);
|
||||
@ -706,7 +669,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
}
|
||||
|
||||
// found active CAS target
|
||||
if (next == p || x.casNext(next, p))
|
||||
if (next == p || NEXT.compareAndSet(x, next, p))
|
||||
return;
|
||||
|
||||
} while (x.item != null || x.prev == null);
|
||||
@ -751,7 +714,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
else if (p == h
|
||||
// It is possible that p is PREV_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
|| casHead(h, p))
|
||||
|| HEAD.compareAndSet(this, h, p))
|
||||
return p;
|
||||
else
|
||||
continue restartFromHead;
|
||||
@ -776,7 +739,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
else if (p == t
|
||||
// It is possible that p is NEXT_TERMINATOR,
|
||||
// but if so, the CAS is guaranteed to fail.
|
||||
|| casTail(t, p))
|
||||
|| TAIL.compareAndSet(this, t, p))
|
||||
return p;
|
||||
else
|
||||
continue restartFromTail;
|
||||
@ -802,7 +765,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
* Constructs an empty deque.
|
||||
*/
|
||||
public ConcurrentLinkedDeque() {
|
||||
head = tail = new Node<E>(null);
|
||||
head = tail = new Node<E>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -818,12 +781,12 @@ public class ConcurrentLinkedDeque<E>
|
||||
// Copy c into a private chain of Nodes
|
||||
Node<E> h = null, t = null;
|
||||
for (E e : c) {
|
||||
Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
t.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(t);
|
||||
NEXT.set(t, newNode);
|
||||
PREV.set(newNode, t);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -836,12 +799,12 @@ public class ConcurrentLinkedDeque<E>
|
||||
private void initHeadTail(Node<E> h, Node<E> t) {
|
||||
if (h == t) {
|
||||
if (h == null)
|
||||
h = t = new Node<E>(null);
|
||||
h = t = new Node<E>();
|
||||
else {
|
||||
// Avoid edge case of a single Node with non-null item.
|
||||
Node<E> newNode = new Node<E>(null);
|
||||
t.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(t);
|
||||
Node<E> newNode = new Node<E>();
|
||||
NEXT.set(t, newNode);
|
||||
PREV.set(newNode, t);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -934,7 +897,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
public E pollFirst() {
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && p.casItem(item, null)) {
|
||||
if (item != null && ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return item;
|
||||
}
|
||||
@ -945,7 +908,7 @@ public class ConcurrentLinkedDeque<E>
|
||||
public E pollLast() {
|
||||
for (Node<E> p = last(); p != null; p = pred(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && p.casItem(item, null)) {
|
||||
if (item != null && ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return item;
|
||||
}
|
||||
@ -1031,7 +994,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
Objects.requireNonNull(o);
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && o.equals(item) && p.casItem(item, null)) {
|
||||
if (item != null && o.equals(item) &&
|
||||
ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return true;
|
||||
}
|
||||
@ -1055,7 +1019,8 @@ public class ConcurrentLinkedDeque<E>
|
||||
Objects.requireNonNull(o);
|
||||
for (Node<E> p = last(); p != null; p = pred(p)) {
|
||||
E item = p.item;
|
||||
if (item != null && o.equals(item) && p.casItem(item, null)) {
|
||||
if (item != null && o.equals(item) &&
|
||||
ITEM.compareAndSet(p, item, null)) {
|
||||
unlink(p);
|
||||
return true;
|
||||
}
|
||||
@ -1159,12 +1124,12 @@ public class ConcurrentLinkedDeque<E>
|
||||
// Copy c into a private chain of Nodes
|
||||
Node<E> beginningOfTheEnd = null, last = null;
|
||||
for (E e : c) {
|
||||
Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
|
||||
Node<E> newNode = newNode(Objects.requireNonNull(e));
|
||||
if (beginningOfTheEnd == null)
|
||||
beginningOfTheEnd = last = newNode;
|
||||
else {
|
||||
last.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(last);
|
||||
NEXT.set(last, newNode);
|
||||
PREV.set(newNode, last);
|
||||
last = newNode;
|
||||
}
|
||||
}
|
||||
@ -1184,16 +1149,16 @@ public class ConcurrentLinkedDeque<E>
|
||||
continue restartFromTail;
|
||||
else {
|
||||
// p is last node
|
||||
beginningOfTheEnd.lazySetPrev(p); // CAS piggyback
|
||||
if (p.casNext(null, beginningOfTheEnd)) {
|
||||
PREV.set(beginningOfTheEnd, p); // CAS piggyback
|
||||
if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for all elements to be added to this deque.
|
||||
if (!casTail(t, last)) {
|
||||
if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
|
||||
// Try a little harder to update tail,
|
||||
// since we may be adding many elements.
|
||||
t = tail;
|
||||
if (last.next == null)
|
||||
casTail(t, last);
|
||||
TAIL.weakCompareAndSetVolatile(this, t, last);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1586,41 +1551,38 @@ public class ConcurrentLinkedDeque<E>
|
||||
Node<E> h = null, t = null;
|
||||
for (Object item; (item = s.readObject()) != null; ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Node<E> newNode = new Node<E>((E) item);
|
||||
Node<E> newNode = newNode((E) item);
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
t.lazySetNext(newNode);
|
||||
newNode.lazySetPrev(t);
|
||||
NEXT.set(t, newNode);
|
||||
PREV.set(newNode, t);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
initHeadTail(h, t);
|
||||
}
|
||||
|
||||
private boolean casHead(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casTail(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, TAIL, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle PREV;
|
||||
private static final VarHandle NEXT;
|
||||
private static final VarHandle ITEM;
|
||||
static {
|
||||
PREV_TERMINATOR = new Node<Object>();
|
||||
PREV_TERMINATOR.next = PREV_TERMINATOR;
|
||||
NEXT_TERMINATOR = new Node<Object>();
|
||||
NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(ConcurrentLinkedDeque.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(ConcurrentLinkedDeque.class.getDeclaredField("tail"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head",
|
||||
Node.class);
|
||||
TAIL = l.findVarHandle(ConcurrentLinkedDeque.class, "tail",
|
||||
Node.class);
|
||||
PREV = l.findVarHandle(Node.class, "prev", Node.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
ITEM = l.findVarHandle(Node.class, "item", Object.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -166,9 +168,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* this is merely an optimization.
|
||||
*
|
||||
* When constructing a Node (before enqueuing it) we avoid paying
|
||||
* for a volatile write to item by using Unsafe.putObject instead
|
||||
* of a normal write. This allows the cost of enqueue to be
|
||||
* "one-and-a-half" CASes.
|
||||
* for a volatile write to item. This allows the cost of enqueue
|
||||
* to be "one-and-a-half" CASes.
|
||||
*
|
||||
* Both head and tail may or may not point to a Node with a
|
||||
* non-null item. If the queue is empty, all items must of course
|
||||
@ -178,33 +179,21 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* optimization.
|
||||
*/
|
||||
|
||||
private static class Node<E> {
|
||||
static final class Node<E> {
|
||||
volatile E item;
|
||||
volatile Node<E> next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new node holding item. Uses relaxed write because item
|
||||
* can only be seen after piggy-backing publication via casNext.
|
||||
* can only be seen after piggy-backing publication via CAS.
|
||||
*/
|
||||
static <E> Node<E> newNode(E item) {
|
||||
Node<E> node = new Node<E>();
|
||||
U.putObject(node, ITEM, item);
|
||||
ITEM.set(node, item);
|
||||
return node;
|
||||
}
|
||||
|
||||
static <E> boolean casItem(Node<E> node, E cmp, E val) {
|
||||
return U.compareAndSwapObject(node, ITEM, cmp, val);
|
||||
}
|
||||
|
||||
static <E> void lazySetNext(Node<E> node, Node<E> val) {
|
||||
U.putObjectRelease(node, NEXT, val);
|
||||
}
|
||||
|
||||
static <E> boolean casNext(Node<E> node, Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(node, NEXT, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* A node from which the first live (non-deleted) node (if any)
|
||||
* can be reached in O(1) time.
|
||||
@ -256,7 +245,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
lazySetNext(t, newNode);
|
||||
NEXT.set(t, newNode);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -286,8 +275,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
final void updateHead(Node<E> h, Node<E> p) {
|
||||
// assert h != null && p != null && (h == p || h.item == null);
|
||||
if (h != p && casHead(h, p))
|
||||
lazySetNext(h, h);
|
||||
if (h != p && HEAD.compareAndSet(this, h, p))
|
||||
NEXT.setRelease(h, h);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,12 +303,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
Node<E> q = p.next;
|
||||
if (q == null) {
|
||||
// p is last node
|
||||
if (casNext(p, null, newNode)) {
|
||||
if (NEXT.compareAndSet(p, null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this queue,
|
||||
// and for newNode to become "live".
|
||||
if (p != t) // hop two nodes at a time
|
||||
casTail(t, newNode); // Failure is OK.
|
||||
if (p != t) // hop two nodes at a time; failure is OK
|
||||
TAIL.weakCompareAndSetVolatile(this, t, newNode);
|
||||
return true;
|
||||
}
|
||||
// Lost CAS race to another thread; re-read next
|
||||
@ -342,7 +331,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
for (Node<E> h = head, p = h, q;;) {
|
||||
E item = p.item;
|
||||
|
||||
if (item != null && casItem(p, item, null)) {
|
||||
if (item != null && ITEM.compareAndSet(p, item, null)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for item to be removed from this queue.
|
||||
if (p != h) // hop two nodes at a time
|
||||
@ -483,12 +472,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
next = succ(p);
|
||||
continue;
|
||||
}
|
||||
removed = casItem(p, item, null);
|
||||
removed = ITEM.compareAndSet(p, item, null);
|
||||
}
|
||||
|
||||
next = succ(p);
|
||||
if (pred != null && next != null) // unlink
|
||||
casNext(pred, p, next);
|
||||
NEXT.weakCompareAndSetVolatile(pred, p, next);
|
||||
if (removed)
|
||||
return true;
|
||||
}
|
||||
@ -520,7 +509,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (beginningOfTheEnd == null)
|
||||
beginningOfTheEnd = last = newNode;
|
||||
else {
|
||||
lazySetNext(last, newNode);
|
||||
NEXT.set(last, newNode);
|
||||
last = newNode;
|
||||
}
|
||||
}
|
||||
@ -532,15 +521,15 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
Node<E> q = p.next;
|
||||
if (q == null) {
|
||||
// p is last node
|
||||
if (casNext(p, null, beginningOfTheEnd)) {
|
||||
if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for all elements to be added to this queue.
|
||||
if (!casTail(t, last)) {
|
||||
if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
|
||||
// Try a little harder to update tail,
|
||||
// since we may be adding many elements.
|
||||
t = tail;
|
||||
if (last.next == null)
|
||||
casTail(t, last);
|
||||
TAIL.weakCompareAndSetVolatile(this, t, last);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -744,7 +733,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
// unlink deleted nodes
|
||||
if ((q = succ(p)) != null)
|
||||
casNext(pred, p, q);
|
||||
NEXT.compareAndSet(pred, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
@ -801,7 +790,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
lazySetNext(t, newNode);
|
||||
NEXT.set(t, newNode);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
@ -919,31 +908,20 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
return new CLQSpliterator<E>(this);
|
||||
}
|
||||
|
||||
private boolean casTail(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, TAIL, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casHead(Node<E> cmp, Node<E> val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle ITEM;
|
||||
private static final VarHandle NEXT;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(ConcurrentLinkedQueue.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(ConcurrentLinkedQueue.class.getDeclaredField("tail"));
|
||||
ITEM = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head",
|
||||
Node.class);
|
||||
TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail",
|
||||
Node.class);
|
||||
ITEM = l.findVarHandle(Node.class, "item", Object.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractMap;
|
||||
@ -401,7 +403,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
* compareAndSet head node.
|
||||
*/
|
||||
private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
return HEAD.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/* ---------------- Nodes -------------- */
|
||||
@ -444,14 +446,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
* compareAndSet value field.
|
||||
*/
|
||||
boolean casValue(Object cmp, Object val) {
|
||||
return U.compareAndSwapObject(this, VALUE, cmp, val);
|
||||
return VALUE.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* compareAndSet next field.
|
||||
*/
|
||||
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
|
||||
return U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
return NEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -532,20 +534,16 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
private static final long NEXT;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle VALUE;
|
||||
private static final VarHandle NEXT;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("value"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(Node.class, "value", Object.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -577,7 +575,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
* compareAndSet right field.
|
||||
*/
|
||||
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
|
||||
return U.compareAndSwapObject(this, RIGHT, cmp, val);
|
||||
return RIGHT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -613,13 +611,12 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
return node.value != null && casRight(succ, succ.right);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long RIGHT;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle RIGHT;
|
||||
static {
|
||||
try {
|
||||
RIGHT = U.objectFieldOffset
|
||||
(Index.class.getDeclaredField("right"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
RIGHT = l.findVarHandle(Index.class, "right", Index.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -3607,13 +3604,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(ConcurrentSkipListMap.class.getDeclaredField("head"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head",
|
||||
HeadIndex.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -507,15 +509,16 @@ public class ConcurrentSkipListSet<E>
|
||||
|
||||
// Support for resetting map in clone
|
||||
private void setMap(ConcurrentNavigableMap<E,Object> map) {
|
||||
U.putObjectVolatile(this, MAP, map);
|
||||
MAP.setVolatile(this, map);
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long MAP;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle MAP;
|
||||
static {
|
||||
try {
|
||||
MAP = U.objectFieldOffset
|
||||
(ConcurrentSkipListSet.class.getDeclaredField("m"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
MAP = l.findVarHandle(ConcurrentSkipListSet.class, "m",
|
||||
ConcurrentNavigableMap.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -1541,17 +1542,21 @@ public class CopyOnWriteArrayList<E>
|
||||
}
|
||||
}
|
||||
|
||||
// Support for resetting lock while deserializing
|
||||
/** Initializes the lock; for use when deserializing or cloning. */
|
||||
private void resetLock() {
|
||||
U.putObjectVolatile(this, LOCK, new Object());
|
||||
}
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long LOCK;
|
||||
static {
|
||||
Field lockField = java.security.AccessController.doPrivileged(
|
||||
(java.security.PrivilegedAction<Field>) () -> {
|
||||
try {
|
||||
Field f = CopyOnWriteArrayList.class
|
||||
.getDeclaredField("lock");
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}});
|
||||
try {
|
||||
LOCK = U.objectFieldOffset
|
||||
(CopyOnWriteArrayList.class.getDeclaredField("lock"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
lockField.set(this, new Object());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* A {@link ForkJoinTask} with a completion action performed when
|
||||
* triggered and there are no remaining pending actions.
|
||||
@ -524,7 +527,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
* @param delta the value to add
|
||||
*/
|
||||
public final void addToPendingCount(int delta) {
|
||||
U.getAndAddInt(this, PENDING, delta);
|
||||
PENDING.getAndAdd(this, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -536,7 +539,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean compareAndSetPendingCount(int expected, int count) {
|
||||
return U.compareAndSwapInt(this, PENDING, expected, count);
|
||||
return PENDING.compareAndSet(this, expected, count);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -548,7 +551,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
public final int decrementPendingCountUnlessZero() {
|
||||
int c;
|
||||
do {} while ((c = pending) != 0 &&
|
||||
!U.compareAndSwapInt(this, PENDING, c, c - 1));
|
||||
!PENDING.weakCompareAndSetVolatile(this, c, c - 1));
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -581,7 +584,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
|
||||
else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -604,7 +607,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
|
||||
else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -649,7 +652,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
for (int c;;) {
|
||||
if ((c = pending) == 0)
|
||||
return this;
|
||||
else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
|
||||
else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1))
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -753,13 +756,13 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
|
||||
*/
|
||||
protected void setRawResult(T t) { }
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long PENDING;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle PENDING;
|
||||
static {
|
||||
try {
|
||||
PENDING = U.objectFieldOffset
|
||||
(CountedCompleter.class.getDeclaredField("pending"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);
|
||||
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -36,6 +36,10 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* A synchronization point at which threads can pair and swap elements
|
||||
* within pairs. Each thread presents some object on entry to the
|
||||
@ -155,9 +159,7 @@ public class Exchanger<V> {
|
||||
* a value that is enough for common platforms. Additionally,
|
||||
* extra care elsewhere is taken to avoid other false/unintended
|
||||
* sharing and to enhance locality, including adding padding (via
|
||||
* @Contended) to Nodes, embedding "bound" as an Exchanger field,
|
||||
* and reworking some park/unpark mechanics compared to
|
||||
* LockSupport versions.
|
||||
* @Contended) to Nodes, embedding "bound" as an Exchanger field.
|
||||
*
|
||||
* The arena starts out with only one used slot. We expand the
|
||||
* effective arena size by tracking collisions; i.e., failed CASes
|
||||
@ -233,29 +235,23 @@ public class Exchanger<V> {
|
||||
* As is too common in this sort of code, methods are monolithic
|
||||
* because most of the logic relies on reads of fields that are
|
||||
* maintained as local variables so can't be nicely factored --
|
||||
* mainly, here, bulky spin->yield->block/cancel code), and
|
||||
* heavily dependent on intrinsics (Unsafe) to use inlined
|
||||
* embedded CAS and related memory access operations (that tend
|
||||
* not to be as readily inlined by dynamic compilers when they are
|
||||
* hidden behind other methods that would more nicely name and
|
||||
* encapsulate the intended effects). This includes the use of
|
||||
* putXRelease to clear fields of the per-thread Nodes between
|
||||
* uses. Note that field Node.item is not declared as volatile
|
||||
* even though it is read by releasing threads, because they only
|
||||
* do so after CAS operations that must precede access, and all
|
||||
* uses by the owning thread are otherwise acceptably ordered by
|
||||
* other operations. (Because the actual points of atomicity are
|
||||
* slot CASes, it would also be legal for the write to Node.match
|
||||
* in a release to be weaker than a full volatile write. However,
|
||||
* this is not done because it could allow further postponement of
|
||||
* the write, delaying progress.)
|
||||
* mainly, here, bulky spin->yield->block/cancel code. Note that
|
||||
* field Node.item is not declared as volatile even though it is
|
||||
* read by releasing threads, because they only do so after CAS
|
||||
* operations that must precede access, and all uses by the owning
|
||||
* thread are otherwise acceptably ordered by other operations.
|
||||
* (Because the actual points of atomicity are slot CASes, it
|
||||
* would also be legal for the write to Node.match in a release to
|
||||
* be weaker than a full volatile write. However, this is not done
|
||||
* because it could allow further postponement of the write,
|
||||
* delaying progress.)
|
||||
*/
|
||||
|
||||
/**
|
||||
* The byte distance (as a shift value) between any two used slots
|
||||
* in the arena. 1 << ASHIFT should be at least cacheline size.
|
||||
* The index distance (as a shift value) between any two used slots
|
||||
* in the arena, spacing them out to avoid false sharing.
|
||||
*/
|
||||
private static final int ASHIFT = 7;
|
||||
private static final int ASHIFT = 5;
|
||||
|
||||
/**
|
||||
* The maximum supported arena index. The maximum allocatable
|
||||
@ -356,27 +352,31 @@ public class Exchanger<V> {
|
||||
*/
|
||||
private final Object arenaExchange(Object item, boolean timed, long ns) {
|
||||
Node[] a = arena;
|
||||
int alen = a.length;
|
||||
Node p = participant.get();
|
||||
for (int i = p.index;;) { // access slot at i
|
||||
int b, m, c; long j; // j is raw array offset
|
||||
Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
|
||||
if (q != null && U.compareAndSwapObject(a, j, q, null)) {
|
||||
int b, m, c;
|
||||
int j = (i << ASHIFT) + ((1 << ASHIFT) - 1);
|
||||
if (j < 0 || j >= alen)
|
||||
j = alen - 1;
|
||||
Node q = (Node)AA.getAcquire(a, j);
|
||||
if (q != null && AA.compareAndSet(a, j, q, null)) {
|
||||
Object v = q.item; // release
|
||||
q.match = item;
|
||||
Thread w = q.parked;
|
||||
if (w != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
return v;
|
||||
}
|
||||
else if (i <= (m = (b = bound) & MMASK) && q == null) {
|
||||
p.item = item; // offer
|
||||
if (U.compareAndSwapObject(a, j, null, p)) {
|
||||
if (AA.compareAndSet(a, j, null, p)) {
|
||||
long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
|
||||
Thread t = Thread.currentThread(); // wait
|
||||
for (int h = p.hash, spins = SPINS;;) {
|
||||
Object v = p.match;
|
||||
if (v != null) {
|
||||
U.putObjectRelease(p, MATCH, null);
|
||||
MATCH.setRelease(p, null);
|
||||
p.item = null; // clear for next use
|
||||
p.hash = h;
|
||||
return v;
|
||||
@ -389,22 +389,24 @@ public class Exchanger<V> {
|
||||
(--spins & ((SPINS >>> 1) - 1)) == 0)
|
||||
Thread.yield(); // two yields per wait
|
||||
}
|
||||
else if (U.getObjectVolatile(a, j) != p)
|
||||
else if (AA.getAcquire(a, j) != p)
|
||||
spins = SPINS; // releaser hasn't set match yet
|
||||
else if (!t.isInterrupted() && m == 0 &&
|
||||
(!timed ||
|
||||
(ns = end - System.nanoTime()) > 0L)) {
|
||||
U.putObject(t, BLOCKER, this); // emulate LockSupport
|
||||
p.parked = t; // minimize window
|
||||
if (U.getObjectVolatile(a, j) == p)
|
||||
U.park(false, ns);
|
||||
if (AA.getAcquire(a, j) == p) {
|
||||
if (ns == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, ns);
|
||||
}
|
||||
p.parked = null;
|
||||
U.putObject(t, BLOCKER, null);
|
||||
}
|
||||
else if (U.getObjectVolatile(a, j) == p &&
|
||||
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
|
||||
U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
|
||||
BOUND.compareAndSet(this, b, b + SEQ - 1);
|
||||
p.item = null;
|
||||
p.hash = h;
|
||||
i = p.index >>>= 1; // descend
|
||||
@ -426,7 +428,7 @@ public class Exchanger<V> {
|
||||
i = (i != m || m == 0) ? m : m - 1;
|
||||
}
|
||||
else if ((c = p.collides) < m || m == FULL ||
|
||||
!U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
|
||||
!BOUND.compareAndSet(this, b, b + SEQ + 1)) {
|
||||
p.collides = c + 1;
|
||||
i = (i == 0) ? m : i - 1; // cyclically traverse
|
||||
}
|
||||
@ -455,24 +457,24 @@ public class Exchanger<V> {
|
||||
|
||||
for (Node q;;) {
|
||||
if ((q = slot) != null) {
|
||||
if (U.compareAndSwapObject(this, SLOT, q, null)) {
|
||||
if (SLOT.compareAndSet(this, q, null)) {
|
||||
Object v = q.item;
|
||||
q.match = item;
|
||||
Thread w = q.parked;
|
||||
if (w != null)
|
||||
U.unpark(w);
|
||||
LockSupport.unpark(w);
|
||||
return v;
|
||||
}
|
||||
// create arena on contention, but continue until slot null
|
||||
if (NCPU > 1 && bound == 0 &&
|
||||
U.compareAndSwapInt(this, BOUND, 0, SEQ))
|
||||
BOUND.compareAndSet(this, 0, SEQ))
|
||||
arena = new Node[(FULL + 2) << ASHIFT];
|
||||
}
|
||||
else if (arena != null)
|
||||
return null; // caller must reroute to arenaExchange
|
||||
else {
|
||||
p.item = item;
|
||||
if (U.compareAndSwapObject(this, SLOT, null, p))
|
||||
if (SLOT.compareAndSet(this, null, p))
|
||||
break;
|
||||
p.item = null;
|
||||
}
|
||||
@ -495,19 +497,21 @@ public class Exchanger<V> {
|
||||
spins = SPINS;
|
||||
else if (!t.isInterrupted() && arena == null &&
|
||||
(!timed || (ns = end - System.nanoTime()) > 0L)) {
|
||||
U.putObject(t, BLOCKER, this);
|
||||
p.parked = t;
|
||||
if (slot == p)
|
||||
U.park(false, ns);
|
||||
if (slot == p) {
|
||||
if (ns == 0L)
|
||||
LockSupport.park(this);
|
||||
else
|
||||
LockSupport.parkNanos(this, ns);
|
||||
}
|
||||
p.parked = null;
|
||||
U.putObject(t, BLOCKER, null);
|
||||
}
|
||||
else if (U.compareAndSwapObject(this, SLOT, p, null)) {
|
||||
else if (SLOT.compareAndSet(this, p, null)) {
|
||||
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
U.putObjectRelease(p, MATCH, null);
|
||||
MATCH.setRelease(p, null);
|
||||
p.item = null;
|
||||
p.hash = h;
|
||||
return v;
|
||||
@ -556,8 +560,9 @@ public class Exchanger<V> {
|
||||
@SuppressWarnings("unchecked")
|
||||
public V exchange(V x) throws InterruptedException {
|
||||
Object v;
|
||||
Node[] a;
|
||||
Object item = (x == null) ? NULL_ITEM : x; // translate null args
|
||||
if ((arena != null ||
|
||||
if (((a = arena) != null ||
|
||||
(v = slotExchange(item, false, 0L)) == null) &&
|
||||
((Thread.interrupted() || // disambiguates null return
|
||||
(v = arenaExchange(item, false, 0L)) == null)))
|
||||
@ -623,31 +628,18 @@ public class Exchanger<V> {
|
||||
return (v == NULL_ITEM) ? null : (V)v;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long BOUND;
|
||||
private static final long SLOT;
|
||||
private static final long MATCH;
|
||||
private static final long BLOCKER;
|
||||
private static final int ABASE;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle BOUND;
|
||||
private static final VarHandle SLOT;
|
||||
private static final VarHandle MATCH;
|
||||
private static final VarHandle AA;
|
||||
static {
|
||||
try {
|
||||
BOUND = U.objectFieldOffset
|
||||
(Exchanger.class.getDeclaredField("bound"));
|
||||
SLOT = U.objectFieldOffset
|
||||
(Exchanger.class.getDeclaredField("slot"));
|
||||
|
||||
MATCH = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("match"));
|
||||
|
||||
BLOCKER = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("parkBlocker"));
|
||||
|
||||
int scale = U.arrayIndexScale(Node[].class);
|
||||
if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
|
||||
throw new Error("Unsupported array scale");
|
||||
// ABASE absorbs padding in front of element 0
|
||||
ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
BOUND = l.findVarHandle(Exchanger.class, "bound", int.class);
|
||||
SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class);
|
||||
MATCH = l.findVarHandle(Node.class, "match", Object.class);
|
||||
AA = MethodHandles.arrayElementVarHandle(Node[].class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,8 @@
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
@ -92,7 +94,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* encountering the exception; minimally only the latter.
|
||||
*
|
||||
* <p>It is possible to define and use ForkJoinTasks that may block,
|
||||
* but doing do requires three further considerations: (1) Completion
|
||||
* but doing so requires three further considerations: (1) Completion
|
||||
* of few if any <em>other</em> tasks should be dependent on a task
|
||||
* that blocks on external synchronization or I/O. Event-style async
|
||||
* tasks that are never joined (for example, those subclassing {@link
|
||||
@ -259,7 +261,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
for (int s;;) {
|
||||
if ((s = status) < 0)
|
||||
return s;
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
|
||||
if (STATUS.compareAndSet(this, s, s | completion)) {
|
||||
if ((s >>> 16) != 0)
|
||||
synchronized (this) { notifyAll(); }
|
||||
return completion;
|
||||
@ -297,7 +299,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
final void internalWait(long timeout) {
|
||||
int s;
|
||||
if ((s = status) >= 0 && // force completer to issue notify
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
try { wait(timeout); } catch (InterruptedException ie) { }
|
||||
@ -319,7 +321,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
if (s >= 0 && (s = status) >= 0) {
|
||||
boolean interrupted = false;
|
||||
do {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0) {
|
||||
try {
|
||||
@ -353,7 +355,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
|
||||
0)) >= 0) {
|
||||
while ((s = status) >= 0) {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait(0L);
|
||||
@ -400,22 +402,24 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
// Exception table support
|
||||
|
||||
/**
|
||||
* Table of exceptions thrown by tasks, to enable reporting by
|
||||
* callers. Because exceptions are rare, we don't directly keep
|
||||
* Hash table of exceptions thrown by tasks, to enable reporting
|
||||
* by callers. Because exceptions are rare, we don't directly keep
|
||||
* them with task objects, but instead use a weak ref table. Note
|
||||
* that cancellation exceptions don't appear in the table, but are
|
||||
* instead recorded as status values.
|
||||
*
|
||||
* Note: These statics are initialized below in static block.
|
||||
* The exception table has a fixed capacity.
|
||||
*/
|
||||
private static final ExceptionNode[] exceptionTable;
|
||||
private static final ReentrantLock exceptionTableLock;
|
||||
private static final ReferenceQueue<Object> exceptionTableRefQueue;
|
||||
private static final ExceptionNode[] exceptionTable
|
||||
= new ExceptionNode[32];
|
||||
|
||||
/**
|
||||
* Fixed capacity for exceptionTable.
|
||||
*/
|
||||
private static final int EXCEPTION_MAP_CAPACITY = 32;
|
||||
/** Lock protecting access to exceptionTable. */
|
||||
private static final ReentrantLock exceptionTableLock
|
||||
= new ReentrantLock();
|
||||
|
||||
/** Reference queue of stale exceptionally completed tasks. */
|
||||
private static final ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue
|
||||
= new ReferenceQueue<ForkJoinTask<?>>();
|
||||
|
||||
/**
|
||||
* Key-value nodes for exception table. The chained hash table
|
||||
@ -435,7 +439,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
final long thrower; // use id not ref to avoid weak cycles
|
||||
final int hashCode; // store task hashCode before weak ref disappears
|
||||
ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next,
|
||||
ReferenceQueue<Object> exceptionTableRefQueue) {
|
||||
ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue) {
|
||||
super(task, exceptionTableRefQueue);
|
||||
this.ex = ex;
|
||||
this.next = next;
|
||||
@ -599,9 +603,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
private static void expungeStaleExceptions() {
|
||||
for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
|
||||
if (x instanceof ExceptionNode) {
|
||||
int hashCode = ((ExceptionNode)x).hashCode;
|
||||
ExceptionNode[] t = exceptionTable;
|
||||
int i = hashCode & (t.length - 1);
|
||||
int i = ((ExceptionNode)x).hashCode & (t.length - 1);
|
||||
ExceptionNode e = t[i];
|
||||
ExceptionNode pred = null;
|
||||
while (e != null) {
|
||||
@ -1031,7 +1034,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
while ((s = status) >= 0 &&
|
||||
(ns = deadline - System.nanoTime()) > 0L) {
|
||||
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
STATUS.compareAndSet(this, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait(ms); // OK to throw InterruptedException
|
||||
@ -1324,8 +1327,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
*/
|
||||
public final short setForkJoinTaskTag(short newValue) {
|
||||
for (int s;;) {
|
||||
if (U.compareAndSwapInt(this, STATUS, s = status,
|
||||
(s & ~SMASK) | (newValue & SMASK)))
|
||||
if (STATUS.compareAndSet(this, s = status,
|
||||
(s & ~SMASK) | (newValue & SMASK)))
|
||||
return (short)s;
|
||||
}
|
||||
}
|
||||
@ -1348,8 +1351,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
for (int s;;) {
|
||||
if ((short)(s = status) != expect)
|
||||
return false;
|
||||
if (U.compareAndSwapInt(this, STATUS, s,
|
||||
(s & ~SMASK) | (update & SMASK)))
|
||||
if (STATUS.compareAndSet(this, s,
|
||||
(s & ~SMASK) | (update & SMASK)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1510,17 +1513,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
setExceptionalCompletion((Throwable)ex);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATUS;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATUS;
|
||||
static {
|
||||
exceptionTableLock = new ReentrantLock();
|
||||
exceptionTableRefQueue = new ReferenceQueue<Object>();
|
||||
exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
|
||||
try {
|
||||
STATUS = U.objectFieldOffset
|
||||
(ForkJoinTask.class.getDeclaredField("status"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATUS = l.findVarHandle(ForkJoinTask.class, "status", int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -66,8 +66,9 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
* owning thread.
|
||||
*
|
||||
* Support for (non-public) subclass InnocuousForkJoinWorkerThread
|
||||
* requires that we break quite a lot of encapsulation (via Unsafe)
|
||||
* both here and in the subclass to access and set Thread fields.
|
||||
* requires that we break quite a lot of encapsulation (via helper
|
||||
* methods in ThreadLocalRandom) both here and in the subclass to
|
||||
* access and set Thread fields.
|
||||
*/
|
||||
|
||||
final ForkJoinPool pool; // the pool this thread works in
|
||||
@ -92,8 +93,8 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
|
||||
AccessControlContext acc) {
|
||||
super(threadGroup, null, "aForkJoinWorkerThread");
|
||||
U.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, acc);
|
||||
eraseThreadLocals(); // clear before registering
|
||||
ThreadLocalRandom.setInheritedAccessControlContext(this, acc);
|
||||
ThreadLocalRandom.eraseThreadLocals(this); // clear before registering
|
||||
this.pool = pool;
|
||||
this.workQueue = pool.registerWorker(this);
|
||||
}
|
||||
@ -170,38 +171,12 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases ThreadLocals by nulling out Thread maps.
|
||||
*/
|
||||
final void eraseThreadLocals() {
|
||||
U.putObject(this, THREADLOCALS, null);
|
||||
U.putObject(this, INHERITABLETHREADLOCALS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-public hook method for InnocuousForkJoinWorkerThread.
|
||||
*/
|
||||
void afterTopLevelExec() {
|
||||
}
|
||||
|
||||
// Set up to allow setting thread fields in constructor
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long THREADLOCALS;
|
||||
private static final long INHERITABLETHREADLOCALS;
|
||||
private static final long INHERITEDACCESSCONTROLCONTEXT;
|
||||
static {
|
||||
try {
|
||||
THREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocals"));
|
||||
INHERITABLETHREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritableThreadLocals"));
|
||||
INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritedAccessControlContext"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A worker thread that has no permissions, is not a member of any
|
||||
* user-defined ThreadGroup, and erases all ThreadLocals after
|
||||
@ -210,7 +185,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
|
||||
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
|
||||
private static final ThreadGroup innocuousThreadGroup =
|
||||
createThreadGroup();
|
||||
ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup");
|
||||
|
||||
/** An AccessControlContext supporting no privileges */
|
||||
private static final AccessControlContext INNOCUOUS_ACC =
|
||||
@ -225,7 +200,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
|
||||
@Override // to erase ThreadLocals
|
||||
void afterTopLevelExec() {
|
||||
eraseThreadLocals();
|
||||
ThreadLocalRandom.eraseThreadLocals(this);
|
||||
}
|
||||
|
||||
@Override // to always report system loader
|
||||
@ -241,33 +216,5 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
throw new SecurityException("setContextClassLoader");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new group with the system ThreadGroup (the
|
||||
* topmost, parent-less group) as parent. Uses Unsafe to
|
||||
* traverse Thread.group and ThreadGroup.parent fields.
|
||||
*/
|
||||
private static ThreadGroup createThreadGroup() {
|
||||
try {
|
||||
jdk.internal.misc.Unsafe u = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
long tg = u.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("group"));
|
||||
long gp = u.objectFieldOffset
|
||||
(ThreadGroup.class.getDeclaredField("parent"));
|
||||
ThreadGroup group = (ThreadGroup)
|
||||
u.getObject(Thread.currentThread(), tg);
|
||||
while (group != null) {
|
||||
ThreadGroup parent = (ThreadGroup)u.getObject(group, gp);
|
||||
if (parent == null)
|
||||
return new ThreadGroup(group,
|
||||
"InnocuousForkJoinWorkerThreadGroup");
|
||||
group = parent;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
// fall through if null as cannot-happen safeguard
|
||||
throw new Error("Cannot create ThreadGroup");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
@ -69,9 +71,6 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
* cancellation races. Sync control in the current design relies
|
||||
* on a "state" field updated via CAS to track completion, along
|
||||
* with a simple Treiber stack to hold waiting threads.
|
||||
*
|
||||
* Style note: As usual, we bypass overhead of using
|
||||
* AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -163,9 +162,8 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
}
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (!(state == NEW &&
|
||||
U.compareAndSwapInt(this, STATE, NEW,
|
||||
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
|
||||
if (!(state == NEW && STATE.compareAndSet
|
||||
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
|
||||
return false;
|
||||
try { // in case call to interrupt throws exception
|
||||
if (mayInterruptIfRunning) {
|
||||
@ -174,7 +172,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
if (t != null)
|
||||
t.interrupt();
|
||||
} finally { // final state
|
||||
U.putIntRelease(this, STATE, INTERRUPTED);
|
||||
STATE.setRelease(this, INTERRUPTED);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -228,9 +226,9 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
* @param v the value
|
||||
*/
|
||||
protected void set(V v) {
|
||||
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
|
||||
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
|
||||
outcome = v;
|
||||
U.putIntRelease(this, STATE, NORMAL); // final state
|
||||
STATE.setRelease(this, NORMAL); // final state
|
||||
finishCompletion();
|
||||
}
|
||||
}
|
||||
@ -246,16 +244,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
* @param t the cause of failure
|
||||
*/
|
||||
protected void setException(Throwable t) {
|
||||
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
|
||||
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
|
||||
outcome = t;
|
||||
U.putIntRelease(this, STATE, EXCEPTIONAL); // final state
|
||||
STATE.setRelease(this, EXCEPTIONAL); // final state
|
||||
finishCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (state != NEW ||
|
||||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
|
||||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
|
||||
return;
|
||||
try {
|
||||
Callable<V> c = callable;
|
||||
@ -296,7 +294,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
*/
|
||||
protected boolean runAndReset() {
|
||||
if (state != NEW ||
|
||||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
|
||||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
|
||||
return false;
|
||||
boolean ran = false;
|
||||
int s = state;
|
||||
@ -363,7 +361,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
private void finishCompletion() {
|
||||
// assert state > COMPLETING;
|
||||
for (WaitNode q; (q = waiters) != null;) {
|
||||
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
|
||||
if (WAITERS.weakCompareAndSetVolatile(this, q, null)) {
|
||||
for (;;) {
|
||||
Thread t = q.thread;
|
||||
if (t != null) {
|
||||
@ -425,8 +423,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
q = new WaitNode();
|
||||
}
|
||||
else if (!queued)
|
||||
queued = U.compareAndSwapObject(this, WAITERS,
|
||||
q.next = waiters, q);
|
||||
queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q);
|
||||
else if (timed) {
|
||||
final long parkNanos;
|
||||
if (startTime == 0L) { // first time
|
||||
@ -475,7 +472,7 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
if (pred.thread == null) // check for race
|
||||
continue retry;
|
||||
}
|
||||
else if (!U.compareAndSwapObject(this, WAITERS, q, s))
|
||||
else if (!WAITERS.compareAndSet(this, q, s))
|
||||
continue retry;
|
||||
}
|
||||
break;
|
||||
@ -483,19 +480,16 @@ public class FutureTask<V> implements RunnableFuture<V> {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
private static final long RUNNER;
|
||||
private static final long WAITERS;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
private static final VarHandle RUNNER;
|
||||
private static final VarHandle WAITERS;
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(FutureTask.class.getDeclaredField("state"));
|
||||
RUNNER = U.objectFieldOffset
|
||||
(FutureTask.class.getDeclaredField("runner"));
|
||||
WAITERS = U.objectFieldOffset
|
||||
(FutureTask.class.getDeclaredField("waiters"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(FutureTask.class, "state", int.class);
|
||||
RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
|
||||
WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -444,7 +446,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
/**
|
||||
* Queue nodes. Uses Object, not E, for items to allow forgetting
|
||||
* them after use. Relies heavily on Unsafe mechanics to minimize
|
||||
* them after use. Relies heavily on VarHandles to minimize
|
||||
* unnecessary ordering constraints: Writes that are intrinsically
|
||||
* ordered wrt other accesses or CASes use simple relaxed forms.
|
||||
*/
|
||||
@ -456,12 +458,12 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
// CAS methods for fields
|
||||
final boolean casNext(Node cmp, Node val) {
|
||||
return U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
return NEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
final boolean casItem(Object cmp, Object val) {
|
||||
// assert cmp == null || cmp.getClass() != Node.class;
|
||||
return U.compareAndSwapObject(this, ITEM, cmp, val);
|
||||
return ITEM.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -469,7 +471,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* only be seen after publication via casNext.
|
||||
*/
|
||||
Node(Object item, boolean isData) {
|
||||
U.putObject(this, ITEM, item); // relaxed write
|
||||
ITEM.set(this, item); // relaxed write
|
||||
this.isData = isData;
|
||||
}
|
||||
|
||||
@ -478,7 +480,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* only after CASing head field, so uses relaxed write.
|
||||
*/
|
||||
final void forgetNext() {
|
||||
U.putObject(this, NEXT, this);
|
||||
NEXT.set(this, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,8 +493,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* else we don't care).
|
||||
*/
|
||||
final void forgetContents() {
|
||||
U.putObject(this, ITEM, this);
|
||||
U.putObject(this, WAITER, null);
|
||||
ITEM.set(this, this);
|
||||
WAITER.set(this, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -537,19 +539,16 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
private static final long serialVersionUID = -3375979862319811754L;
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
private static final long WAITER;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle ITEM;
|
||||
private static final VarHandle NEXT;
|
||||
private static final VarHandle WAITER;
|
||||
static {
|
||||
try {
|
||||
ITEM = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("next"));
|
||||
WAITER = U.objectFieldOffset
|
||||
(Node.class.getDeclaredField("waiter"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
ITEM = l.findVarHandle(Node.class, "item", Object.class);
|
||||
NEXT = l.findVarHandle(Node.class, "next", Node.class);
|
||||
WAITER = l.findVarHandle(Node.class, "waiter", Thread.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -567,15 +566,15 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
// CAS methods for fields
|
||||
private boolean casTail(Node cmp, Node val) {
|
||||
return U.compareAndSwapObject(this, TAIL, cmp, val);
|
||||
return TAIL.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casHead(Node cmp, Node val) {
|
||||
return U.compareAndSwapObject(this, HEAD, cmp, val);
|
||||
return HEAD.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casSweepVotes(int cmp, int val) {
|
||||
return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val);
|
||||
return SWEEPVOTES.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1562,20 +1561,19 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
private static final long SWEEPVOTES;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle SWEEPVOTES;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(LinkedTransferQueue.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(LinkedTransferQueue.class.getDeclaredField("tail"));
|
||||
SWEEPVOTES = U.objectFieldOffset
|
||||
(LinkedTransferQueue.class.getDeclaredField("sweepVotes"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
HEAD = l.findVarHandle(LinkedTransferQueue.class, "head",
|
||||
Node.class);
|
||||
TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail",
|
||||
Node.class);
|
||||
SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes",
|
||||
int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
@ -221,7 +223,6 @@ import java.util.concurrent.locks.LockSupport;
|
||||
* phaser.arriveAndDeregister();
|
||||
* }}</pre>
|
||||
*
|
||||
*
|
||||
* <p>To create a set of {@code n} tasks using a tree of phasers, you
|
||||
* could use code of the following form, assuming a Task class with a
|
||||
* constructor accepting a {@code Phaser} that it registers with upon
|
||||
@ -384,7 +385,7 @@ public class Phaser {
|
||||
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
|
||||
if (unarrived <= 0)
|
||||
throw new IllegalStateException(badArrive(s));
|
||||
if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
|
||||
if (STATE.compareAndSet(this, s, s-=adjust)) {
|
||||
if (unarrived == 1) {
|
||||
long n = s & PARTIES_MASK; // base of next state
|
||||
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
|
||||
@ -397,12 +398,12 @@ public class Phaser {
|
||||
n |= nextUnarrived;
|
||||
int nextPhase = (phase + 1) & MAX_PHASE;
|
||||
n |= (long)nextPhase << PHASE_SHIFT;
|
||||
U.compareAndSwapLong(this, STATE, s, n);
|
||||
STATE.compareAndSet(this, s, n);
|
||||
releaseWaiters(phase);
|
||||
}
|
||||
else if (nextUnarrived == 0) { // propagate deregistration
|
||||
phase = parent.doArrive(ONE_DEREGISTER);
|
||||
U.compareAndSwapLong(this, STATE, s, s | EMPTY);
|
||||
STATE.compareAndSet(this, s, s | EMPTY);
|
||||
}
|
||||
else
|
||||
phase = parent.doArrive(ONE_ARRIVAL);
|
||||
@ -437,13 +438,13 @@ public class Phaser {
|
||||
if (parent == null || reconcileState() == s) {
|
||||
if (unarrived == 0) // wait out advance
|
||||
root.internalAwaitAdvance(phase, null);
|
||||
else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
|
||||
else if (STATE.compareAndSet(this, s, s + adjust))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (parent == null) { // 1st root registration
|
||||
long next = ((long)phase << PHASE_SHIFT) | adjust;
|
||||
if (U.compareAndSwapLong(this, STATE, s, next))
|
||||
if (STATE.compareAndSet(this, s, next))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
@ -455,8 +456,8 @@ public class Phaser {
|
||||
// finish registration whenever parent registration
|
||||
// succeeded, even when racing with termination,
|
||||
// since these are part of the same "transaction".
|
||||
while (!U.compareAndSwapLong
|
||||
(this, STATE, s,
|
||||
while (!STATE.weakCompareAndSetVolatile
|
||||
(this, s,
|
||||
((long)phase << PHASE_SHIFT) | adjust)) {
|
||||
s = state;
|
||||
phase = (int)(root.state >>> PHASE_SHIFT);
|
||||
@ -487,8 +488,8 @@ public class Phaser {
|
||||
// CAS to root phase with current parties, tripping unarrived
|
||||
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
|
||||
(int)(s >>> PHASE_SHIFT) &&
|
||||
!U.compareAndSwapLong
|
||||
(this, STATE, s,
|
||||
!STATE.weakCompareAndSetVolatile
|
||||
(this, s,
|
||||
s = (((long)phase << PHASE_SHIFT) |
|
||||
((phase < 0) ? (s & COUNTS_MASK) :
|
||||
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
|
||||
@ -677,7 +678,7 @@ public class Phaser {
|
||||
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
|
||||
if (unarrived <= 0)
|
||||
throw new IllegalStateException(badArrive(s));
|
||||
if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
|
||||
if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) {
|
||||
if (unarrived > 1)
|
||||
return root.internalAwaitAdvance(phase, null);
|
||||
if (root != this)
|
||||
@ -692,7 +693,7 @@ public class Phaser {
|
||||
n |= nextUnarrived;
|
||||
int nextPhase = (phase + 1) & MAX_PHASE;
|
||||
n |= (long)nextPhase << PHASE_SHIFT;
|
||||
if (!U.compareAndSwapLong(this, STATE, s, n))
|
||||
if (!STATE.compareAndSet(this, s, n))
|
||||
return (int)(state >>> PHASE_SHIFT); // terminated
|
||||
releaseWaiters(phase);
|
||||
return nextPhase;
|
||||
@ -808,7 +809,7 @@ public class Phaser {
|
||||
final Phaser root = this.root;
|
||||
long s;
|
||||
while ((s = root.state) >= 0) {
|
||||
if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
|
||||
if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) {
|
||||
// signal all threads
|
||||
releaseWaiters(0); // Waiters on evenQ
|
||||
releaseWaiters(1); // Waiters on oddQ
|
||||
@ -1043,6 +1044,8 @@ public class Phaser {
|
||||
node = new QNode(this, phase, false, false, 0L);
|
||||
node.wasInterrupted = interrupted;
|
||||
}
|
||||
else
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
else if (node.isReleasable()) // done or aborted
|
||||
break;
|
||||
@ -1131,14 +1134,12 @@ public class Phaser {
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long STATE;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle STATE;
|
||||
static {
|
||||
try {
|
||||
STATE = U.objectFieldOffset
|
||||
(Phaser.class.getDeclaredField("state"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(Phaser.class, "state", long.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -289,7 +291,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
|
||||
lock.unlock(); // must release and then re-acquire main lock
|
||||
Object[] newArray = null;
|
||||
if (allocationSpinLock == 0 &&
|
||||
U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
|
||||
ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) {
|
||||
try {
|
||||
int newCap = oldCap + ((oldCap < 64) ?
|
||||
(oldCap + 2) : // grow faster if small
|
||||
@ -1009,13 +1011,14 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
|
||||
return new PBQSpliterator<E>(this, null, 0, -1);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ALLOCATIONSPINLOCK;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle ALLOCATIONSPINLOCK;
|
||||
static {
|
||||
try {
|
||||
ALLOCATIONSPINLOCK = U.objectFieldOffset
|
||||
(PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class,
|
||||
"allocationSpinLock",
|
||||
int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
@ -866,7 +868,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
|
||||
/** Subscriber for method consume */
|
||||
private static final class ConsumerSubscriber<T>
|
||||
implements Flow.Subscriber<T> {
|
||||
implements Flow.Subscriber<T> {
|
||||
final CompletableFuture<Void> status;
|
||||
final Consumer<? super T> consumer;
|
||||
Flow.Subscription subscription;
|
||||
@ -906,7 +908,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
static final class ConsumerTask<T> extends ForkJoinTask<Void>
|
||||
implements Runnable {
|
||||
implements Runnable, CompletableFuture.AsynchronousCompletionTask {
|
||||
final BufferedSubscription<T> consumer;
|
||||
ConsumerTask(BufferedSubscription<T> consumer) {
|
||||
this.consumer = consumer;
|
||||
@ -959,11 +961,9 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
* Blocking control relies on the "waiter" field. Producers set
|
||||
* the field before trying to block, but must then recheck (via
|
||||
* offer) before parking. Signalling then just unparks and clears
|
||||
* waiter field. If the producer and consumer are both in the same
|
||||
* ForkJoinPool, or consumers are running in commonPool, the
|
||||
* producer attempts to help run consumer tasks that it forked
|
||||
* before blocking. To avoid potential cycles, only one level of
|
||||
* helping is currently supported.
|
||||
* waiter field. If the producer and/or consumer are using a
|
||||
* ForkJoinPool, the producer attempts to help run consumer tasks
|
||||
* via ForkJoinPool.helpAsyncBlocker before blocking.
|
||||
*
|
||||
* This class uses @Contended and heuristic field declaration
|
||||
* ordering to reduce false-sharing-based memory contention among
|
||||
@ -983,7 +983,6 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
volatile long demand; // # unfilled requests
|
||||
int maxCapacity; // reduced on OOME
|
||||
int putStat; // offer result for ManagedBlocker
|
||||
int helpDepth; // nested helping depth (at most 1)
|
||||
volatile int ctl; // atomic run state flags
|
||||
volatile int head; // next position to take
|
||||
int tail; // next position to put
|
||||
@ -1077,7 +1076,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
alloc = true;
|
||||
}
|
||||
else {
|
||||
U.fullFence(); // recheck
|
||||
VarHandle.fullFence(); // recheck
|
||||
int h = head, t = tail, size = t + 1 - h;
|
||||
if (cap >= size) {
|
||||
a[(cap - 1) & t] = item;
|
||||
@ -1116,10 +1115,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
if (a != null && cap > 0) {
|
||||
int mask = cap - 1;
|
||||
for (int j = head; j != t; ++j) {
|
||||
long k = ((long)(j & mask) << ASHIFT) + ABASE;
|
||||
Object x = U.getObjectVolatile(a, k);
|
||||
int k = j & mask;
|
||||
Object x = QA.getAcquire(a, k);
|
||||
if (x != null && // races with consumer
|
||||
U.compareAndSwapObject(a, k, x, null))
|
||||
QA.compareAndSet(a, k, x, null))
|
||||
newArray[j & newMask] = x;
|
||||
}
|
||||
}
|
||||
@ -1136,100 +1135,43 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
* initial offer return 0.
|
||||
*/
|
||||
final int submit(T item) {
|
||||
int stat; Executor e; ForkJoinWorkerThread w;
|
||||
if ((stat = offer(item)) == 0 && helpDepth == 0 &&
|
||||
((e = executor) instanceof ForkJoinPool)) {
|
||||
helpDepth = 1;
|
||||
Thread thread = Thread.currentThread();
|
||||
if ((thread instanceof ForkJoinWorkerThread) &&
|
||||
((w = (ForkJoinWorkerThread)thread)).getPool() == e)
|
||||
stat = internalHelpConsume(w.workQueue, item);
|
||||
else if (e == ForkJoinPool.commonPool())
|
||||
stat = externalHelpConsume
|
||||
(ForkJoinPool.commonSubmitterQueue(), item);
|
||||
helpDepth = 0;
|
||||
}
|
||||
if (stat == 0 && (stat = offer(item)) == 0) {
|
||||
int stat;
|
||||
if ((stat = offer(item)) == 0) {
|
||||
putItem = item;
|
||||
timeout = 0L;
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
putStat = 0;
|
||||
ForkJoinPool.helpAsyncBlocker(executor, this);
|
||||
if ((stat = putStat) == 0) {
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
}
|
||||
stat = putStat;
|
||||
}
|
||||
stat = putStat;
|
||||
if (timeout < 0L)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries helping for FJ submitter.
|
||||
*/
|
||||
private int internalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
|
||||
int stat = 0;
|
||||
if (w != null) {
|
||||
ForkJoinTask<?> t;
|
||||
while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
|
||||
if ((stat = offer(item)) != 0 || !w.tryUnpush(t))
|
||||
break;
|
||||
((ConsumerTask<?>)t).consumer.consume();
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries helping for non-FJ submitter.
|
||||
*/
|
||||
private int externalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
|
||||
int stat = 0;
|
||||
if (w != null) {
|
||||
ForkJoinTask<?> t;
|
||||
while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
|
||||
if ((stat = offer(item)) != 0 || !w.trySharedUnpush(t))
|
||||
break;
|
||||
((ConsumerTask<?>)t).consumer.consume();
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout version; similar to submit.
|
||||
*/
|
||||
final int timedOffer(T item, long nanos) {
|
||||
int stat; Executor e;
|
||||
if ((stat = offer(item)) == 0 && helpDepth == 0 &&
|
||||
((e = executor) instanceof ForkJoinPool)) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (((thread instanceof ForkJoinWorkerThread) &&
|
||||
((ForkJoinWorkerThread)thread).getPool() == e) ||
|
||||
e == ForkJoinPool.commonPool()) {
|
||||
helpDepth = 1;
|
||||
ForkJoinTask<?> t;
|
||||
long deadline = System.nanoTime() + nanos;
|
||||
while ((t = ForkJoinTask.peekNextLocalTask()) != null &&
|
||||
(t instanceof ConsumerTask)) {
|
||||
if ((stat = offer(item)) != 0 ||
|
||||
(nanos = deadline - System.nanoTime()) <= 0L ||
|
||||
!t.tryUnfork())
|
||||
break;
|
||||
((ConsumerTask<?>)t).consumer.consume();
|
||||
}
|
||||
helpDepth = 0;
|
||||
}
|
||||
}
|
||||
if (stat == 0 && (stat = offer(item)) == 0 &&
|
||||
(timeout = nanos) > 0L) {
|
||||
int stat;
|
||||
if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) {
|
||||
putItem = item;
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
putStat = 0;
|
||||
ForkJoinPool.helpAsyncBlocker(executor, this);
|
||||
if ((stat = putStat) == 0) {
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
timeout = INTERRUPTED;
|
||||
}
|
||||
stat = putStat;
|
||||
}
|
||||
stat = putStat;
|
||||
if (timeout < 0L)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
@ -1249,22 +1191,20 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
}
|
||||
else if ((c & ACTIVE) != 0) { // ensure keep-alive
|
||||
if ((c & CONSUME) != 0 ||
|
||||
U.compareAndSwapInt(this, CTL, c,
|
||||
c | CONSUME))
|
||||
CTL.compareAndSet(this, c, c | CONSUME))
|
||||
break;
|
||||
}
|
||||
else if (demand == 0L || tail == head)
|
||||
break;
|
||||
else if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE | CONSUME))) {
|
||||
else if (CTL.compareAndSet(this, c, c | (ACTIVE | CONSUME))) {
|
||||
try {
|
||||
e.execute(new ConsumerTask<T>(this));
|
||||
break;
|
||||
} catch (RuntimeException | Error ex) { // back out
|
||||
do {} while (((c = ctl) & DISABLED) == 0 &&
|
||||
(c & ACTIVE) != 0 &&
|
||||
!U.compareAndSwapInt(this, CTL, c,
|
||||
c & ~ACTIVE));
|
||||
!CTL.weakCompareAndSetVolatile
|
||||
(this, c, c & ~ACTIVE));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@ -1300,10 +1240,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
break;
|
||||
else if ((c & ACTIVE) != 0) {
|
||||
pendingError = ex;
|
||||
if (U.compareAndSwapInt(this, CTL, c, c | ERROR))
|
||||
if (CTL.compareAndSet(this, c, c | ERROR))
|
||||
break; // cause consumer task to exit
|
||||
}
|
||||
else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
|
||||
else if (CTL.compareAndSet(this, c, DISABLED)) {
|
||||
Flow.Subscriber<? super T> s = subscriber;
|
||||
if (s != null && ex != null) {
|
||||
try {
|
||||
@ -1330,7 +1270,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
for (int c;;) {
|
||||
if ((c = ctl) == DISABLED || (c & ACTIVE) == 0)
|
||||
break;
|
||||
if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
|
||||
if (CTL.compareAndSet(this, c, c & ~ACTIVE)) {
|
||||
onError(ex);
|
||||
break;
|
||||
}
|
||||
@ -1343,8 +1283,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
for (int c;;) {
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE | CONSUME | COMPLETE))) {
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (ACTIVE | CONSUME | COMPLETE))) {
|
||||
if ((c & ACTIVE) == 0)
|
||||
startOrDisable();
|
||||
break;
|
||||
@ -1356,8 +1296,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
for (int c;;) {
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE | CONSUME | SUBSCRIBE))) {
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (ACTIVE | CONSUME | SUBSCRIBE))) {
|
||||
if ((c & ACTIVE) == 0)
|
||||
startOrDisable();
|
||||
break;
|
||||
@ -1375,11 +1315,11 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
else if ((c & ACTIVE) != 0) {
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (CONSUME | ERROR)))
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (CONSUME | ERROR)))
|
||||
break;
|
||||
}
|
||||
else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
|
||||
else if (CTL.compareAndSet(this, c, DISABLED)) {
|
||||
detach();
|
||||
break;
|
||||
}
|
||||
@ -1395,19 +1335,18 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
long prev = demand, d;
|
||||
if ((d = prev + n) < prev) // saturate
|
||||
d = Long.MAX_VALUE;
|
||||
if (U.compareAndSwapLong(this, DEMAND, prev, d)) {
|
||||
if (DEMAND.compareAndSet(this, prev, d)) {
|
||||
for (int c, h;;) {
|
||||
if ((c = ctl) == DISABLED)
|
||||
break;
|
||||
else if ((c & ACTIVE) != 0) {
|
||||
if ((c & CONSUME) != 0 ||
|
||||
U.compareAndSwapInt(this, CTL, c,
|
||||
c | CONSUME))
|
||||
CTL.compareAndSet(this, c, c | CONSUME))
|
||||
break;
|
||||
}
|
||||
else if ((h = head) != tail) {
|
||||
if (U.compareAndSwapInt(this, CTL, c,
|
||||
c | (ACTIVE|CONSUME))) {
|
||||
if (CTL.compareAndSet(this, c,
|
||||
c | (ACTIVE|CONSUME))) {
|
||||
startOrDisable();
|
||||
break;
|
||||
}
|
||||
@ -1476,16 +1415,14 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
if ((s = subscriber) != null) { // else disabled
|
||||
for (;;) {
|
||||
long d = demand;
|
||||
int c; Object[] a; int n; long i; Object x; Thread w;
|
||||
int c; Object[] a; int n, i; Object x; Thread w;
|
||||
if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) {
|
||||
if (!checkControl(s, c))
|
||||
break;
|
||||
}
|
||||
else if ((a = array) == null || h == tail ||
|
||||
(n = a.length) == 0 ||
|
||||
(x = U.getObjectVolatile
|
||||
(a, (i = ((long)((n - 1) & h) << ASHIFT) + ABASE)))
|
||||
== null) {
|
||||
(x = QA.getAcquire(a, i = (n - 1) & h)) == null) {
|
||||
if (!checkEmpty(s, c))
|
||||
break;
|
||||
}
|
||||
@ -1494,10 +1431,10 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
break;
|
||||
}
|
||||
else if (((c & CONSUME) != 0 ||
|
||||
U.compareAndSwapInt(this, CTL, c, c | CONSUME)) &&
|
||||
U.compareAndSwapObject(a, i, x, null)) {
|
||||
U.putIntRelease(this, HEAD, ++h);
|
||||
U.getAndAddLong(this, DEMAND, -1L);
|
||||
CTL.compareAndSet(this, c, c | CONSUME)) &&
|
||||
QA.compareAndSet(a, i, x, null)) {
|
||||
HEAD.setRelease(this, ++h);
|
||||
DEMAND.getAndAdd(this, -1L);
|
||||
if ((w = waiter) != null)
|
||||
signalWaiter(w);
|
||||
try {
|
||||
@ -1528,7 +1465,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
}
|
||||
}
|
||||
else if ((c & SUBSCRIBE) != 0) {
|
||||
if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) {
|
||||
if (CTL.compareAndSet(this, c, c & ~SUBSCRIBE)) {
|
||||
try {
|
||||
if (s != null)
|
||||
s.onSubscribe(this);
|
||||
@ -1551,9 +1488,9 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
boolean stat = true;
|
||||
if (head == tail) {
|
||||
if ((c & CONSUME) != 0)
|
||||
U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
|
||||
CTL.compareAndSet(this, c, c & ~CONSUME);
|
||||
else if ((c & COMPLETE) != 0) {
|
||||
if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
|
||||
if (CTL.compareAndSet(this, c, DISABLED)) {
|
||||
try {
|
||||
if (s != null)
|
||||
s.onComplete();
|
||||
@ -1561,7 +1498,7 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
|
||||
else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
|
||||
stat = false;
|
||||
}
|
||||
return stat;
|
||||
@ -1574,8 +1511,8 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
boolean stat = true;
|
||||
if (demand == 0L) {
|
||||
if ((c & CONSUME) != 0)
|
||||
U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
|
||||
else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
|
||||
CTL.compareAndSet(this, c, c & ~CONSUME);
|
||||
else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
|
||||
stat = false;
|
||||
}
|
||||
return stat;
|
||||
@ -1595,31 +1532,25 @@ public class SubmissionPublisher<T> implements Flow.Publisher<T>,
|
||||
onError(ex);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long CTL;
|
||||
private static final long TAIL;
|
||||
private static final long HEAD;
|
||||
private static final long DEMAND;
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle CTL;
|
||||
private static final VarHandle TAIL;
|
||||
private static final VarHandle HEAD;
|
||||
private static final VarHandle DEMAND;
|
||||
private static final VarHandle QA;
|
||||
|
||||
static {
|
||||
try {
|
||||
CTL = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("ctl"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("tail"));
|
||||
HEAD = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("head"));
|
||||
DEMAND = U.objectFieldOffset
|
||||
(BufferedSubscription.class.getDeclaredField("demand"));
|
||||
|
||||
ABASE = U.arrayBaseOffset(Object[].class);
|
||||
int scale = U.arrayIndexScale(Object[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("data type scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
CTL = l.findVarHandle(BufferedSubscription.class, "ctl",
|
||||
int.class);
|
||||
TAIL = l.findVarHandle(BufferedSubscription.class, "tail",
|
||||
int.class);
|
||||
HEAD = l.findVarHandle(BufferedSubscription.class, "head",
|
||||
int.class);
|
||||
DEMAND = l.findVarHandle(BufferedSubscription.class, "demand",
|
||||
long.class);
|
||||
QA = MethodHandles.arrayElementVarHandle(Object[].class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -36,6 +36,8 @@
|
||||
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -247,7 +249,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
|
||||
boolean casNext(SNode cmp, SNode val) {
|
||||
return cmp == next &&
|
||||
U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
SNEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,7 +262,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
boolean tryMatch(SNode s) {
|
||||
if (match == null &&
|
||||
U.compareAndSwapObject(this, MATCH, null, s)) {
|
||||
SMATCH.compareAndSet(this, null, s)) {
|
||||
Thread w = waiter;
|
||||
if (w != null) { // waiters need at most one unpark
|
||||
waiter = null;
|
||||
@ -275,24 +277,21 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
* Tries to cancel a wait by matching node to itself.
|
||||
*/
|
||||
void tryCancel() {
|
||||
U.compareAndSwapObject(this, MATCH, null, this);
|
||||
SMATCH.compareAndSet(this, null, this);
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
return match == this;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long MATCH;
|
||||
private static final long NEXT;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle SMATCH;
|
||||
private static final VarHandle SNEXT;
|
||||
static {
|
||||
try {
|
||||
MATCH = U.objectFieldOffset
|
||||
(SNode.class.getDeclaredField("match"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(SNode.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
|
||||
SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -304,7 +303,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
|
||||
boolean casHead(SNode h, SNode nh) {
|
||||
return h == head &&
|
||||
U.compareAndSwapObject(this, HEAD, h, nh);
|
||||
SHEAD.compareAndSet(this, h, nh);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,8 +450,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (spins > 0)
|
||||
if (spins > 0) {
|
||||
Thread.onSpinWait();
|
||||
spins = shouldSpin(s) ? (spins - 1) : 0;
|
||||
}
|
||||
else if (s.waiter == null)
|
||||
s.waiter = w; // establish waiter so can park next iter
|
||||
else if (!timed)
|
||||
@ -508,13 +509,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle SHEAD;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(TransferStack.class.getDeclaredField("head"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -546,19 +546,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
|
||||
boolean casNext(QNode cmp, QNode val) {
|
||||
return next == cmp &&
|
||||
U.compareAndSwapObject(this, NEXT, cmp, val);
|
||||
QNEXT.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
boolean casItem(Object cmp, Object val) {
|
||||
return item == cmp &&
|
||||
U.compareAndSwapObject(this, ITEM, cmp, val);
|
||||
QITEM.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to cancel by CAS'ing ref to this as item.
|
||||
*/
|
||||
void tryCancel(Object cmp) {
|
||||
U.compareAndSwapObject(this, ITEM, cmp, this);
|
||||
QITEM.compareAndSet(this, cmp, this);
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
@ -574,17 +574,14 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
return next == this;
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long ITEM;
|
||||
private static final long NEXT;
|
||||
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle QITEM;
|
||||
private static final VarHandle QNEXT;
|
||||
static {
|
||||
try {
|
||||
ITEM = U.objectFieldOffset
|
||||
(QNode.class.getDeclaredField("item"));
|
||||
NEXT = U.objectFieldOffset
|
||||
(QNode.class.getDeclaredField("next"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
QITEM = l.findVarHandle(QNode.class, "item", Object.class);
|
||||
QNEXT = l.findVarHandle(QNode.class, "next", QNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -614,7 +611,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
void advanceHead(QNode h, QNode nh) {
|
||||
if (h == head &&
|
||||
U.compareAndSwapObject(this, HEAD, h, nh))
|
||||
QHEAD.compareAndSet(this, h, nh))
|
||||
h.next = h; // forget old next
|
||||
}
|
||||
|
||||
@ -623,7 +620,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
void advanceTail(QNode t, QNode nt) {
|
||||
if (tail == t)
|
||||
U.compareAndSwapObject(this, TAIL, t, nt);
|
||||
QTAIL.compareAndSet(this, t, nt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -631,7 +628,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
boolean casCleanMe(QNode cmp, QNode val) {
|
||||
return cleanMe == cmp &&
|
||||
U.compareAndSwapObject(this, CLEANME, cmp, val);
|
||||
QCLEANME.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -752,8 +749,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (spins > 0)
|
||||
if (spins > 0) {
|
||||
--spins;
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
else if (s.waiter == null)
|
||||
s.waiter = w;
|
||||
else if (!timed)
|
||||
@ -817,18 +816,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
}
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long HEAD;
|
||||
private static final long TAIL;
|
||||
private static final long CLEANME;
|
||||
// VarHandle mechanics
|
||||
private static final VarHandle QHEAD;
|
||||
private static final VarHandle QTAIL;
|
||||
private static final VarHandle QCLEANME;
|
||||
static {
|
||||
try {
|
||||
HEAD = U.objectFieldOffset
|
||||
(TransferQueue.class.getDeclaredField("head"));
|
||||
TAIL = U.objectFieldOffset
|
||||
(TransferQueue.class.getDeclaredField("tail"));
|
||||
CLEANME = U.objectFieldOffset
|
||||
(TransferQueue.class.getDeclaredField("cleanMe"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
QHEAD = l.findVarHandle(TransferQueue.class, "head",
|
||||
QNode.class);
|
||||
QTAIL = l.findVarHandle(TransferQueue.class, "tail",
|
||||
QNode.class);
|
||||
QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe",
|
||||
QNode.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.io.ObjectStreamField;
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.Random;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -47,6 +48,7 @@ import java.util.stream.DoubleStream;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* A random number generator isolated to the current thread. Like the
|
||||
@ -95,7 +97,9 @@ public class ThreadLocalRandom extends Random {
|
||||
* ThreadLocalRandom sequence. The dual use is a marriage of
|
||||
* convenience, but is a simple and efficient way of reducing
|
||||
* application-level overhead and footprint of most concurrent
|
||||
* programs.
|
||||
* programs. Even more opportunistically, we also define here
|
||||
* other package-private utilities that access Thread class
|
||||
* fields.
|
||||
*
|
||||
* Even though this class subclasses java.util.Random, it uses the
|
||||
* same basic algorithm as java.util.SplittableRandom. (See its
|
||||
@ -958,6 +962,49 @@ public class ThreadLocalRandom extends Random {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Support for other package-private ThreadLocal access
|
||||
|
||||
/**
|
||||
* Erases ThreadLocals by nulling out Thread maps.
|
||||
*/
|
||||
static final void eraseThreadLocals(Thread thread) {
|
||||
U.putObject(thread, THREADLOCALS, null);
|
||||
U.putObject(thread, INHERITABLETHREADLOCALS, null);
|
||||
}
|
||||
|
||||
static final void setInheritedAccessControlContext(Thread thread,
|
||||
AccessControlContext acc) {
|
||||
U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new group with the system ThreadGroup (the
|
||||
* topmost, parent-less group) as parent. Uses Unsafe to
|
||||
* traverse Thread.group and ThreadGroup.parent fields.
|
||||
*/
|
||||
static final ThreadGroup createThreadGroup(String name) {
|
||||
if (name == null)
|
||||
throw new NullPointerException();
|
||||
try {
|
||||
long tg = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("group"));
|
||||
long gp = U.objectFieldOffset
|
||||
(ThreadGroup.class.getDeclaredField("parent"));
|
||||
ThreadGroup group = (ThreadGroup)
|
||||
U.getObject(Thread.currentThread(), tg);
|
||||
while (group != null) {
|
||||
ThreadGroup parent = (ThreadGroup)U.getObject(group, gp);
|
||||
if (parent == null)
|
||||
return new ThreadGroup(group, name);
|
||||
group = parent;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
// fall through if null as cannot-happen safeguard
|
||||
throw new Error("Cannot create ThreadGroup");
|
||||
}
|
||||
|
||||
// Serialization support
|
||||
|
||||
private static final long serialVersionUID = -5851777807851030925L;
|
||||
@ -1022,10 +1069,13 @@ public class ThreadLocalRandom extends Random {
|
||||
static final String BAD_SIZE = "size must be non-negative";
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long SEED;
|
||||
private static final long PROBE;
|
||||
private static final long SECONDARY;
|
||||
private static final long THREADLOCALS;
|
||||
private static final long INHERITABLETHREADLOCALS;
|
||||
private static final long INHERITEDACCESSCONTROLCONTEXT;
|
||||
static {
|
||||
try {
|
||||
SEED = U.objectFieldOffset
|
||||
@ -1034,6 +1084,12 @@ public class ThreadLocalRandom extends Random {
|
||||
(Thread.class.getDeclaredField("threadLocalRandomProbe"));
|
||||
SECONDARY = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
|
||||
THREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("threadLocals"));
|
||||
INHERITABLETHREADLOCALS = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritableThreadLocals"));
|
||||
INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
|
||||
(Thread.class.getDeclaredField("inheritedAccessControlContext"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@ -35,27 +35,26 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* A {@code boolean} value that may be updated atomically. See the
|
||||
* {@link java.util.concurrent.atomic} package specification for
|
||||
* description of the properties of atomic variables. An
|
||||
* {@code AtomicBoolean} is used in applications such as atomically
|
||||
* updated flags, and cannot be used as a replacement for a
|
||||
* {@link java.lang.Boolean}.
|
||||
* {@link VarHandle} specification for descriptions of the properties
|
||||
* of atomic accesses. An {@code AtomicBoolean} is used in
|
||||
* applications such as atomically updated flags, and cannot be used
|
||||
* as a replacement for a {@link java.lang.Boolean}.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicBoolean implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 4654671469794556979L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
private static final VarHandle VALUE;
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
(AtomicBoolean.class.getDeclaredField("value"));
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@ -79,7 +78,8 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@ -88,40 +88,39 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(boolean expect, boolean update) {
|
||||
return U.compareAndSwapInt(this, VALUE,
|
||||
(expect ? 1 : 0),
|
||||
(update ? 1 : 0));
|
||||
public final boolean compareAndSet(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.compareAndSet(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public boolean weakCompareAndSet(boolean expect, boolean update) {
|
||||
return U.compareAndSwapInt(this, VALUE,
|
||||
(expect ? 1 : 0),
|
||||
(update ? 1 : 0));
|
||||
public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSet(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
@ -130,17 +129,19 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(boolean newValue) {
|
||||
U.putIntRelease(this, VALUE, (newValue ? 1 : 0));
|
||||
VALUE.setRelease(this, (newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the previous value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
@ -161,4 +162,178 @@ public class AtomicBoolean implements java.io.Serializable {
|
||||
return Boolean.toString(get());
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as
|
||||
* if the variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean getPlain() {
|
||||
return (int)VALUE.get(this) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(boolean newValue) {
|
||||
VALUE.set(this, newValue ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean getOpaque() {
|
||||
return (int)VALUE.getOpaque(this) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(boolean newValue) {
|
||||
VALUE.setOpaque(this, newValue ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean getAcquire() {
|
||||
return (int)VALUE.getAcquire(this) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(boolean newValue) {
|
||||
VALUE.setRelease(this, newValue ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean compareAndExchange(boolean expectedValue, boolean newValue) {
|
||||
return (int)VALUE.compareAndExchange(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean compareAndExchangeAcquire(boolean expectedValue, boolean newValue) {
|
||||
return (int)VALUE.compareAndExchangeAcquire(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean compareAndExchangeRelease(boolean expectedValue, boolean newValue) {
|
||||
return (int)VALUE.compareAndExchangeRelease(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if the current
|
||||
* value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSetVolatile(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if the current
|
||||
* value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSetAcquire(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if the current
|
||||
* value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(boolean expectedValue, boolean newValue) {
|
||||
return VALUE.weakCompareAndSetRelease(this,
|
||||
(expectedValue ? 1 : 0),
|
||||
(newValue ? 1 : 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,18 +35,18 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
/**
|
||||
* An {@code int} value that may be updated atomically. See the
|
||||
* {@link java.util.concurrent.atomic} package specification for
|
||||
* description of the properties of atomic variables. An
|
||||
* {@code AtomicInteger} is used in applications such as atomically
|
||||
* incremented counters, and cannot be used as a replacement for an
|
||||
* {@link java.lang.Integer}. However, this class does extend
|
||||
* {@code Number} to allow uniform access by tools and utilities that
|
||||
* deal with numerically-based classes.
|
||||
* {@link VarHandle} specification for descriptions of the properties
|
||||
* of atomic accesses. An {@code AtomicInteger} is used in
|
||||
* applications such as atomically incremented counters, and cannot be
|
||||
* used as a replacement for an {@link java.lang.Integer}. However,
|
||||
* this class does extend {@code Number} to allow uniform access by
|
||||
* tools and utilities that deal with numerically-based classes.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
@ -54,6 +54,10 @@ import java.util.function.IntUnaryOperator;
|
||||
public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 6214790243416807050L;
|
||||
|
||||
/*
|
||||
* This class intended to be implemented using VarHandles, but there
|
||||
* are unresolved cyclic startup dependencies.
|
||||
*/
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
@ -84,7 +88,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@ -93,7 +98,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
@ -102,7 +108,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
@ -112,7 +119,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the old value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
@ -122,36 +130,37 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int expect, int update) {
|
||||
return U.compareAndSwapInt(this, VALUE, expect, update);
|
||||
public final boolean compareAndSet(int expectedValue, int newValue) {
|
||||
return U.compareAndSwapInt(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int expect, int update) {
|
||||
return U.compareAndSwapInt(this, VALUE, expect, update);
|
||||
public final boolean weakCompareAndSet(int expectedValue, int newValue) {
|
||||
return U.weakCompareAndSwapInt(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
@ -160,7 +169,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(-1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
@ -169,7 +181,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
@ -179,7 +192,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
@ -188,7 +204,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(-1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
@ -197,7 +216,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
@ -217,12 +237,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int getAndUpdate(IntUnaryOperator updateFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,12 +258,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int updateAndGet(IntUnaryOperator updateFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,12 +284,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final int getAndAccumulate(int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,12 +310,14 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final int accumulateAndGet(int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
int prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
int prev = get(), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,7 +329,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as an {@code int}.
|
||||
* Returns the current value of this {@code AtomicInteger} as an
|
||||
* {@code int},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* Equivalent to {@link #get()}.
|
||||
*/
|
||||
public int intValue() {
|
||||
@ -309,8 +340,9 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as a {@code long}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicInteger} as a
|
||||
* {@code long} after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public long longValue() {
|
||||
@ -318,8 +350,9 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as a {@code float}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicInteger} as a
|
||||
* {@code float} after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public float floatValue() {
|
||||
@ -327,12 +360,175 @@ public class AtomicInteger extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicInteger} as a {@code double}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicInteger} as a
|
||||
* {@code double} after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public double doubleValue() {
|
||||
return (double)get();
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as
|
||||
* if the variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getPlain() {
|
||||
return U.getInt(this, VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int newValue) {
|
||||
U.putInt(this, VALUE, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getOpaque() {
|
||||
return U.getIntOpaque(this, VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int newValue) {
|
||||
U.putIntOpaque(this, VALUE, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getAcquire() {
|
||||
return U.getIntAcquire(this, VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int newValue) {
|
||||
U.putIntRelease(this, VALUE, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchange(int expectedValue, int newValue) {
|
||||
return U.compareAndExchangeIntVolatile(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeAcquire(int expectedValue, int newValue) {
|
||||
return U.compareAndExchangeIntAcquire(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeRelease(int expectedValue, int newValue) {
|
||||
return U.compareAndExchangeIntRelease(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if
|
||||
* the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int expectedValue, int newValue) {
|
||||
return U.weakCompareAndSwapIntVolatile(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if
|
||||
* the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int expectedValue, int newValue) {
|
||||
return U.weakCompareAndSwapIntAcquire(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue} if
|
||||
* the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int expectedValue, int newValue) {
|
||||
return U.weakCompareAndSwapIntRelease(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,44 +35,24 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
/**
|
||||
* An {@code int} array in which elements may be updated atomically.
|
||||
* See the {@link java.util.concurrent.atomic} package
|
||||
* specification for description of the properties of atomic
|
||||
* variables.
|
||||
* See the {@link VarHandle} specification for descriptions of the
|
||||
* properties of atomic accesses.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicIntegerArray implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 2862133569453604235L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
private static final VarHandle AA
|
||||
= MethodHandles.arrayElementVarHandle(int[].class);
|
||||
private final int[] array;
|
||||
|
||||
static {
|
||||
ABASE = U.arrayBaseOffset(int[].class);
|
||||
int scale = U.arrayIndexScale(int[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("array index scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
}
|
||||
|
||||
private long checkedByteOffset(int i) {
|
||||
if (i < 0 || i >= array.length)
|
||||
throw new IndexOutOfBoundsException("index " + i);
|
||||
|
||||
return byteOffset(i);
|
||||
}
|
||||
|
||||
private static long byteOffset(int i) {
|
||||
return ((long) i << ASHIFT) + ABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AtomicIntegerArray of the given length, with all
|
||||
* elements initially zero.
|
||||
@ -105,147 +85,155 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value at position {@code i}.
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the current value
|
||||
*/
|
||||
public final int get(int i) {
|
||||
return getRaw(checkedByteOffset(i));
|
||||
}
|
||||
|
||||
private int getRaw(long offset) {
|
||||
return U.getIntVolatile(array, offset);
|
||||
return (int)AA.getVolatile(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public final void set(int i, int newValue) {
|
||||
U.putIntVolatile(array, checkedByteOffset(i), newValue);
|
||||
AA.setVolatile(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(int i, int newValue) {
|
||||
U.putIntRelease(array, checkedByteOffset(i), newValue);
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* value and returns the old value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndSet(int i, int newValue) {
|
||||
return U.getAndSetInt(array, checkedByteOffset(i), newValue);
|
||||
return (int)AA.getAndSet(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int i, int expect, int update) {
|
||||
return compareAndSetRaw(checkedByteOffset(i), expect, update);
|
||||
}
|
||||
|
||||
private boolean compareAndSetRaw(long offset, int expect, int update) {
|
||||
return U.compareAndSwapInt(array, offset, expect, update);
|
||||
public final boolean compareAndSet(int i, int expectedValue, int newValue) {
|
||||
return AA.compareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int i, int expect, int update) {
|
||||
return compareAndSet(i, expect, update);
|
||||
public final boolean weakCompareAndSet(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndIncrement(int i) {
|
||||
return getAndAdd(i, 1);
|
||||
return (int)AA.getAndAdd(array, i, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndDecrement(int i) {
|
||||
return getAndAdd(i, -1);
|
||||
return (int)AA.getAndAdd(array, i, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
*/
|
||||
public final int getAndAdd(int i, int delta) {
|
||||
return U.getAndAddInt(array, checkedByteOffset(i), delta);
|
||||
return (int)AA.getAndAdd(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int incrementAndGet(int i) {
|
||||
return getAndAdd(i, 1) + 1;
|
||||
return (int)AA.addAndGet(array, i, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int decrementAndGet(int i) {
|
||||
return getAndAdd(i, -1) - 1;
|
||||
return (int)AA.addAndGet(array, i, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
*/
|
||||
public final int addAndGet(int i, int delta) {
|
||||
return getAndAdd(i, delta) + delta;
|
||||
return (int)AA.addAndGet(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,13 +248,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,23 +270,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsInt(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the previous value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the previous value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -307,23 +298,25 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
*/
|
||||
public final int getAndAccumulate(int i, int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the updated value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the updated value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -333,13 +326,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
*/
|
||||
public final int accumulateAndGet(int i, int x,
|
||||
IntBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
int prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
int prev = get(i), next = 0;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsInt(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,11 +348,190 @@ public class AtomicIntegerArray implements java.io.Serializable {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('[');
|
||||
for (int i = 0; ; i++) {
|
||||
b.append(getRaw(byteOffset(i)));
|
||||
b.append(get(i));
|
||||
if (i == iMax)
|
||||
return b.append(']').toString();
|
||||
b.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory semantics of reading as if the variable was declared
|
||||
* non-{@code volatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getPlain(int i) {
|
||||
return (int)AA.get(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory semantics of setting as if the variable was
|
||||
* declared non-{@code volatile} and non-{@code final}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int i, int newValue) {
|
||||
AA.set(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getOpaque(int i) {
|
||||
return (int)AA.getOpaque(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int i, int newValue) {
|
||||
AA.setOpaque(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final int getAcquire(int i) {
|
||||
return (int)AA.getAcquire(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int i, int newValue) {
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchange(int i, int expectedValue, int newValue) {
|
||||
return (int)AA.compareAndExchange(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeAcquire(int i, int expectedValue, int newValue) {
|
||||
return (int)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final int compareAndExchangeRelease(int i, int expectedValue, int newValue) {
|
||||
return (int)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int i, int expectedValue, int newValue) {
|
||||
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
@ -150,8 +151,8 @@ public abstract class AtomicIntegerFieldUpdater<T> {
|
||||
public abstract void lazySet(T obj, int newValue);
|
||||
|
||||
/**
|
||||
* Gets the current value held in the field of the given object managed
|
||||
* by this updater.
|
||||
* Returns the current value held in the field of the given object
|
||||
* managed by this updater.
|
||||
*
|
||||
* @param obj An object whose field to get
|
||||
* @return the current value
|
||||
@ -367,7 +368,7 @@ public abstract class AtomicIntegerFieldUpdater<T> {
|
||||
*/
|
||||
private static final class AtomicIntegerFieldUpdaterImpl<T>
|
||||
extends AtomicIntegerFieldUpdater<T> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
|
||||
@ -35,18 +35,18 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
/**
|
||||
* A {@code long} value that may be updated atomically. See the
|
||||
* {@link java.util.concurrent.atomic} package specification for
|
||||
* description of the properties of atomic variables. An
|
||||
* {@code AtomicLong} is used in applications such as atomically
|
||||
* incremented sequence numbers, and cannot be used as a replacement
|
||||
* for a {@link java.lang.Long}. However, this class does extend
|
||||
* {@code Number} to allow uniform access by tools and utilities that
|
||||
* deal with numerically-based classes.
|
||||
* {@link VarHandle} specification for descriptions of the properties
|
||||
* of atomic accesses. An {@code AtomicLong} is used in applications
|
||||
* such as atomically incremented sequence numbers, and cannot be used
|
||||
* as a replacement for a {@link java.lang.Long}. However, this class
|
||||
* does extend {@code Number} to allow uniform access by tools and
|
||||
* utilities that deal with numerically-based classes.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
@ -54,12 +54,9 @@ import java.util.function.LongUnaryOperator;
|
||||
public class AtomicLong extends Number implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 1927816293512124184L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
/**
|
||||
* Records whether the underlying JVM supports lockless
|
||||
* compareAndSwap for longs. While the Unsafe.compareAndSwapLong
|
||||
* compareAndSwap for longs. While the intrinsic compareAndSwapLong
|
||||
* method works in either case, some constructions should be
|
||||
* handled at Java level to avoid locking user-visible locks.
|
||||
*/
|
||||
@ -71,6 +68,13 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
*/
|
||||
private static native boolean VMSupportsCS8();
|
||||
|
||||
/*
|
||||
* This class intended to be implemented using VarHandles, but there
|
||||
* are unresolved cyclic startup dependencies.
|
||||
*/
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long VALUE;
|
||||
|
||||
static {
|
||||
try {
|
||||
VALUE = U.objectFieldOffset
|
||||
@ -98,7 +102,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @return the current value
|
||||
*/
|
||||
@ -107,7 +112,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
@ -118,7 +124,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets to the given value.
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
@ -128,7 +135,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets to the given value and returns the old value.
|
||||
* Atomically sets the value to {@code newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
@ -138,36 +146,37 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(long expect, long update) {
|
||||
return U.compareAndSwapLong(this, VALUE, expect, update);
|
||||
public final boolean compareAndSet(long expectedValue, long newValue) {
|
||||
return U.compareAndSwapLong(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given updated value
|
||||
* if the current value {@code ==} the expected value.
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
*
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(long expect, long update) {
|
||||
return U.compareAndSwapLong(this, VALUE, expect, update);
|
||||
public final boolean weakCompareAndSet(long expectedValue, long newValue) {
|
||||
return U.weakCompareAndSwapLong(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
@ -176,7 +185,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(-1)}.
|
||||
*
|
||||
* @return the previous value
|
||||
*/
|
||||
@ -185,7 +197,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
@ -195,7 +208,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the current value.
|
||||
* Atomically increments the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
@ -204,7 +220,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the current value.
|
||||
* Atomically decrements the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(-1)}.
|
||||
*
|
||||
* @return the updated value
|
||||
*/
|
||||
@ -213,7 +232,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the current value.
|
||||
* Atomically adds the given value to the current value,
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
@ -233,12 +253,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long getAndUpdate(LongUnaryOperator updateFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,12 +274,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long updateAndGet(LongUnaryOperator updateFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,12 +300,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final long getAndAccumulate(long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return prev;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -300,12 +326,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
*/
|
||||
public final long accumulateAndGet(long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long prev, next;
|
||||
do {
|
||||
prev = get();
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSet(prev, next));
|
||||
return next;
|
||||
long prev = get(), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -317,8 +345,9 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as an {@code int}
|
||||
* after a narrowing primitive conversion.
|
||||
* Returns the current value of this {@code AtomicLong} as an {@code int}
|
||||
* after a narrowing primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.3 Narrowing Primitive Conversions
|
||||
*/
|
||||
public int intValue() {
|
||||
@ -326,7 +355,8 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as a {@code long}.
|
||||
* Returns the current value of this {@code AtomicLong} as a {@code long},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* Equivalent to {@link #get()}.
|
||||
*/
|
||||
public long longValue() {
|
||||
@ -334,8 +364,9 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as a {@code float}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicLong} as a {@code float}
|
||||
* after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public float floatValue() {
|
||||
@ -343,12 +374,175 @@ public class AtomicLong extends Number implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code AtomicLong} as a {@code double}
|
||||
* after a widening primitive conversion.
|
||||
* Returns the current value of this {@code AtomicLong} as a {@code double}
|
||||
* after a widening primitive conversion,
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
* @jls 5.1.2 Widening Primitive Conversions
|
||||
*/
|
||||
public double doubleValue() {
|
||||
return (double)get();
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value, with memory semantics of reading as if the
|
||||
* variable was declared non-{@code volatile}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getPlain() {
|
||||
return U.getLong(this, VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue}, with memory semantics
|
||||
* of setting as if the variable was declared non-{@code volatile}
|
||||
* and non-{@code final}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(long newValue) {
|
||||
U.putLong(this, VALUE, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getOpaque() {
|
||||
return U.getLongOpaque(this, VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(long newValue) {
|
||||
U.putLongOpaque(this, VALUE, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value,
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getAcquire() {
|
||||
return U.getLongAcquire(this, VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(long newValue) {
|
||||
U.putLongRelease(this, VALUE, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchange(long expectedValue, long newValue) {
|
||||
return U.compareAndExchangeLongVolatile(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeAcquire(long expectedValue, long newValue) {
|
||||
return U.compareAndExchangeLongAcquire(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to {@code newValue} if the current value,
|
||||
* referred to as the <em>witness value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeRelease(long expectedValue, long newValue) {
|
||||
return U.compareAndExchangeLongRelease(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(long expectedValue, long newValue) {
|
||||
return U.weakCompareAndSwapLongVolatile(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(long expectedValue, long newValue) {
|
||||
return U.weakCompareAndSwapLongAcquire(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the value to {@code newValue}
|
||||
* if the current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(long expectedValue, long newValue) {
|
||||
return U.weakCompareAndSwapLongRelease(this, VALUE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,43 +35,24 @@
|
||||
|
||||
package java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
/**
|
||||
* A {@code long} array in which elements may be updated atomically.
|
||||
* See the {@link java.util.concurrent.atomic} package specification
|
||||
* for description of the properties of atomic variables.
|
||||
* See the {@link VarHandle} specification for descriptions of the
|
||||
* properties of atomic accesses.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class AtomicLongArray implements java.io.Serializable {
|
||||
private static final long serialVersionUID = -2308431214976778248L;
|
||||
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final int ABASE;
|
||||
private static final int ASHIFT;
|
||||
private static final VarHandle AA
|
||||
= MethodHandles.arrayElementVarHandle(long[].class);
|
||||
private final long[] array;
|
||||
|
||||
static {
|
||||
ABASE = U.arrayBaseOffset(long[].class);
|
||||
int scale = U.arrayIndexScale(long[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("array index scale not a power of two");
|
||||
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
}
|
||||
|
||||
private long checkedByteOffset(int i) {
|
||||
if (i < 0 || i >= array.length)
|
||||
throw new IndexOutOfBoundsException("index " + i);
|
||||
|
||||
return byteOffset(i);
|
||||
}
|
||||
|
||||
private static long byteOffset(int i) {
|
||||
return ((long) i << ASHIFT) + ABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AtomicLongArray of the given length, with all
|
||||
* elements initially zero.
|
||||
@ -104,147 +85,155 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value at position {@code i}.
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the current value
|
||||
*/
|
||||
public final long get(int i) {
|
||||
return getRaw(checkedByteOffset(i));
|
||||
}
|
||||
|
||||
private long getRaw(long offset) {
|
||||
return U.getLongVolatile(array, offset);
|
||||
return (long)AA.getVolatile(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public final void set(int i, long newValue) {
|
||||
U.putLongVolatile(array, checkedByteOffset(i), newValue);
|
||||
AA.setVolatile(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually sets the element at position {@code i} to the given value.
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 1.6
|
||||
*/
|
||||
public final void lazySet(int i, long newValue) {
|
||||
U.putLongRelease(array, checkedByteOffset(i), newValue);
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given value
|
||||
* and returns the old value.
|
||||
* Atomically sets the element at index {@code i} to {@code
|
||||
* newValue} and returns the old value,
|
||||
* with memory effects as specified by {@link VarHandle#getAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndSet(int i, long newValue) {
|
||||
return U.getAndSetLong(array, checkedByteOffset(i), newValue);
|
||||
return (long)AA.getAndSet(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#compareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful. False return indicates that
|
||||
* the actual value was not equal to the expected value.
|
||||
*/
|
||||
public final boolean compareAndSet(int i, long expect, long update) {
|
||||
return compareAndSetRaw(checkedByteOffset(i), expect, update);
|
||||
}
|
||||
|
||||
private boolean compareAndSetRaw(long offset, long expect, long update) {
|
||||
return U.compareAndSwapLong(array, offset, expect, update);
|
||||
public final boolean compareAndSet(int i, long expectedValue, long newValue) {
|
||||
return AA.compareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at position {@code i} to the given
|
||||
* updated value if the current value {@code ==} the expected value.
|
||||
*
|
||||
* <p><a href="package-summary.html#weakCompareAndSet">May fail
|
||||
* spuriously and does not provide ordering guarantees</a>, so is
|
||||
* only rarely an appropriate alternative to {@code compareAndSet}.
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expect the expected value
|
||||
* @param update the new value
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public final boolean weakCompareAndSet(int i, long expect, long update) {
|
||||
return compareAndSet(i, expect, update);
|
||||
public final boolean weakCompareAndSet(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSet(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndIncrement(int i) {
|
||||
return getAndAdd(i, 1);
|
||||
return (long)AA.getAndAdd(array, i, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* <p>Equivalent to {@code getAndAdd(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndDecrement(int i) {
|
||||
return getAndAdd(i, -1);
|
||||
return (long)AA.getAndAdd(array, i, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAndAdd}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the previous value
|
||||
*/
|
||||
public final long getAndAdd(int i, long delta) {
|
||||
return U.getAndAddLong(array, checkedByteOffset(i), delta);
|
||||
return (long)AA.getAndAdd(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increments by one the element at index {@code i}.
|
||||
* Atomically increments the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, 1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long incrementAndGet(int i) {
|
||||
return getAndAdd(i, 1) + 1;
|
||||
return (long)AA.addAndGet(array, i, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements by one the element at index {@code i}.
|
||||
* Atomically decrements the value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* <p>Equivalent to {@code addAndGet(i, -1)}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the updated value
|
||||
*/
|
||||
public final long decrementAndGet(int i) {
|
||||
return getAndAdd(i, -1) - 1;
|
||||
return (long)AA.addAndGet(array, i, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically adds the given value to the element at index {@code i}.
|
||||
* Atomically adds the given value to the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#addAndGet}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param delta the value to add
|
||||
* @return the updated value
|
||||
*/
|
||||
public long addAndGet(int i, long delta) {
|
||||
return getAndAdd(i, delta) + delta;
|
||||
return (long)AA.addAndGet(array, i, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,13 +248,14 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long getAndUpdate(int i, LongUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,23 +270,25 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
* @since 1.8
|
||||
*/
|
||||
public final long updateAndGet(int i, LongUnaryOperator updateFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = updateFunction.applyAsLong(prev);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the previous value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the previous value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -306,23 +298,25 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
*/
|
||||
public final long getAndAccumulate(int i, long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return prev;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return prev;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates the element at index {@code i} with the
|
||||
* results of applying the given function to the current and
|
||||
* given values, returning the updated value. The function should
|
||||
* be side-effect-free, since it may be re-applied when attempted
|
||||
* results of applying the given function to the current and given
|
||||
* values, returning the updated value. The function should be
|
||||
* side-effect-free, since it may be re-applied when attempted
|
||||
* updates fail due to contention among threads. The function is
|
||||
* applied with the current value at index {@code i} as its first
|
||||
* argument, and the given update as the second argument.
|
||||
* applied with the current value of the element at index {@code i}
|
||||
* as its first argument, and the given update as the second
|
||||
* argument.
|
||||
*
|
||||
* @param i the index
|
||||
* @param x the update value
|
||||
@ -332,13 +326,14 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
*/
|
||||
public final long accumulateAndGet(int i, long x,
|
||||
LongBinaryOperator accumulatorFunction) {
|
||||
long offset = checkedByteOffset(i);
|
||||
long prev, next;
|
||||
do {
|
||||
prev = getRaw(offset);
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
} while (!compareAndSetRaw(offset, prev, next));
|
||||
return next;
|
||||
long prev = get(i), next = 0L;
|
||||
for (boolean haveNext = false;;) {
|
||||
if (!haveNext)
|
||||
next = accumulatorFunction.applyAsLong(prev, x);
|
||||
if (weakCompareAndSetVolatile(i, prev, next))
|
||||
return next;
|
||||
haveNext = (prev == (prev = get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,11 +348,189 @@ public class AtomicLongArray implements java.io.Serializable {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('[');
|
||||
for (int i = 0; ; i++) {
|
||||
b.append(getRaw(byteOffset(i)));
|
||||
b.append(get(i));
|
||||
if (i == iMax)
|
||||
return b.append(']').toString();
|
||||
b.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
// jdk9
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory semantics of reading as if the variable was declared
|
||||
* non-{@code volatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getPlain(int i) {
|
||||
return (long)AA.get(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory semantics of setting as if the variable was
|
||||
* declared non-{@code volatile} and non-{@code final}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setPlain(int i, long newValue) {
|
||||
AA.set(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getOpaque(int i) {
|
||||
return (long)AA.getOpaque(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setOpaque}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setOpaque(int i, long newValue) {
|
||||
AA.setOpaque(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the element at index {@code i},
|
||||
* with memory effects as specified by {@link VarHandle#getAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the value
|
||||
* @since 9
|
||||
*/
|
||||
public final long getAcquire(int i) {
|
||||
return (long)AA.getAcquire(array, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at index {@code i} to {@code newValue},
|
||||
* with memory effects as specified by {@link VarHandle#setRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param newValue the new value
|
||||
* @since 9
|
||||
*/
|
||||
public final void setRelease(int i, long newValue) {
|
||||
AA.setRelease(array, i, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchange}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchange(int i, long expectedValue, long newValue) {
|
||||
return (long)AA.compareAndExchange(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeAcquire(int i, long expectedValue, long newValue) {
|
||||
return (long)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the element at index {@code i} to {@code newValue}
|
||||
* if the element's current value, referred to as the <em>witness
|
||||
* value</em>, {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#compareAndExchangeRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return the witness value, which will be the same as the
|
||||
* expected value if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final long compareAndExchangeRelease(int i, long expectedValue, long newValue) {
|
||||
return (long)AA.compareAndExchangeRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetVolatile}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetVolatile(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetAcquire}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetAcquire(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly atomically sets the element at index {@code i} to
|
||||
* {@code newValue} if the element's current value {@code == expectedValue},
|
||||
* with memory effects as specified by
|
||||
* {@link VarHandle#weakCompareAndSetRelease}.
|
||||
*
|
||||
* @param i the index
|
||||
* @param expectedValue the expected value
|
||||
* @param newValue the new value
|
||||
* @return {@code true} if successful
|
||||
* @since 9
|
||||
*/
|
||||
public final boolean weakCompareAndSetRelease(int i, long expectedValue, long newValue) {
|
||||
return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
@ -153,8 +154,8 @@ public abstract class AtomicLongFieldUpdater<T> {
|
||||
public abstract void lazySet(T obj, long newValue);
|
||||
|
||||
/**
|
||||
* Gets the current value held in the field of the given object managed
|
||||
* by this updater.
|
||||
* Returns the current value held in the field of the given object
|
||||
* managed by this updater.
|
||||
*
|
||||
* @param obj An object whose field to get
|
||||
* @return the current value
|
||||
@ -366,7 +367,7 @@ public abstract class AtomicLongFieldUpdater<T> {
|
||||
}
|
||||
|
||||
private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
@ -497,7 +498,7 @@ public abstract class AtomicLongFieldUpdater<T> {
|
||||
}
|
||||
|
||||
private static final class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
|
||||
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private final long offset;
|
||||
/**
|
||||
* if field is protected, the subclass constructing updater, else
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user