OpenJDK / lambda / lambda / jdk
changeset 6011:1ebaa3f08ed1
Merge
author | mduigou |
---|---|
date | Fri, 14 Sep 2012 09:09:24 -0700 |
parents | 257bb5320a2b 5e95936891d2 |
children | 9fffedf7ed04 |
files | src/share/classes/java/util/streams/ops/AbstractTask.java src/share/classes/java/util/streams/ops/TreeUtils.java |
diffstat | 22 files changed, 349 insertions(+), 244 deletions(-) [+] |
line wrap: on
line diff
--- a/combo-tests/build.xml Thu Sep 13 21:40:37 2012 -0700 +++ b/combo-tests/build.xml Fri Sep 14 09:09:24 2012 -0700 @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <project name="jdk" default="test"> - <property name="build.dir" value="build" /> - <property name="gen.dir" value="gen" /> + <property name="build.dir" value="../../build/combo-tests" /> + <property name="gen.dir" value="${build.dir}/gen" /> <property name="test.classes.dir" value="${build.dir}/test-classes"/> <property name="test.reports.dir" value="${build.dir}/test-reports"/> <property name="test.src.dir" value="tests"/>
--- a/combo-tests/tests/tools/javac/lambda/DefaultMethodAddTest.java Thu Sep 13 21:40:37 2012 -0700 +++ b/combo-tests/tests/tools/javac/lambda/DefaultMethodAddTest.java Fri Sep 14 09:09:24 2012 -0700 @@ -33,19 +33,16 @@ @DimensionVar("ADD") AddType addType; @SourceFile(value="B.java", group="A") - String interfaceBModified = "interface B #{SHAPE.EXTEND} { #{ADD} }"; + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{ADD} }"; @SourceFile(value="B.java", group="B") - String interfaceB = "interface B #{SHAPE.EXTEND} {}"; + String interfaceB = "interface B #{SHAPE.B_DECL} {}"; @SourceFile(value="A.java", group="B") - String interfaceA = "interface A { String m() default { return \"A\"; } }"; - - @SourceFile(value="AB.java", group="B") - String interfaceAB = "#{SHAPE.COMMENT}interface AB extends A, B {}"; + String interfaceA = "interface A { String m() default { return \"A\"; } }"; @SourceFile(value="C.java", group="B") - String classC = "class C implements #{SHAPE.IMPLEMENT} {}"; + String classC = "#{SHAPE.C}"; @SourceFile(value="Main.java", group="B") String classMain = "public class Main {\n" + @@ -67,42 +64,59 @@ } catch (InvocationTargetException ex) { output = ex.getCause().getMessage(); } - if(shapeType == CShapes.C_B_A){ + if(shapeType == CShapes.C_B_A) { if(addType == AddType.ADD) assertEquals(result, "B"); else //redeclare - assertEquals(result, "A"); - } else { - if(addType == AddType.ADD) - assertTrue(output.matches("Conflicting default methods: .+[.]m .+[.]m")); - else //redeclare - assertEquals(result, "A"); + assertEquals(output, "Method B.m()Ljava/lang/String; is abstract"); } + else if(shapeType == CShapes.C_I_AB2) + assertEquals(result, "A"); + else if(shapeType == CShapes.C_CI) + assertEquals(result, "D"); + else if(shapeType == CShapes.C_I_AB3) + assertEquals(result, "AB"); + else + assertTrue(output.matches("Conflicting default methods: .+[.]m .+[.]m")); } enum CShapes implements Template { //shapes of class hirarchy //class C implements interface A, B - C_AB("", "A, B", "//"), + C_AB("", + "class C implements A, B {}"), //class C implments interface B, B extends interface A - C_B_A("extends A", "B", "//"), + C_B_A("extends A", + "class C implements B {}"), + //class C implments interface AB, AB extends interface A, B - C_I_AB("", "AB", ""); + C_I_AB("", + "interface AB extends A, B { }\n" + + "class C implements AB {}"), + //class C implments interface AB, AB extends interface A, B and explicitly inherits the default method in A + C_I_AB2("", + "interface AB extends A, B { String m() default { return A.super.m(); } }\n" + + "class C implements AB {}"), + //class C implments interface AB, AB extends interface A, B and overrides the default method inherited + C_I_AB3("", + "interface AB extends A, B { String m() default { return \"AB\"; } }\n" + + "class C implements AB {}"), + //class C extends Class D implements Interface B + C_CI("", + "class D { public String m() { return \"D\"; } }\n" + + "class C extends D implements B {}"); - private final String extend; - private final String implement; - private final String commentSign; + private final String sB_DECL; + private final String sC; - CShapes(String extend, String implement, String commentSign) { - this.extend = extend; - this.implement = implement; - this.commentSign = commentSign; + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; } public String expand(String selector) { switch(selector) { - case "EXTEND": return extend; - case "IMPLEMENT": return implement; - case "COMMENT": return commentSign; + case "B_DECL": return sB_DECL; + case "C": return sC; default: return toString(); } }
--- a/combo-tests/tests/tools/javac/lambda/DefaultMethodRemoveTest.java Thu Sep 13 21:40:37 2012 -0700 +++ b/combo-tests/tests/tools/javac/lambda/DefaultMethodRemoveTest.java Fri Sep 14 09:09:24 2012 -0700 @@ -32,19 +32,16 @@ @DimensionVar("REMOVE") RemoveType removeType; @SourceFile(value="B.java", group="A") - String interfaceBModified = "interface B #{SHAPE.EXTENDS} { #{REMOVE} }"; + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{REMOVE} }"; @SourceFile(value="A.java", group="B") String interfaceA = "interface A { String m() default { return \"A\"; } }"; @SourceFile(value="B.java", group="B") - String interfaceB = "interface B #{SHAPE.EXTENDS} { String m() default { return \"B\"; } }"; - - @SourceFile(value="D.java", group="B") - String interfaceD = "#{SHAPE.COMMENT}interface D { String m() default { return \"D\"; } }"; - + String interfaceB = "interface B #{SHAPE.B_DECL} { String m() default { return \"B\"; } }"; + @SourceFile(value="C.java", group="B") - String classC = "class C implements B {}"; + String classC = "#{SHAPE.C}"; @SourceFile(value="Main.java", group="B") String classMain = "public class Main {\n" + @@ -72,40 +69,56 @@ } catch (InvocationTargetException ex) { output = ex.getCause().getMessage(); } - if(shapeType == CShapes.C_B_A) - assertEquals(result, "A"); - else if(shapeType == CShapes.C_B) - assertEquals(output, "C.m()Ljava/lang/String;"); - else if (shapeType == CShapes.C_B_AD) - assertTrue(output.matches("Conflicting default methods: .+[.]m .+[.]m")); + if(shapeType == CShapes.C_B_A) { + if(removeType == RemoveType.REMOVE) + assertEquals(result, "A"); + else + assertEquals(output, "Method B.m()Ljava/lang/String; is abstract"); + } + else if(shapeType == CShapes.C_CI) + assertEquals(result, "D"); + else { + if(shapeType == CShapes.C_B_AD && removeType == RemoveType.REDECLARE) + assertEquals(output, "Method B.m()Ljava/lang/String; is abstract"); + else + assertEquals(output, "C.m()Ljava/lang/String;"); + } } enum CShapes implements Template { //shapes of class hirarchy //class C implements interface B - C_B("", "//"), + C_B("", + "class C implements B {}"), //class C implments interface B, B extends interface A - C_B_A("extends A", "//"), + C_B_A("extends A", + "class C implements B {}"), //class C implments interface B, B extends interface A, D - C_B_AD("extends A, D", ""); + C_B_AD("extends A, D", + "interface D { String m() default { return \"D\"; } }\n" + + "class C implements B {}"), + //class C extends Class D implements Interface B + C_CI("", + "class D { public String m() { return \"D\"; } }\n" + + "class C extends D implements B {}"); - private final String extend; - private final String commentSign; + private final String sB_DECL; + private final String sC; - CShapes(String extend, String commentSign) { - this.extend = extend; - this.commentSign = commentSign; + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; } public String expand(String selector) { switch(selector) { - case "EXTENDS": return extend; - case "COMMENT": return commentSign; + case "B_DECL": return sB_DECL; + case "C": return sC; default: return toString(); } } } - enum RemoveType implements Template { // remove by removing default method code or redeclaring the interface method + enum RemoveType implements Template { //remove by removing default method code or redeclaring the interface method REMOVE(""), REDECLARE("String m();");
--- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Sep 14 09:09:24 2012 -0700 @@ -25,11 +25,17 @@ package java.lang.invoke; import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * Abstract implementation of a meta-factory which provides parameter unrolling and input validation. @@ -63,6 +69,9 @@ final MethodType implMethodType; // Type of the implementation method "(int)String" final MethodType instantiatedMethodType; // Instantiated erased functional interface method type "(Integer)Object" + static final Executor logPool = Executors.newSingleThreadExecutor(); // @@@ For debugging only + + /** * Meta-factory constructor. * @@ -304,4 +313,101 @@ || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false); } + // @@@ Logging support -- for debugging only -- delete before shipping + protected static void log(final String s) { + MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() { + @Override + public void run() { + System.out.println(s); + } + }); + } + + protected static void log(final String s, final Throwable e) { + MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() { + @Override + public void run() { + System.out.println(s); + e.printStackTrace(System.out); + } + }); + } + + /** + * Find the SAM method and corresponding methods which should be bridged. SAM method and those to be bridged + * will have the same name and number of parameters. Check for matching default methods (non-abstract), they + * should not be bridged-over and indicate a complex bridging situation. + */ + class MethodAnalyzer { + private final Method[] methods = samBase.getMethods(); + private final List<Method> methodsFound = new ArrayList<>(methods.length); + + private Method samMethod = null; + private final List<Method> methodsToBridge = new ArrayList<>(methods.length); + private boolean defaultMethodFound = false; + + MethodAnalyzer() { + String samMethodName = samInfo.getName(); + Class<?>[] samParamTypes = samMethodType.parameterArray(); + int samParamLength = samParamTypes.length; + Class<?> samReturnType = samMethodType.returnType(); + Class<?> objectClass = Object.class; + + for (Method m : methods) { + if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) { + Class<?>[] mParamTypes = m.getParameterTypes(); + if (mParamTypes.length == samParamLength) { + if (Modifier.isAbstract(m.getModifiers())) { + // Exclude methods with duplicate signatures + if (methodUnique(m)) { + if (m.getReturnType().equals(samReturnType) && Arrays.equals(mParamTypes, samParamTypes)) { + // Exact match, this is the SAM method signature + samMethod = m; + } else { + methodsToBridge.add(m); + } + } + } else { + // This is a default method, flag for special processing + defaultMethodFound = true; + // Ignore future matching abstracts. + // Note, due to reabstraction, this is really a punt, hence pass-off to VM + methodUnique(m); + } + } + } + } + } + + Method getSamMethod() { + return samMethod; + } + + List<Method> getMethodsToBridge() { + return methodsToBridge; + } + + boolean wasDefaultMethodFound() { + return defaultMethodFound; + } + + /** + * Search the list of previously found methods to determine if there is a method with the same signature + * (return and parameter types) as the specified method. If it wasn't found before, add to the found list. + * + * @param m The method to match + * @return False if the method was found, True otherwise + */ + private boolean methodUnique(Method m) { + Class<?>[] ptypes = m.getParameterTypes(); + Class<?> rtype = m.getReturnType(); + for (Method md : methodsFound) { + if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) { + return false; + } + } + methodsFound.add(m); + return true; + } + } }
--- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Fri Sep 14 09:09:24 2012 -0700 @@ -147,7 +147,6 @@ private <T> Class<? extends T> spinInnerClass() { String samMethodName = samInfo.getName(); - Method[] methods = samBase.getMethods(); String samName = samBase.getName().replace('.', '/'); Type samType = Type.getType(samBase); @@ -162,65 +161,28 @@ generateConstructor(); - // Generate methods - Class<?>[] samParamTypes = samMethodType.parameterArray(); - int samParamLength = samParamTypes.length; - Class<?> samReturnType = samMethodType.returnType(); - boolean defaultMethodFound = false; - Method samMethod = null; - List<Method> methodsFound = new ArrayList<>(methods.length); - List<Method> methodsToBridge = new ArrayList<>(methods.length); - Class<?> objectClass = Object.class; - - // Find the SAM method and corresponding methods which should be bridged. - // SAM method and those to be bridged will have the same name and number of parameters. - // Check for default methods (non-abstract), they should not be bridged-over and indicate a complex bridging situation. - for (Method m : methods) { - if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) { - Class<?>[] mParamTypes = m.getParameterTypes(); - if (mParamTypes.length == samParamLength) { - if (Modifier.isAbstract(m.getModifiers())) { - // Exclude methods with duplicate signatures - if (!matchesAnyMethod(m, methodsFound)) { - methodsFound.add(m); - if (m.getReturnType().equals(samReturnType) && Arrays.equals(mParamTypes, samParamTypes)) { - // Exact match, this is the SAM method signature - samMethod = m; - } else { - methodsToBridge.add(m); - } - } - } else { - // This is a default method, flag for special processing - defaultMethodFound = true; - // Ignore future matching abstracts. - // Note, due to reabstraction, this is really a punt, hence pass-off to VM - if (!matchesAnyMethod(m, methodsFound)) { - methodsFound.add(m); - } - } - } - } - } + MethodAnalyzer ma = new MethodAnalyzer(); // Forward the SAM method - if (samMethod == null) { + if (ma.getSamMethod() == null) { throw new LambdaConversionException(String.format("SAM method not found: %s", samMethodType)); } else { - generateForwardingMethod(samMethod, false); + generateForwardingMethod(ma.getSamMethod(), false); } // Forward the bridges // @@@ Once the VM can do fail-over, uncomment the default method test - if (!methodsToBridge.isEmpty() /* && !defaultMethodFound*/) { - for (Method m : methodsToBridge) { + if (!ma.getMethodsToBridge().isEmpty() /* && !ma.wasDefaultMethodFound() */) { + for (Method m : ma.getMethodsToBridge()) { generateForwardingMethod(m, true); } } + /***** Serialization not yet supported if (isSerializable) { generateSerializationMethod(samType, samMethodName); } + ******/ cw.visitEnd(); @@ -243,23 +205,6 @@ } /** - * Search the specified list of methods to determine if there is a method with the same signature (return and parameter types) as the specified method. - * @param m The method to match - * @param methods The list of methods to search - * @return True if a method was found in 'methods' which the same signature as 'm', False otherwise - */ - private static boolean matchesAnyMethod(Method m, List<Method> methods) { - Class<?>[] ptypes = m.getParameterTypes(); - Class<?> rtype = m.getReturnType(); - for (Method md : methods) { - if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) { - return true; - } - } - return false; - } - - /** * Generate the constructor for the class */ private void generateConstructor() { @@ -283,6 +228,7 @@ /** * Generate the serialization method (if needed) */ + /****** This code is out of date -- known to be wrong -- and not currently used ****** private void generateSerializationMethod(Type samType, String samMethodName) { String samMethodDesc = samMethodType.toMethodDescriptorString(); TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null)); @@ -313,6 +259,7 @@ mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored mv.visitEnd(); } + ********/ /** * Generate a method which calls the lambda implementation method,
--- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java Fri Sep 14 09:09:24 2012 -0700 @@ -69,8 +69,9 @@ * as well as a method signature describing the number and static types (but not the values) of the dynamic * arguments, and the static return type of the invokedynamic site. * - * <p>The implementation method can, in theory, be anything representable with a method handle. Currently supported - * are method handles representing invocation of virtual, interface, constructor and static methods. + * <p>The implementation method is described with a method handle. In theory, any method handle could be used. + * Currently supported are method handles representing invocation of virtual, interface, constructor and static + * methods. * * <p>Assume: * <ul> @@ -86,7 +87,8 @@ * <ul> * <li>Rd is a subtype of F</li> * <li>For i=1..N, Ti is a subtype of Ui</li> - * <li>Rt is a subtype of Ru</li> + * <li>Either Rt and Ru are primitive and are the same type, or both are reference types and + * Rt is a subtype of Ru</li> * <li>If the implementation method is a static method: * <ul> * <li>K + N = M</li> @@ -106,8 +108,8 @@ * <p>Note that the potentially parameterized implementation return type provides the value for the SAM. Whereas * the completely known instantiated return type is adapted to the implementation arguments. Because the * instantiated type of the implementation method is not available, the adaptability of return types cannot be - * checked as precisely at link-time as arguments. Thus a loose version of link-time checking is done on return - * type, while a strict version is applied to arguments. + * checked as precisely at link-time as the arguments can be checked. Thus a loose version of link-time checking is + * done on return type, while a strict version is applied to arguments. * * <p>A type Q is considered adaptable to S as follows: * <table>
--- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java Fri Sep 14 09:09:24 2012 -0700 @@ -79,21 +79,21 @@ private String getReferenceKindString() { switch (referenceKind) { case REF_NONE: return "REF_NONE"; - case REF_getField: return "getField"; - case REF_getStatic: return "getStatic"; - case REF_putField: return "putField"; - case REF_putStatic: return "putStatic"; - case REF_invokeVirtual: return "invokeVirtual"; - case REF_invokeStatic: return "invokeStatic"; - case REF_invokeSpecial: return "invokeSpecial"; - case REF_newInvokeSpecial: return "newInvokeSpecial"; - case REF_invokeInterface: return "invokeInterface"; + case REF_getField: return "getfield"; + case REF_getStatic: return "getstatic"; + case REF_putField: return "putfield"; + case REF_putStatic: return "putstatic"; + case REF_invokeVirtual: return "invokevirtual"; + case REF_invokeStatic: return "invokestatic"; + case REF_invokeSpecial: return "invokespecial"; + case REF_newInvokeSpecial: return "newinvokespecial"; + case REF_invokeInterface: return "invokeinterface"; default: return "UNKNOWN_REFENCE_KIND"; } } @Override public String toString() { - return String.format("MethodHandleInfo[%s %s.%s%s]", getReferenceKindString(), declaringClass.getName(), name, methodType); + return String.format("%s %s.%s:%s", getReferenceKindString(), declaringClass.getName(), name, methodType); } }
--- a/src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java Fri Sep 14 09:09:24 2012 -0700 @@ -25,8 +25,6 @@ package java.lang.invoke; import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; /** * MethodHandleProxyLambdaMetafactory @@ -37,27 +35,6 @@ private final MethodHandle implMethod; - // @@@ For debugging only -- delete before shipping - static Executor logPool = Executors.newSingleThreadExecutor(); - private static void log(final String s) { - logPool.execute(new Runnable() { - @Override - public void run() { - System.out.println(s); - } - }); - } - private static void log(final String s, final Throwable e) { - logPool.execute(new Runnable() { - @Override - public void run() { - System.out.println(s); - e.printStackTrace(System.out); - } - }); - } - // -- dev-only -- - /** * Meta-factory constructor. *
--- a/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Fri Sep 14 09:09:24 2012 -0700 @@ -180,8 +180,13 @@ boxingDescriptor(sort)); } - void unbox(Type sType, Type tType) { - int sSort = sType.getSort(); + /** + * Convert types by unboxing. The source type is known to be a primitive wrapper. + * @param spType A primitive type corresponding to wrapped reference source type + * @param tType A primitive type being converted to + */ + void unbox(Type spType, Type tType) { + int sSort = spType.getSort(); int tSort = tType.getSort(); visitMethodInsn(INVOKEVIRTUAL, NAME_PRIMITIVE_WRAPPER[sSort], @@ -189,6 +194,12 @@ unboxingDescriptor(tSort)); } + /** + * Convert types by unboxing. Use the base wrapper (for example, Number for int, short, etc) to go directly + * to the desired type. + * @param sType A reference type being converted from + * @param tType A primitive type being converted to + */ void looseUnbox(Type sType, Type tType) { int tSort = tType.getSort(); String baseWrapper = NAME_PRIMITIVE_BASE_WRAPPER[tSort]; @@ -230,6 +241,13 @@ return NAME_PRIMITIVE_WRAPPER[type.getSort()]; } + /** + * Convert an argument of type 'argType' to be passed to 'targetType' assuring that it is 'functionalType'. + * Insert the needed conversion instructions in the method code. + * @param argType + * @param targetType + * @param functionalType + */ void convertType(Type argType, Type targetType, Type functionalType) { if (argType.equals(targetType) || argType.equals(VOID_TYPE)) { return;
--- a/src/share/classes/java/util/streams/AbstractPipeline.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/AbstractPipeline.java Fri Sep 14 09:09:24 2012 -0700 @@ -185,16 +185,20 @@ } @Override - public Sink<T, ?, ?> sink(Sink sink) { - return wrapSink(ops, sink); + public Sink<T, ?, ?> wrapSink(Sink sink) { + return AbstractPipeline.wrapSink(ops, sink); + } + + @Override + public Iterator wrapIterator(Iterator it) { + for (IntermediateOp op : ops) + it = op.wrapIterator(it); + return it; } @Override public Iterator iterator() { - Iterator it = source.iterator(); - for (IntermediateOp op : ops) - it = op.wrapIterator(it); - return it; + return wrapIterator(source.iterator()); } @Override
--- a/src/share/classes/java/util/streams/ops/AbstractTask.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/AbstractTask.java Fri Sep 14 09:09:24 2012 -0700 @@ -44,7 +44,7 @@ protected T children; protected T nextSibling; protected boolean isLeaf; - protected R result; + private R rawResult; protected AbstractTask(Spliterator<E> spliterator, long targetSize) { super(null); @@ -62,17 +62,42 @@ protected abstract R doLeaf(); + @Override + public R getRawResult() { + return rawResult; + } + + @Override + protected void setRawResult(R r) { + rawResult = r; + } + protected T getParent() { return (T) getCompleter(); } + protected T getRoot() { + T result = (T) this; + while (true) { + T parent = result.getParent(); + if (parent == null) + return result; + else + result = parent; + } + } + + protected void completeRoot(R result) { + getRoot().complete(result); + } + @Override public void compute() { int remaining = spliterator.getRemainingSizeIfKnown(); int naturalSplits = spliterator.getNaturalSplitArity(); isLeaf = ((remaining <= targetSize) && (remaining >= 0)) || (naturalSplits == 0); if (isLeaf) { - result = doLeaf(); + setRawResult(doLeaf()); tryComplete(); } else {
--- a/src/share/classes/java/util/streams/ops/CumulateOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/CumulateOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -180,7 +180,7 @@ else { leafData = StreamBuilders.make(); TerminalSink<T, T> terminalSink = wrapSink(leafData); - source.into(problem.helper.sink(terminalSink)); + source.into(problem.helper.wrapSink(terminalSink)); upward = terminalSink.getAndClearState(); // Special case -- if problem.depth == 0, just wrap the result and be done if (isRoot())
--- a/src/share/classes/java/util/streams/ops/FoldOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/FoldOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -91,7 +91,7 @@ public <V> U computeParallel(ParallelOpHelper<T, V> helper) { ReduceTask<T, U, V> task = new ReduceTask<>(helper.spliterator(), helper.suggestTargetSize(), helper, this); helper.invoke(task); - return task.result; + return task.getRawResult(); } private static class ReduceTask<T, U, V> extends AbstractTask<V, U, ReduceTask<T,U,V>> { @@ -118,7 +118,7 @@ @Override protected U doLeaf() { final TerminalSink<T, U> reduceStage = op.sink(); - spliterator.into(helper.sink(reduceStage)); + spliterator.into(helper.wrapSink(reduceStage)); return reduceStage.getAndClearState(); } @@ -126,10 +126,11 @@ public void onCompletion(CountedCompleter caller) { if (!isLeaf) { ReduceTask<T,U,V> child = children; - result = child.result; + U result = child.getRawResult(); child = child.nextSibling; for (; child != null; child = child.nextSibling) - result = op.combiner.operate(result, child.result); + result = op.combiner.operate(result, child.getRawResult()); + setRawResult(result); } } }
--- a/src/share/classes/java/util/streams/ops/ForEachOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/ForEachOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -96,7 +96,7 @@ @Override public <V> Void computeParallel(ParallelOpHelper<T, V> helper) { long targetSize = helper.suggestTargetSize(); - Sink<V, ?, ?> compoundSink = helper.sink(sink); + Sink<V, ?, ?> compoundSink = helper.wrapSink(sink); Spliterator<V> spliterator = helper.spliterator(); if (helper.suggestDepth() == 0) { spliterator.into(compoundSink);
--- a/src/share/classes/java/util/streams/ops/MatchOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/MatchOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.concurrent.CountedCompleter; +import java.util.concurrent.atomic.AtomicReference; import java.util.functions.Predicate; import java.util.streams.Spliterator; @@ -48,93 +49,82 @@ return matchKind.match(iterator, predicate); } - // Code sketch for parallel implementation: - // - Decompose as per usual - // - run match on leaf chunks, returns b - // - if b == matchKind.shortCircuitOn, complete early and return b - // - else if we complete normally, return !shortCircuitOn + @Override + public <V> Boolean computeParallel(ParallelOpHelper<T, V> helper) { + // Approach for parallel implementation: + // - Decompose as per usual + // - run match on leaf chunks, call result "b" + // - if b == matchKind.shortCircuitOn, complete early and return b + // - else if we complete normally, return !shortCircuitOn - private static class MatchTask<T> extends AbstractTask<T, Boolean, MatchTask<T>> { + MatchTask<T, V> task = new MatchTask<>(helper.spliterator(), helper.suggestTargetSize(), this, helper); + helper.invoke(task); + return task.answer.get(); + } + + private static class MatchTask<T, V> extends AbstractTask<V, Boolean, MatchTask<T, V>> { private final MatchOp<T> op; - private final ParallelOpHelper<T, Boolean> helper; + private final ParallelOpHelper<T, V> helper; + private final AtomicReference<Boolean> answer; - private MatchTask(Spliterator<T> spliterator, long targetSize, MatchOp<T> op, ParallelOpHelper<T, Boolean> helper) { + private MatchTask(Spliterator<V> spliterator, long targetSize, MatchOp<T> op, ParallelOpHelper<T, V> helper) { super(spliterator, targetSize); this.op = op; this.helper = helper; + this.answer = new AtomicReference<>(null); } - private MatchTask(MatchTask<T> parent, Spliterator<T> spliterator) { + private MatchTask(MatchTask<T, V> parent, Spliterator<V> spliterator) { super(parent, spliterator); this.op = parent.op; this.helper = parent.helper; + this.answer = parent.answer; } @Override - protected MatchTask<T> makeChild(Spliterator<T> spliterator) { + protected MatchTask<T, V> makeChild(Spliterator<V> spliterator) { return new MatchTask<>(this, spliterator); } @Override protected Boolean doLeaf() { - boolean b = op.evaluate(helper.iterator()); - if (b == op.matchKind.shortCircuitOn) - complete(null); // @@@ Should be complete(b); - return null; + boolean b = op.matchKind.match(helper.wrapIterator(spliterator.iterator()), op.predicate); + if (b == op.matchKind.shortCircuitResult) { + answer.compareAndSet(null, b); + completeRoot(b); + } + return b; } @Override public void onCompletion(CountedCompleter caller) { - // if getRawValue() returns non-null, then complete the parent with that - // if we have no parent, then complete with !shortCircuitOn + if (getParent() == null) + answer.compareAndSet(null, !op.matchKind.shortCircuitResult); } } public enum MatchKind { - ANY(true) { - @Override - <T> boolean match(Iterator<? extends T> iterator, Predicate<? super T> predicate) { - while (iterator.hasNext()) { - if(predicate.test(iterator.next())) { - return true; - } - } + ANY(true, true), + ALL(false, false), + NONE(true, false); - return false; - } - }, - ALL(false) { - @Override - <T> boolean match(Iterator<? extends T> iterator, Predicate<? super T> predicate) { - while (iterator.hasNext()) { - if(!predicate.test(iterator.next())) { - return false; - } - } + private final boolean stopOnPredicateMatches; + private final boolean shortCircuitResult; - return true; - } - }, - NONE(false) { - @Override - <T> boolean match(Iterator<? extends T> iterator, Predicate<? super T> predicate) { - while (iterator.hasNext()) { - if(predicate.test(iterator.next())) { - return false; - } - } - - return true; - } - }; - - private final boolean shortCircuitOn; - - MatchKind(boolean shortCircuitOn) { - this.shortCircuitOn = shortCircuitOn; + MatchKind(boolean stopOnPredicateMatches, boolean shortCircuitResult) { + this.stopOnPredicateMatches = stopOnPredicateMatches; + this.shortCircuitResult = shortCircuitResult; } - abstract<T> boolean match(Iterator<? extends T> iterator, Predicate<? super T> predicate); + <T> boolean match(Iterator<? extends T> iterator, Predicate<? super T> predicate) { + while (iterator.hasNext()) { + if(predicate.test(iterator.next()) == stopOnPredicateMatches) { + return shortCircuitResult; + } + } + + return !shortCircuitResult; + } } }
--- a/src/share/classes/java/util/streams/ops/ParallelOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/ParallelOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -43,7 +43,8 @@ public int suggestDepth(); public long suggestTargetSize(); public Spliterator<V> spliterator(); - public Sink<V, ?, ?> sink(Sink sink); + public Sink<V, ?, ?> wrapSink(Sink sink); + public Iterator wrapIterator(Iterator it); public Iterator<T> iterator(); public<Z> Z invoke(ForkJoinTask<Z> task); }
--- a/src/share/classes/java/util/streams/ops/StatefulOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/StatefulOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -24,7 +24,7 @@ <Z> TreeUtils.Node<U> computeParallel(ParallelOpHelper<T, Z> helper) default { // dumb default serial implementation final StreamBuilder<U> sb = StreamBuilders.make(); - helper.spliterator().into(helper.sink(wrapSink(sb))); + helper.spliterator().into(helper.wrapSink(wrapSink(sb))); return TreeUtils.node(sb); } }
--- a/src/share/classes/java/util/streams/ops/TerminalOp.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/TerminalOp.java Fri Sep 14 09:09:24 2012 -0700 @@ -58,7 +58,7 @@ <V> U computeParallel(ParallelOpHelper<T, V> helper) default { // dumb default serial version TerminalSink<T, U> toArraySink = sink(); - helper.spliterator().into(helper.sink(toArraySink)); + helper.spliterator().into(helper.wrapSink(toArraySink)); return toArraySink.getAndClearState(); }
--- a/src/share/classes/java/util/streams/ops/TreeUtils.java Thu Sep 13 21:40:37 2012 -0700 +++ b/src/share/classes/java/util/streams/ops/TreeUtils.java Fri Sep 14 09:09:24 2012 -0700 @@ -52,12 +52,12 @@ // Need to account for SIZED flag from pipeline if (size != -1 && splitSizesKnown) { builder = StreamBuilders.makeFixed(size); - spliterator.into(helper.sink(builder)); + spliterator.into(helper.wrapSink(builder)); return node((T[]) builder.toArray()); } else { builder = StreamBuilders.make(); - spliterator.into(helper.sink(builder)); + spliterator.into(helper.wrapSink(builder)); return node(builder); } } @@ -72,7 +72,7 @@ else { CollectorTask<U, T> task = new CollectorTask<>(spliterator, helper.suggestTargetSize(), helper); helper.invoke(task); - Node<T> node = task.result; + Node<T> node = task.getRawResult(); if (flattenTree) { T[] array = (T[]) new Object[node.size()]; helper.invoke(new ToArrayTask<>(node, array, 0)); @@ -123,7 +123,7 @@ protected Node<U> doLeaf() { // @@@ Usual comment about using fixed stream builders if we know enough to do so StreamBuilder<U> builder = StreamBuilders.make(); - spliterator.into(helper.sink(builder)); + spliterator.into(helper.wrapSink(builder)); return node(builder); } @@ -134,13 +134,13 @@ Node<U>[] nodes = (Node<U>[]) new Node[numChildren]; int idx = 0; for (CollectorTask<T, U> cur = children; cur != null; cur = cur.nextSibling) - nodes[idx++] = cur.result; - result = node(nodes); + nodes[idx++] = cur.getRawResult(); + setRawResult(node(nodes)); } } } - private static class SizedCollectorTask<T, U> extends CountedCompleter { + private static class SizedCollectorTask<T, U> extends CountedCompleter<Void> { private final Spliterator<T> spliterator; private final ParallelOp.ParallelOpHelper<U, T> helper; private final long targetSize; @@ -173,7 +173,7 @@ int naturalSplits = spliterator.getNaturalSplitArity(); boolean isLeaf = ((remaining <= targetSize) && (remaining >= 0)) || (naturalSplits == 0); if (isLeaf) { - spliterator.into(helper.sink(Arrays.sink(array, offset, length))); + spliterator.into(helper.wrapSink(Arrays.sink(array, offset, length))); tryComplete(); } else { @@ -195,7 +195,7 @@ } } - private static class ToArrayTask<T> extends CountedCompleter { + private static class ToArrayTask<T> extends CountedCompleter<Void> { private final T[] array; private final Node<T> node; private final int offset;
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/MapStreamTestDataProvider.java Thu Sep 13 21:40:37 2012 -0700 +++ b/test-ng/tests/org/openjdk/tests/java/util/streams/MapStreamTestDataProvider.java Fri Sep 14 09:09:24 2012 -0700 @@ -91,7 +91,7 @@ list.add(new Object[]{ String.format("%s%s%s", name, "<Integer,Integer>", range), - new StreamOpTestCase.MapTestData(makeIntegerIntegerMap((Class<? extends Map<Integer,Integer>>) type, size)) + new StreamOpTestCase.MapTestData<>(makeIntegerIntegerMap((Class<? extends Map<Integer,Integer>>) type, size)) }); list.add(new Object[]{ String.format("%s%s%s", name, "<Integer,String>", range),
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/MatchOpTest.java Thu Sep 13 21:40:37 2012 -0700 +++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/MatchOpTest.java Fri Sep 14 09:09:24 2012 -0700 @@ -61,7 +61,7 @@ ALL { @Override <T> boolean match(Stream<T> stream, Predicate<T> predicate) { - return stream.anyMatch(predicate); + return stream.allMatch(predicate); } @Override @@ -72,7 +72,7 @@ NONE { @Override <T> boolean match(Stream<T> stream, Predicate<T> predicate) { - return stream.anyMatch(predicate); + return stream.noneMatch(predicate); } @Override @@ -86,11 +86,12 @@ } private<T> void assertPredicates(Streamable<T> source, MatchKind matchKind, Predicate<T>[] predicates, boolean... answers) { - for (int i=0; i<predicates.length; i++) + for (int i=0; i<predicates.length; i++) { assertEquals(answers[i], matchKind.match(source.stream(), predicates[i]), matchKind.toString() + predicates[i].toString()); + } } - void testMatches() { + public void testMatches() { assertPredicates(countTo(0), MatchKind.ANY, INTEGER_PREDICATES, false, false, false, false); assertPredicates(countTo(0), MatchKind.ALL, INTEGER_PREDICATES, true, true, true, true); assertPredicates(countTo(0), MatchKind.NONE, INTEGER_PREDICATES, true, true, true, true); @@ -107,7 +108,8 @@ @Test(dataProvider = "opArrays", dataProviderClass = StreamTestDataProvider.class) public void testOps(String name, TestData<Integer> data) { for (Predicate<Integer> p : INTEGER_PREDICATES) - for (MatchKind matchKind : MatchKind.values()) + for (MatchKind matchKind : MatchKind.values()) { exerciseOps(data, matchKind.op(p)); + } } }
--- a/test-ng/tests/org/openjdk/tests/java/util/streams/ops/StreamOpTestCase.java Thu Sep 13 21:40:37 2012 -0700 +++ b/test-ng/tests/org/openjdk/tests/java/util/streams/ops/StreamOpTestCase.java Fri Sep 14 09:09:24 2012 -0700 @@ -321,11 +321,16 @@ @Override @SuppressWarnings({ "raw", "unchecked" }) - public Sink<T, ?, ?> sink(Sink sink) { + public Sink<T, ?, ?> wrapSink(Sink sink) { return sink; } @Override + public Iterator wrapIterator(Iterator it) { + return it; + } + + @Override public Iterator<T> iterator() { return data.iterator(); }