# HG changeset patch # User robm # Date 1511497502 0 # Node ID 512cfc54ab900f4d561e15a0a7f2d0163fe6e7ae # Parent bc316611d4509ebaf0257fd130f1fd3adf206997 8174109: Better queuing priorities Summary: Include ArrayDeque type/doc cleanup from 8011426 Reviewed-by: smarks diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/io/ObjectInputStream.java --- a/src/share/classes/java/io/ObjectInputStream.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/io/ObjectInputStream.java Fri Nov 24 04:25:02 2017 +0000 @@ -253,6 +253,12 @@ public ObjectInputFilter getObjectInputFilter(ObjectInputStream stream) { return stream.getInternalObjectInputFilter(); } + + public void checkArray(ObjectInputStream stream, Class arrayType, int arrayLength) + throws InvalidClassException + { + stream.checkArray(arrayType, arrayLength); + } }); } @@ -1255,6 +1261,33 @@ } /** + * Checks the given array type and length to ensure that creation of such + * an array is permitted by this ObjectInputStream. The arrayType argument + * must represent an actual array type. + * + * This private method is called via SharedSecrets. + * + * @param arrayType the array type + * @param arrayLength the array length + * @throws NullPointerException if arrayType is null + * @throws IllegalArgumentException if arrayType isn't actually an array type + * @throws NegativeArraySizeException if arrayLength is negative + * @throws InvalidClassException if the filter rejects creation + */ + private void checkArray(Class arrayType, int arrayLength) throws InvalidClassException { + Objects.requireNonNull(arrayType); + if (! arrayType.isArray()) { + throw new IllegalArgumentException("not an array type"); + } + + if (arrayLength < 0) { + throw new NegativeArraySizeException(); + } + + filterCheck(arrayType, arrayLength); + } + + /** * Provide access to the persistent fields read from the input stream. */ public static abstract class GetField { diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/util/ArrayDeque.java --- a/src/share/classes/java/util/ArrayDeque.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/util/ArrayDeque.java Fri Nov 24 04:25:02 2017 +0000 @@ -33,7 +33,13 @@ */ package java.util; -import java.io.*; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import sun.misc.SharedSecrets; /** * Resizable-array implementation of the {@link Deque} interface. Array @@ -44,16 +50,16 @@ * {@link Stack} when used as a stack, and faster than {@link LinkedList} * when used as a queue. * - *

Most ArrayDeque operations run in amortized constant time. + *

Most {@code ArrayDeque} operations run in amortized constant time. * Exceptions include {@link #remove(Object) remove}, {@link * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence * removeLastOccurrence}, {@link #contains contains}, {@link #iterator * iterator.remove()}, and the bulk operations, all of which run in linear * time. * - *

The iterators returned by this class's iterator method are + *

The iterators returned by this class's {@code iterator} method are * fail-fast: If the deque is modified at any time after the iterator - * is created, in any way except through the iterator's own remove + * is created, in any way except through the iterator's own {@code remove} * method, the iterator will generally throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking @@ -63,7 +69,7 @@ *

Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators - * throw ConcurrentModificationException on a best-effort basis. + * throw {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: the fail-fast behavior of iterators * should be used only to detect bugs. @@ -93,7 +99,7 @@ * other. We also guarantee that all array cells not holding * deque elements are always null. */ - private transient E[] elements; + private transient Object[] elements; /** * The index of the element at the head of the deque (which is the @@ -116,12 +122,7 @@ // ****** Array allocation and resizing utilities ****** - /** - * Allocate empty array to hold the given number of elements. - * - * @param numElements the number of elements to hold - */ - private void allocateElements(int numElements) { + private static int calculateSize(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests "<=" because arrays aren't kept full. @@ -137,11 +138,20 @@ if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } - elements = (E[]) new Object[initialCapacity]; + return initialCapacity; } /** - * Double the capacity of this deque. Call only when full, i.e., + * Allocates empty array to hold the given number of elements. + * + * @param numElements the number of elements to hold + */ + private void allocateElements(int numElements) { + elements = new Object[calculateSize(numElements)]; + } + + /** + * Doubles the capacity of this deque. Call only when full, i.e., * when head and tail have wrapped around to become equal. */ private void doubleCapacity() { @@ -183,7 +193,7 @@ * sufficient to hold 16 elements. */ public ArrayDeque() { - elements = (E[]) new Object[16]; + elements = new Object[16]; } /** @@ -249,7 +259,7 @@ * Inserts the specified element at the front of this deque. * * @param e the element to add - * @return true (as specified by {@link Deque#offerFirst}) + * @return {@code true} (as specified by {@link Deque#offerFirst}) * @throws NullPointerException if the specified element is null */ public boolean offerFirst(E e) { @@ -261,7 +271,7 @@ * Inserts the specified element at the end of this deque. * * @param e the element to add - * @return true (as specified by {@link Deque#offerLast}) + * @return {@code true} (as specified by {@link Deque#offerLast}) * @throws NullPointerException if the specified element is null */ public boolean offerLast(E e) { @@ -291,7 +301,9 @@ public E pollFirst() { int h = head; - E result = elements[h]; // Element is null if deque empty + @SuppressWarnings("unchecked") + E result = (E) elements[h]; + // Element is null if deque empty if (result == null) return null; elements[h] = null; // Must null out slot @@ -301,7 +313,8 @@ public E pollLast() { int t = (tail - 1) & (elements.length - 1); - E result = elements[t]; + @SuppressWarnings("unchecked") + E result = (E) elements[t]; if (result == null) return null; elements[t] = null; @@ -313,48 +326,53 @@ * @throws NoSuchElementException {@inheritDoc} */ public E getFirst() { - E x = elements[head]; - if (x == null) + @SuppressWarnings("unchecked") + E result = (E) elements[head]; + if (result == null) throw new NoSuchElementException(); - return x; + return result; } /** * @throws NoSuchElementException {@inheritDoc} */ public E getLast() { - E x = elements[(tail - 1) & (elements.length - 1)]; - if (x == null) + @SuppressWarnings("unchecked") + E result = (E) elements[(tail - 1) & (elements.length - 1)]; + if (result == null) throw new NoSuchElementException(); - return x; + return result; } + @SuppressWarnings("unchecked") public E peekFirst() { - return elements[head]; // elements[head] is null if deque empty + // elements[head] is null if deque empty + return (E) elements[head]; } + @SuppressWarnings("unchecked") public E peekLast() { - return elements[(tail - 1) & (elements.length - 1)]; + return (E) elements[(tail - 1) & (elements.length - 1)]; } /** * Removes the first occurrence of the specified element in this * deque (when traversing the deque from head to tail). * If the deque does not contain the element, it is unchanged. - * More formally, removes the first element e such that - * o.equals(e) (if such an element exists). - * Returns true if this deque contained the specified element + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present - * @return true if the deque contained the specified element + * @return {@code true} if the deque contained the specified element */ public boolean removeFirstOccurrence(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) { delete(i); @@ -369,20 +387,20 @@ * Removes the last occurrence of the specified element in this * deque (when traversing the deque from head to tail). * If the deque does not contain the element, it is unchanged. - * More formally, removes the last element e such that - * o.equals(e) (if such an element exists). - * Returns true if this deque contained the specified element + * More formally, removes the last element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present - * @return true if the deque contained the specified element + * @return {@code true} if the deque contained the specified element */ public boolean removeLastOccurrence(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = (tail - 1) & mask; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) { delete(i); @@ -401,7 +419,7 @@ *

This method is equivalent to {@link #addLast}. * * @param e the element to add - * @return true (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws NullPointerException if the specified element is null */ public boolean add(E e) { @@ -415,7 +433,7 @@ *

This method is equivalent to {@link #offerLast}. * * @param e the element to add - * @return true (as specified by {@link Queue#offer}) + * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { @@ -440,12 +458,12 @@ /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns - * null if this deque is empty. + * {@code null} if this deque is empty. * *

This method is equivalent to {@link #pollFirst}. * * @return the head of the queue represented by this deque, or - * null if this deque is empty + * {@code null} if this deque is empty */ public E poll() { return pollFirst(); @@ -467,12 +485,12 @@ /** * Retrieves, but does not remove, the head of the queue represented by - * this deque, or returns null if this deque is empty. + * this deque, or returns {@code null} if this deque is empty. * *

This method is equivalent to {@link #peekFirst}. * * @return the head of the queue represented by this deque, or - * null if this deque is empty + * {@code null} if this deque is empty */ public E peek() { return peekFirst(); @@ -527,7 +545,7 @@ */ private boolean delete(int i) { checkInvariants(); - final E[] elements = this.elements; + final Object[] elements = this.elements; final int mask = elements.length - 1; final int h = head; final int t = tail; @@ -576,9 +594,9 @@ } /** - * Returns true if this deque contains no elements. + * Returns {@code true} if this deque contains no elements. * - * @return true if this deque contains no elements + * @return {@code true} if this deque contains no elements */ public boolean isEmpty() { return head == tail; @@ -625,7 +643,8 @@ public E next() { if (cursor == fence) throw new NoSuchElementException(); - E result = elements[cursor]; + @SuppressWarnings("unchecked") + E result = (E) elements[cursor]; // This check doesn't catch all possible comodifications, // but does catch the ones that corrupt traversal if (tail != fence || result == null) @@ -664,7 +683,8 @@ if (cursor == fence) throw new NoSuchElementException(); cursor = (cursor - 1) & (elements.length - 1); - E result = elements[cursor]; + @SuppressWarnings("unchecked") + E result = (E) elements[cursor]; if (head != fence || result == null) throw new ConcurrentModificationException(); lastRet = cursor; @@ -683,19 +703,19 @@ } /** - * Returns true if this deque contains the specified element. - * More formally, returns true if and only if this deque contains - * at least one element e such that o.equals(e). + * Returns {@code true} if this deque contains the specified element. + * More formally, returns {@code true} if and only if this deque contains + * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this deque - * @return true if this deque contains the specified element + * @return {@code true} if this deque contains the specified element */ public boolean contains(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) return true; @@ -707,15 +727,15 @@ /** * Removes a single instance of the specified element from this deque. * If the deque does not contain the element, it is unchanged. - * More formally, removes the first element e such that - * o.equals(e) (if such an element exists). - * Returns true if this deque contained the specified element + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * - *

This method is equivalent to {@link #removeFirstOccurrence}. + *

This method is equivalent to {@link #removeFirstOccurrence(Object)}. * * @param o element to be removed from this deque, if present - * @return true if this deque contained the specified element + * @return {@code true} if this deque contained the specified element */ public boolean remove(Object o) { return removeFirstOccurrence(o); @@ -767,22 +787,21 @@ *

If this deque fits in the specified array with room to spare * (i.e., the array has more elements than this deque), the element in * the array immediately following the end of the deque is set to - * null. + * {@code null}. * *

Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * - *

Suppose x is a deque known to contain only strings. + *

Suppose {@code x} is a deque known to contain only strings. * The following code can be used to dump the deque into a newly - * allocated array of String: + * allocated array of {@code String}: * - *

-     *     String[] y = x.toArray(new String[0]);
+ *
 {@code String[] y = x.toArray(new String[0]);}
* - * Note that toArray(new Object[0]) is identical in function to - * toArray(). + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. * * @param a the array into which the elements of the deque are to * be stored, if it is big enough; otherwise, a new array of the @@ -813,10 +832,10 @@ */ public ArrayDeque clone() { try { + @SuppressWarnings("unchecked") ArrayDeque result = (ArrayDeque) super.clone(); result.elements = Arrays.copyOf(elements, elements.length); return result; - } catch (CloneNotSupportedException e) { throw new AssertionError(); } @@ -828,9 +847,9 @@ private static final long serialVersionUID = 2340985798034038923L; /** - * Serialize this deque. + * Saves this deque to a stream (that is, serializes it). * - * @serialData The current size (int) of the deque, + * @serialData The current size ({@code int}) of the deque, * followed by all of its elements (each an object reference) in * first-to-last order. */ @@ -847,7 +866,7 @@ } /** - * Deserialize this deque. + * Reconstitutes this deque from a stream (that is, deserializes it). */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { @@ -855,12 +874,14 @@ // Read in size and allocate array int size = s.readInt(); + int capacity = calculateSize(size); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); allocateElements(size); head = 0; tail = size; // Read in all elements in the proper order. for (int i = 0; i < size; i++) - elements[i] = (E)s.readObject(); + elements[i] = s.readObject(); } } diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/util/ArrayList.java --- a/src/share/classes/java/util/ArrayList.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/util/ArrayList.java Fri Nov 24 04:25:02 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.util; +import sun.misc.SharedSecrets; + /** * Resizable-array implementation of the List interface. Implements * all optional list operations, and permits all elements, including @@ -200,12 +202,15 @@ } } - private void ensureCapacityInternal(int minCapacity) { + private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { - minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); + return Math.max(DEFAULT_CAPACITY, minCapacity); } + return minCapacity; + } - ensureExplicitCapacity(minCapacity); + private void ensureCapacityInternal(int minCapacity) { + ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { @@ -763,6 +768,8 @@ if (size > 0) { // be like clone(), allocate array based upon size not capacity + int capacity = calculateCapacity(elementData, size); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); ensureCapacityInternal(size); Object[] a = elementData; diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/util/HashMap.java --- a/src/share/classes/java/util/HashMap.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/util/HashMap.java Fri Nov 24 04:25:02 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package java.util; import java.io.*; +import sun.misc.SharedSecrets; /** * Hash table based implementation of the Map interface. This @@ -298,7 +299,7 @@ putAllForCreate(m); } - private static int roundUpToPowerOf2(int number) { + static int roundUpToPowerOf2(int number) { // assert number >= 0 : "number must be non-negative"; return number >= MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY @@ -1174,6 +1175,11 @@ init(); // Give subclass a chance to do its thing. + + // Check Map.Entry[].class since it's the nearest public type to + // what we're actually creating. + SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, capacity); + // Read the keys and values, and put the mappings in the HashMap for (int i = 0; i < mappings; i++) { K key = (K) s.readObject(); diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/util/HashSet.java --- a/src/share/classes/java/util/HashSet.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/util/HashSet.java Fri Nov 24 04:25:02 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -27,6 +27,8 @@ import java.io.InvalidObjectException; +import sun.misc.SharedSecrets; + /** * This class implements the Set interface, backed by a hash table * (actually a HashMap instance). It makes no guarantees as to the @@ -315,12 +317,19 @@ throw new InvalidObjectException("Illegal size: " + size); } - // Set the capacity according to the size and load factor ensuring that // the HashMap is at least 25% full but clamping to maximum capacity. capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); + // Constructing the backing map will lazily create an array when the first element is + // added, so check it before construction. Call HashMap.roundUpToPowerOf2 to compute the + // actual allocation size. Check Map.Entry[].class since it's the nearest public type to + // what is actually created. + + SharedSecrets.getJavaOISAccess() + .checkArray(s, Map.Entry[].class, HashMap.roundUpToPowerOf2(capacity)); + // Create backing HashMap map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap(capacity, loadFactor) : diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/util/Hashtable.java --- a/src/share/classes/java/util/Hashtable.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/util/Hashtable.java Fri Nov 24 04:25:02 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package java.util; import java.io.*; +import sun.misc.SharedSecrets; /** * This class implements a hash table, which maps keys to values. Any @@ -996,6 +997,10 @@ length--; length = Math.min(length, origlength); + // Check Map.Entry[].class since it's the nearest public type to + // what we're actually creating. + SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length); + Entry[] newTable = new Entry[length]; threshold = (int) Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); count = 0; diff -r bc316611d450 -r 512cfc54ab90 src/share/classes/java/util/IdentityHashMap.java --- a/src/share/classes/java/util/IdentityHashMap.java Thu Nov 23 20:15:51 2017 +0000 +++ b/src/share/classes/java/util/IdentityHashMap.java Fri Nov 24 04:25:02 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.util; +import sun.misc.SharedSecrets; + /** * This class implements the Map interface with a hash table, using * reference-equality in place of object-equality when comparing keys (and @@ -1205,7 +1207,9 @@ if (size < 0) throw new java.io.StreamCorruptedException ("Illegal mappings count: " + size); - init(capacity(size)); + int cap = capacity(size); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, cap); + init(cap); // Read the keys and values, and put the mappings in the table for (int i=0; i arrayType, int arrayLength) + throws InvalidClassException; } diff -r bc316611d450 -r 512cfc54ab90 test/java/io/Serializable/serialFilter/SerialFilterTest.java --- a/test/java/io/Serializable/serialFilter/SerialFilterTest.java Thu Nov 23 20:15:51 2017 +0000 +++ b/test/java/io/Serializable/serialFilter/SerialFilterTest.java Fri Nov 24 04:25:02 2017 +0000 @@ -34,9 +34,11 @@ import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.Set; import javax.lang.model.SourceVersion; @@ -142,6 +144,10 @@ Object[] objArray = new Object[7]; objArray[objArray.length - 1] = objArray; + List> classList = new ArrayList<>(); + classList.add(HashSet.class); + classList.addAll(Collections.nCopies(21, Map.Entry[].class)); + Object[][] objects = { { null, 0, -1, 0, 0, 0, new HashSet<>()}, // no callback, no values @@ -153,8 +159,7 @@ new HashSet<>(Arrays.asList(SerialFilterTest.class))}, { new byte[14], 2, 14, 1, 1, 27, new HashSet<>(Arrays.asList(byteArray.getClass()))}, - { deepHashSet(10), 48, -1, 49, 11, 619, - new HashSet<>(Arrays.asList(HashSet.class))}, + { deepHashSet(10), 69, 4, 50, 11, 619, classList }, }; return objects; }