)
+ () -> Boolean.getBoolean("jdk.crypto.KeyAgreement.legacyKDF"));
+ }
+ }
+
/**
* Empty constructor
*/
@@ -367,6 +380,14 @@
if (algorithm == null) {
throw new NoSuchAlgorithmException("null algorithm");
}
+
+ if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") &&
+ !AllowKDF.VALUE) {
+
+ throw new NoSuchAlgorithmException("Unsupported secret key "
+ + "algorithm: " + algorithm);
+ }
+
byte[] secret = engineGenerateSecret();
if (algorithm.equalsIgnoreCase("DES")) {
// DES
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java
--- a/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java Fri Jan 19 09:32:10 2018 -0800
@@ -25,6 +25,7 @@
package com.sun.crypto.provider;
+import java.lang.ref.Reference;
import java.security.MessageDigest;
import java.security.KeyRep;
import java.security.spec.InvalidKeySpecException;
@@ -80,7 +81,11 @@
}
public byte[] getEncoded() {
- return this.key.clone();
+ // The key is zeroized by finalize()
+ // The reachability fence ensures finalize() isn't called early
+ byte[] result = key.clone();
+ Reference.reachabilityFence(this);
+ return result;
}
public String getAlgorithm() {
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java
--- a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Fri Jan 19 09:32:10 2018 -0800
@@ -26,6 +26,7 @@
package com.sun.crypto.provider;
import java.io.ObjectStreamException;
+import java.lang.ref.Reference;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
@@ -208,7 +209,11 @@
}
public byte[] getEncoded() {
- return key.clone();
+ // The key is zeroized by finalize()
+ // The reachability fence ensures finalize() isn't called early
+ byte[] result = key.clone();
+ Reference.reachabilityFence(this);
+ return result;
}
public String getAlgorithm() {
@@ -220,7 +225,11 @@
}
public char[] getPassword() {
- return passwd.clone();
+ // The password is zeroized by finalize()
+ // The reachability fence ensures finalize() isn't called early
+ char[] result = passwd.clone();
+ Reference.reachabilityFence(this);
+ return result;
}
public byte[] getSalt() {
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/lang/Runtime.java
--- a/src/java.base/share/classes/java/lang/Runtime.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/lang/Runtime.java Fri Jan 19 09:32:10 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2018, 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
@@ -1099,16 +1099,23 @@
m.group(VersionPattern.OPT_GROUP));
// empty '+'
- if ((m.group(VersionPattern.PLUS_GROUP) != null)
- && !build.isPresent()) {
- if (optional.isPresent()) {
- if (pre.isPresent())
- throw new IllegalArgumentException("'+' found with"
- + " pre-release and optional components:'" + s
- + "'");
+ if (!build.isPresent()) {
+ if (m.group(VersionPattern.PLUS_GROUP) != null) {
+ if (optional.isPresent()) {
+ if (pre.isPresent())
+ throw new IllegalArgumentException("'+' found with"
+ + " pre-release and optional components:'" + s
+ + "'");
+ } else {
+ throw new IllegalArgumentException("'+' found with neither"
+ + " build or optional components: '" + s + "'");
+ }
} else {
- throw new IllegalArgumentException("'+' found with neither"
- + " build or optional components: '" + s + "'");
+ if (optional.isPresent() && !pre.isPresent()) {
+ throw new IllegalArgumentException("optional component"
+ + " must be preceeded by a pre-release component"
+ + " or '+': '" + s + "'");
+ }
}
}
return new Version(List.of(version), pre, build, optional);
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
--- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Fri Jan 19 09:32:10 2018 -0800
@@ -80,13 +80,20 @@
mtype = mtype.insertParameterTypes(0, receiver);
}
if (!member.isField()) {
- if (refKind == REF_invokeSpecial) {
- member = member.asSpecial();
- LambdaForm lform = preparedLambdaForm(member);
- return new Special(mtype, lform, member);
- } else {
- LambdaForm lform = preparedLambdaForm(member);
- return new DirectMethodHandle(mtype, lform, member);
+ switch (refKind) {
+ case REF_invokeSpecial: {
+ member = member.asSpecial();
+ LambdaForm lform = preparedLambdaForm(member);
+ return new Special(mtype, lform, member);
+ }
+ case REF_invokeInterface: {
+ LambdaForm lform = preparedLambdaForm(member);
+ return new Interface(mtype, lform, member, receiver);
+ }
+ default: {
+ LambdaForm lform = preparedLambdaForm(member);
+ return new DirectMethodHandle(mtype, lform, member);
+ }
}
} else {
LambdaForm lform = preparedFieldLambdaForm(member);
@@ -190,6 +197,7 @@
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
boolean needsInit = (which == LF_INVSTATIC_INIT);
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
+ boolean needsReceiverCheck = (which == LF_INVINTERFACE);
String linkerName;
LambdaForm.Kind kind;
switch (which) {
@@ -219,6 +227,7 @@
int nameCursor = ARG_LIMIT;
final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
final int GET_MEMBER = nameCursor++;
+ final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1);
final int LINKER_CALL = nameCursor++;
Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
assert(names.length == nameCursor);
@@ -233,6 +242,10 @@
}
assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
+ if (needsReceiverCheck) {
+ names[CHECK_RECEIVER] = new Name(getFunction(NF_checkReceiver), names[DMH_THIS], names[ARG_BASE]);
+ outArgs[0] = names[CHECK_RECEIVER];
+ }
assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args!
int result = LAST_RESULT;
if (doesAlloc) {
@@ -376,6 +389,29 @@
}
}
+ /** This subclass represents invokeinterface instructions. */
+ static class Interface extends DirectMethodHandle {
+ private final Class> refc;
+ private Interface(MethodType mtype, LambdaForm form, MemberName member, Class> refc) {
+ super(mtype, form, member);
+ assert refc.isInterface() : refc;
+ this.refc = refc;
+ }
+ @Override
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ return new Interface(mt, lf, member, refc);
+ }
+
+ Object checkReceiver(Object recv) {
+ if (!refc.isInstance(recv)) {
+ String msg = String.format("Class %s does not implement the requested interface %s",
+ recv.getClass().getName(), refc.getName());
+ throw new IncompatibleClassChangeError(msg);
+ }
+ return recv;
+ }
+ }
+
/** This subclass handles constructor references. */
static class Constructor extends DirectMethodHandle {
final MemberName initMethod;
@@ -738,7 +774,8 @@
NF_allocateInstance = 8,
NF_constructorMethod = 9,
NF_UNSAFE = 10,
- NF_LIMIT = 11;
+ NF_checkReceiver = 11,
+ NF_LIMIT = 12;
private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
@@ -785,6 +822,11 @@
return new NamedFunction(
MemberName.getFactory()
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
+ case NF_checkReceiver:
+ member = new MemberName(Interface.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
+ return new NamedFunction(
+ MemberName.getFactory()
+ .resolveOrFail(REF_invokeVirtual, member, Interface.class, NoSuchMethodException.class));
default:
throw newInternalError("Unknown function: " + func);
}
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/lang/invoke/MethodHandles.java
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Fri Jan 19 09:32:10 2018 -0800
@@ -3766,6 +3766,7 @@
* specified in the elements of the {@code filters} array.
* The first element of the filter array corresponds to the {@code pos}
* argument of the target, and so on in sequence.
+ * The filter functions are invoked in left to right order.
*
* Null arguments in the array are treated as identity functions,
* and the corresponding arguments left unchanged.
@@ -3836,11 +3837,12 @@
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
filterArgumentsCheckArity(target, pos, filters);
MethodHandle adapter = target;
- int curPos = pos-1; // pre-incremented
- for (MethodHandle filter : filters) {
- curPos += 1;
+ // process filters in reverse order so that the invocation of
+ // the resulting adapter will invoke the filters in left-to-right order
+ for (int i = filters.length - 1; i >= 0; --i) {
+ MethodHandle filter = filters[i];
if (filter == null) continue; // ignore null elements of filters
- adapter = filterArgument(adapter, curPos, filter);
+ adapter = filterArgument(adapter, pos + i, filter);
}
return adapter;
}
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/ResourceBundle.java
--- a/src/java.base/share/classes/java/util/ResourceBundle.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/ResourceBundle.java Fri Jan 19 09:32:10 2018 -0800
@@ -591,7 +591,7 @@
*/
private static ClassLoader getLoaderForControl(Module module) {
ClassLoader loader = getLoader(module);
- return loader == null ? ClassLoader.getSystemClassLoader() : loader;
+ return loader == null ? ClassLoader.getPlatformClassLoader() : loader;
}
/**
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java
--- a/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java Fri Jan 19 09:32:10 2018 -0800
@@ -1608,4 +1608,30 @@
}
}
+ /**
+ * Deserializes this queue and then checks some invariants.
+ *
+ * @param s the input stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.InvalidObjectException if invariants are violated
+ * @throws java.io.IOException if an I/O error occurs
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+
+ // Read in items array and various fields
+ s.defaultReadObject();
+
+ // Check invariants over count and index fields. Note that
+ // if putIndex==takeIndex, count can be either 0 or items.length.
+ if (items.length == 0 ||
+ takeIndex < 0 || takeIndex >= items.length ||
+ putIndex < 0 || putIndex >= items.length ||
+ count < 0 || count > items.length ||
+ Math.floorMod(putIndex - takeIndex, items.length) !=
+ Math.floorMod(count, items.length)) {
+ throw new java.io.InvalidObjectException("invariants violated");
+ }
+ }
}
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/stream/DoublePipeline.java
--- a/src/java.base/share/classes/java/util/stream/DoublePipeline.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/stream/DoublePipeline.java Fri Jan 19 09:32:10 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -40,7 +40,6 @@
import java.util.function.DoubleToLongFunction;
import java.util.function.DoubleUnaryOperator;
import java.util.function.IntFunction;
-import java.util.function.LongPredicate;
import java.util.function.ObjDoubleConsumer;
import java.util.function.Supplier;
@@ -265,6 +264,12 @@
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedDouble(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
+ // cache the consumer to avoid creation on every accepted element
+ DoubleConsumer downstreamAsDouble = downstream::accept;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -273,11 +278,27 @@
@Override
public void accept(double t) {
try (DoubleStream result = mapper.apply(t)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(i -> downstream.accept(i));
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstreamAsDouble);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsDouble));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ // If this method is called then an operation within the stream
+ // pipeline is short-circuiting (see AbstractPipeline.copyInto).
+ // Note that we cannot differentiate between an upstream or
+ // downstream operation
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/stream/IntPipeline.java
--- a/src/java.base/share/classes/java/util/stream/IntPipeline.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/stream/IntPipeline.java Fri Jan 19 09:32:10 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -298,6 +298,12 @@
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedInt(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
+ // cache the consumer to avoid creation on every accepted element
+ IntConsumer downstreamAsInt = downstream::accept;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -306,11 +312,27 @@
@Override
public void accept(int t) {
try (IntStream result = mapper.apply(t)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(i -> downstream.accept(i));
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstreamAsInt);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsInt));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ // If this method is called then an operation within the stream
+ // pipeline is short-circuiting (see AbstractPipeline.copyInto).
+ // Note that we cannot differentiate between an upstream or
+ // downstream operation
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/stream/LongPipeline.java
--- a/src/java.base/share/classes/java/util/stream/LongPipeline.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/stream/LongPipeline.java Fri Jan 19 09:32:10 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -280,6 +280,12 @@
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedLong(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
+ // cache the consumer to avoid creation on every accepted element
+ LongConsumer downstreamAsLong = downstream::accept;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -288,11 +294,27 @@
@Override
public void accept(long t) {
try (LongStream result = mapper.apply(t)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(i -> downstream.accept(i));
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstreamAsLong);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsLong));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ // If this method is called then an operation within the stream
+ // pipeline is short-circuiting (see AbstractPipeline.copyInto).
+ // Note that we cannot differentiate between an upstream or
+ // downstream operation
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/stream/ReferencePipeline.java
--- a/src/java.base/share/classes/java/util/stream/ReferencePipeline.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/stream/ReferencePipeline.java Fri Jan 19 09:32:10 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -253,12 +253,14 @@
@Override
public final Stream flatMap(Function super P_OUT, ? extends Stream extends R>> mapper) {
Objects.requireNonNull(mapper);
- // We can do better than this, by polling cancellationRequested when stream is infinite
return new StatelessOp(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedReference(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -267,11 +269,27 @@
@Override
public void accept(P_OUT u) {
try (Stream extends R> result = mapper.apply(u)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(downstream);
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstream);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstream));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ // If this method is called then an operation within the stream
+ // pipeline is short-circuiting (see AbstractPipeline.copyInto).
+ // Note that we cannot differentiate between an upstream or
+ // downstream operation
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
@@ -280,13 +298,17 @@
@Override
public final IntStream flatMapToInt(Function super P_OUT, ? extends IntStream> mapper) {
Objects.requireNonNull(mapper);
- // We can do better than this, by polling cancellationRequested when stream is infinite
return new IntPipeline.StatelessOp(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedReference(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
+ // cache the consumer to avoid creation on every accepted element
IntConsumer downstreamAsInt = downstream::accept;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -295,11 +317,23 @@
@Override
public void accept(P_OUT u) {
try (IntStream result = mapper.apply(u)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(downstreamAsInt);
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstreamAsInt);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsInt));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
@@ -308,13 +342,17 @@
@Override
public final DoubleStream flatMapToDouble(Function super P_OUT, ? extends DoubleStream> mapper) {
Objects.requireNonNull(mapper);
- // We can do better than this, by polling cancellationRequested when stream is infinite
return new DoublePipeline.StatelessOp(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedReference(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
+ // cache the consumer to avoid creation on every accepted element
DoubleConsumer downstreamAsDouble = downstream::accept;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -323,11 +361,23 @@
@Override
public void accept(P_OUT u) {
try (DoubleStream result = mapper.apply(u)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(downstreamAsDouble);
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstreamAsDouble);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsDouble));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
@@ -342,7 +392,12 @@
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedReference(sink) {
+ // true if cancellationRequested() has been called
+ boolean cancellationRequestedCalled;
+
+ // cache the consumer to avoid creation on every accepted element
LongConsumer downstreamAsLong = downstream::accept;
+
@Override
public void begin(long size) {
downstream.begin(-1);
@@ -351,11 +406,23 @@
@Override
public void accept(P_OUT u) {
try (LongStream result = mapper.apply(u)) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- if (result != null)
- result.sequential().forEach(downstreamAsLong);
+ if (result != null) {
+ if (!cancellationRequestedCalled) {
+ result.sequential().forEach(downstreamAsLong);
+ }
+ else {
+ var s = result.sequential().spliterator();
+ do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsLong));
+ }
+ }
}
}
+
+ @Override
+ public boolean cancellationRequested() {
+ cancellationRequestedCalled = true;
+ return downstream.cancellationRequested();
+ }
};
}
};
diff -r e4b03365ddbf -r 371c6d66d2ec src/java.base/share/classes/java/util/stream/SortedOps.java
--- a/src/java.base/share/classes/java/util/stream/SortedOps.java Thu Jan 18 11:22:28 2018 +0530
+++ b/src/java.base/share/classes/java/util/stream/SortedOps.java Fri Jan 19 09:32:10 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -304,7 +304,8 @@
private abstract static class AbstractRefSortingSink