OpenJDK / lambda / lambda / jdk
changeset 8138:ca312c0133c2
Merge
author | mduigou |
---|---|
date | Tue, 16 Apr 2013 13:13:13 -0700 |
parents | 081327aac5be cd46f0f9ca1e |
children | aa7ac2dee56f |
files | .hgtags test/java/util/ComparatorsTest.java |
diffstat | 553 files changed, 117579 insertions(+), 6445 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Thu Apr 11 09:40:13 2013 -0700 +++ b/.hgignore Tue Apr 16 13:13:13 2013 -0700 @@ -5,3 +5,19 @@ ^make/netbeans/.*/dist/ ^.hgtip .DS_Store +^test-ng/gen-separate/ +^test-ng/lib/ +^.idea/ +^combo-tests/gen/ +^combo-tests/build/ +^out/ +.*\.rej$ +.*\.orig$ +.*\.iml$ +.*~$ +^webrev/ +webrev.zip$ +^.classpath +^.project +JTreport +JTwork
--- a/.hgtags Thu Apr 11 09:40:13 2013 -0700 +++ b/.hgtags Tue Apr 16 13:13:13 2013 -0700 @@ -167,16 +167,20 @@ b3246687c3695dff6f461bb407f9db88f7d072e7 jdk8-b43 db471a7af03168e4441c245b1d9976f720a7cb77 jdk8-b44 b92353a01aa049bc508fc56f0347d5934b7c4390 jdk8-b45 +077225955d5722f0fbd780650178c5931693c07e lambda-b45 8d2ed9d58453c8049715a72a6d26b6b66b37a94c jdk8-b46 00b22b23269a57d0bb46c57753be2fe9a9d2c1a3 jdk8-b47 3e4ab821f46166fcf63e8fe5c8046216003c941f jdk8-b48 +b3b65a3d441e7f39cb68140d2db547704ea0a670 lambda-b48 51707c3b75c0f521794d9ab425f4e5b2351c70c1 jdk8-b49 e4bae5c53fca8fcb9393d47fd36a34b9e2e8d4ec jdk8-b50 +958eaa191b79bf79be82979240903199791ed9f7 lambda-b50 e865efbc71059a414b3b2dd2e0adfcb3d2ab6ff9 jdk8-b51 e8569a473cee7f4955bd9e76a9bdf6c6a07ced27 jdk8-b52 2c6933c5106b81a8578b70996fe5b735fb3adb60 jdk8-b53 70ad0ed1d6cef0e7712690d1bab21e4769708aad jdk8-b54 1f3f4b333341873f00da3dee85e4879f0e89c9bb jdk8-b55 +2e6170973d921fe4b8d2dfd6032f5aaf4150a542 lambda-b56 2e9eeef2909b33c9224a024afddb61ccb0b77f14 jdk8-b56 51594d095a4bcffac4a314bf6e148214501399e0 jdk8-b57 d94613ac03d8de375ef60493e2bb76dbd30d875d jdk8-b58
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/build.xml Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="jdk" default="test"> + + <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"/> + <property name="lib.dir" location="lib" /> + <property name="test.pattern" value="*Test" /> + <property name="lambda.metafactory" value="" /> + <property name="combo.debug" value="" /> + <property name="heap.size" value="4G" /> + + <property name="lib.testng.jar" value="${lib.dir}/testng-6.7.jar"/> + <property name="lib.tools.jar" value="${java.home}/../lib/tools.jar"/> + + <path id="test.class.path"> + <pathelement path="${sun.boot.class.path}" /> + <pathelement location="${test.classes.dir}" /> + <pathelement location="${lib.testng.jar}"/> + <pathelement location="${lib.tools.jar}"/> + </path> + + <taskdef name="testng" classpathref="test.class.path" classname="org.testng.TestNGAntTask" /> + + <target name="prepare"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${lib.dir}"/> + <mkdir dir="${test.classes.dir}"/> + <mkdir dir="${test.reports.dir}"/> + </target> + + <target name="test-compile" depends="prepare"> + <javac destdir="${test.classes.dir}" debug="on" srcdir="${test.src.dir}" fork="true" + classpathref="test.class.path"> + <compilerarg value="-XDlambdaToMethod"/> + </javac> + </target> + + <target name="test" depends="test-compile" > + <delete dir="${gen.dir}" /> + <testng outputdir="${test.reports.dir}" usedefaultlisteners="false" + listeners="org.testng.reporters.FailedReporter,org.testng.reporters.XMLReporter"> + <classpath refid="test.class.path" /> + <classfileset dir="${test.classes.dir}" includes="**/${test.pattern}.class"/> + <jvmarg value="-Xms1G" /> + <jvmarg value="-Xmx${heap.size}" /> + <jvmarg value="-XX:+UseNUMA" /> + <jvmarg value="-XX:+UseG1GC" /> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <sysproperty key="lambda.metafactory" value="${lambda.metafactory}" /> + <sysproperty key="combo.debug" value="${combo.debug}" /> + </testng> + </target> + + <!-- New target added that uses a different set of listeners to show results in Aurora --> + <target name="aurora-test" depends="test-compile" > + <testng outputdir="${test.reports.dir}" usedefaultlisteners="false" + listeners="org.testng.reporters.JUnitXMLReporter,org.testng.reporters.JUnitReportReporter, + org.testng.reporters.XMLReporter"> + <classpath refid="test.class.path" /> + <classfileset dir="${test.classes.dir}" includes="**/${test.pattern}.class"/> + <jvmarg value="-Xms1G" /> + <jvmarg value="-Xmx2G" /> + <jvmarg value="-XX:+UseNUMA" /> + <jvmarg value="-XX:+UseG1GC" /> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <sysproperty key="lambda.metafactory" value="${lambda.metafactory}" /> + <sysproperty key="combo.debug" value="${combo.debug}" /> + </testng> + </target> + + <target name="clean"> + <delete dir="${build.dir}" /> + <delete dir="${gen.dir}" /> + </target> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/BridgeMethodTestCase.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +import com.sun.tools.javac.util.Pair; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.testng.Assert.fail; + +/** + * BridgeMethodTestCase -- used for asserting linkage to bridges under separate compilation. + * + * Example test case: + * public void test1() throws IOException, ReflectiveOperationException { + * compileSpec("C(Bc1(A))"); + * assertLinkage("C", LINKAGE_ERROR, "B1"); + * recompileSpec("C(Bc1(Ac0))", "A"); + * assertLinkage("C", "A0", "B1"); + * } + * + * This compiles A, B, and C, asserts that C.m()Object does not exist, asserts + * that C.m()Number eventually invokes B.m()Number, recompiles B, and then asserts + * that the result of calling C.m()Object now arrives at A. + * + * @author Brian Goetz + */ +@Test +public abstract class BridgeMethodTestCase extends JavacTemplateTestBase { + private static final String TYPE_LETTERS = "ABCIJK"; + private static final String[] OVERRIDE_NAMES = { "Object", "Number", "Integer" }; + + protected static String LINKAGE_ERROR = "-1"; + + private List<File> compileDirs = new ArrayList<>(); + + /** + * Compile all the classes in a class spec, and put them on the classpath. + * + * The spec is the specification for a nest of classes, using the following notation + * A, B represent abstract classes + * C represents a concrete class + * I, J, K represent interfaces + * Lowercase 'c' following a class means that the method m() is concrete + * Lowercase 'a' following a class or interface means that the method m() is abstract + * Lowercase 'd' following an interface means that the method m() is default + * A number 0, 1, or 2 following the lowercase letter indicates the return type of that method + * 0 = Object, 1 = Number, 2 = Integer (these form an inheritance chain so bridges are generated) + * A classes supertypes follow its method spec, in parentheses + * Examples: + * C(Ia0, Jd0) -- C extends I and J, I has abstract m()Object, J has default m()Object + * Cc1(Ia0) -- C has concrete m()Number, extends I with abstract m()Object + * If a type must appear multiple times, its full spec must be in the first occurrence + * Example: + * C(I(Kd0), J(K)) + */ + protected void compileSpec(String spec) throws IOException { + ClassModel cm = new Parser(spec).parseClassModel(); + for (Map.Entry<String, ClassModel> e : classes(cm).entrySet()) + addSourceFile(e.getKey() + ".java", new StringTemplate(e.getValue().toSource())); + compileDirs.add(compile(true)); + resetSourceFiles(); + assertCompileSucceeded(); + } + + /** + * Recompile only a subset of classes in the class spec, as named by names, + * and put them on the classpath such that they shadow earlier versions of that class. + */ + protected void recompileSpec(String spec, String... names) throws IOException { + List<String> nameList = Arrays.asList(names); + ClassModel cm = new Parser(spec).parseClassModel(); + for (Map.Entry<String, ClassModel> e : classes(cm).entrySet()) + if (nameList.contains(e.getKey())) + addSourceFile(e.getKey() + ".java", new StringTemplate(e.getValue().toSource())); + compileDirs.add(compile(Arrays.asList(classPaths()), true)); + resetSourceFiles(); + assertCompileSucceeded(); + } + + protected void assertLinkage(String name, String... expected) throws ReflectiveOperationException { + for (int i=0; i<expected.length; i++) { + String e = expected[i]; + if (e.equals(LINKAGE_ERROR)) { + try { + int actual = invoke(name, i); + fail("Expected linkage error, got" + fromNum(actual)); + } + catch (LinkageError x) { + // success + } + } + else { + if (e.length() == 1) + e += "0"; + int expectedInt = toNum(e); + int actual = invoke(name, i); + if (expectedInt != actual) + fail(String.format("Expected %s but found %s for %s.m()%d", fromNum(expectedInt), fromNum(actual), name, i)); + } + } + } + + private Map<String, ClassModel> classes(ClassModel cm) { + HashMap<String, ClassModel> m = new HashMap<>(); + classesHelper(cm, m); + return m; + } + + private void classesHelper(ClassModel cm, Map<String, ClassModel> m) { + if (!m.containsKey(cm.name)) + m.put(cm.name, cm); + for (ClassModel s : cm.supertypes) + classesHelper(s, m); + } + + private static String fromNum(int num) { + return String.format("%c%d", TYPE_LETTERS.charAt(num / 10), num % 10); + } + + private static int toNum(String name, int index) { + return 10*(TYPE_LETTERS.indexOf(name.charAt(0))) + index; + } + + private static int toNum(String string) { + return 10*(TYPE_LETTERS.indexOf(string.charAt(0))) + Integer.parseInt(string.substring(1, 2)); + } + + private int invoke(String name, int index) throws ReflectiveOperationException { + File[] files = classPaths(); + Class clazz = loadClass(name, files); + Method[] ms = clazz.getMethods(); + for (Method m : ms) { + if (m.getName().equals("m") && m.getReturnType().getName().equals("java.lang." + OVERRIDE_NAMES[index])) { + m.setAccessible(true); + Object instance = clazz.newInstance(); + Object x = m.invoke(instance); + return (int) x; + } + } + throw new NoSuchMethodError("cannot find method m()" + index + " in class " + name); + } + + private File[] classPaths() { + File[] files = new File[compileDirs.size()]; + for (int i=0; i<files.length; i++) + files[files.length - i - 1] = compileDirs.get(i); + return files; + } + + @BeforeMethod + @Override + public void reset() { + compileDirs.clear(); + super.reset(); + } + + private static class ClassModel { + + enum MethodType { + ABSTRACT('a'), CONCRETE('c'), DEFAULT('d'); + + public final char designator; + + MethodType(char designator) { + this.designator = designator; + } + + public static MethodType find(char c) { + for (MethodType m : values()) + if (m.designator == c) + return m; + throw new IllegalArgumentException(); + } + } + + private final String name; + private final boolean isInterface; + private final List<ClassModel> supertypes; + private final MethodType methodType; + private final int methodIndex; + + private ClassModel(String name, + boolean anInterface, + List<ClassModel> supertypes, + MethodType methodType, + int methodIndex) { + this.name = name; + isInterface = anInterface; + this.supertypes = supertypes; + this.methodType = methodType; + this.methodIndex = methodIndex; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(name); + if (methodType != null) { + sb.append(methodType.designator); + sb.append(methodIndex); + } + if (!supertypes.isEmpty()) { + sb.append("("); + for (int i=0; i<supertypes.size(); i++) { + if (i > 0) + sb.append(","); + sb.append(supertypes.get(i).toString()); + } + sb.append(")"); + } + return sb.toString(); + } + + public String toSource() { + String extendsClause = ""; + String implementsClause = ""; + String methodBody = ""; + boolean isAbstract = "AB".contains(name); + + for (ClassModel s : supertypes) { + if (!s.isInterface) { + extendsClause = String.format("extends %s", s.name); + break; + } + } + + StringJoiner sj = new StringJoiner(", "); + for (ClassModel s : supertypes) + if (s.isInterface) + sj.add(s.name); + if (sj.length() > 0) + implementsClause = "implements " + sj.toString(); + + if (methodType != null) { + switch (methodType) { + case ABSTRACT: + methodBody = String.format("public abstract %s m();", OVERRIDE_NAMES[methodIndex]); + break; + case CONCRETE: + methodBody = String.format("public %s m() { return %d; };", OVERRIDE_NAMES[methodIndex], toNum(name, methodIndex)); + break; + case DEFAULT: + methodBody = String.format("public default %s m() { return %d; };", OVERRIDE_NAMES[methodIndex], toNum(name, methodIndex)); + break; + + } + } + + return String.format("public %s %s %s %s %s %n{ %n %s %n}", isAbstract ? "abstract" : "", + isInterface ? "interface" : "class", + name, extendsClause, implementsClause, methodBody); + } + } + + private static class Parser { + private final String input; + private final char[] chars; + private int index; + + private Parser(String s) { + input = s; + chars = s.toCharArray(); + } + + private char peek() { + return index < chars.length ? chars[index] : 0; + } + + private boolean peek(String validChars) { + return validChars.indexOf(peek()) >= 0; + } + + private char advanceIf(String validChars) { + if (peek(validChars)) + return chars[index++]; + else + return 0; + } + + private char advance() { + return chars[index++]; + } + + private char expect(String validChars) { + char c = advanceIf(validChars); + if (c == 0) + throw new IllegalArgumentException(String.format("Expecting %s at position %d of %s", validChars, index, input)); + return c; + } + + public ClassModel parseClassModel() { + List<ClassModel> supers = new ArrayList<>(); + char name = expect(TYPE_LETTERS); + boolean isInterface = "IJK".indexOf(name) >= 0; + ClassModel.MethodType methodType = peek(isInterface ? "ad" : "ac") ? ClassModel.MethodType.find(advance()) : null; + int methodIndex = 0; + if (methodType != null) { + methodIndex = advanceIf("012"); + if (methodIndex != 0) + methodIndex -= '0'; + } + if (peek() == '(') { + advance(); + supers.add(parseClassModel()); + while (peek() == ',') { + advance(); + supers.add(parseClassModel()); + } + expect(")"); + } + return new ClassModel(new String(new char[]{ name }), isInterface, supers, methodType, methodIndex); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/BridgeMethodsLinearTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.io.IOException; + +import org.testng.annotations.Test; + +/** + * BridgeMethodsLinearTest + * + * @author Brian Goetz + */ +@Test +public class BridgeMethodsLinearTest extends BridgeMethodTestCase { + public void test1() throws IOException, ReflectiveOperationException { + compileSpec("C(Bc1(A))"); + assertLinkage("C", LINKAGE_ERROR, "B1"); + recompileSpec("C(Bc1(Ac0))", "A"); + assertLinkage("C", "A0", "B1"); + } + + public void test2() throws IOException, ReflectiveOperationException { + compileSpec("C(B(Ac0))"); + assertLinkage("C", "A0", LINKAGE_ERROR); + recompileSpec("C(Bc1(Ac0))", "B"); + assertLinkage("C", "B1", "B1"); + } + + public void test3() throws IOException, ReflectiveOperationException { + compileSpec("C(B(A))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR); + recompileSpec("C(Bc1(Ac0))", "A", "B"); + assertLinkage("C", "B1", "B1"); + } + + public void test4() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac1(I))"); + assertLinkage("C", LINKAGE_ERROR, "A1"); + recompileSpec("C(Ac1(Id0))", "I"); + assertLinkage("C", "I0", "A1"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/ComboTestBase.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import com.sun.tools.javac.util.Pair; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.Test; + +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * ComboTestBase + * + * @author Brian Goetz + */ +public abstract class ComboTestBase<T extends ComboTestBase<T>> extends JavacTemplateTestBase { + private final static Map<Class, ComboTestMetadata> metadataMap = new HashMap<>(); + private final static boolean DEBUG_PROPERTY_SET = !System.getProperty("combo.debug", "").equals(""); + private final static long timestamp = System.currentTimeMillis(); + private final static Map</* filename */ String, + Map</* combo vars */ Map<String, String>, /* data */ Map<String, String>>> debugInfo + = new HashMap<>(); + + protected final ComboTestMetadata<T> metadata; + protected final Object[] comboArgs; + private boolean debugOnly = false; + + // This class cannot have a constructor; it interferes with testng's ability to find the factory method + //in subclasses. Anything that would be done by a constructor should insteady be done by init blocks + //or the pseudoConstructor() method. + + /* init */ { + metadata = getMetadata((Class<T>) getClass()); + comboArgs = new Object[metadata.dimensions.length]; + } + + /** + * This method is meant to be called from an @Factory method in the subclass, passing its own class literal + * as the clazz argument. It will introspect over the provided class, find the dimension variables, and + * generate the combinatorial explosion of test cases. (It consults shouldSkip() to determine if a particular + * combination should be skipped.) + */ + public static<T extends ComboTestBase> Object[] factory(Class<T> clazz) throws ReflectiveOperationException { + int index = 0; + try { + ComboTestMetadata<T> md = getMetadata(clazz); + List<T> list = new ArrayList<>(); + for (Object[] args : md.makeLoop()) { + T instance = md.makeInstance(args); + if (!instance.shouldSkip()) { + instance.pseudoConstructor(args); + list.add(instance); + instance.templates.put("__INDEX__", instance.new StringTemplate(Integer.toString(index++))); + if (DEBUG_PROPERTY_SET) + ((ComboTestBase) instance).debugOnly = true; + } + } + + return list.toArray(); + } catch (Throwable t) { + // This is a needed annoyance; TestNG swallows exception data from exceptions thrown from @Factory methods + t.printStackTrace(System.err); + t.printStackTrace(System.out); + throw t; + } + } + + /** For debugging of test cases -- sets tests into debug-only mode, where we print out the instantiations of + * the dimension variables and the templates. + */ + public static<T extends ComboTestBase> Object[] debugFactory(Class<T> clazz) throws ReflectiveOperationException { + Object[] results = factory(clazz); + for (Object o : results) + ((ComboTestBase) o).debugOnly = true; + return results; + } + + private static<T extends ComboTestBase<T>> ComboTestMetadata<T> getMetadata(Class<T> clazz) { + try { + ComboTestMetadata<T> md = metadataMap.get(clazz); + if (md == null) { + md = new ComboTestMetadata<T>(clazz); + metadataMap.put(clazz, md); + } + return md; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + // Should be private, but can't be + void pseudoConstructor(Object[] args) throws ReflectiveOperationException { + System.arraycopy(args, 0, comboArgs, 0, args.length); + } + + private void addTemplates() throws ReflectiveOperationException { + for (int i=0; i<comboArgs.length; i++) + addTemplate(metadata.dimensions[i].name, asTemplate(comboArgs[i])); + + for (ComboTestMetadata.TemplateMember<TemplateVar, Field> m : metadata.templateFields) + addTemplate(m.name, asTemplate(m.member.get(this))); + + for (ComboTestMetadata.TemplateMember<TemplateVar, Method> m : metadata.templateMethods) + addTemplate(m.name, asTemplate(m.member.invoke(this))); + } + + private void addAllSourceFiles() throws ReflectiveOperationException { + for (ComboTestMetadata.TemplateMember<SourceFile, Field> m : metadata.sourceFields) + addSourceFile(m.name, asTemplate(m.member.get(this))); + + for (ComboTestMetadata.TemplateMember<SourceFile, Method> m : metadata.sourceMethods) + addSourceFile(m.name, asTemplate(m.member.invoke(this))); + } + + private void addSourceFiles(String group) throws ReflectiveOperationException { + for (ComboTestMetadata.TemplateMember<SourceFile, Field> m : metadata.sourceFields) + if (group.equals(m.annotation.group())) + addSourceFile(m.name, asTemplate(m.member.get(this))); + + for (ComboTestMetadata.TemplateMember<SourceFile, Method> m : metadata.sourceMethods) + if (group.equals(m.annotation.group())) + addSourceFile(m.name, asTemplate(m.member.invoke(this))); + } + + /** + * Should the current combination of inputs be skipped? Default implementation returns false for + * all combinations (indicating all combinations are valid.) + */ + protected boolean shouldSkip() { + return false; + } + + protected boolean shouldRun() { + return false; + } + + /** + * Called after test() compiles, allowing subclass an opportunity to examine diagonstics and assert + * their properties. Default implementation calls assertCompileSucceeded(). + * @param group + */ + protected void postCompile(String group) { + assertCompileSucceeded(); + } + + protected String[] getCompileOptions(String group) { + return new String[0]; + } + + /** + * Called before test() compiles, allowing subclass an opportunity to set compilation options. + */ + protected void preCompile(String group) { + } + + @Test + public void test() throws Exception { + if (debugOnly) { + addTemplates(); + addAllSourceFiles(); + System.out.println("Test case : " + getTestCaseDescription()); + for (Pair<String, Template> e : sourceFiles) + System.out.println("Source file " + e.fst + ": " + e.snd); + + String testFile = getClass().getName(); + Map<Map<String, String>, Map<String, String>> testCaseMap = debugInfo.get(testFile); + if (testCaseMap == null) { + testCaseMap = new HashMap<>(); + debugInfo.put(testFile, testCaseMap); + } + + Map<String, String> comboKeys = new HashMap<>(); + for (int i=0; i<metadata.dimensions.length; i++) + comboKeys.put(metadata.dimensions[i].name, comboArgs[i].toString()); + String compileOptions = Arrays.asList(getCompileOptions("")).toString(); + Map<String, String> data = new HashMap<>(); + int count = 0; + for (Pair<String, Template> e : sourceFiles) { + count++; + data.put("File." + count + ".FileName", e.fst); + data.put("File." + count + ".Source", e.snd.toString()); + data.put("File." + count + ".Group", ""); + data.put("File." + count + ".CompileOptions", compileOptions); + } + Object prev = testCaseMap.put(comboKeys, data); + if (prev != null) + fail(String.format("Duplicate key for file %s, keys %s", testFile, comboKeys.toString())); + } + else { + boolean generate = shouldRun(); + boolean errors = false; + List<File> files = new ArrayList<>(); + List<String> groupsReversed = new ArrayList<>(metadata.groups); + Collections.reverse(groupsReversed); + for (String g : groupsReversed) { + addTemplates(); + addSourceFiles(g); + preCompile(g); + addCompileOptions(getCompileOptions(g)); + for (File f : files) + addClassPath(f); + files.add(0, compile(generate)); + errors |= diags.errorsFound(); + postCompile(g); + reset(); + } + + if (generate && !errors) { + run(loadClass("Main", files.toArray(new File[files.size()]))); + } + } + } + + @AfterSuite + public void dumpDebug() throws IOException { + if (DEBUG_PROPERTY_SET) { + for (Map.Entry<String, Map<Map<String, String>, Map<String, String>>> f : debugInfo.entrySet()) { + File out = new File(String.format("ComboLog-%s-%d.ser", f.getKey(), timestamp)); + try (ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(out))) { + os.writeObject(f.getValue()); + } + } + } + } + + protected void run(Class<?> clazz) throws ReflectiveOperationException { + assertTrue(clazz != null); + } + + protected String getTestCaseDescription() { + StringBuilder sb = new StringBuilder(); + sb.append("ComboTest[").append(getClass().getName()).append(": "); + for (int i=0; i<metadata.dimensions.length; i++) + sb.append(metadata.dimensions[i].name).append(" = ").append(comboArgs[i]).append(", "); + sb.append("]"); + return sb.toString(); + } + + protected boolean skipConstrained(int arity, Enum... enums) { + for (int i=arity; i<enums.length; i++) + if (enums[i].ordinal() != 0) + return true; + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/ComboTestDebug.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * ComboTestDebug -- utility program for dumping or comparing combo test debug logs. Debug logs can be + * used for debugging combo tests, as they contain all the generated source files and options (but are + * primarily used for testing for regressions in the combo test framework when changes are made to the + * framework.) + * + * @author Brian Goetz + */ +public class ComboTestDebug { + public static List<String> files = new ArrayList<>(); + public static boolean compare; + + private static void compareFiles(String s1, String s2) throws IOException, ClassNotFoundException { + Map</* combo vars */ Map<String, String>, + /* data */ Map<String, String>> f1, f2; + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(s1))) { + f1 = (Map<Map<String, String>, Map<String, String>>) ois.readObject(); + } + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(s2))) { + f2 = (Map<Map<String, String>, Map<String, String>>) ois.readObject(); + } + + if (f1.size() != f2.size()) + System.out.printf("Number of test cases differ: %d, %d%n", f1.size(), f2.size()); + + if (!f1.keySet().equals(f2.keySet())) { + System.out.printf("Test cases differ%n"); + for (Map<String, String> k : f1.keySet()) { + if (!f2.containsKey(k)) + System.out.printf("Only in file 1: test case %s%n", k); + } + for (Map<String, String> k : f2.keySet()) { + if (!f1.containsKey(k)) + System.out.printf("Only in file 2: test case %s%n", k); + } + } + + for (Map<String, String> k : f1.keySet()) { + if (!f2.keySet().contains(k)) + continue; + Map<String, String> d1 = f1.get(k); + Map<String, String> d2 = f2.get(k); + if (!d1.equals(d2)) { + System.out.printf("Test case %s%n", k); + for (String s : d1.keySet()) + if (!d2.containsKey(s)) + System.out.printf(" Only in file 1: key %s%n", s); + else { + if (!d1.get(s).equals(d2.get(s))) { + System.out.printf(" Key values differ for key %s: [%s] [%s]%n", s, d1.get(s), d2.get(s)); + } + } + for (String s : d2.keySet()) + if (!d1.containsKey(s)) + System.out.printf(" Only in file 2: key %s%n", s); + System.out.println(); + } + } + } + + private static void dumpFile(String s) throws IOException, ClassNotFoundException { + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(s))) { + Map</* combo vars */ Map<String, String>, + /* data */ Map<String, String>> debugInfo; + + debugInfo = (Map<Map<String, String>, Map<String, String>>) ois.readObject(); + System.out.printf("%d test cases%n%n", debugInfo.size()); + for (Map.Entry<Map<String, String>, Map<String, String>> e : debugInfo.entrySet()) { + System.out.println("Test case: " + e.getKey()); + for (Map.Entry<String, String> de : new TreeMap<>(e.getValue()).entrySet()) + System.out.printf(" %s = %s%n", de.getKey(), de.getValue()); + System.out.println(); + } + } + + } + + public static void main(String[] args) throws IOException, ClassNotFoundException { + for (String a : args) { + if (a.equals("-c")) + compare = true; + else if (a.startsWith("-")) + usage(); + else + files.add(a); + } + + if (compare) { + if (files.size() != 2) + usage(); + compareFiles(files.get(0), files.get(1)); + } + else { + for (String f : files) + dumpFile(f); + } + } + + public static void usage() { + System.out.println("Usage: ComboTestDebug [ -c ] files..."); + System.exit(0); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/ComboTestMetadata.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.*; + +/** + * ComboTestMetadata + * + * @author Brian Goetz*/ +class ComboTestMetadata<T> { + public final Class<T> clazz; + public final Constructor<T> ctor; + public final List<TemplateMember<TemplateVar, Field>> templateFields = new ArrayList<>(); + public final List<TemplateMember<TemplateVar, Method>> templateMethods = new ArrayList<>(); + public final List<TemplateMember<SourceFile, Field>> sourceFields = new ArrayList<>(); + public final List<TemplateMember<SourceFile, Method>> sourceMethods = new ArrayList<>(); + public final Set<String> groups = new TreeSet<>(); + public final Dimension[] dimensions; + + public ComboTestMetadata(Class<T> clazz) throws ReflectiveOperationException { + this.clazz = clazz; + Constructor[] constructors = clazz.getConstructors(); + ctor = (constructors.length == 1 && constructors[0].getParameterTypes().length > 0) ? constructors[0] : null; + + Map<DimensionVar, Field> tmpDimFields = new LinkedHashMap<>(); + for (Field f : clazz.getDeclaredFields()) { + TemplateVar template = f.getAnnotation(TemplateVar.class); + if (template != null) { + if (!Template.class.isAssignableFrom(f.getType()) && !String.class.isAssignableFrom(f.getType())) + throw new AssertionError(String.format("Found @TemplateVar on field %s whose type is not Template or String", f)); + f.setAccessible(true); + templateFields.add(new TemplateMember<>(template.value(), template, f)); + } + + SourceFile source = f.getAnnotation(SourceFile.class); + if (source != null) { + if (!Template.class.isAssignableFrom(f.getType()) && !String.class.isAssignableFrom(f.getType())) + throw new AssertionError(String.format("Found @SourceFile on field %s whose type is not Template or String", f)); + f.setAccessible(true); + sourceFields.add(new TemplateMember<>(source.value(), source, f)); + groups.add(source.group()); + } + + DimensionVar dim = f.getAnnotation(DimensionVar.class); + if (dim != null) + tmpDimFields.put(dim, f); + } + + for (Method m : clazz.getDeclaredMethods()) { + TemplateVar template = m.getAnnotation(TemplateVar.class); + if (template != null) { + if (!Template.class.isAssignableFrom(m.getReturnType()) && !String.class.isAssignableFrom(m.getReturnType())) + throw new AssertionError(String.format("Found @TemplateVar on method %s whose return type is not Template or String", m)); + m.setAccessible(true); + templateMethods.add(new TemplateMember<>(template.value(), template, m)); + } + + SourceFile source = m.getAnnotation(SourceFile.class); + if (source != null) { + if (!Template.class.isAssignableFrom(m.getReturnType()) && !String.class.isAssignableFrom(m.getReturnType())) + throw new AssertionError(String.format("Found @SourceFile on method %s whose return type is not Template or String ", m)); + sourceMethods.add(new TemplateMember<>(source.value(), source, m)); + groups.add(source.group()); + } + } + + if (constructors.length > 1) + throw new AssertionError(String.format("Multiple constructors for class %s", clazz)); + else if (ctor != null && !tmpDimFields.isEmpty()) + throw new AssertionError(String.format("Class %s has both constructors and fields with @DimensionVar", clazz)); + else if (constructors.length == 0 && tmpDimFields.isEmpty()) + throw new AssertionError(String.format("Class %s has neither constructors nor fields with @DimensionVar", clazz)); + else if (ctor != null) { + Class<?>[] argTypes = ctor.getParameterTypes(); + Annotation[][] paramAnnotations = ctor.getParameterAnnotations(); + dimensions = new Dimension[argTypes.length]; + for (int i = 0, argTypesLength = argTypes.length; i < argTypesLength; i++) { + DimensionVar cd = null; + for (Annotation a : paramAnnotations[i]) + if (a instanceof DimensionVar) { + cd = (DimensionVar) a; + break; + } + if (cd == null) + throw new AssertionError(String.format("Parameter %d of constructor %s does not have @DimensionVar annotation", i, ctor)); + dimensions[i] = new Dimension(cd, argTypes[i], String.format("Parameter %d of constructor %s", i, ctor)); + } + } + else { + dimensions = new Dimension[tmpDimFields.size()]; + int i = 0; + for (Map.Entry<DimensionVar, Field> e : tmpDimFields.entrySet()) + dimensions[i++] = new Dimension(e.getKey(), e.getValue()); + } + } + + public T makeInstance(Object[] args) throws ReflectiveOperationException { + if (ctor != null) + return ctor.newInstance(args); + else { + T t = clazz.newInstance(); + for (int i=0; i< dimensions.length; i++) + dimensions[i].field.set(t, args[i]); + return t; + } + } + + public SyntheticLoop makeLoop() { + int n = dimensions.length; + Object[][] dimValues = new Object[n][]; + for (int i=0; i<n; i++) + dimValues[i] = dimensions[i].values; + return new SyntheticLoop(dimValues); + } + + static class Dimension { + public final Object[] values; + public final String name; + public final Field field; + + public Dimension(DimensionVar dv, Field f) throws ReflectiveOperationException { + field = f; + field.setAccessible(true); + name = dv.value(); + values = getValues(dv, f.getType(), String.format("Field %s", f.getName())); + } + + public Dimension(DimensionVar dv, Class<?> paramType, String descr) throws ReflectiveOperationException { + field = null; + name = dv.value(); + values = getValues(dv, paramType, descr); + } + + private Object[] getValues(DimensionVar dv, Class<?> clazz, String descr) throws ReflectiveOperationException { + if (Enum.class.isAssignableFrom(clazz) && Template.class.isAssignableFrom(clazz)) { + Method m = clazz.getDeclaredMethod("values"); + m.setAccessible(true); + return (Template[]) m.invoke(null); + } + else if (int.class.isAssignableFrom(clazz)) { + Object[] ts = new Object[dv.rangeUpper() - dv.rangeLower() + 1]; + for (int i=dv.rangeLower(); i <= dv.rangeUpper(); i++) + ts[i-dv.rangeLower()] = i; + return ts; + } + else + throw new AssertionError(String.format("Invalid type %s for %s", clazz.getName(), descr)); + } + } + + static class TemplateMember<A extends Annotation, M extends Member> { + public final String name; + public final A annotation; + public final M member; + + TemplateMember(String name, A annotation, M member) { + this.name = name; + this.annotation = annotation; + this.member = member; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/Diagnostics.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.util.ArrayList; +import java.util.List; + +/** +* Diagnostics + * + * @author Brian Goetz +*/ +public class Diagnostics implements javax.tools.DiagnosticListener<JavaFileObject> { + + protected List<String> errors = new ArrayList<>(); + protected List<String> warnings = new ArrayList<>(); + + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { + switch (diagnostic.getKind()) { + case ERROR: + errors.add(diagnostic.getCode()); + break; + + case WARNING: + case MANDATORY_WARNING: + warnings.add(diagnostic.getCode()); + break; + } + } + + public boolean errorsFound() { + return !errors.isEmpty(); + } + + public int getErrorCount() { + return errors.size(); + } + + public List<String> errors() { + return errors; + } + + public String toString() { return errors.toString(); } + + public void reset() { + errors.clear(); + warnings.clear(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/DimensionVar.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** +* DimensionVar +*/ +@Retention(RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({ElementType.PARAMETER, ElementType.FIELD}) +public @interface DimensionVar { + String value(); + int rangeLower() default 0; + int rangeUpper() default 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/JavacTemplateTestBase.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.util.Pair; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.BeforeMethod; + +import javax.tools.*; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.testng.Assert.fail; + +/** + * JavacTestBase -- base class for template-driven javac tests + * + * @author Brian Goetz + */ +public abstract class JavacTemplateTestBase { + private static final Set<String> suiteErrors = Collections.synchronizedSet(new HashSet<String>()); + private static final AtomicInteger counter = new AtomicInteger(); + private static final File root = new File(System.getProperty("user.dir"), "gen"); + private static final File nullDir = new File(System.getProperty("user.dir"), "empty"); + + protected final Map<String, Template> templates = new HashMap<>(); + protected final Diagnostics diags = new Diagnostics(); + protected final List<Pair<String, Template>> sourceFiles = new ArrayList<>(); + protected final List<String> compileOptions = new ArrayList<>(); + protected final List<File> classpaths = new ArrayList<>(); + protected final Template.Resolver defaultResolver = new MapResolver(templates); + + private Template.Resolver currentResolver = defaultResolver; + + protected void addTemplate(String name, Template t) { + templates.put(name, t); + } + + protected void addTemplate(String name, String s) { + templates.put(name, new StringTemplate(s)); + } + + protected void addSourceFile(String name, Template t) { + sourceFiles.add(new Pair<>(name, t)); + } + + protected void addClassPath(File path) { + classpaths.add(path); + } + + /** + * Add a set of compilation command-line options + */ + protected void addCompileOptions(String... opts) { + Collections.addAll(compileOptions, opts); + } + + protected void resetCompileOptions() { compileOptions.clear(); } + protected void resetTemplates() { templates.clear(); } + protected void resetDiagnostics() { diags.reset(); } + protected void resetSourceFiles() { sourceFiles.clear(); } + protected void resetClassPaths() { classpaths.clear(); } + + @BeforeMethod + public void reset() { + resetCompileOptions(); + resetDiagnostics(); + resetSourceFiles(); + resetTemplates(); + resetClassPaths(); + } + + @AfterMethod + public void copyErrors(ITestResult result) { + if (!result.isSuccess()) { + suiteErrors.addAll(diags.errors()); + + List<Object> list = new ArrayList<>(); + Collections.addAll(list, result.getParameters()); + list.add("Test case: " + getTestCaseDescription()); + for (Pair<String, Template> e : sourceFiles) + list.add("Source file " + e.fst + ": " + e.snd); + if (diags.getErrorCount() != 0) + list.add("Compile diagnostics: " + diags.toString()); + result.setParameters(list.toArray(new Object[list.size()])); + } + } + + @AfterSuite + public void dumpErrors() { + if (!suiteErrors.isEmpty()) + System.err.println("Errors found in test suite: " + suiteErrors); + } + + protected String getTestCaseDescription() { + return this.toString(); + } + + protected void assertCompileSucceeded() { + if (diags.errorsFound()) + fail("Expected successful compilation"); + } + + protected void assertCompileSucceededIff(boolean b) { + if (b) + assertCompileSucceeded(); + else + assertCompileFailed(); + } + + protected void assertCompileFailed() { + if (!diags.errorsFound()) + fail("Expected failed compilation"); + } + + protected void assertCompileFailed(String message) { + if (!diags.errorsFound()) + fail("Expected failed compilation: " + message); + } + + protected void assertCompileErrors(String... keys) { + if (!diags.errorsFound()) + fail("Expected failed compilation"); + for (String k : keys) + if (!diags.errors().contains(k)) + fail("Expected compilation error " + k); + } + + protected Template asTemplate(Object o) { + if (o instanceof Template) + return (Template) o; + else if (o instanceof String) + return new StringTemplate((String) o); + else + return new StringTemplate(o.toString()); + } + + protected void compile() throws IOException { + compile(false); + } + + protected File compile(boolean generate) throws IOException { + List<JavaFileObject> files = new ArrayList<>(); + for (Pair<String, Template> e : sourceFiles) + files.add(new FileAdapter(e.fst, asTemplate(e.snd))); + return compile(classpaths, files, generate); + } + + protected File compile(List<File> classpaths, boolean generate) throws IOException { + List<JavaFileObject> files = new ArrayList<>(); + for (Pair<String, Template> e : sourceFiles) + files.add(new FileAdapter(e.fst, asTemplate(e.snd))); + return compile(classpaths, files, generate); + } + + private File compile(List<File> classpaths, List<JavaFileObject> files, boolean generate) throws IOException { + JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fm = systemJavaCompiler.getStandardFileManager(null, null, null); + if (classpaths.size() > 0) + fm.setLocation(StandardLocation.CLASS_PATH, classpaths); + JavacTask ct = (JavacTask) systemJavaCompiler.getTask(null, fm, diags, compileOptions, null, files); + if (generate) { + File destDir = new File(root, Integer.toString(counter.incrementAndGet())); + // @@@ Assert that this directory didn't exist, or start counter at max+1 + destDir.mkdirs(); + fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir)); + ct.generate(); + return destDir; + } + else { + ct.analyze(); + return nullDir; + } + } + + protected Class<?> loadClass(String className, File... destDirs) { + try { + List<URL> list = new ArrayList<>(); + for (File f : destDirs) + list.add(new URL("file:" + f.toString().replace("\\", "/") + "/")); + return Class.forName(className, true, new URLClassLoader(list.toArray(new URL[list.size()]))); + } catch (ClassNotFoundException | MalformedURLException e) { + throw new RuntimeException("Error loading class " + className, e); + } + } + + protected class StringTemplate implements Template { + protected final String template; + + public StringTemplate(String template) { + this.template = template; + } + + public String expand(String selector) { + return Behavior.expandTemplate(template, currentResolver); + } + + public String toString() { + return expand(""); + } + + public StringTemplate with(final String key, final String value) { + return new StringTemplateWithResolver(template, new KeyResolver(key, value)); + } + + } + + protected class StringTemplateWithResolver extends StringTemplate { + private final Resolver localResolver; + + public StringTemplateWithResolver(String template, Resolver localResolver) { + super(template); + this.localResolver = localResolver; + } + + @Override + public String expand(String selector) { + Resolver saved = currentResolver; + currentResolver = new ChainedResolver(currentResolver, localResolver); + try { + return super.expand(selector); + } + finally { + currentResolver = saved; + } + } + + @Override + public StringTemplate with(String key, String value) { + return new StringTemplateWithResolver(template, new ChainedResolver(localResolver, new KeyResolver(key, value))); + } + } + + private class KeyResolver implements Template.Resolver { + private final String key; + private final String value; + + public KeyResolver(String key, String value) { + this.key = key; + this.value = value; + } + + @Override + public Template lookup(String k) { + return key.equals(k) ? new StringTemplate(value) : null; + } + } + + protected class FileAdapter extends SimpleJavaFileObject { + private final String filename; + private final Template template; + + public FileAdapter(String filename, Template template) { + super(URI.create("myfo:/" + filename), Kind.SOURCE); + this.template = template; + this.filename = filename; + } + + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return toString(); + } + + public String toString() { + return Template.Behavior.expandTemplate(template.expand(filename), defaultResolver); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SeparateCompilationComboTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import org.testng.annotations.Factory; + +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; + +/** + * SeparateCompilationComboTest + */ +public class SeparateCompilationComboTest extends ComboTestBase<SeparateCompilationComboTest> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(SeparateCompilationComboTest.class); + } + + @DimensionVar("FOO") Dummy dummy; + + @SourceFile(value="Main.java", group="B") String a2 = "public class Main { public String main() { return Other.b(); } }"; + @SourceFile(value="Other.java", group="B") String b2 = "public class Other { public static String b() { return \"1\"; } }"; + @SourceFile(value="Other.java", group="A") String b1 = "public class Other { public static String b() { return \"2\"; } }"; + + @Override + protected boolean shouldRun() { + return true; + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Object obj = clazz.newInstance(); + Method m = clazz.getMethod("main"); + String result = (String) m.invoke(obj); + assertEquals("2", result); + } + + static enum Dummy implements Template { + A(); + + public String expand(String selector) { return toString(); } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SeparateCompilationComboTest2.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import org.testng.annotations.Factory; + +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; + +/** + * SeparateCompilationComboTest2 + */ +public class SeparateCompilationComboTest2 extends ComboTestBase<SeparateCompilationComboTest2> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(SeparateCompilationComboTest2.class); + } + + @DimensionVar("FOO") Dummy dummy; + + @SourceFile(value="Main.java", group="A" ) + String main = "public class Main { public String main() { return new B().x(); } }"; + @SourceFile(value="B.java", group="B" ) + String b = "public class B extends C { public String x() { return \"2\"; } }"; + @SourceFile(value="C.java", group="C" ) + String c = "public class C { public String x() { return \"1\"; } }"; + + @Override + protected boolean shouldRun() { + return true; + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Object obj = clazz.newInstance(); + Method m = clazz.getMethod("main"); + String result = (String) m.invoke(obj); + assertEquals("2", result); + } + + static enum Dummy implements Template { + A(); + + public String expand(String selector) { return toString(); } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SeparateCompilationTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * SeparateCompilationTest -- test of test framework to do separate compilation + */ +@Test +public class SeparateCompilationTest extends JavacTemplateTestBase { + + public void testCompletelySeparate() throws ReflectiveOperationException, IOException { + addSourceFile("A.java", new StringTemplate("public class A { public String a() throws Throwable { return Class.forName(\"B\").getName(); } }")); + File aFile = compile(true); + assertCompileSucceeded(); + reset(); + + addSourceFile("B.java", new StringTemplate("public class B { }")); + File bFile = compile(true); + assertCompileSucceeded(); + + Class clazz = loadClass("A", aFile, bFile); + Method m = clazz.getMethod("a"); + Object instance = clazz.newInstance(); + String x = (String) m.invoke(instance); + assertEquals("B", x); + } + + public void testIdenticalRecompile() throws ReflectiveOperationException, IOException { + addSourceFile("A.java", new StringTemplate("public class A { public String a() { return B.b(); } }")); + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"1\"; } }")); + File aFile = compile(true); + assertCompileSucceeded(); + // Deleting B.class is not generally needed for test cases that simply replace B, but here we're testing the framework's ability to find B elsewhere + File bClass = new File(aFile, "B.class"); + assertTrue(bClass.exists()); + boolean deleted = bClass.delete(); + assertTrue(deleted); + reset(); + + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"1\"; } }")); + File bFile = compile(true); + assertCompileSucceeded(); + + Class clazz = loadClass("A", bFile, aFile); + Method m = clazz.getMethod("a"); + Object instance = clazz.newInstance(); + String x = (String) m.invoke(instance); + assertEquals("1", x); + } + + public void testConsistentRecompile() throws ReflectiveOperationException, IOException { + addSourceFile("A.java", new StringTemplate("public class A { public String a() { return B.b(); } }")); + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"1\"; } }")); + File aFile = compile(true); + assertCompileSucceeded(); + reset(); + + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"2\"; } }")); + File bFile = compile(true); + assertCompileSucceeded(); + + Class clazz = loadClass("A", bFile, aFile); + Method m = clazz.getMethod("a"); + Object instance = clazz.newInstance(); + String x = (String) m.invoke(instance); + assertEquals("2", x); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SourceFile.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * SourceFile + */ +@Retention(RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({ElementType.METHOD, ElementType.FIELD}) +public @interface SourceFile { + String value(); + String group() default ""; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/StackProcessingUtils.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, 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. + */ + +package tools.javac.combo; + +/** + * Utility class for defining stack processing-like operations + * + * @author Maurizio Cimadamore + */ +public class StackProcessingUtils { + + /** + * interface representing a stack item - each item has an arity --- + * if arity is > 0 - then the item is an operator + */ + public interface StackItem<T extends StackItem<T>> { + int arity(); + } + + /** + * A reducer takes an element of the stack and produces a result of a given + * type by applying the stack item to zero or more operands + */ + public interface StackReducer<I extends StackItem<I>, T, O> { + T reduce(I item, T[] operands, O o); + Class<T> resultToken(); + O reducerArg(I item, int i); + } + + static class StackResult<T> { + T result; + int nextPos; + + StackResult(T result, int nextPos) { + this.result = result; + this.nextPos = nextPos; + } + } + + public static <I extends StackItem<I>, T, O> T process(I[] elems, StackReducer<I, T, O> reducer, O o) { + StackResult<T> result = processInternal(0, elems, reducer, o); + assert result.nextPos == elems.length; + return result.result; + } + + private static <I extends StackItem<I>, T, O> StackResult<T> processInternal(int start, I[] elems, StackReducer<I, T, O> reducer, O o) { + @SuppressWarnings("unchecked") + I head = elems[start]; + T[] operands = (T[])java.lang.reflect.Array.newInstance(reducer.resultToken(), head.arity()); + int nextPos = start + 1; + for (int i = 0 ; i < head.arity() ; i++) { + StackResult<T> partialResult = processInternal(nextPos, elems, reducer, reducer.reducerArg(head, i)); + nextPos = partialResult.nextPos; + operands[i] = partialResult.result; + } + return new StackResult<T>(reducer.reduce(head, operands, o), nextPos); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/StringTemplateTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * StringTemplateTest + */ +@Test +public class StringTemplateTest extends JavacTemplateTestBase { + private void assertTemplate(String expected, Template template) { + String result = template.expand(""); + assertEquals(result, expected, "for " + template); + } + + public void testStringTemplate() { + assertTemplate("XYZ", new StringTemplate("XYZ")); + addTemplate("NONE", "SOME"); + assertTemplate("SOME", new StringTemplate("#{NONE}")); + + addTemplate("A", "#{B}"); + addTemplate("B", "C"); + assertTemplate("C", new StringTemplate("#{A}")); + } + + public void testChainedTemplate() { + assertTemplate("A", new StringTemplate("#{A}").with("A", "A")); + assertTemplate("AB", new StringTemplate("#{A}#{B}").with("A", "A").with("B", "B")); + assertTemplate("TWO", new StringTemplate("#{A}").with("A", "ONE").with("A", "TWO")); + addTemplate("A", "BASE_A"); + addTemplate("AA", "#{A}"); + assertTemplate("FAKE_A", new StringTemplate("#{A}").with("A", "FAKE_A")); + assertTemplate("BASE_A", new StringTemplate("#{AA}")); + assertTemplate("FAKE_A", new StringTemplate("#{AA}").with("A", "FAKE_A")); + + addTemplate("X", new StringTemplate("#{A}").with("A", "intercepted")); + assertTemplate("intercepted", new StringTemplate("#{B}").with("B", "#{X}")); + addTemplate("Y", new StringTemplate("#{A}")); + assertTemplate("again", new StringTemplate("#{B}").with("B", "#{Y}").with("A", "again")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SyntheticLoop.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.util.Iterator; + +/** + * SyntheticLoop -- exhaustively generate a set of combinations of variables + * + * @author Brian Goetz + */ +public class SyntheticLoop implements Iterable<Object[]> { + final int dimensions; + final int[] maxs; + final Object[][] values; + + /** Simple version -- generate the cartesian product of the provided sets of axis values */ + public SyntheticLoop(Object[][] dimValues) { + dimensions = dimValues.length; + maxs = new int[dimensions]; + values = new Object[dimensions][]; + for (int i=0; i<dimensions; i++) { + values[i] = dimValues[i]; + maxs[i] = values[i].length; + } + } + +// /** Complex version -- allow the contents of some dimensions to depend on previous dimensions, such +// * as generating tuples (a,b) where foo(b) <= foo(a). Of the two arrays provided, for each index i, +// * exactly one of dimValues[i], dimGenerators[i] must be non-null. +// */ +// public SyntheticLoop(Object[][] dimValues, DimensionFactory[] dimGenerators) { +// @@@NYI +// } + + public Iterator<Object[]> iterator() { + return new Iterator<Object[]>() { + final int[] idxs = new int[dimensions]; + boolean hasNext = (dimensions > 0); + + public boolean hasNext() { + return hasNext; + } + + public Object[] next() { + Object[] ret = new Object[dimensions]; + for (int i=0; i<dimensions; i++) + ret[i] = values[i][idxs[i]]; + hasNext = !advance(dimensions-1); + return ret; + } + + public void remove() { throw new UnsupportedOperationException(); } + + private boolean advance(int i) { + if (i < 0) + return true; + if (++idxs[i] == maxs[i]) { + idxs[i] = 0; + return advance(i - 1); + } + return false; + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SyntheticLoopTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import org.testng.annotations.Test; + +import java.util.Iterator; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * SyntheticLoopTest + */ +@Test +public class SyntheticLoopTest { + public void testEmpty() { + SyntheticLoop sl = new SyntheticLoop(new Object[0][]); + Iterator<Object[]> it = sl.iterator(); + assertTrue(!it.hasNext()); + } + + private void assertSL(String expected, SyntheticLoop sl) { + StringBuilder sb = new StringBuilder(); + for (Object[] row : sl) { + for (Object o : row) { + sb.append(o); + } + sb.append(" "); + } + assertEquals(sb.toString(), expected); + } + + private void assertSL(int size, SyntheticLoop sl) { + int count = 0; + for (Object row : sl) { + ++count; + } + assertEquals(count, size); + } + + public void testSimple() { + Object[] abc = {"A", "B", "C"}; + assertSL("A B C ", new SyntheticLoop(new Object[][] {abc})); + assertSL("AA AB AC BA BB BC CA CB CC ", new SyntheticLoop(new Object[][] {abc, abc})); + assertSL(9, new SyntheticLoop(new Object[][] {abc, abc})); + assertSL(9, new SyntheticLoop(new Object[][] {abc, abc, new Object[] { "X" }})); + assertSL(27, new SyntheticLoop(new Object[][] {abc, abc, abc})); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/Template.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** +* Template +*/ +public interface Template { + String expand(String selector); + + interface Resolver { + public Template lookup(String key); + } + + public static class Behavior { + private static final Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z0-9_]*))?\\}"); + + public static String expandTemplate(String template, final Map<String, Template> vars) { + return expandTemplate(template, new MapResolver(vars)); + } + + public static String expandTemplate(String template, Resolver res) { + CharSequence in = template; + StringBuffer out = new StringBuffer(); + while (true) { + boolean more = false; + Matcher m = pattern.matcher(in); + while (m.find()) { + String major = m.group(1); + String minor = m.group(2); + Template key = res.lookup(major); + if (key == null) + throw new IllegalStateException("Unknown major key " + major); + + String replacement = key.expand(minor == null ? "" : minor); + more |= pattern.matcher(replacement).find(); + m.appendReplacement(out, replacement); + } + m.appendTail(out); + if (!more) + return out.toString(); + else { + in = out; + out = new StringBuffer(); + } + } + } + + } +} + +class MapResolver implements Template.Resolver { + private final Map<String, Template> vars; + + public MapResolver(Map<String, Template> vars) {this.vars = vars;} + + public Template lookup(String key) { + return vars.get(key); + } +} + +class ChainedResolver implements Template.Resolver { + private final Template.Resolver upstreamResolver, thisResolver; + + public ChainedResolver(Template.Resolver upstreamResolver, Template.Resolver thisResolver) { + this.upstreamResolver = upstreamResolver; + this.thisResolver = thisResolver; + } + + public Template.Resolver getUpstreamResolver() { + return upstreamResolver; + } + + @Override + public Template lookup(String key) { + Template result = thisResolver.lookup(key); + if (result == null) + result = upstreamResolver.lookup(key); + return result; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/TemplateTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.testng.Assert.assertEquals; + +/** + * TemplateTest + */ +@Test +public class TemplateTest { + Map<String, Template> vars = new HashMap<>(); + + @BeforeTest + void before() { vars.clear(); } + + private void assertTemplate(String expected, String template) { + String result = Template.Behavior.expandTemplate(template, vars); + assertEquals(result, expected, "for " + template); + } + + private String dotIf(String s) { + return s == null || s.isEmpty() ? "" : "." + s; + } + + public void testTemplateExpansion() { + vars.put("A", s -> "a" + dotIf(s)); + vars.put("B", s -> "b" + dotIf(s)); + vars.put("C", s -> "#{A}#{B}"); + vars.put("D", s -> "#{A" + dotIf(s) + "}#{B" + dotIf(s) + "}"); + vars.put("_D", s -> "d"); + + assertTemplate("", ""); + assertTemplate("foo", "foo"); + assertTemplate("a", "#{A}"); + assertTemplate("a", "#{A.}"); + assertTemplate("a.FOO", "#{A.FOO}"); + assertTemplate("aa", "#{A}#{A}"); + assertTemplate("ab", "#{C}"); + assertTemplate("ab", "#{C.FOO}"); + assertTemplate("ab", "#{C.}"); + assertTemplate("a.FOOb.FOO", "#{D.FOO}"); + assertTemplate("ab", "#{D}"); + assertTemplate("d", "#{_D}"); + assertTemplate("#{A", "#{A"); + } + + public void testIndexedTemplate() { + vars.put("A[0]", s -> "a" ); + vars.put("A[1]", s -> "b" ); + vars.put("A[2]", s -> "c" ); + vars.put("X", s -> "0"); + assertTemplate("a", "#{A[0]}"); + assertTemplate("b", "#{A[1]}"); + assertTemplate("c", "#{A[2]}"); + } + + public void testAngleBrackets() { + vars.put("X", s -> "xyz"); + assertTemplate("List<String> ls = xyz;", "List<String> ls = #{X};"); + } + + @Test(expectedExceptions = IllegalStateException.class ) + public void testUnknownKey() { + assertTemplate("#{Q}", "#{Q}"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/TemplateVar.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.combo; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * ComboTemplate + */ +@Retention(RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({ElementType.METHOD, ElementType.FIELD}) +public @interface TemplateVar { + String value(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/ConditionalExpressionTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; +import tools.javac.combo.TemplateVar; + +/** + * ConditionalExpressionTest + */ +public class ConditionalExpressionTest extends ComboTestBase<ConditionalExpressionTest> { + @Factory + public static Object[] testConditionalExpression() throws Exception { + return factory(ConditionalExpressionTest.class); + } + + @DimensionVar("TYPE1") Type type1; + @DimensionVar("TYPE2") Type type2; + @DimensionVar("TARGET") Type target; + @DimensionVar("STMT") Statement stmt; + + @SourceFile("Main.java") + String clientFile = "public class Main {\n" + + " #{STMT_DECL};\n" + + " #{DECL.1};\n" + + " #{DECL.2};\n" + + " void test(boolean cond) { \n" + + " #{STMT};" + + " }\n" + +"}"; + + @Override + protected void postCompile(String grp) { + CondResult res = Type.condType(type1, type2); + if (!res.check(type1, type2, target)) { + assertCompileFailed(); + } else { + assertCompileSucceeded(); + } + } + + @Override + protected boolean shouldSkip() { + return target == Type.INT_CONST; + } + + interface CondResult { + boolean check(Type op1, Type op2, Type target); + } + + static CondResult ERR = new CondResult() { + public boolean check(Type op1, Type op2, Type target) { return false; } + }; + + static CondResult TARGET = new CondResult() { + public boolean check(Type op1, Type op2, Type target) { + return op1.compatibleWith(target) && op2.compatibleWith(target); + } + }; + + enum TypeKind { + NUMERIC, + BOOLEAN, + OTHER; + } + + enum Type implements Template, CondResult { + BYTE("byte", "0", TypeKind.NUMERIC), + SHORT("short", "0", TypeKind.NUMERIC), + INT("int", "0", TypeKind.NUMERIC), + INT_CONST("int", "0", TypeKind.NUMERIC), + LONG("long", "0", TypeKind.NUMERIC), + FLOAT("float", "0", TypeKind.NUMERIC), + DOUBLE("double", "0", TypeKind.NUMERIC), + CHAR("char", "'0'", TypeKind.NUMERIC), + BOOLEAN("boolean", "false", TypeKind.BOOLEAN), + J_L_BYTE("Byte", "null", TypeKind.NUMERIC), + J_L_SHORT("Short", "null", TypeKind.NUMERIC), + J_L_INT("Integer", "null", TypeKind.NUMERIC), + J_L_LONG("Long", "null", TypeKind.NUMERIC), + J_L_FLOAT("Float", "null", TypeKind.NUMERIC), + J_L_DOUBLE("Double", "null", TypeKind.NUMERIC), + J_L_CHAR("Character", "null", TypeKind.NUMERIC), + J_L_BOOLEAN("Boolean", "null", TypeKind.BOOLEAN), + NUMBER("Number", "null", TypeKind.OTHER), + SERIAL("java.io.Serializable", "null", TypeKind.OTHER); + + public boolean check(Type op1, Type op2, Type target) { + return compatibleWith(target); + } + + public boolean isErroneous() { + return false; + } + + public String expand(String qualifier) { + return typeStr; + } + + boolean compatibleWith(Type that) { + return that.anyOf(compatibleWith[this.ordinal()]); + } + + boolean anyOf(Type... ts) { + for (Type t : ts) { + if (t == this) return true; + } + return false; + } + + Type unbox() { + switch (this) { + case BYTE: + case J_L_BYTE: return BYTE; + case SHORT: + case J_L_SHORT: return SHORT; + case INT: + case INT_CONST: + case J_L_INT: return INT; + case LONG: + case J_L_LONG: return LONG; + case FLOAT: + case J_L_FLOAT: return FLOAT; + case DOUBLE: + case J_L_DOUBLE: return DOUBLE; + case BOOLEAN: + case J_L_BOOLEAN: return BOOLEAN; + case CHAR: + case J_L_CHAR: return CHAR; + default: + return null; + } + } + + String typeStr; + String defaultVal; + TypeKind kind; + + Type(String typeStr, String defaultVal, TypeKind kind) { + this.typeStr = typeStr; + this.defaultVal = defaultVal; + this.kind = kind; + } + + static CondResult condType(Type type1, Type type2) { + if (type1.kind == TypeKind.BOOLEAN && type2.kind == TypeKind.BOOLEAN) { + //boolean conditional + return BOOLEAN; + } else if (type1.kind == TypeKind.NUMERIC && type2.kind == TypeKind.NUMERIC) { + //numeric conditional + Type unbox1 = type1.unbox(); + Type unbox2 = type2.unbox(); + if (type1 == INT_CONST && + unbox2.anyOf(CHAR, BYTE, SHORT)) { + return unbox2; + } + if (type2 == INT_CONST && + unbox1.anyOf(CHAR, BYTE, SHORT)) { + return unbox1; + } + if (unbox1 == unbox2) { + return unbox1; + } + if (unbox1 == CHAR) { + return condType(INT, unbox2); + } + if (unbox2 == CHAR) { + return condType(unbox1, INT); + } + if (unbox1.compatibleWith(unbox2)) { + return unbox2; + } else if (unbox2.compatibleWith(unbox1)) { + return unbox1; + } else { + return ERR; + } + } else { + //reference conditional + return TARGET; + } + } + + static Type[][] compatibleWith = new Type[][] { + /* BYTE */ { BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, J_L_BYTE, NUMBER, SERIAL }, + /* SHORT */ { SHORT, INT, LONG, FLOAT, DOUBLE, J_L_SHORT, NUMBER, SERIAL }, + /* INT */ { INT, LONG, FLOAT, DOUBLE, J_L_INT, NUMBER, SERIAL }, + /* INT_CONST */ { INT, LONG, FLOAT, DOUBLE, J_L_INT, NUMBER, SERIAL }, + /* LONG */ { LONG, FLOAT, DOUBLE, J_L_LONG, NUMBER, SERIAL }, + /* FLOAT */ { FLOAT, DOUBLE, J_L_FLOAT, NUMBER, SERIAL }, + /* DOUBLE */ { DOUBLE, J_L_DOUBLE, NUMBER, SERIAL }, + /* CHAR */ { CHAR, INT, LONG, FLOAT, DOUBLE, J_L_CHAR, SERIAL }, + /* BOOLEAN */ { BOOLEAN, J_L_BOOLEAN, SERIAL }, + /* J_L_BYTE */ { J_L_BYTE, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_SHORT */ { J_L_SHORT, SHORT, INT, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_INT */ { J_L_INT, INT, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_LONG */ { J_L_LONG, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_FLOAT */ { J_L_FLOAT, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_DOUBLE */ { J_L_DOUBLE, DOUBLE, NUMBER, SERIAL }, + /* J_L_CHAR */ { J_L_CHAR, CHAR, INT, LONG, FLOAT, DOUBLE, SERIAL }, + /* J_L_BOOLEAN */ { J_L_BOOLEAN, BOOLEAN, SERIAL }, + /* NUMBER */ { NUMBER, SERIAL }, + /* SERIAL */ { SERIAL } }; + } + + enum Statement implements Template { + ASSIGN("#{TARGET} t = cond ? #{EXPR.1} : #{EXPR.2}"), + METHOD("m(cond ? #{EXPR.1} : #{EXPR.2})"); + + String stmtStr; + + Statement(String stmtStr) { + this.stmtStr = stmtStr; + } + + public String expand(String qual) { + return stmtStr; + } + } + + @TemplateVar("STMT_DECL") + Template stmt_decl = new Template() { + public String expand(String selector) { + return stmt == Statement.ASSIGN ? "" : "void m(#{TARGET} arg) { }"; + } + }; + + @TemplateVar("DECL") + Template decl = new Template() { + public String expand(String selector) { + int sel = Integer.valueOf(selector); + Type t = sel == 1 ? type1 : type2; + return t == Type.INT_CONST ? "" : String.format("#{TYPE%d} m%d() { return %s; }", sel, sel, t.defaultVal); + } + }; + + @TemplateVar("EXPR") + Template expr = new Template() { + public String expand(String selector) { + int sel = Integer.valueOf(selector); + Type t = sel == 1 ? type1 : type2; + return t == Type.INT_CONST ? t.defaultVal : String.format("m%d()", sel); + } + }; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/DefaultMethodAddTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * DefaultMethodAddTest: Test default method by running bytecode compiled at different times + * where conflicting default methods are added or declared + */ +public class DefaultMethodAddTest extends ComboTestBase<DefaultMethodAddTest> { + @Factory + public static Object[] testDefaultMethodAdd() throws Exception { + return factory(DefaultMethodAddTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + + /** + * The separate-compilation operation to be performed, such as adding a default method, + * adding an abstract itnerface method, redeclaring an inherited default method as + * abstract interface method, etc + */ + @DimensionVar("ADD") AddType addType; + + @SourceFile(value="B.java", group="A") + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{ADD} }"; + + @SourceFile(value="B.java", group="B") + String interfaceB = "interface B #{SHAPE.B_DECL} {}"; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { default String m() { return \"A\"; } }"; + + @SourceFile(value="C.java", group="B") + String classC = "#{SHAPE.C}"; + + @SourceFile(value="Main.java", group="B") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = null, output = null; + try { + result = (String) m.invoke(obj); + } catch (InvocationTargetException ex) { + output = ex.getCause().toString(); + } + if(shapeType == CShapes.C_B_A) { + if(addType == AddType.ADD) { + assertEquals(output, null); + assertEquals(result, "B"); + } + else //redeclare + assertEquals(output, "java.lang.AbstractMethodError: Method B.m()Ljava/lang/String; is abstract"); + } + else if(shapeType == CShapes.C_I_AB2) { + assertEquals(output, null); + assertEquals(result, "A"); + } + else if(shapeType == CShapes.C_CI) { + assertEquals(output, null); + assertEquals(result, "D"); + } + else if(shapeType == CShapes.C_I_AB3) { + assertEquals(output, null); + assertEquals(result, "AB"); + } + else + assertTrue(output.matches("java.lang.AbstractMethodError: Conflicting default methods: .+[.]m .+[.]m")); + } + + enum CShapes implements Template { //shapes of class hirarchy + //class C implements interface A, B + C_AB("", + "class C implements A, B {}"), + //class C implments interface B, B extends interface A + C_B_A("extends A", + "class C implements B {}"), + //class C implments interface AB, AB extends interface A, B + 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 { default String m() { 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 { default String m() { 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 sB_DECL; + private final String sC; + + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; + } + + public String expand(String selector) { + switch(selector) { + case "B_DECL": return sB_DECL; + case "C": return sC; + default: return toString(); + } + } + } + + enum AddType implements Template { // add by adding default method or abstract interface method + ADD("default String m() { return \"B\"; }"), + REDECLARE("String m();"); + + final String newCode; + + AddType(String str) { + newCode = str; + } + + public String expand(String selector) { + return newCode; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/DefaultMethodRemoveTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * DefaultMethodRemoveTest: Test default method by running bytecode compiled at different times + * where default methods are removed or redeclared + */ +public class DefaultMethodRemoveTest extends ComboTestBase<DefaultMethodRemoveTest> { + @Factory + public static Object[] testDefalutMethodRemove() throws Exception { + return factory(DefaultMethodRemoveTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + + /** + * The separate-compilation operation to be performed, such as removing a default method, + * redeclaring as abstract interface method, etc + */ + @DimensionVar("REMOVE") RemoveType removeType; + + @SourceFile(value="B.java", group="A") + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{REMOVE} }"; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { default String m() { return \"A\"; } }"; + + @SourceFile(value="B.java", group="B") + String interfaceB = "interface B #{SHAPE.B_DECL} { default String m() { return \"B\"; } }"; + + @SourceFile(value="C.java", group="B") + String classC = "#{SHAPE.C}"; + + @SourceFile(value="Main.java", group="B") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void postCompile(String group) { + if( group.equals("A") && shapeType == CShapes.C_B_AD && removeType == RemoveType.REMOVE ) + assertCompileErrors("compiler.err.types.incompatible.unrelated.defaults"); + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = null, output = null; + try { + result = (String) m.invoke(obj); + } catch (InvocationTargetException ex) { + output = ex.getCause().toString(); + } + if(shapeType == CShapes.C_B_A) { + if(removeType == RemoveType.REMOVE) + assertEquals(result, "A"); + else + assertEquals(output, "java.lang.AbstractMethodError: Method B.m()Ljava/lang/String; is abstract"); + } + else if(shapeType == CShapes.C_CI) + assertEquals(result, "D"); + else if(shapeType == CShapes.C_B_AD) { + if(removeType == RemoveType.REMOVE) + assertEquals(output, "java.lang.AbstractMethodError: C.m()Ljava/lang/String;"); + else + assertEquals(output, "java.lang.AbstractMethodError: Method B.m()Ljava/lang/String; is abstract"); + } + else { //C_B + if( removeType == RemoveType.REMOVE) + assertEquals(output, "java.lang.NoSuchMethodError: C.m()Ljava/lang/String;"); + else + assertEquals(output, "java.lang.AbstractMethodError: C.m()Ljava/lang/String;"); + } + } + + enum CShapes implements Template { //shapes of class hirarchy + //class C implements interface B + C_B("", + "class C implements B {}"), + //class C implments interface B, B extends interface 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", + "interface D { default String m() { 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 sB_DECL; + private final String sC; + + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; + } + + public String expand(String selector) { + switch(selector) { + 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 + REMOVE(""), + REDECLARE("String m();"); + + final String newCode; + + RemoveType(String str) { + newCode = str; + } + + public String expand(String selector) { + return newCode; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaArgAdaptationTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; + +import java.lang.reflect.Method; +import java.util.EnumMap; +import java.util.EnumSet; + +import static org.testng.Assert.assertEquals; + +/** + * LambdaArgAdaptationTest + */ +public class LambdaArgAdaptationTest extends ComboTestBase<LambdaArgAdaptationTest> { + @Factory + public static Object[] testArgAdaptation() throws Exception { + return factory(LambdaArgAdaptationTest.class); + } + + @DimensionVar("FROM") Types fromType; + @DimensionVar("TO") Types toType; + + @SourceFile("Sam.java") + String samFile = "public interface Sam { String m(#{FROM} arg); }"; + + @SourceFile("Main.java") + String clientFile = "public class Main {\n" + +" static String m(#{TO} arg) { \n" + +" return String.valueOf(arg);\n" + +" }\n" + +" public String main() { \n" + + " Sam s = Main::m;\n" + + " return s.m(#{FROM.ARG});\n" + + " }\n" + +"}"; + + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @Override + protected void postCompile(String group) { + if (fromType == Types.VOID || toType == Types.VOID) + assertCompileFailed(); + else if (fromType.pr == PR.REF && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(fromType.clazz)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(Types.boxMap.get(fromType).clazz)); + else if (fromType.pr == PR.REF && toType.pr == PR.PRIM) + assertCompileSucceededIff(fromType.unboxed != null && Types.widenMap.get(fromType.unboxed).contains(toType)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.PRIM) + assertCompileSucceededIff(Types.widenMap.get(fromType).contains(toType)); + else + assertCompileSucceeded(); + } + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = (String) m.invoke(obj); + String formatted = fromType.asObject.toString(); + if (fromType == Types.CHAR || fromType == Types.BOXED_CHAR) { + result = result.replace("A", "65"); + formatted = formatted.replace("A", "65"); + } + assertEquals(result.replace(".0", ""), formatted.replace(".0", "")); + } +} + +enum PR {PRIM, REF} + +enum Types implements Template { + BYTE(PR.PRIM, "byte", byte.class, "(byte) 1", (byte) 1), + SHORT(PR.PRIM, "short", short.class, "(short) 2", (short) 2), + CHAR(PR.PRIM, "char", char.class, "'A'", 'A'), + INT(PR.PRIM, "int", int.class, "3", 3), + LONG(PR.PRIM, "long", long.class, "4L", 4L), + FLOAT(PR.PRIM, "float", float.class, "5.0f", 5.0f), + DOUBLE(PR.PRIM, "double", double.class, "6.0", 6.0), + BOOLEAN(PR.PRIM, "boolean", boolean.class, "true", true), + VOID(PR.PRIM, "void", void.class, "null", null), + BOXED_BYTE(PR.REF, "Byte", Byte.class, "Byte.valueOf((byte) 1)", (byte) 1, BYTE), + BOXED_SHORT(PR.REF, "Short", Short.class, "Short.valueOf((short) 2)", (short) 2, SHORT), + BOXED_CHAR(PR.REF, "Character", Character.class, "Character.valueOf('A')", 'A', CHAR), + BOXED_INT(PR.REF, "Integer", Integer.class, "Integer.valueOf(3)", 3, INT), + BOXED_LONG(PR.REF, "Long", Long.class, "Long.valueOf(4)", (long) 4, LONG), + BOXED_FLOAT(PR.REF, "Float", Float.class, "Float.valueOf(5.0f)", 5.0f, FLOAT), + BOXED_DOUBLE(PR.REF, "Double", Double.class, "Double.valueOf(6.0)", 6.0, DOUBLE), + BOXED_BOOLEAN(PR.REF, "Boolean", Boolean.class, "Boolean.valueOf(true)", true, BOOLEAN), + NUMBER(PR.REF, "Number", Number.class, "9", 9), + OBJECT(PR.REF, "Object", Object.class, "10", 10), + STRING(PR.REF, "String", String.class, "\"foo\"", "foo"); + + public static EnumMap<Types, Types> boxMap = new EnumMap<>(Types.class); + public static EnumMap<Types, Types> unboxMap = new EnumMap<>(Types.class); + public static EnumMap<Types, EnumSet<Types>> widenMap = new EnumMap<>(Types.class); + + static { + for (Types t : values()) { + if (t.unboxed != null) { + boxMap.put(t.unboxed, t); + unboxMap.put(t, t.unboxed); + } + } + + widenMap.put(BYTE, EnumSet.of(BYTE, SHORT, INT, LONG, FLOAT, DOUBLE)); + widenMap.put(SHORT, EnumSet.of(SHORT, INT, LONG, FLOAT, DOUBLE)); + widenMap.put(CHAR, EnumSet.of(CHAR, INT, LONG, FLOAT, DOUBLE)); + widenMap.put(INT, EnumSet.of(INT, LONG, FLOAT, DOUBLE)); + widenMap.put(LONG, EnumSet.of(LONG, FLOAT, DOUBLE)); + widenMap.put(FLOAT, EnumSet.of(FLOAT, DOUBLE)); + widenMap.put(DOUBLE, EnumSet.of(DOUBLE)); + widenMap.put(BOOLEAN, EnumSet.of(BOOLEAN)); + } + + final PR pr; + final String name; + final Class<?> clazz; + final String valueString; + final Object asObject; + final Types unboxed; + + Types(PR pr, String name, Class<?> clazz, String valueString, Object asObject) { + this (pr, name, clazz, valueString, asObject, null); + } + + Types(PR pr, String name, Class<?> clazz, String valueString, Object asObject, Types unboxed) { + this.pr = pr; + this.name = name; + this.clazz = clazz; + this.valueString = valueString; + this.asObject = asObject; + this.unboxed = unboxed; + } + + public String expand(String selector) { + switch (selector) { + case "ARG": return valueString; + default: return name; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaCaptureTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import tools.javac.combo.*; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.assertEquals; + +/** + * LambdaCaptureTest + * + * @author Brian Goetz + */ +public class LambdaCaptureTest extends ComboTestBase<LambdaCaptureTest> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(LambdaCaptureTest.class); + } + + final int arity; + final ArgType[] argTypes; + final int capArity; + final CapType[] capTypes; + final boolean shouldSkip; + + public LambdaCaptureTest(@DimensionVar(value = "ARITY", rangeLower = 0, rangeUpper = 2) int arity, + @DimensionVar("ARGTYPE1") ArgType argType1, + @DimensionVar("ARGTYPE2") ArgType argType2, + @DimensionVar(value = "CAP_ARITY", rangeLower = 0, rangeUpper = 2) int capArity, + @DimensionVar("CAPTYPE1") CapType capType1, + @DimensionVar("CAPTYPE2") CapType capType2) { + this.arity = arity; + this.capArity = capArity; + this.argTypes = new ArgType[] { argType1, argType2 }; + this.capTypes = new CapType[] { capType1, capType2 }; + shouldSkip = skipConstrained(arity, argTypes) || skipConstrained(capArity, capTypes); + } + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @SourceFile("Main.java") String test + = "public class Main {\n" + + " #{CAPTYPE1} f0 = #{CAPV.1};\n" + + " #{CAPTYPE2} f1 = #{CAPV.2};\n" + + " interface Target { String m(#{SAM_FORMALS}); }\n" + + " public String convert() { #{CAPTYPE1} loc0 = #{CAPV.1};\n" + + " #{CAPTYPE2} loc1 = #{CAPV.2};\n" + + " Target lambda = (#{LAMBDA_FORMALS}) -> String.format(\"#{FORMAT_STRING}\" #{FORMAT_ARGS});\n" + + " return lambda.m(#{INVOKE_ARGS}); }\n" + + "}"; + + @TemplateVar("LAMBDA_FORMALS") Template formalArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<arity; i++) + list.add(argTypes[i].typeName + " a" + i); + return intersperse(list, ", "); + } + }; + + @TemplateVar("SAM_FORMALS") Template samArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<arity; i++) + list.add(argTypes[i].typeName + " a" + i); + return intersperse(list, ", "); + } + }; + + @TemplateVar("FORMAT_STRING") Template formatString = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<capArity; i++) + list.add(capTypes[i].argType.format); + for (int i=0; i<arity; i++) + list.add(argTypes[i].format); + return intersperse(list, " "); + } + }; + + @TemplateVar("FORMAT_ARGS") Template formatArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<capArity; i++) + list.add((capTypes[i].lf == LF.LOCAL ? "loc" : "f") + i); + for (int i=0; i<arity; i++) + list.add("a" + i); + return ((list.size()) > 0 ? ", " : "") + intersperse(list, ", "); + } + }; + + @TemplateVar("INVOKE_ARGS") Template invokeArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<arity; i++) + list.add(argTypes[i].formatValue(i+capArity)); + return intersperse(list, ", "); + } + }; + + @TemplateVar("CAPV") Template capv = new Template() { + public String expand(String selector) { + int i = Integer.parseInt(selector) - 1; + return capTypes[i].argType.formatValue(i); + } + }; + + private String intersperse(List<String> list, String delim) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String s : list) { + if (!first) + sb.append(delim); + first = false; + sb.append(s); + } + return sb.toString(); + } + + @Override + protected boolean shouldSkip() { + return shouldSkip; + } + + @Override + protected boolean shouldRun() { + return true; + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Object obj = clazz.newInstance(); + Method m = clazz.getMethod("convert"); + Object o = m.invoke(obj); + + List<String> list = new ArrayList<>(); + for (int i=0; i<capArity; i++) + list.add(capTypes[i].argType.formatResult(i)); + for (int i=0; i<arity; i++) + list.add(argTypes[i].formatResult(i+capArity)); + String result = intersperse(list, " "); + assertEquals(result, o); + } + + static enum ArgType implements Template { + LONG("long", "Long", "J%d", null) { + String formatValue(int x) { return String.format("(long) %d", x); } + String formatResult(int x) { return "J" + Integer.toString(x); } + }, + BYTE("byte", "Byte", "B%d", LONG) { + String formatValue(int x) { return String.format("(byte) %d", x); } + String formatResult(int x) { return "B" + Integer.toString(x); } + }, + SHORT("short", "Short", "S%d", LONG) { + String formatValue(int x) { return String.format("(short) %d", x); } + String formatResult(int x) { return "S" + Integer.toString(x); } + }, + CHAR("char", "Character", "C%c", LONG) { + String formatValue(int x) { return String.format("(char) (((int) 'A') + %d)", x); } + String formatResult(int x) { return "C" + (char) (((int) 'A') + x); } + }, + INT("int", "Integer", "I%d", LONG) { + String formatValue(int x) { return String.format("%d", x); } + String formatResult(int x) { return "I" + Integer.toString(x); } + }, + BOOLEAN("boolean", "Boolean", "Z%b", null) { + String formatValue(int x) { return String.format("%d %% 2 != 0", x); } + String formatResult(int x) { return "Z" + (x % 2 != 0); } + }, + DOUBLE("double", "Double", "D%f", null) { + String formatValue(int x) { return String.format("(double) %d", x); } + String formatResult(int x) { return "D" + String.format("%f", (double) x); } + }, + FLOAT("float", "Float", "F%f", DOUBLE) { + String formatValue(int x) { return String.format("(float) %d", x); } + String formatResult(int x) { return "F" + String.format("%f", (float) x); } + }, + OBJECT("Object", "Object", "O%s", null) { + String formatValue(int x) { return String.format("Integer.toString(%d)", x); } + String formatResult(int x) { return "O" + Integer.toString(x); } + }, + STRING("String", "String", "S%s", null) { + String formatValue(int x) { return String.format("Integer.toString(%d)", x); } + String formatResult(int x) { return "S" + Integer.toString(x); } + }; + + final String typeName, boxed, format; + final ArgType widened; + + ArgType(String typeName, String boxed, String format, ArgType widened) { + this.typeName = typeName; + this.boxed = boxed; + this.format = format; + this.widened = widened; + } + + abstract String formatValue(int x); + abstract String formatResult(int x); + + public String expand(String selector) { + return typeName; + } + } + + private static enum LF { LOCAL, FIELD } + + static enum CapType implements Template { + LOCAL_INT(ArgType.INT, LF.LOCAL), + LOCAL_LONG(ArgType.LONG, LF.LOCAL), + LOCAL_STRING(ArgType.STRING, LF.LOCAL), + FIELD_INT(ArgType.INT, LF.FIELD), + FIELD_LONG(ArgType.LONG, LF.FIELD), + FIELD_STRING(ArgType.STRING, LF.FIELD); + + final ArgType argType; + final LF lf; + + CapType(ArgType argType, LF lf) { + this.argType = argType; + this.lf = lf; + } + + public String expand(String selector) { + return argType.typeName; + } + } +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaConversionTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.*; + +/** + * LambdaConversionTestCase + * + * @author Maurizio Cimadamore + */ +public class LambdaConversionTest extends ComboTestBase<LambdaConversionTest> { + + @Factory + public static Object[] testCombo() throws Exception { + return factory(LambdaConversionTest.class); + } + + @DimensionVar("PACKAGE") PackageKind samPkg; + @DimensionVar("MODIFIER") ModifierKind modKind; + @DimensionVar("CLASS") SamKind samKind; + @DimensionVar("METH") MethodKind meth; + @DimensionVar("RET") TypeKind retType; + @DimensionVar("ARG") TypeKind argType; + @DimensionVar("THROWN") TypeKind thrownType; + + @SourceFile("Sam.java") + String samSource = "#{PACKAGE.PACKAGE_DECL} \n #{CLASS}"; + + @SourceFile("PackageClass.java") + String packageSource = "#{PACKAGE.PACKAGE_DECL}\n #{MODIFIER} class PackageClass extends Exception { }"; + + @SourceFile("Client.java") + String clientSource = "#{PACKAGE.IMPORT_STATEMENT}\n class Client { Sam s = x -> null; }"; + + @Override + protected void postCompile(String group) { + if (samKind != SamKind.INTERFACE) { + assertCompileFailed("SAM type must be an interface"); + } else if (meth != MethodKind.NON_GENERIC) { + assertCompileFailed("target method must be non-generic"); + } else if (samPkg != PackageKind.NO_PKG && + modKind != ModifierKind.PUBLIC && + (retType == TypeKind.PKG_CLASS + || argType == TypeKind.PKG_CLASS + || thrownType == TypeKind.PKG_CLASS)) { + assertCompileFailed("target must not contain inaccessible types"); + } else { + assertCompileSucceeded(); + } + } + + static enum PackageKind implements Template { + NO_PKG(""), + PKG_A("a"); + + String pkg; + + PackageKind(String pkg) { + this.pkg = pkg; + } + + public String expand(String selector) { + if (this == NO_PKG) + return ""; + switch (selector) { + case "PACKAGE_DECL": return String.format("package %s;", pkg); + case "IMPORT_STATEMENT": return String.format("import %s.*;", pkg); + default: throw new IllegalArgumentException(selector); + } + } + } + + static enum SamKind implements Template { + CLASS("public class Sam { }"), + ABSTRACT_CLASS("public abstract class Sam { }"), + ANNOTATION("public @interface Sam { }"), + ENUM("public enum Sam { }"), + INTERFACE("public interface Sam { \n #{METH}; \n }"); + + String template; + + SamKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum ModifierKind implements Template { + PUBLIC("public"), + PACKAGE(""); + + String template; + + ModifierKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum TypeKind implements Template { + EXCEPTION("Exception"), + PKG_CLASS("PackageClass"); + + String template; + + private TypeKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum MethodKind implements Template { + NONE(""), + NON_GENERIC("public #{RET} m(#{ARG} s) throws #{THROWN};"), + GENERIC("public <X> #{RET} m(#{ARG} s) throws #{THROWN};"); + + String template; + + private MethodKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaExpressionTypeInferenceTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; + +import static tools.javac.combo.StackProcessingUtils.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * LambdaExpressionTypeInferenceTest + */ +public class LambdaExpressionTypeInferenceTest extends ComboTestBase<LambdaExpressionTypeInferenceTest> { + @Factory + public static Object[] testLambdaExpressionInference() throws Exception { + return factory(LambdaExpressionTypeInferenceTest.class); + } + + @DimensionVar("FORMAL_SHAPE") FormalTypeShape formalShape; + @DimensionVar("TREE_SHAPE") TreeShape treeShape; + @DimensionVar("ARG_SHAPE_1") ArgTypeShape argShape1; + @DimensionVar("ARG_SHAPE_2") ArgTypeShape argShape2; + @DimensionVar("ARG_SHAPE_3") ArgTypeShape argShape3; + + @SourceFile("SAM.java") + String samFile = "public interface SAM<X,Y> {\n" + +" Y apply(X x);\n" + +"}"; + + + @SourceFile("Main.java") + String clientFile = "import java.util.*;\n" + +"public class Main {\n" + +" <A,R> void m(#{FORMAL_SHAPE} x) { }\n" + +" void test() {\n" + +" m(#{TREE_SHAPE});\n" + +" }\n" + +"}"; + + @Override + protected void postCompile(String grp) { + if (treeShape.compute().typeCheck(formalShape.compute(), new Type[] { argShape1.compute(), argShape2.compute(), argShape3.compute() }).isErroneous()) { + assertCompileFailed(); + } else { + assertCompileSucceeded(); + } + } + + @Override + protected boolean shouldSkip() { + return argShape2.ordinal() != 0 && treeShape.depth < 2 || argShape3.ordinal() != 0 && treeShape.depth < 3; + } + + static class Tree { + TreeTag treeTag; + Tree[] subtrees; + + Tree(TreeTag treeTag, Tree... subtrees) { + this.treeTag = treeTag; + this.subtrees = subtrees; + } + + public String toString() { + Object[] substrings = new Object[subtrees.length]; + for (int i = 0; i < subtrees.length; i++) { + substrings[i] = subtrees[i].toString(); + } + return String.format(treeTag.treeStr, substrings); + } + + Type typeCheck(Type target, Type[] argtypes) { + switch (treeTag) { + case INT: + Type type = new Type(TypeToken.INTEGER); + return type.compatibleWith(target) ? type : errType; + case STRING: + type = new Type(TypeToken.STRING); + return type.compatibleWith(target) ? type : errType; + case EXPR_LAMBDA: + Type restype = target.getReturnType(); + if (restype == null) { + return errType; + } + Type argtype = target.getArgType(); + int idx = subtrees[0].treeTag.argIndex(); + if (argtype.isFree() && idx < 0) { + //implicit arg - stuck + return errType; + } + if (idx >= 0 && !argtypes[idx].compatibleWith(argtype)) { + //arg type mismatch + return errType; + } + + for (int i = 1; i < subtrees.length; i++) { + if (subtrees[i].typeCheck(restype, argtypes).isErroneous()) { + return errType; + } + } + return target; + case COND: + Type typeToCheck = target; + if (subtrees[0].treeTag == TreeTag.INT && subtrees[1].treeTag == TreeTag.INT) { + Type owntype = new Type(TypeToken.INTEGER); + return owntype.compatibleWith(target) ? owntype : errType; + } else { + for (int i = 0; i < subtrees.length; i++) { + if (subtrees[i].typeCheck(target, argtypes).isErroneous()) { + return errType; + } + } + return target; + } + default: + throw new AssertionError(); + } + } + } + + enum TreeTag implements StackItem<TreeTag> { + INT("1", 0), + STRING("\"\"", 0), + COND("true ? %s : %s", 2), + EXPR_LAMBDA("(%s)->%s", 2), + IMPLICIT_1("x1", 0), + IMPLICIT_2("x2", 0), + IMPLICIT_3("x3", 0), + EXPLICIT_1("#{ARG_SHAPE_1} x1", 0), + EXPLICIT_2("#{ARG_SHAPE_2} x2", 0), + EXPLICIT_3("#{ARG_SHAPE_3} x3", 0); + + String treeStr; + int arity; + + TreeTag(String treeStr, int arity) { + this.treeStr = treeStr; + this.arity = arity; + } + + public int arity() { + return arity; + } + + Tree apply(Tree... args) { + return new Tree(this, args); + } + + int argIndex() { + switch (this) { + case EXPLICIT_1: + return 0; + case EXPLICIT_2: + return 1; + case EXPLICIT_3: + return 2; + default: + return -1; + } + } + } + + enum TreeShape implements Template, StackReducer<TreeTag, Tree, Void> { + LAMBDA_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.INT), + LAMBDA_STRING(1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.STRING), + LAMBDA_COND_INT_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.INT, TreeTag.INT), + IMPL_LAMBDA_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_1, TreeTag.INT), + IMPL_LAMBDA_STRING(1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_1, TreeTag.STRING), + IMPL_LAMBDA_COND_INT_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_1, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_LAMBDA_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.INT), + LAMBDA_LAMBDA_STRING(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.STRING), + LAMBDA_LAMBDA_COND_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_IMPL_LAMBDA_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.INT), + LAMBDA_IMPL_LAMBDA_STRING(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.STRING), + LAMBDA_IMPL_LAMBDA_COND_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_LAMBDA_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_LAMBDA_STRING_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.STRING, TreeTag.INT), + LAMBDA_COND_LAMBDA_COND_INT_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_IMPL_LAMBDA_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_IMPL_LAMBDA_STRING_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.STRING, TreeTag.INT), + LAMBDA_COND_IMPL_LAMBDA_COND_INT_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT, TreeTag.INT), + LAMBDA_LAMBDA_LAMBDA_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_3, TreeTag.INT), + LAMBDA_LAMBDA_LAMBDA_STRING(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_3, TreeTag.STRING), + LAMBDA_LAMBDA_LAMBDA_COND_INT_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_3, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_LAMBDA_IMPL_LAMBDA_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_3, TreeTag.INT), + LAMBDA_LAMBDA_IMPL_LAMBDA_STRING(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_3, TreeTag.STRING), + LAMBDA_LAMBDA_IMPL_LAMBDA_COND_INT_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_3, TreeTag.COND, TreeTag.INT, TreeTag.INT); + + TreeTag[] treeTags; + int depth; + + TreeShape(int depth, TreeTag... treeTags) { + this.depth = depth; + this.treeTags = treeTags; + } + + Tree compute() { + return process(treeTags, this, null); + } + + public String expand(String selector) { + return compute().toString(); + } + + public Tree reduce(TreeTag tag, Tree[] args, Void _unused) { + return new Tree(tag, args); + } + + public Void reducerArg(TreeTag item, int i) { return null; } + + public Class<Tree> resultToken() { + return Tree.class; + } + } + + static class Type { + TypeToken typeToken; + Type[] typeargs; + + Type(TypeToken typeToken, Type... typeargs) { + this.typeToken = typeToken; + this.typeargs = typeargs; + } + + public String toString() { + Object[] typeargStrings = new Object[typeargs.length]; + for (int i = 0; i < typeargs.length; i++) { + typeargStrings[i] = typeargs[i].toString(); + } + return String.format(typeToken.tokenStr, typeargStrings); + } + + boolean isErroneous() { return false; } + + Type getReturnType() { + return typeToken == TypeToken.SAM ? + typeargs[1] : null; + } + + Type getArgType() { + return typeToken == TypeToken.SAM ? + typeargs[0] : null; + } + + boolean isFree() { + switch (typeToken) { + case A: + case R: + return true; + default: + for (Type t : typeargs) { + if (t.isFree()) return true; + } + return false; + } + } + + boolean isFreeVar() { + switch (typeToken) { + case A: + case R: + return true; + default: + return false; + } + } + + boolean compatibleWith(Type that) { + if (that.isFreeVar()) { + return true; + } else { + if (typeToken != that.typeToken) + return false; + if (typeargs.length != that.typeargs.length) { + return false; + } + for (int i = 0; i < typeargs.length ; i++) { + if (!typeargs[i].compatibleWith(that.typeargs[i])) + return false; + } + return true; + } + } + } + + static Type errType = new Type(null) { + boolean isErroneous() { + return true; + } + }; + + enum TypeToken implements StackItem<TypeToken> { + STRING("String", 0), + INTEGER("Integer", 0), + A("A", 0), + R("R", 0), + LIST("List<%s>", 1), + SAM("SAM<%s,%s>", 2); + + int arity; + String tokenStr; + + TypeToken(String tokenStr, int arity) { + this.tokenStr = tokenStr; + this.arity = arity; + } + + public int arity() { + return arity; + } + } + + enum ArgTypeShape implements Template, StackReducer<TypeToken, Type, Void> { + STRING(TypeToken.STRING), + INTEGER(TypeToken.INTEGER), + LIST_STRING(TypeToken.LIST, TypeToken.STRING), + LIST_INTEGER(TypeToken.LIST, TypeToken.INTEGER); + + TypeToken[] tokens; + + ArgTypeShape(TypeToken... tokens) { + this.tokens = tokens; + } + + Type compute() { + return process(tokens, this, null); + } + + public String expand(String selector) { + return compute().toString(); + } + + public Type reduce(TypeToken token, Type[] args, Void _unused) { + return new Type(token, args); + } + + public Void reducerArg(TypeToken item, int i) { return null; } + + public Class<Type> resultToken() { + return Type.class; + } + } + + enum FormalTypeShape implements Template, StackReducer<TypeToken, Type, Void> { + SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_Integer_String(TypeToken.SAM, TypeToken.INTEGER, TypeToken.STRING), + SAM_List_String_String(TypeToken.SAM, TypeToken.LIST, TypeToken.STRING, TypeToken.STRING), + SAM_List_Integer_String(TypeToken.SAM, TypeToken.LIST, TypeToken.INTEGER, TypeToken.STRING), + SAM_A_String(TypeToken.SAM, TypeToken.A, TypeToken.STRING), + SAM_List_A_String(TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.STRING), + SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.R), + SAM_A_R(TypeToken.SAM, TypeToken.A, TypeToken.R), + SAM_List_A_R(TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.R), + SAM_String_SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_List_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_List_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_List_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.R), + SAM_String_SAM_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.R), + SAM_String_SAM_List_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.R), + SAM_A_SAM_String_String(TypeToken.SAM, TypeToken.A, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_String_SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_String_SAM_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_String_SAM_List_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_String_SAM_List_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_String_SAM_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_String_SAM_List_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_String_SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.R), + SAM_String_SAM_String_SAM_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.R), + SAM_String_SAM_String_SAM_List_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.R), + SAM_String_SAM_A_SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_A_SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.SAM, TypeToken.STRING, TypeToken.R); + + TypeToken[] tokens; + + FormalTypeShape(TypeToken... tokens) { + this.tokens = tokens; + } + + Type compute() { + return process(tokens, this, null); + } + + public String expand(String selector) { + return compute().toString(); + } + + public Type reduce(TypeToken token, Type[] args, Void _unused) { + return new Type(token, args); + } + + public Void reducerArg(TypeToken item, int i) { return null; } + + public Class<Type> resultToken() { + return Type.class; + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaReturnAdaptationTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.*; + +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * LambaReturnAdaptationTest + */ +public class LambdaReturnAdaptationTest extends ComboTestBase<LambdaReturnAdaptationTest> { + @Factory + public static Object[] testReturnAdaptation() throws Exception { + return factory(LambdaReturnAdaptationTest.class); + } + + @DimensionVar("FROM") Types fromType; + @DimensionVar("TO") Types toType; + + @SourceFile("Sam.java") + String samFile = "public interface Sam { #{TO} m(int arg); }"; + + @SourceFile("Main.java") + String clientFile = "public class Main {\n" + +" static #{FROM} m(int arg) { \n" + +" #{RET};\n" + +" }\n" + +" public #{TO} main() { \n" + + " Sam s = Main::m;\n" + + " #{RET_MAIN};\n" + + " }\n" + +"}"; + + @TemplateVar("RET") + Template retTemplate = new Template() { + public String expand(String selector) { + if (fromType == Types.VOID) + return ""; + else if (fromType == Types.STRING) + return "return \"66\""; + else if (fromType == Types.BOOLEAN || fromType == Types.BOXED_BOOLEAN) + return "return true"; + else if (fromType.unboxed != null) + return "return (" + fromType.unboxed.name + ") arg"; + else + return "return (" + fromType.name + ") arg"; + } + }; + + @TemplateVar("RET_MAIN") + Template retMainTemplate = new Template() { + public String expand(String selector) { + return toType == Types.VOID ? "s.m(66)" : "return s.m(66)"; + } + }; + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @Override + protected void postCompile(String group) { + if (fromType != Types.VOID && toType == Types.VOID) + assertCompileSucceeded(); + else if (fromType == Types.VOID && toType != Types.VOID) + assertCompileFailed(); + else if (fromType == Types.VOID && toType == Types.VOID) + assertCompileSucceeded(); + else if (fromType.pr == PR.REF && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(fromType.clazz)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(Types.boxMap.get(fromType).clazz)); + else if (fromType.pr == PR.REF && toType.pr == PR.PRIM) + assertCompileSucceededIff(fromType.unboxed != null && Types.widenMap.get(fromType.unboxed).contains(toType)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.PRIM) + assertCompileSucceededIff(Types.widenMap.get(fromType).contains(toType)); + else + assertCompileSucceeded(); + } + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + Object in = 66; + Object out = m.invoke(obj); + if (toType == Types.VOID) + assertTrue(out == null); + else if (fromType == Types.BOOLEAN || fromType == Types.BOXED_BOOLEAN) + assertEquals("true", out.toString()); + else + assertEquals(in.toString(), out.toString().replace("B", "66").replace(".0", "")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/MethodRefCaptureTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.*; + +/** + * MethodRefCaptureTest + * + * @author Brian Goetz + */ +public class MethodRefCaptureTest extends ComboTestBase<MethodRefCaptureTest> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(MethodRefCaptureTest.class); + } + + @DimensionVar("VAR") ContextKind context; + @DimensionVar("LOCATION") LocationKind location; + @DimensionVar("ACC") AccessibilityKind accessibility; + @DimensionVar("METHOD") RefKind ref; + + @TemplateVar("PACKAGE") + String makePackage() { + return location == LocationKind.OTHER_PACKAGE ? "package a;" : ""; + } + + // @@@ not yet used + @TemplateVar("MAIN") String main = "public static void main(String[] args) { #{METHOD.SAMTYPE} x = #{VAR.REF}"; + + @SourceFile("Sam1.java") String sam1 = "#{PACKAGE} \npublic interface Sam1 { public String get(String s); }"; + @SourceFile("Sam2.java") String sam2 = "#{PACKAGE} \npublic interface Sam2<T> { public String get(T target, String s); }"; + @SourceFile("Sam3.java") String sam3 = "#{PACKAGE} \npublic interface Sam3<T> { public T get(String s); }"; + @SourceFile("A.java") String mainFile = "#{LOCATION.CAPTURING_CLASS}"; + @SourceFile("Target.java") String extFile = "#{LOCATION.EXT_CLASS}"; + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @Override + protected void postCompile(String group) { + if (location == LocationKind.OTHER_FILE && accessibility == AccessibilityKind.PRIVATE) + assertCompileFailed(); + else if (location == LocationKind.OTHER_PACKAGE && accessibility != AccessibilityKind.PUBLIC) + assertCompileFailed(); + else + assertCompileSucceeded(); + } + + + static enum ContextKind implements Template { + STATIC_FIELD_INIT("static #{METHOD.SAMTYPE} var = #{METHOD.MREF};"), + STATIC_FIELD_CLINIT("static #{METHOD.SAMTYPE} var; static { var = #{METHOD.MREF}; };"), + INSTANCE_FIELD_INIT("#{METHOD.SAMTYPE} var = #{METHOD.MREF};"), + LOCAL_VAR("void m() { #{METHOD.SAMTYPE} var = #{METHOD.MREF}; };"), + METHOD_CALL("static void m(#{METHOD.SAMTYPE} s) { }; static void m() { m(#{METHOD.MREF}); };"), + RETURN("#{METHOD.SAMTYPE} m() { return #{METHOD.MREF}; };"), + ARRAY_INITIALIZER("#{METHOD.SAMTYPE}[] var = (#{METHOD.SAMTYPE}[]) new #{METHOD.ERASED_SAMTYPE}[] { (#{METHOD.SAMTYPE}) #{METHOD.MREF} };"), + CONDITIONAL("#{METHOD.SAMTYPE} var = new Object().equals(new Object()) ? #{METHOD.MREF} : #{METHOD.MREF};"), + CAST("#{METHOD.SAMTYPE} var = (#{METHOD.SAMTYPE}) #{METHOD.MREF};"); + + private final String t1; + + ContextKind(String t1) { this.t1 = t1; } + + public String expand(String selector) { + switch (selector) { + case "DECL": return t1; + default: throw new IllegalArgumentException(selector); + } + } + } + + static enum LocationKind implements Template { + SAME_CLASS("class Target { \n#{VAR.DECL} \n#{METHOD.DECL} }"), + ENCLOSING_CLASS("class Target { \n#{METHOD.DECL} \nstatic class Capturing { #{VAR.DECL} } }"), + SIBLING_INNER_CLASS("class Outer { \nstatic class Capturing { #{VAR.DECL} } \nstatic class Target { #{METHOD.DECL} } }"), + NESTED_CLASS("class Capturing { \n#{VAR.DECL} \nstatic class Target { #{METHOD.DECL} } }"), + OTHER_FILE("class Capturing { \n#{VAR.DECL} }", "class Target { \n#{METHOD.DECL} }"), + OTHER_PACKAGE("package a; \nimport b.*; \nclass Capturing { \n#{VAR.DECL} }", + "package b; \npublic class Target { \n#{METHOD.DECL} }"); + + private final String t1, t2; + + LocationKind(String t1, String t2) { + this.t1 = t1; + this.t2 = t2; + } + LocationKind(String t1) { this(t1, "class Unused {}"); } + + public String expand(String selector) { + switch (selector) { + case "CAPTURING_CLASS": return t1; + case "EXT_CLASS": return t2; + default: throw new IllegalArgumentException(selector); + } + } + } + + static enum AccessibilityKind implements Template { + PUBLIC("public"), + PROTECTED("protected"), + PACKAGE(""), + PRIVATE("private"); + + private final String template; + + AccessibilityKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum RefKind implements Template { + STATIC(0, "Target::staticMethod", "static #{ACC} String staticMethod(String s) { return \"1\"; }"), + UNBOUND_INSTANCE(1, "Target::instanceMethod", "#{ACC} String instanceMethod(String s) { return \"2\"; }"), + BOUND_INSTANCE(0, "(new Target())::instanceMethod", "#{ACC} String instanceMethod(String s) { return \"3\"; }"), + CTOR(2, "Target::new", "#{ACC} Target(String s) { }"), +// UNBOUND_INNER_CTOR, +// BOUND_INNER_CTOR +// , SUPER + ; + + private static final String[] sams = { "Sam1", "Sam2<Target>", "Sam3<Target>" }; + private final int samNo; + private final String t1, t2; + + RefKind(int samNo, String t1, String t2) { this.samNo = samNo; this.t1 = t1; this.t2 = t2; } + + public String expand(String selector) { + switch (selector) { + case "MREF": return t1; + case "DECL": return t2; + case "SAMTYPE": return sams[samNo]; + case "ERASED_SAMTYPE": return sams[samNo].replaceAll("<Target>", "<?>"); + default: throw new IllegalArgumentException(selector); + } + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/NestedGenericMethodCallTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; + +import static tools.javac.combo.StackProcessingUtils.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * NestedGenericMethodCallTest + */ +public class NestedGenericMethodCallTest extends ComboTestBase<NestedGenericMethodCallTest> { + @Factory + public static Object[] testNestedGenericMethodCallType() throws Exception { + return factory(NestedGenericMethodCallTest.class); + } + + @DimensionVar("ID_METHOD") IdMethod idMethod; + @DimensionVar("NIL_METHOD") NilMethod nilMethod; + @DimensionVar("CONS_METHOD") ConsMethod consMethod; + @DimensionVar("SHAPE") Shape shape; + @DimensionVar("TARGET") Type target; + + @SourceFile("Main.java") + String clientFile = "import java.util.*;\n" + +"public class Main {\n" + +" #{ID_METHOD}\n" + +" #{NIL_METHOD}\n" + +" #{CONS_METHOD}\n" + +" void test() { \n" + + " #{TARGET} t = #{SHAPE};\n" + + " }\n" + +"}"; + + @Override + protected void postCompile(String grp) { + Type res = shape.compile(idMethod, nilMethod, consMethod, target); + if (res.asSubtypeOf(target).isError()) { + assertCompileFailed("Bad result = " + res); + } else { + assertCompileSucceeded(); + } + } + + @Override + protected boolean shouldSkip() { + return target.free || target == Type.ERROR; + } + + enum Type implements Template { + Z("Z", true), + OBJECT("Object", false), + STRING("String", false), + LIST_Z("List<Z>", true), + LIST_OBJECT("List<Object>", false), + LIST_STRING("List<String>", false), + ERROR("<any>", false); + + String typeStr; + boolean free; + + Type(String typeStr, boolean free) { + this.typeStr = typeStr; + this.free = free; + } + + boolean isError() { + return this == ERROR; + } + + Type asSubtypeOf(Type that) { + return asSubtypeMap[this.ordinal()][that.ordinal()]; + } + + boolean isSubtypeOf(Type that) { + return !asSubtypeOf(that).isError(); + } + + public String expand(String selector) { + return typeStr; + } + + enum ConstraintKind { + Z_EXT_OBJ(false, OBJECT), + Z_EXT_STRING(false, STRING), + Z_EXT_LIST_OBJECT(false, LIST_OBJECT), + Z_EXT_LIST_STRING(false, STRING), + Z_SUP_OBJECT(true, OBJECT), + Z_SUP_STRING(true, STRING), + Z_SUP_LIST_OBJECT(true, LIST_OBJECT), + Z_SUP_LIST_STRING(true, LIST_STRING), + ERROR(false, Type.ERROR); + + boolean isSuper; + Type type; + + ConstraintKind(boolean isSuper, Type type) { + this.isSuper = isSuper; + this.type = type; + } + } + + static Type[][] asSubtypeMap = new Type[][] { + // Z OBJECT STRING LIST_Z LIST_OBJECT LIST_STRING ERROR + { /* Z */ OBJECT ,OBJECT ,STRING ,ERROR ,LIST_OBJECT ,LIST_STRING ,ERROR }, + { /* OBJECT */ OBJECT ,OBJECT ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR }, + { /* STRING */ STRING ,STRING ,STRING ,ERROR ,ERROR ,ERROR ,ERROR }, + { /* LIST_Z */ LIST_OBJECT,LIST_OBJECT,ERROR ,LIST_OBJECT,LIST_OBJECT ,LIST_STRING ,ERROR }, + { /* LIST_OBJECT */ LIST_OBJECT,LIST_OBJECT,ERROR ,LIST_OBJECT,LIST_OBJECT ,ERROR ,ERROR }, + { /* LIST_STRING */ LIST_STRING,LIST_STRING,ERROR ,LIST_STRING,ERROR ,LIST_STRING ,ERROR }, + { /* ERROR */ ERROR ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR }}; + + static boolean isSubtypes(Type[] ts1, Type[] ts2) { + assertEquals(ts1.length, ts2.length); + for (int i = 0; i < ts1.length ; i++) { + if (!ts1[i].isSubtypeOf(ts2[i])) return false; + } + return true; + } + + static Type listOf(Type t) { + switch (t) { + case OBJECT: return LIST_OBJECT; + case STRING: return LIST_STRING; + case Z: return LIST_Z; + default: return ERROR; + } + } + + static Type elemtype(Type t) { + switch (t) { + case LIST_OBJECT: return OBJECT; + case LIST_STRING: return STRING; + case LIST_Z: return Z; + default: return ERROR; + } + } + + static Type lub(Type t1, Type t2) { + if (!t1.asSubtypeOf(t2).isError()) { + return t2; + } else if (!t2.asSubtypeOf(t1).isError()) { + return t1; + } else { + return Type.ERROR; + } + } + } + + interface MethodSig { + Type apply(Type target, Type... argTypes); + Type[] getFormals(); + } + + enum IdMethod implements Template, MethodSig { + NON_GENERIC_1("String id(String s) { return null; };", Type.STRING), + NON_GENERIC_2("List<String> id(List<String> s) { return null; };", Type.LIST_STRING), + GENERIC_1("<Z> Z id(Z z) { return null; };", Type.Z), + GENERIC_2("<Z> List<Z> id(List<Z> z) { return null; };", Type.LIST_Z); + + String sig; + Type formal; + + IdMethod(String sig, Type formal) { + this.sig = sig; + this.formal = formal; + } + + public String expand(String selector) { + return sig; + } + + public Type apply(Type target, Type... argTypes) { + return argTypes[0].asSubtypeOf(formal); + } + + public Type[] getFormals() { + return new Type[] { formal }; + } + } + + enum NilMethod implements Template, MethodSig { + NON_GENERIC("List<String> nil() { return null; }", Type.LIST_STRING), + GENERIC("<Z> List<Z> nil() { return null; }", Type.LIST_Z); + + String sig; + Type returnType; + + NilMethod(String sig, Type returnType) { + this.sig = sig; + this.returnType = returnType; + } + + public String expand(String selector) { + return sig; + } + + public Type apply(Type target, Type... argTypes) { + return this == NON_GENERIC ? + Type.LIST_STRING : + returnType.asSubtypeOf(target); + } + + public Type[] getFormals() { + return null; + } + } + + enum ConsMethod implements Template, MethodSig { + NON_GENERIC("List<String> cons(String s, List<String> ls) { return null; }", Type.STRING, Type.LIST_STRING) { + Type getReturnType(Type target, Type... argTypes) { return Type.LIST_STRING; } + }, + GENERIC_1("<Z> List<Z> cons(Z z, List<String> ls) { return null; }", Type.Z, Type.LIST_STRING) { + Type getReturnType(Type target, Type... argTypes) { + switch (target) { + case LIST_OBJECT: + case LIST_STRING: + return Type.LIST_Z.asSubtypeOf(target); + default: + return Type.listOf(argTypes[0].asSubtypeOf(Type.Z)); + } + } + }, + GENERIC_2("<Z> List<Z> cons(String s, List<Z> lz) { return null; }", Type.STRING, Type.LIST_Z) { + Type getReturnType(Type target, Type... argTypes) { return Type.listOf(Type.elemtype(argTypes[1].asSubtypeOf(Type.LIST_Z))); } + }, + GENERIC_3("<Z> List<Z> cons(Z z, List<Z> lz) { return null; }", Type.Z, Type.LIST_Z) { + Type getReturnType(Type target, Type... argTypes) { + Type lub = Type.lub(argTypes[0], Type.elemtype(argTypes[1].asSubtypeOf(Type.LIST_Z))); + return lub.isError() ? lub : Type.listOf(lub); + } + }, + GENERIC_4("<U, V> List<U> cons(U z, List<V> lz) { return null; }", Type.Z, Type.LIST_Z) { + Type getReturnType(Type target, Type... argTypes) { + switch (target) { + case LIST_OBJECT: + case LIST_STRING: + return Type.LIST_Z.asSubtypeOf(target); + default: + return Type.listOf(argTypes[0].asSubtypeOf(Type.Z)); + } + } + }; + + String sig; + Type[] formals; + + ConsMethod(String sig, Type... formals) { + this.sig = sig; + this.formals = formals; + } + + public String expand(String selector) { + return sig; + } + + public Type apply(Type target, Type... argTypes) { + return Type.isSubtypes(argTypes, formals) ? + getReturnType(target, argTypes) : Type.ERROR; + } + + abstract Type getReturnType(Type target, Type... argTypes); + + public Type[] getFormals() { + return formals; + } + } + + enum Token implements StackItem<Token> { + NIL(0, "nil"), + DIAMOND(0, "new ArrayList<>"), + STRING(0, "\"\""), + ID(1, "id"), + CONS(2, "cons"); + + int arity; + String tokenStr; + + Token(int arity, String tokenStr) { + this.arity = arity; + this.tokenStr = tokenStr; + } + + public int arity() { return arity; } + } + + enum Shape implements Template { + NIL(Token.NIL), + ID_NIL(Token.ID, Token.NIL), + ID_ID_NIL(Token.ID, Token.ID, Token.NIL), + CONS_STRING_NIL(Token.CONS, Token.STRING, Token.NIL), + CONS_STRING_ID_NIL(Token.CONS, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_NIL(Token.CONS, Token.ID, Token.STRING, Token.NIL), + CONS_ID_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_CONS_STRING_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.NIL), + CONS_ID_STRING_CONS_ID_STRING_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.NIL), + CONS_ID_STRING_CONS_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_CONS_ID_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_ID_CONS_ID_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + ID_CONS_ID_STRING_ID_CONS_ID_STRING_ID_NIL(Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + DIAMOND(Token.DIAMOND), + ID_DIAMOND(Token.ID, Token.DIAMOND), + CONS_STRING_DIAMOND(Token.CONS, Token.STRING, Token.DIAMOND), + CONS_STRING_ID_DIAMOND(Token.CONS, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.DIAMOND), + CONS_ID_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_CONS_STRING_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.DIAMOND), + CONS_ID_STRING_CONS_ID_STRING_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.DIAMOND), + CONS_ID_STRING_CONS_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_CONS_ID_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_ID_CONS_ID_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND), + ID_CONS_ID_STRING_ID_CONS_ID_STRING_ID_DIAMOND(Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND); + + Token[] tokens; + + Shape(Token... tokens) { + this.tokens = tokens; + } + + Type compile(final IdMethod idMethod, final NilMethod nilMethod, final ConsMethod consMethod, Type target) { + return process(tokens, new StackReducer<Token, Type, Type>() { + public Type reduce(Token t, Type[] operands, Type target) { + switch (t) { + case DIAMOND: return Type.LIST_Z.asSubtypeOf(target); + case NIL: return nilMethod.apply(target, operands); + case STRING: return Type.STRING; + case ID: return idMethod.apply(target, operands); + case CONS: return consMethod.apply(target, operands); + default: throw new AssertionError(); + } + } + public Class<Type> resultToken() { return Type.class; } + public Type reducerArg(Token item, int i) { + switch (item) { + case ID: + return idMethod.getFormals()[i]; + case CONS: + return consMethod.getFormals()[i]; + default: return Type.OBJECT; + } + } + }, target); + } + + public String expand(String selector) { + return process(tokens, new StackReducer<Token, String, Void>() { + public String reduce(Token t, String[] operands, Void _unused) { + switch (t) { + case DIAMOND: + case NIL: + case ID: + case CONS: + StringBuilder buf = new StringBuilder(); + String sep = ""; + for (int i = 0; i < t.arity; i ++) { + buf.append(sep); + buf.append(operands[i]); + sep = ","; + } + buf.insert(0, "("); + buf.insert(0, t.tokenStr); + buf.append(")"); + return buf.toString(); + case STRING: + return t.tokenStr; + default: throw new AssertionError(); + } + } + public Class<String> resultToken() { return String.class; } + public Void reducerArg(Token item, int i) { return null; } + }, null); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/SourceTargetVersionTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2012, 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. + */ +package tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * SourceTargetVersionTest: + * Test source and class file compatibility by compiling and running lambda specific code + * against some class files compiled separately with different source and target option + * (1.5, 1.6, and 1.7). + * The goal is achieved by putting source files in different groups (A, B, C..,), each group + * is set with its own compile options, and we compile in the reverse alphebetic order for the + * groups (..., C, B, A) to set a previously compiled group of files as classpath for the next group. + * And when executing, we Run class Main with classpath A:B:C... in the order. + * + */ +public class SourceTargetVersionTest extends ComboTestBase<SourceTargetVersionTest> { + @Factory + public static Object[] testSourceTarget() throws Exception { + return factory(SourceTargetVersionTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + @DimensionVar("CLIENT_CODE") ClientType clientType; + @DimensionVar("RELEASE") ReleaseVersion rv; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { #{SHAPE.METHOD} }"; + + @SourceFile(value="B.java", group="A") + String interfaceB = "interface B #{SHAPE.EXTEND} { default String m() { return \"B\"; } }"; + + @SourceFile(value="C.java", group="A") + String classC = "class C #{SHAPE.IMPLEMENT} {\n" + + " #{CLIENT_CODE}\n" + + "}"; + + @SourceFile(value="Main.java", group="A") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @TemplateVar("LAMBDA_EXP") String le = "public String m() {\n" + + " A a = () -> \"A\";\n" + + " return a.m();\n" + + "}"; + @TemplateVar("METHOD_REF") String mr = "String foo() {\n" + + " return \"foo\";\n" + + "}\n\n" + + + "public String m() {\n" + + " A a = this::foo;\n" + + " return a.m();\n" + + "}"; + + @Override + protected boolean shouldSkip() { + return (shapeType == CShapes.C_AB && clientType != ClientType.DEF_MED) || + (shapeType == CShapes.C && clientType == ClientType.DEF_MED); + } + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = (String) m.invoke(obj); + String expected = clientType.returnValue; + assertEquals(result, expected); + } + + @Override + protected String[] getCompileOptions(String group) { + switch(group) { + case "B": return new String[]{"-source", rv.versionStr, "-target", rv.versionStr}; + default: return new String[0]; + } + } + + enum CShapes implements Template { //shapes of class hirarchy + //class C implements interface A, B + C_AB("", "", "implements A, B"), + //class C implments interface B, B extends interface A + C_B_A("String m();", "extends A", "implements B"), + //class C + C("String m();", "", ""); + + private final String absMethod; + private final String extend; + private final String implement; + + CShapes(String absMethod, String extend, String implement) { + this.absMethod = absMethod; + this.extend = extend; + this.implement = implement; + } + + public String expand(String selector) { + switch(selector) { + case "METHOD": return absMethod; + case "EXTEND": return extend; + case "IMPLEMENT": return implement; + default: return toString(); + } + } + } + + enum ClientType implements Template { // jdk8 specific code in client program + LAMBDA_EXP("#{LAMBDA_EXP}", "A"), // lambda expressions + METHOD_REF("#{METHOD_REF}", "foo"), // method references + DEF_MED("", "B"); // default method inheritance + + private final String clientCode; + private final String returnValue; + + ClientType(String str, String ret) { + clientCode = str; + returnValue = ret; + } + + public String expand(String selector) { + return clientCode; + } + } + + enum ReleaseVersion implements Template { + FIVE("1.5"), + SIX("1.6"), + SEVEN("1.7"); + + final String versionStr; + + ReleaseVersion(String vstr) { + versionStr = vstr; + } + + public String expand(String selector) { + return toString(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/StaticMethodTest.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013, 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. + */ +package tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * StaticMethodTest: Test static methods in interfaces by running bytecode + * compiled at different times; where a static method is added/declared + * in one interface and only that interface is recompiled. + * + * Static methods in interface are not inherited and + * are not visible to any implementing (extending) classes (interfaces). + * @bug 8005968 + * @author sogoel + * + */ +public class StaticMethodTest extends ComboTestBase<StaticMethodTest> { + @Factory + public static Object[] testStaticMethod() throws Exception { + return factory(StaticMethodTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + + @DimensionVar("ADD") AddType addType; + + @SourceFile(value="B.java", group="A") + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{ADD} }"; + + @SourceFile(value="B.java", group="B") + String interfaceB = "interface B #{SHAPE.B_DECL} {}"; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { static String m() { return \"A\"; } }"; + + @SourceFile(value="C.java", group="B") + String classC = "#{SHAPE.C}"; + + @SourceFile(value="Main.java", group="B") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = null, output = null; + try { + result = (String) m.invoke(obj); + } catch (InvocationTargetException ex) { + output = ex.getCause().toString(); + } + assertEquals(output, null); + assertEquals(result, "C"); + } + + /* + * In order for class Main to compile in group B, object of class C should have + * access to a method m(). Therefore, a method m() is needed in class C. + * static m() in interfaces A, B is not visible to C. + */ + static String methodSig = "public String m () { return \"C\"; }"; + enum CShapes implements Template { //shapes of class hierarchy + //class C implements interface A, B + C_AB("", + "class C implements A, B { " + methodSig + " }"), + //class C implements interface B, B extends interface A + C_B_A("extends A", + "class C implements B { " + methodSig + " }"), + //class C implements interface AB, AB extends interface A, B + C_I_AB("", + "interface AB extends A, B { }\n" + + "class C implements AB { " + methodSig + " }"), + //class C implements interface AB, AB extends interface A, B and defines its own static method + C_I_AB2("", + "interface AB extends A, B { static String m() { return \"AB\"; } }\n" + + "class C implements AB { " + methodSig + " }"), + //class C extends Class D implements Interface B + C_CI("", + "class D { public String m() { return \"D\"; } }\n" + + "class C extends D implements B { " + methodSig + " }"); + + private final String sB_DECL; + private final String sC; + + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; + } + + public String expand(String selector) { + switch(selector) { + case "B_DECL": return sB_DECL; + case "C": return sC; + default: return toString(); + } + } + } + + enum AddType implements Template { // add by adding static method or abstract interface method + ADD("static String m() { return \"B\"; }"), + REDECLARE("String m();"); + + final String newCode; + + AddType(String str) { + newCode = str; + } + + public String expand(String selector) { + return newCode; + } + } +} +
--- a/make/common/internal/Defs-langtools.gmk Thu Apr 11 09:40:13 2013 -0700 +++ b/make/common/internal/Defs-langtools.gmk Tue Apr 16 13:13:13 2013 -0700 @@ -28,7 +28,8 @@ IMPORT_RT_PACKAGES += \ javax/annotation/processing \ javax/lang/model \ - javax/tools + javax/tools \ + com/sun/runtime IMPORT_TOOLS_PACKAGES += \ com/sun/javadoc \
--- a/make/docs/CORE_PKGS.gmk Thu Apr 11 09:40:13 2013 -0700 +++ b/make/docs/CORE_PKGS.gmk Tue Apr 16 13:13:13 2013 -0700 @@ -142,6 +142,7 @@ java.util.prefs \ java.util.regex \ java.util.spi \ + java.util.stream \ java.util.zip \ javax.accessibility \ javax.activation \
--- a/make/docs/NON_CORE_PKGS.gmk Thu Apr 11 09:40:13 2013 -0700 +++ b/make/docs/NON_CORE_PKGS.gmk Tue Apr 16 13:13:13 2013 -0700 @@ -84,6 +84,8 @@ SCTPAPI_PKGS = com.sun.nio.sctp +LAMBDA_RTAPI = com.sun.runtime + ifeq ($(PLATFORM), macosx) APPLE_EXT_PKGS = com.apple.concurrent \ com.apple.eawt \ @@ -101,5 +103,6 @@ $(HTTPSERVER_PKGS) \ $(SMARTCARDIO_PKGS) \ $(SCTPAPI_PKGS) \ + $(LAMBDA_RTAPI) \ $(APPLE_EXT_PKGS)
--- a/make/java/java/FILES_java.gmk Thu Apr 11 09:40:13 2013 -0700 +++ b/make/java/java/FILES_java.gmk Tue Apr 16 13:13:13 2013 -0700 @@ -299,12 +299,20 @@ java/util/EnumMap.java \ java/util/Arrays.java \ java/util/ArraysParallelSortHelpers.java \ + java/util/ArrayPrefixHelpers.java \ + java/util/ArraySortHelpers.java \ java/util/DualPivotQuicksort.java \ java/util/TimSort.java \ java/util/ComparableTimSort.java \ + java/util/Comparators.java \ java/util/ConcurrentModificationException.java \ + java/util/OptionalDouble.java \ + java/util/OptionalInt.java \ + java/util/Optional.java \ + java/util/OptionalLong.java \ java/util/ServiceLoader.java \ java/util/ServiceConfigurationError.java \ + java/util/StringJoiner.java \ java/util/Timer.java \ java/util/TimerTask.java \ java/util/Objects.java \ @@ -331,6 +339,8 @@ java/util/concurrent/CyclicBarrier.java \ java/util/concurrent/DelayQueue.java \ java/util/concurrent/Delayed.java \ + java/util/concurrent/DoubleAdder.java \ + java/util/concurrent/DoubleMaxUpdater.java \ java/util/concurrent/Exchanger.java \ java/util/concurrent/ExecutionException.java \ java/util/concurrent/Executor.java \ @@ -345,6 +355,10 @@ java/util/concurrent/LinkedBlockingDeque.java \ java/util/concurrent/LinkedBlockingQueue.java \ java/util/concurrent/LinkedTransferQueue.java \ + java/util/concurrent/LongAdder.java \ + java/util/concurrent/LongAdderTable.java \ + java/util/concurrent/LongMaxUpdater.java \ + java/util/concurrent/package-info.java \ java/util/concurrent/Phaser.java \ java/util/concurrent/PriorityBlockingQueue.java \ java/util/concurrent/RecursiveAction.java \ @@ -357,6 +371,8 @@ java/util/concurrent/ScheduledFuture.java \ java/util/concurrent/ScheduledThreadPoolExecutor.java \ java/util/concurrent/Semaphore.java \ + java/util/concurrent/SequenceLock.java \ + java/util/concurrent/Striped64.java \ java/util/concurrent/SynchronousQueue.java \ java/util/concurrent/ThreadFactory.java \ java/util/concurrent/ThreadLocalRandom.java \ @@ -376,18 +392,17 @@ java/util/concurrent/atomic/AtomicReferenceArray.java \ java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java \ java/util/concurrent/atomic/AtomicStampedReference.java \ - java/util/concurrent/atomic/DoubleAccumulator.java \ - java/util/concurrent/atomic/DoubleAdder.java \ - java/util/concurrent/atomic/LongAccumulator.java \ - java/util/concurrent/atomic/LongAdder.java \ - java/util/concurrent/atomic/Striped64.java \ + java/util/concurrent/atomic/package-info.java \ + java/util/concurrent/extra/AtomicDoubleArray.java \ + java/util/concurrent/extra/AtomicDouble.java \ + java/util/concurrent/extra/ReadMostlyVector.java \ java/util/concurrent/locks/AbstractOwnableSynchronizer.java \ java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java \ java/util/concurrent/locks/AbstractQueuedSynchronizer.java \ - java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java \ java/util/concurrent/locks/Condition.java \ java/util/concurrent/locks/Lock.java \ java/util/concurrent/locks/LockSupport.java \ + java/util/concurrent/locks/package-info.java \ java/util/concurrent/locks/ReadWriteLock.java \ java/util/concurrent/locks/ReentrantLock.java \ java/util/concurrent/locks/ReentrantReadWriteLock.java \
--- a/make/java/java/Makefile Thu Apr 11 09:40:13 2013 -0700 +++ b/make/java/java/Makefile Tue Apr 16 13:13:13 2013 -0700 @@ -37,7 +37,7 @@ JAVAC_MAX_WARNINGS=true include $(BUILDDIR)/common/Defs.gmk -AUTO_FILES_JAVA_DIRS = java/util/function +AUTO_FILES_JAVA_DIRS = java/util/concurrent java/util/function java/util/stream # windows compiler flags ifeq ($(PLATFORM),windows) @@ -51,7 +51,7 @@ OTHER_CFLAGS += -DJDK_MAJOR_VERSION='"$(JDK_MAJOR_VERSION)"' \ -DJDK_MINOR_VERSION='"$(JDK_MINOR_VERSION)"' \ -DJDK_MICRO_VERSION='"$(JDK_MICRO_VERSION)"' \ - -DJDK_BUILD_NUMBER='"$(JDK_BUILD_NUMBER)"' + -DJDK_BUILD_NUMBER='"$(JDK_BUILD_NUMBER)"' ifdef JDK_UPDATE_VERSION OTHER_CFLAGS += -DJDK_UPDATE_VERSION='"$(JDK_UPDATE_VERSION)"' @@ -279,7 +279,7 @@ $(GENSRCDIR)/java/lang/UNIXProcess.java: $(PLATFORM_UNIX_PROCESS) $(install-file) -clean:: +clean:: $(RM) $(GENSRCDIR)/java/lang/UNIXProcess.java endif @@ -293,10 +293,10 @@ # # Special rules. # -clean:: +clean:: $(RM) -r $(CLASSHDRDIR) -clobber:: +clobber:: $(RM) -r $(CLASSBINDIR)/java/io $(CLASSBINDIR)/java/lang \ $(CLASSBINDIR)/java/security $(CLASSBINDIR)/java/util \ $(CLASSBINDIR)/sun/misc @@ -332,7 +332,7 @@ $(LIBDIR)/$(CAL_PROPS): $(SHARE_SRC)/lib/$(CAL_PROPS) $(install-file) -clean:: +clean:: $(RM) -r $(LIBDIR)/$(PROPS) $(TZMAP) # @@ -353,12 +353,12 @@ $(MV) $@.temp $@ $(call chmod-file, 444) -clean:: +clean:: $(RM) $(CURDATA) # -# Rules to create $(GENSRCDIR)/sun/lang/CharacterData*.java +# Rules to create $(GENSRCDIR)/sun/lang/CharacterData*.java # CHARACTERDATA = $(BUILDDIR)/tools/GenerateCharacter UNICODEDATA = $(BUILDDIR)/tools/UnicodeData @@ -413,12 +413,12 @@ $(install-file) clean:: - $(RM) $(GENSRCDIR)/java/lang/CharacterDataLatin1.java + $(RM) $(GENSRCDIR)/java/lang/CharacterDataLatin1.java $(RM) $(GENSRCDIR)/java/lang/CharacterData00.java - $(RM) $(GENSRCDIR)/java/lang/CharacterData01.java + $(RM) $(GENSRCDIR)/java/lang/CharacterData01.java $(RM) $(GENSRCDIR)/java/lang/CharacterData02.java $(RM) $(GENSRCDIR)/java/lang/CharacterData0E.java - $(RM) $(GENSRCDIR)/java/lang/CharacterDataUndefined.java + $(RM) $(GENSRCDIR)/java/lang/CharacterDataUndefined.java $(RM) $(GENSRCDIR)/java/lang/CharacterDataPrivateUse.java # @@ -439,7 +439,7 @@ build.tools.generatecharacter.CharacterName \ $(UNICODEDATA)/UnicodeData.txt $(UNINAME) -clean:: +clean:: $(RM) $(UNINAME) # @@ -447,15 +447,15 @@ # # -# Rule to precompile CoreResourceBundleControl.java +# Rule to precompile CoreResourceBundleControl.java # LOCALES_GEN_SH = localelist.sh $(GENSRCDIR)/sun/util/CoreResourceBundleControl.java: \ $(SHARE_SRC)/classes/sun/util/CoreResourceBundleControl-XLocales.java.template $(LOCALES_GEN_SH) - @$(prep-target) + @$(prep-target) NAWK="$(NAWK)" SED="$(SED)" $(SH) $(LOCALES_GEN_SH) "$(JRE_NONEXIST_LOCALES)" \ - $< $@ + $< $@ clean:: $(RM) $(GENSRCDIR)/sun/util/CoreResourceBundleControl.java
--- a/make/netbeans/common/java-data-native.ent Thu Apr 11 09:40:13 2013 -0700 +++ b/make/netbeans/common/java-data-native.ent Tue Apr 16 13:13:13 2013 -0700 @@ -36,7 +36,7 @@ <package-root>${root}/src/share/classes</package-root> <package-root>${root}/src/windows/classes</package-root> <package-root>${root}/src/solaris/classes</package-root> - <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> +<!-- <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> --> <built-to>${root}/build/${platform}-${arch}/classes</built-to> <javadoc-built-to>${root}/build/${platform}-${arch}/docs/api</javadoc-built-to> <source-level>1.8</source-level> @@ -45,5 +45,6 @@ <package-root>${root}/test</package-root> <unit-tests/> <source-level>1.8</source-level> + <classpath mode="compile">${jtreg.home}/lib/testng.jar</classpath> </compilation-unit> </java-data>
--- a/make/netbeans/common/java-data-no-native.ent Thu Apr 11 09:40:13 2013 -0700 +++ b/make/netbeans/common/java-data-no-native.ent Tue Apr 16 13:13:13 2013 -0700 @@ -34,7 +34,7 @@ <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3"> <compilation-unit> <package-root>${root}/src/share/classes</package-root> - <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> +<!-- <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> --> <built-to>${root}/build/${platform}-${arch}/classes</built-to> <javadoc-built-to>${root}/build/${platform}-${arch}/docs/api</javadoc-built-to> <source-level>1.8</source-level> @@ -43,5 +43,6 @@ <package-root>${root}/test</package-root> <unit-tests/> <source-level>1.8</source-level> + <classpath mode="compile">${jtreg.home}/lib/testng.jar</classpath> </compilation-unit> </java-data>
--- a/make/netbeans/common/properties.ent Thu Apr 11 09:40:13 2013 -0700 +++ b/make/netbeans/common/properties.ent Tue Apr 16 13:13:13 2013 -0700 @@ -41,3 +41,4 @@ <property-file>${user.home}/.openjdk/build.properties</property-file> <property-file>build.properties</property-file> <property name="bootstrap.jdk">${java.home}/..</property> +<property name="jtreg.home">${env.JT_HOME}</property>
--- a/make/netbeans/common/shared.xml Thu Apr 11 09:40:13 2013 -0700 +++ b/make/netbeans/common/shared.xml Tue Apr 16 13:13:13 2013 -0700 @@ -338,7 +338,7 @@ <!-- Note: even with this default value, includes/excludes from share.src.dir get javadoc'd; see packageset below --> <property name="javadoc.packagenames" value="none"/> <!-- default, can be overridden per user or per project --> - <javadoc destdir="${javadoc.dir}" source="1.5" + <javadoc destdir="${javadoc.dir}" source="1.8" windowtitle="UNOFFICIAL" failonerror="true" use="true" author="false" version="false" packagenames="${javadoc.packagenames}">
--- a/make/org/Makefile Thu Apr 11 09:40:13 2013 -0700 +++ b/make/org/Makefile Tue Apr 16 13:13:13 2013 -0700 @@ -31,7 +31,7 @@ PRODUCT = org include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = ietf jcp +SUBDIRS = asm ietf jcp include $(BUILDDIR)/common/Subdirs.gmk all build clean clobber::
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/org/asm/Makefile Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,38 @@ + +BUILDDIR = ../.. +PACKAGE = org.openjdk.org.objectweb.asm +PRODUCT = sun +include $(BUILDDIR)/common/Defs.gmk + +# +# Files to compile +# +AUTO_FILES_JAVA_DIRS = org/openjdk/org/objectweb/asm + +# +# Rules +# +include $(BUILDDIR)/common/Classes.gmk + +# The following is a convenience rule to copy the asm files to $(OUTPUTDIR), +# removing the org.openjdk prefix + +ASMDIR := $(OUTPUTDIR)/asm +ASM_FILES := $(shell $(CD) $(SHARE_SRC)/classes/org/openjdk; $(FIND) org/objectweb -name \*.java) +OUT_FILES := $(ASM_FILES:%.java=$(ASMDIR)/%.java) + +remove-prefix: mkasmdirs $(OUT_FILES) +mkasmdirs: + $(MKDIR) -p $(ASMDIR)/org/objectweb/asm/signature + $(MKDIR) -p $(ASMDIR)/org/objectweb/asm/tree + @echo Result files are in $(ASMDIR)/org/objectweb/asm + +$(ASMDIR)/org/objectweb/asm/%.java: $(SHARE_SRC)/classes/org/openjdk/org/objectweb/asm/%.java + @$(SED) -e 's@org\.openjdk@@g' $< > $@ + +add-prefix: mkasmdirs $(OUT_FILES) + +$(ASMDIR)/org/objectweb/asm/%.java: $(SHARE_SRC)/classes/org/openjdk/org/objectweb/asm/%.java + @$(SED) -e 's@org\.objectweb\.asm@org.openjdk.org.objectweb.asm@g' $< > $@ + +
--- a/src/share/classes/com/sun/tools/jdi/EventSetImpl.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/com/sun/tools/jdi/EventSetImpl.java Tue Apr 16 13:13:13 2013 -0700 @@ -30,6 +30,8 @@ import com.sun.jdi.request.*; import java.util.*; +import java.util.stream.Stream; + enum EventDestination {UNKNOWN_EVENT, INTERNAL_EVENT, CLIENT_EVENT}; /*
--- a/src/share/classes/java/beans/EventHandler.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/beans/EventHandler.java Tue Apr 16 13:13:13 2013 -0700 @@ -485,9 +485,9 @@ } catch (InvocationTargetException ex) { Throwable th = ex.getTargetException(); - throw (th instanceof RuntimeException) + throw (RuntimeException)((th instanceof RuntimeException) ? (RuntimeException) th - : new RuntimeException(th); + : new RuntimeException(th)); } } return null;
--- a/src/share/classes/java/io/BufferedReader.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/io/BufferedReader.java Tue Apr 16 13:13:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, 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 @@ -26,6 +26,13 @@ package java.io; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + /** * Reads text from a character-input stream, buffering characters so as to * provide for the efficient reading of characters, arrays, and lines. @@ -522,4 +529,61 @@ } } } + + /** + * Returns a {@code Stream}, the elemtns of which are lines read from this + * {@code BufferedReader}. The {@link Stream} is lazily populated via + * calls to {@link #readLine()}. + * + * <p> Each element consumed by the {@code Stream} caused a line to be + * read from this {@code BufferedReader}. Since the {@code Stream} does + * not necessary consume all lines, it is possible to mix and use + * different read methods on a {@code BufferedReader}. Each method will + * simply pick up from where it was left on last read. + * + * <p> If an {@link IOException} is thrown when accessing the underlying + * {@code BufferedReader}, it is wrapped in an {@link + * UncheckedIOException} which will be thrown from the {@code Stream} + * method that caused the read to take place. For example, when trying to + * read from the {@code Stream} after the {@code BufferedReader} is + * closed, will throw an {@code UnchecheckedIOException}. + * + * @return a {@code Stream<String>} containing the lines of text + * described by this {@code BufferedReader} + * + * @since 1.8 + */ + public Stream<String> lines() { + Iterator<String> iter = new Iterator<String>() { + String nextLine = null; + + @Override + public boolean hasNext() { + if (nextLine != null) { + return true; + } else { + try { + nextLine = readLine(); + return (nextLine != null); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + @Override + public String next() { + if (nextLine != null || hasNext()) { + try { + return nextLine; + } finally { + nextLine = null; + } + } else { + throw new NoSuchElementException(); + } + } + }; + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED)); + } }
--- a/src/share/classes/java/io/Reader.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/io/Reader.java Tue Apr 16 13:13:13 2013 -0700 @@ -57,7 +57,7 @@ * the object in this field rather than <tt>this</tt> or a synchronized * method. */ - protected Object lock; + protected final Object lock; /** * Creates a new character-stream reader whose critical sections will
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/io/UncheckedIOException.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012, 2013, 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. + */ +package java.io; + +import java.util.Objects; + +/** + * Wraps an <code>{@link IOException}</code> with an unchecked exception + * + * @see java.io.IOException + * @since JDK1.8 + */ +public class UncheckedIOException extends RuntimeException { + private static final long serialVersionUID = -8134305061645241065L; + + /** + * Constructs an instance of this class. + * + * @param message + * the detail message + * @param cause + * the {@code IOException} + * + * @throws NullPointerException + * if the cause is {@code null} + */ + public UncheckedIOException(String message, IOException cause) { + super(message, Objects.requireNonNull(cause)); + } + + /** + * Constructs an instance of this class. + * + * @param cause + * the {@code IOException} + * + * @throws NullPointerException + * if the cause is {@code null} + */ + public UncheckedIOException(IOException cause) { + super(Objects.requireNonNull(cause)); + } + + /** + * Returns the cause of this exception. + * + * @return the cause + */ + @Override + public IOException getCause() { + return (IOException) super.getCause(); + } + + /** + * Called to read the object from a stream. + * + * @throws InvalidObjectException + * if the object is invalid or has a cause that is not + * an {@code IOException} + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + Throwable cause = super.getCause(); + if (!(cause instanceof IOException)) + throw new InvalidObjectException("Cause must be an IOException"); + } +}
--- a/src/share/classes/java/lang/AbstractStringBuilder.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/AbstractStringBuilder.java Tue Apr 16 13:13:13 2013 -0700 @@ -44,7 +44,8 @@ * @author Ulf Zibis * @since 1.5 */ -abstract class AbstractStringBuilder implements Appendable, CharSequence { +abstract class AbstractStringBuilder implements Appendable, CharSequence +{ /** * The value is used for character storage. */ @@ -1417,5 +1418,4 @@ final char[] getValue() { return value; } - }
--- a/src/share/classes/java/lang/Byte.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/Byte.java Tue Apr 16 13:13:13 2013 -0700 @@ -515,6 +515,48 @@ */ public static final int BYTES = SIZE / Byte.SIZE; + /** + * Adds two {@code byte}s together as per the + operator. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Byte>}. + * + * @param a an argument. + * @param b another argument. + * @return the sum of {@code a} and {@code b}. + * @since 1.8 + */ + public static byte sum(byte a, byte b) { + return (byte) (a + b); + } + + /** + * Returns the greater of two {@code byte} values. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Byte>}. + * + * @param a an argument. + * @param b another argument. + * @return the larger of {@code a} and {@code b}. + * @since 1.8 + */ + public static byte max(byte a, byte b) { + return (a >= b) ? a : b; + } + + /** + * Returns the lesser of two {@code byte} values. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Byte>}. + * + * @param a an argument. + * @param b another argument. + * @return the lesser of {@code a} and {@code b}. + * @since 1.8 + */ + public static byte min(byte a, byte b) { + return (a <= b) ? a : b; + } + /** use serialVersionUID from JDK 1.1. for interoperability */ private static final long serialVersionUID = -7183698231559129828L; }
--- a/src/share/classes/java/lang/CharSequence.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/CharSequence.java Tue Apr 16 13:13:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2012, 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,13 @@ package java.lang; +import java.util.NoSuchElementException; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; +import java.util.stream.StreamSupport; /** * A <tt>CharSequence</tt> is a readable sequence of <code>char</code> values. This @@ -108,4 +115,82 @@ */ public String toString(); + /** + * Returns a stream of char values from this sequence. If the sequence is + * mutated while the stream is being read, the result is undefined. + * + * @return an Iterable of Character values from this sequence + */ + public default IntStream chars() { + class CharIterator implements PrimitiveIterator.OfInt { + int cur = 0; + + public boolean hasNext() { + return cur < length(); + } + + public int nextInt() { + if (hasNext()) + return charAt(cur++); + else + throw new NoSuchElementException(); + } + + @Override + public void forEachRemaining(IntConsumer block) { + for (; cur < length(); cur++) + block.accept(charAt(cur)); + } + } + + return StreamSupport.intStream(() -> Spliterators.spliterator(new CharIterator(), length(), Spliterator.ORDERED), + Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED); + } + + /** + * Returns a stream of code point values (as {@code Integer} objects) from + * this sequence. Any surrogate pairs encountered in the sequence are + * combined as if by + * {@linkplain Character#toCodePoint Character.toCodePoint} + * and the result is passed to the stream. Any other code units, including + * ordinary BMP characters, unpaired surrogates, and undefined code units, + * are zero-extended to @{code int} values which are then passed + * to the stream. + * + * If the sequence is mutated while the stream is being read, the result + * is undefined. + * + * @return an Iterable of Unicode code points from this sequence + */ + public default IntStream codePoints() { + class CodePointIterator implements PrimitiveIterator.OfInt { + int cur = 0; + + @Override + public void forEachRemaining(IntConsumer block) { + while (cur < length()) { + int cp = Character.codePointAt(CharSequence.this, cur); + cur += Character.charCount(cp); + block.accept(cp); + } + } + + public boolean hasNext() { + return cur < length(); + } + + public int nextInt() { + if (!hasNext()) + throw new NoSuchElementException(); + // TODO: It probably would be faster if the logic from + // the following two methods were inlined and simplified. + int cp = Character.codePointAt(CharSequence.this, cur); + cur += Character.charCount(cp); + return cp; + } + } + + return StreamSupport.intStream(() -> Spliterators.spliteratorUnknownSize(new CodePointIterator(), Spliterator.ORDERED), + Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED); + } }
--- a/src/share/classes/java/lang/Character.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/Character.java Tue Apr 16 13:13:13 2013 -0700 @@ -7234,4 +7234,46 @@ // should never come here return Integer.toHexString(codePoint).toUpperCase(Locale.ENGLISH); } + + /** + * Adds two chars together as per the + operator. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Character>}. + * + * @param a an argument. + * @param b another argument. + * @return the sum of {@code a} and {@code b}. + * @since 1.8 + */ + public static char sum(char a, char b) { + return (char) (a + b); + } + + /** + * Returns the greater of two {@code char} values. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Character>}. + * + * @param a an argument. + * @param b another argument. + * @return the larger of {@code a} and {@code b}. + * @since 1.8 + */ + public static char max(char a, char b) { + return (a >= b) ? a : b; + } + + /** + * Returns the lesser of two {@code char} values. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Character>}. + * + * @param a an argument. + * @param b another argument. + * @return the lesser of {@code a} and {@code b}. + * @since 1.8 + */ + public static char min(char a, char b) { + return (a <= b) ? a : b; + } }
--- a/src/share/classes/java/lang/Iterable.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/Iterable.java Tue Apr 16 13:13:13 2013 -0700 @@ -22,26 +22,55 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package java.lang; import java.util.Iterator; +import java.util.Objects; +import java.util.function.Consumer; /** * Implementing this interface allows an object to be the target of - * the "foreach" statement. + * the "for-each loop" statement. See + * <strong> + * <a href="{@docRoot}/../technotes/guides/language/foreach.html">For-each Loop</a> + * </strong> * * @param <T> the type of elements returned by the iterator * * @since 1.5 + * @jls 14.14.2 The enhanced for statement */ @FunctionalInterface public interface Iterable<T> { - /** - * Returns an iterator over a set of elements of type T. + * Returns an iterator over elements of type {@code T}. * * @return an Iterator. */ Iterator<T> iterator(); + + /** + * Performs the given action on the contents of the {@code Iterable}, in the + * order elements occur when iterating, until all elements have been + * processed or the action throws an exception. Errors or runtime + * exceptions thrown by the action are relayed to the caller. + * + * @implSpec + * <p>The default implementation behaves as if: + * <pre>{@code + * for (T t : this) + * action.accept(t); + * }</pre> + * + * @param action The action to be performed for each element + * @throws NullPointerException if the specified action is null + * @since 1.8 + */ + default void forEach(Consumer<? super T> action) { + Objects.requireNonNull(action); + for (T t : this) { + action.accept(t); + } + } } +
--- a/src/share/classes/java/lang/Short.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/Short.java Tue Apr 16 13:13:13 2013 -0700 @@ -531,6 +531,48 @@ return ((long) x) & 0xffffL; } + /** + * Adds two {@code short} values together as per the + operator. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Short>}. + * + * @param a an argument. + * @param b another argument. + * @return the sum of {@code a} and {@code b}. + * @since 1.8 + */ + public static short sum(short a, short b) { + return (short) (a + b); + } + + /** + * Returns the greater of two {@code short} values. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Short>}. + * + * @param a an argument. + * @param b another argument. + * @return the larger of {@code a} and {@code b}. + * @since 1.8 + */ + public static short max(short a, short b) { + return (a >= b) ? a : b; + } + + /** + * Returns the lesser of two {@code short} values. + * Suitable for conversion as a method reference to functional interfaces such + * as {@code BinaryOperator<Short>}. + * + * @param a an argument. + * @param b another argument. + * @return the lesser of {@code a} and {@code b}. + * @since 1.8 + */ + public static short min(short a, short b) { + return (a <= b) ? a : b; + } + /** use serialVersionUID from JDK 1.1. for interoperability */ private static final long serialVersionUID = 7515723908773894738L; }
--- a/src/share/classes/java/lang/String.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/String.java Tue Apr 16 13:13:13 2013 -0700 @@ -34,6 +34,7 @@ import java.util.Formatter; import java.util.Locale; import java.util.Objects; +import java.util.StringJoiner; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -131,7 +132,7 @@ * string instance within the stream. */ private static final ObjectStreamField[] serialPersistentFields = - new ObjectStreamField[0]; + new ObjectStreamField[0]; /** * Initializes a newly created {@code String} object so that it represents @@ -264,12 +265,12 @@ for (int i = offset, j = 0; i < end; i++, j++) { int c = codePoints[i]; if (Character.isBmpCodePoint(c)) - v[j] = (char)c; + v[j] = (char) c; else Character.toSurrogates(c, v, j++); } - this.value = v; + this.value = v; } /** @@ -317,13 +318,13 @@ char value[] = new char[count]; if (hibyte == 0) { - for (int i = count; i-- > 0;) { - value[i] = (char)(ascii[i + offset] & 0xff); + for (int i = count ; i-- > 0 ;) { + value[i] = (char) (ascii[i + offset] & 0xff); } } else { hibyte <<= 8; - for (int i = count; i-- > 0;) { - value[i] = (char)(hibyte | (ascii[i + offset] & 0xff)); + for (int i = count ; i-- > 0 ;) { + value[i] = (char) (hibyte | (ascii[i + offset] & 0xff)); } } this.value = value; @@ -970,7 +971,7 @@ return true; } if (anObject instanceof String) { - String anotherString = (String) anObject; + String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; @@ -978,7 +979,7 @@ int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) - return false; + return false; i++; } return true; @@ -1003,7 +1004,7 @@ * @since 1.4 */ public boolean contentEquals(StringBuffer sb) { - return contentEquals((CharSequence) sb); + return contentEquals((CharSequence)sb); } private boolean nonSyncContentEquals(AbstractStringBuilder sb) { @@ -1044,7 +1045,7 @@ if (cs instanceof StringBuffer) { synchronized(cs) { return nonSyncContentEquals((AbstractStringBuilder)cs); - } + } } else { return nonSyncContentEquals((AbstractStringBuilder)cs); } @@ -1148,13 +1149,13 @@ char v2[] = anotherString.value; int k = 0; - while (k < lim) { - char c1 = v1[k]; - char c2 = v2[k]; - if (c1 != c2) { - return c1 - c2; - } - k++; + while (k < lim) { + char c1 = v1[k]; + char c2 = v2[k]; + if (c1 != c2) { + return c1 - c2; + } + k++; } return len1 - len2; } @@ -1174,7 +1175,7 @@ public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); private static class CaseInsensitiveComparator - implements Comparator<String>, java.io.Serializable { + implements Comparator<String>, java.io.Serializable { // use serialVersionUID from JDK 1.2.2 for interoperability private static final long serialVersionUID = 8575799808933029326L; @@ -1261,7 +1262,7 @@ * {@code false} otherwise. */ public boolean regionMatches(int toffset, String other, int ooffset, - int len) { + int len) { char ta[] = value; int to = toffset; char pa[] = other.value; @@ -1307,7 +1308,7 @@ * integer <i>k</i> less than {@code len} such that: * <blockquote><pre> * Character.toLowerCase(this.charAt(toffset+k)) != - Character.toLowerCase(other.charAt(ooffset+k)) + Character.toLowerCase(other.charAt(ooffset+k)) * </pre></blockquote> * and: * <blockquote><pre> @@ -1331,7 +1332,7 @@ * argument. */ public boolean regionMatches(boolean ignoreCase, int toffset, - String other, int ooffset, int len) { + String other, int ooffset, int len) { char ta[] = value; int to = toffset; char pa[] = other.value; @@ -1565,7 +1566,7 @@ final char lo = Character.lowSurrogate(ch); final int max = value.length - 1; for (int i = fromIndex; i < max; i++) { - if (value[i] == hi && value[i + 1] == lo) { + if (value[i] == hi && value[i+1] == lo) { return i; } } @@ -1661,7 +1662,7 @@ char lo = Character.lowSurrogate(ch); int i = Math.min(fromIndex, value.length - 2); for (; i >= 0; i--) { - if (value[i] == hi && value[i + 1] == lo) { + if (value[i] == hi && value[i+1] == lo) { return i; } } @@ -1740,8 +1741,8 @@ * @param fromIndex the index to begin searching from. */ static int indexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { + char[] target, int targetOffset, int targetCount, + int fromIndex) { if (fromIndex >= sourceCount) { return (targetCount == 0 ? sourceCount : -1); } @@ -1752,7 +1753,7 @@ return fromIndex; } - char first = target[targetOffset]; + char first = target[targetOffset]; int max = sourceOffset + (sourceCount - targetCount); for (int i = sourceOffset + fromIndex; i <= max; i++) { @@ -1849,8 +1850,8 @@ * @param fromIndex the index to begin searching from. */ static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { + char[] target, int targetOffset, int targetCount, + int fromIndex) { /* * Check arguments; return immediately where possible. For * consistency, don't check for null str. @@ -1872,7 +1873,7 @@ int min = sourceOffset + targetCount - 1; int i = min + fromIndex; - startSearchForLastChar: + startSearchForLastChar: while (true) { while (i >= min && source[i] != strLastChar) { i--; @@ -1975,9 +1976,9 @@ * This method is defined so that the {@code String} class can implement * the {@link CharSequence} interface. </p> * - * @param beginIndex the begin index, inclusive. - * @param endIndex the end index, exclusive. - * @return the specified subsequence. + * @param beginIndex the begin index, inclusive. + * @param endIndex the end index, exclusive. + * @return the specified subsequence. * * @throws IndexOutOfBoundsException * if {@code beginIndex} or {@code endIndex} is negative, @@ -2064,7 +2065,7 @@ } if (i < len) { char buf[] = new char[len]; - for (int j = 0; j < i; j++) { + for (int j = 0 ; j < i ; j++) { buf[j] = val[j]; } while (i < len) { @@ -2225,7 +2226,7 @@ */ public String replace(CharSequence target, CharSequence replacement) { return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( - this).replaceAll(Matcher.quoteReplacement(replacement.toString())); + this).replaceAll(Matcher.quoteReplacement(replacement.toString())); } /** @@ -2311,11 +2312,11 @@ */ public String[] split(String regex, int limit) { /* fastpath if the regex is a - (1)one-char String and this character is not one of the - RegEx's meta characters ".$|()[{^?*+\\", or - (2)two-char String and the first char is the backslash and - the second is not the ascii digit or ascii letter. - */ + (1)one-char String and this character is not one of the + RegEx's meta characters ".$|()[{^?*+\\", or + (2)two-char String and the first char is the backslash and + the second is not the ascii digit or ascii letter. + */ char ch = 0; if (((regex.value.length == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || @@ -2344,7 +2345,7 @@ } // If no match was found, return this if (off == 0) - return new String[]{this}; + return new String[] { this }; // Add remaining segment if (!limited || list.size() < limit) @@ -2353,7 +2354,7 @@ // Construct result int resultSize = list.size(); if (limit == 0) - while (resultSize > 0 && list.get(resultSize - 1).length() == 0) + while (resultSize > 0 && list.get(resultSize-1).length() == 0) resultSize--; String[] result = new String[resultSize]; return list.subList(0, resultSize).toArray(result); @@ -2404,6 +2405,70 @@ } /** + * Returns a new String composed of the String values returned + * by calling {@link String#valueOf(Object)} on each element and joining them together + * with the specified infix delimiter. + * + * <blockquote>An example: + * <pre> + * String message = String.join("-", "Java", "is", "cool" ); + * // message returned is: "Java-is-cool" + * </pre></blockquote> + * + * @param delimiter the delimiter String + * @param elements the Object elements to join together. + * @return a new String that is composed of the elements parameters using + * the first parameter as the infix delimiter. + * + * @see java.util.StringJoiner + * @since 1.8 + */ + public static String join(CharSequence delimiter, CharSequence... elements) { + Objects.requireNonNull(delimiter); + Objects.requireNonNull(elements); + // Number of elements not likely worth Arrays.stream overhead. + StringJoiner joiner = new StringJoiner(delimiter); + for (CharSequence cs: elements) { + joiner.add(cs); + } + return joiner.toString(); + } + + /** + * Returns a new String composed of the String values returned + * by calling {@link String#valueOf(Object)} on each element and joining them together + * with the specified infix delimiter. + * + * <blockquote>Two examples: + * <pre> + * List<String> strings = new LinkedList<String>(); + * strings.add( "Java" );strings.add( "is" ); strings.add( "cool" ); + * String message = String.join(" ", strings ); + * //message returned is: "Java is cool" + * Set<String> strings = new HashSet<String>(); + * Strings.add( "Java" ); strings.add( "is" ); strings.add( "very" ); strings.add( "cool" ); + * String message = String.join("-", strings ); + * //message returned is: "Java-is-very-cool" + * </pre></blockquote> + * + * @param delimiter a String that is used to separate each element in the resulting String + * @param elements an Iterable that will have its elements joined together. + * @return a new String that is composed from the elements parameters + * + * @see java.util.StringJoiner + * @since 1.8 + */ + public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) { + Objects.requireNonNull(delimiter); + Objects.requireNonNull(elements); + StringJoiner joiner = new StringJoiner(delimiter); + for (CharSequence cs: elements) { + joiner.add(cs); + } + return joiner.toString(); + } + + /** * Converts all of the characters in this {@code String} to lower * case using the rules of the given {@code Locale}. Case mapping is based * on the Unicode Standard version specified by the {@link java.lang.Character Character} @@ -2460,7 +2525,7 @@ throw new NullPointerException(); } - int firstUpper; + int firstUpper; final int len = value.length; /* Now check if there are any characters that need to be changed. */ @@ -2485,15 +2550,15 @@ } char[] result = new char[len]; - int resultOffset = 0; /* result may grow, so i+resultOffset - * is the write location in result */ + int resultOffset = 0; /* result may grow, so i+resultOffset + * is the write location in result */ /* Just copy the first few lowerCase characters. */ System.arraycopy(value, 0, result, 0, firstUpper); String lang = locale.getLanguage(); boolean localeDependent = - (lang == "tr" || lang == "az" || lang == "lt"); + (lang == "tr" || lang == "az" || lang == "lt"); char[] lowerCharArray; int lowerChar; int srcChar; @@ -2517,13 +2582,13 @@ if ((lowerChar == Character.ERROR) || (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) { if (lowerChar == Character.ERROR) { - if (!localeDependent && srcChar == '\u0130') { + if (!localeDependent && srcChar == '\u0130') { + lowerCharArray = + ConditionalSpecialCasing.toLowerCaseCharArray(this, i, Locale.ENGLISH); + } else { lowerCharArray = - ConditionalSpecialCasing.toLowerCaseCharArray(this, i, Locale.ENGLISH); - } else { - lowerCharArray = - ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale); - } + ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale); + } } else if (srcCount == 2) { resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount; continue; @@ -2538,12 +2603,12 @@ System.arraycopy(result, 0, result2, 0, i + resultOffset); result = result2; } - for (int x = 0; x < mapLen; ++x) { - result[i + resultOffset + x] = lowerCharArray[x]; + for (int x=0; x<mapLen; ++x) { + result[i+resultOffset+x] = lowerCharArray[x]; } resultOffset += (mapLen - srcCount); } else { - result[i + resultOffset] = (char)lowerChar; + result[i+resultOffset] = (char)lowerChar; } } return new String(result, 0, len + resultOffset); @@ -2625,7 +2690,7 @@ throw new NullPointerException(); } - int firstLower; + int firstLower; final int len = value.length; /* Now check if there are any characters that need to be changed. */ @@ -2651,15 +2716,15 @@ } char[] result = new char[len]; /* may grow */ - int resultOffset = 0; /* result may grow, so i+resultOffset - * is the write location in result */ + int resultOffset = 0; /* result may grow, so i+resultOffset + * is the write location in result */ /* Just copy the first few upperCase characters. */ System.arraycopy(value, 0, result, 0, firstLower); String lang = locale.getLanguage(); boolean localeDependent = - (lang == "tr" || lang == "az" || lang == "lt"); + (lang == "tr" || lang == "az" || lang == "lt"); char[] upperCharArray; int upperChar; int srcChar; @@ -2683,7 +2748,7 @@ if (upperChar == Character.ERROR) { if (localeDependent) { upperCharArray = - ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale); + ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale); } else { upperCharArray = Character.toUpperCaseCharArray(srcChar); } @@ -2701,12 +2766,12 @@ System.arraycopy(result, 0, result2, 0, i + resultOffset); result = result2; } - for (int x = 0; x < mapLen; ++x) { - result[i + resultOffset + x] = upperCharArray[x]; + for (int x=0; x<mapLen; ++x) { + result[i+resultOffset+x] = upperCharArray[x]; } resultOffset += (mapLen - srcCount); } else { - result[i + resultOffset] = (char)upperChar; + result[i+resultOffset] = (char)upperChar; } } return new String(result, 0, len + resultOffset); @@ -2838,7 +2903,7 @@ * @see java.util.Formatter * @since 1.5 */ - public static String format(String format, Object... args) { + public static String format(String format, Object ... args) { return new Formatter().format(format, args).toString(); } @@ -2879,7 +2944,7 @@ * @see java.util.Formatter * @since 1.5 */ - public static String format(Locale l, String format, Object... args) { + public static String format(Locale l, String format, Object ... args) { return new Formatter(l).format(format, args).toString(); }
--- a/src/share/classes/java/lang/StringBuffer.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/StringBuffer.java Tue Apr 16 13:13:13 2013 -0700 @@ -25,6 +25,8 @@ package java.lang; +import java.util.Arrays; + /** * A thread-safe, mutable sequence of characters. @@ -514,7 +516,7 @@ */ @Override public synchronized StringBuffer insert(int dstOffset, CharSequence s, - int start, int end) + int start, int end) { super.insert(dstOffset, s, start, end); return this; @@ -524,7 +526,7 @@ * @throws StringIndexOutOfBoundsException {@inheritDoc} */ @Override - public StringBuffer insert(int offset, boolean b) { + public StringBuffer insert(int offset, boolean b) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of b to String by super class method super.insert(offset, b); @@ -624,6 +626,7 @@ @Override public synchronized StringBuffer reverse() { super.reverse(); + return this; }
--- a/src/share/classes/java/lang/StringBuilder.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/StringBuilder.java Tue Apr 16 13:13:13 2013 -0700 @@ -25,6 +25,8 @@ package java.lang; +import java.util.Arrays; + /** * A mutable sequence of characters. This class provides an API compatible
--- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Tue Apr 16 13:13:13 2013 -0700 @@ -184,7 +184,7 @@ for (int i=0; i<markerInterfaces.length; i++) { interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/'); } - cw.visit(CLASSFILE_VERSION, ACC_SUPER, + cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL, interfaces);
--- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Tue Apr 16 13:13:13 2013 -0700 @@ -295,6 +295,9 @@ String invokerDesc = invokerType.toMethodDescriptorString(); mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null); + + // Force inlining of this invoker method. + mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); } /** @@ -521,9 +524,6 @@ // Mark this method as a compiled LambdaForm mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); - // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { @@ -943,9 +943,6 @@ // Suppress this method in backtraces displayed to the user. mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - // Don't inline the interpreter entry. - mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); - // create parameter array emitIconstInsn(invokerType.parameterCount()); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); @@ -1008,9 +1005,6 @@ // Suppress this method in backtraces displayed to the user. mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); - // Load receiver emitAloadInsn(0);
--- a/src/share/classes/java/lang/invoke/LambdaForm.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/LambdaForm.java Tue Apr 16 13:13:13 2013 -0700 @@ -69,7 +69,7 @@ * A lambda has a void result if and only if its result index is -1. * If a temporary has the type "V", it cannot be the subject of a NameRef, * even though possesses a number. - * Note that all reference types are erased to "L", which stands for {@code Object). + * Note that all reference types are erased to "L", which stands for {@code Object}. * All subword types (boolean, byte, short, char) are erased to "I" which is {@code int}. * The other types stand for the usual primitive types. * <p> @@ -592,7 +592,6 @@ private int invocationCounter = 0; @Hidden - @DontInline /** Interpretively invoke this form on the given arguments. */ Object interpretWithArguments(Object... argumentValues) throws Throwable { if (TRACE_INTERPRETER) @@ -607,7 +606,6 @@ } @Hidden - @DontInline /** Evaluate a single Name within this form, applying its function to its arguments. */ Object interpretName(Name name, Object[] values) throws Throwable { if (TRACE_INTERPRETER)
--- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java Tue Apr 16 13:13:13 2013 -0700 @@ -220,7 +220,7 @@ * of the caller. * @param invokedName Stacked automatically by VM; the name of the invoked method as it appears at the call site. * Currently unused. - * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes thefu + * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the * expected static type of the returned lambda object, and the static types of the captured * arguments for the lambda. In the event that the implementation method is an instance method, * the first argument in the invocation signature will correspond to the receiver.
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Apr 16 13:13:13 2013 -0700 @@ -310,9 +310,9 @@ } static class AsVarargsCollector extends MethodHandle { - private final MethodHandle target; - private final Class<?> arrayType; - private MethodHandle cache; + MethodHandle target; + final Class<?> arrayType; + MethodHandle cache; AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) { super(type, reinvokerForm(type));
--- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java Tue Apr 16 13:13:13 2013 -0700 @@ -27,10 +27,10 @@ import java.lang.invoke.MethodHandleNatives.Constants; /** - * Cracking (reflecting) method handles back into their constituent symbolic parts. - * + * Cracking (reflecting) method handles back into their constituent symbolic parts. + * */ -final class MethodHandleInfo { +public final class MethodHandleInfo { public static final int REF_getField = Constants.REF_getField, REF_getStatic = Constants.REF_getStatic, @@ -72,15 +72,15 @@ return methodType; } - public int getModifiers() { - return -1; //TODO - } + public int getModifiers() { + return -1; //TODO + } public int getReferenceKind() { return referenceKind; } - - static String getReferenceKindString(int referenceKind) { + + public static String getReferenceKindString(int referenceKind) { switch (referenceKind) { case REF_getField: return "getfield"; case REF_getStatic: return "getstatic";
--- a/src/share/classes/java/lang/invoke/MethodHandleProxies.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleProxies.java Tue Apr 16 13:13:13 2013 -0700 @@ -139,7 +139,7 @@ // public static <T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) { - if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers())) + if (!intfc.isInterface()/* || !Modifier.isPublic(intfc.getModifiers())*/) throw new IllegalArgumentException("not a public interface: "+intfc.getName()); final MethodHandle mh; if (System.getSecurityManager() != null) {
--- a/src/share/classes/java/nio/file/DirectoryStream.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/nio/file/DirectoryStream.java Tue Apr 16 13:13:13 2013 -0700 @@ -28,16 +28,21 @@ import java.util.Iterator; import java.io.Closeable; import java.io.IOException; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * An object to iterate over the entries in a directory. A directory stream - * allows for the convenient use of the for-each construct to iterate over a - * directory. + * allows for the convenient use of the for-each construct or the {@link + * Stream} API to iterate over a directory. * * <p> <b> While {@code DirectoryStream} extends {@code Iterable}, it is not a - * general-purpose {@code Iterable} as it supports only a single {@code - * Iterator}; invoking the {@link #iterator iterator} method to obtain a second - * or subsequent iterator throws {@code IllegalStateException}. </b> + * general-purpose {@code Iterable}. A {@code DirectoryStream} supports only a + * single iteration via either the {@link #iterator iterator} or the {@link + * #entries entries} method. Invoking either method to do a second or + * subsequent iteration throws {@code IllegalStateException}. </b> * * <p> An important property of the directory stream's {@code Iterator} is that * its {@link Iterator#hasNext() hasNext} method is guaranteed to read-ahead by @@ -61,13 +66,13 @@ * } * </pre> * - * <p> Once a directory stream is closed, then further access to the directory, - * using the {@code Iterator}, behaves as if the end of stream has been reached. - * Due to read-ahead, the {@code Iterator} may return one or more elements - * after the directory stream has been closed. Once these buffered elements - * have been read, then subsequent calls to the {@code hasNext} method returns - * {@code false}, and subsequent calls to the {@code next} method will throw - * {@code NoSuchElementException}. + * <p> Once a directory stream is closed, then further access to the + * directory, using the {@code Iterator} or {@code Stream}, behaves as if the + * end of stream has been reached. Due to read-ahead, one or more elements may + * be returned after the directory stream has been closed. Once these buffered + * elements have been read, then subsequent calls to the {@code hasNext} + * method returns {@code false}, and subsequent calls to the {@code next} + * method will throw {@code NoSuchElementException}. * * <p> A directory stream is not required to be <i>asynchronously closeable</i>. * If a thread is blocked on the directory stream's iterator reading from the @@ -75,13 +80,14 @@ * second thread may block until the read operation is complete. * * <p> If an I/O error is encountered when accessing the directory then it - * causes the {@code Iterator}'s {@code hasNext} or {@code next} methods to - * throw {@link DirectoryIteratorException} with the {@link IOException} as the - * cause. As stated above, the {@code hasNext} method is guaranteed to - * read-ahead by at least one element. This means that if {@code hasNext} method - * returns {@code true}, and is followed by a call to the {@code next} method, - * then it is guaranteed that the {@code next} method will not fail with a - * {@code DirectoryIteratorException}. + * causes the methods to throw {@link DirectoryIteratorException} with the + * {@link IOException} as the cause. This could be the {@code Iterator}'s + * {@code hasNext} or {@code next} method or one of the {@code Stream} methods. + * As stated above, the {@code hasNext} method is guaranteed to read-ahead by + * at least one element. This means that if {@code hasNext} method returns + * {@code true}, and is followed by a call to the {@code next} method, then it + * is guaranteed that the {@code next} method will not fail with a {@code + * DirectoryIteratorException}. * * <p> The elements returned by the iterator are in no specific order. Some file * systems maintain special links to the directory itself and the directory's @@ -151,9 +157,23 @@ * @return the iterator associated with this {@code DirectoryStream} * * @throws IllegalStateException - * if this directory stream is closed or the iterator has already - * been returned + * if this directory stream is closed or the iterator or stream + * has already been returned */ @Override Iterator<T> iterator(); + + /** + * Returns the stream associated with this {@code DirectoryStream}. + * + * @return the stream associated with this {@code DirectoryStream} + * + * @throws IllegalStateException + * if this directory stream is closed or the iterator or stream + * has already been returned + * @since 1.8 + */ + default Stream<T> entries() { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(), Spliterator.DISTINCT)); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/nio/file/FileTreeIterator.java Tue Apr 16 13:13:13 2013 -0700 @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2013, 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. + */ + +package java.nio.file; + +import java.io.Closeable; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayDeque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.nio.file.attribute.BasicFileAttributes; + +class FileTreeIterator implements Iterator<FileTreeIterator.Entry>, Closeable { + + /** + * A pair of {@code Path} and its {@code BasicFileAttributes}. + */ + static class Entry { + private final Path file; + private final BasicFileAttributes attrs; + // Latched exception thrown when tried to read the BasicFileAttributes + private RuntimeException latched_ex; + + Entry(Path file, BasicFileAttributes attrs) { + this.file = Objects.requireNonNull(file); + this.attrs = attrs; + latched_ex = null; + } + + Entry(Path file, RuntimeException ex) { + this.file = Objects.requireNonNull(file); + this.latched_ex = Objects.requireNonNull(ex); + attrs = null; + } + + static Entry make(Path file, boolean followLinks) { + Objects.requireNonNull(file); + BasicFileAttributes attrs; + try { + if (followLinks) { + try { + attrs = Files.readAttributes(file, BasicFileAttributes.class); + return new Entry(file, attrs); + } catch (IOException notcare) { + // ignore, try not to follow link + } + } + attrs = Files.readAttributes(file, BasicFileAttributes.class, + LinkOption.NOFOLLOW_LINKS); + return new Entry(file, attrs); + } catch (IOException ioe) { + return new Entry(file, new UncheckedIOException(ioe)); + } catch (RuntimeException ex) { + return new Entry(file, ex); + } + } + + public Entry ignoreException() { + latched_ex = null; + return this; + } + + public Path getPath() { + return file; + } + + /** + * Could return null if ignoreException + */ + public BasicFileAttributes getFileAttributes() { + if (latched_ex != null) { + throw latched_ex; + } + return attrs; + } + + public void checkException() throws IOException { + if (latched_ex != null) { + if (latched_ex instanceof UncheckedIOException) { + throw ((UncheckedIOException) latched_ex).getCause(); + } else { + throw latched_ex; + } + } + } + } + + private static class Context { + final Path file; + final BasicFileAttributes attrs; + final DirectoryStream<Path> ds; + final Iterator<Path> itor; + + Context(Path file, BasicFileAttributes attrs, DirectoryStream<Path> ds, Iterator<Path> itor) { + this.file = file; + this.attrs = attrs; + this.ds = ds; + this.itor = itor; + } + } + + private static class VisitorException extends RuntimeException { + VisitorException(IOException ioe) { + super(ioe); + } + + @Override + public IOException getCause() { + return (IOException) super.getCause(); + } + } + + private final boolean followLinks; + private final int maxDepth; + private final ArrayDeque<Context> stack = new ArrayDeque<>(); + + private FileVisitor<Path> visitorProxy; + private Entry next; + + private FileTreeIterator(int maxDepth, + FileVisitOption... options) { + this.maxDepth = maxDepth; + + boolean follow = false; + for (FileVisitOption opt : options) { + switch(opt) { + case FOLLOW_LINKS: + follow = true; + break; + default: + // nothing should be here + break; + } + } + this.followLinks = follow; + } + + private FileTreeIterator init(Path start, FileVisitor<? super Path> visitor) throws IOException { + next = Entry.make(start, followLinks); + try { + next.checkException(); + } catch (SecurityException se) { + // First level, re-throw it. + throw se; + } catch (IOException ioe) { + if (visitor != null) { + visitor.visitFileFailed(start, ioe); + } else { + throw ioe; + } + } + + // Wrap IOException in VisitorException so we can throw from hasNext() + // and distinguish them for re-throw later. + // For non-proxy mode, exception come in should be re-thrown so the caller know + // it is not processed and deal with it accordingly. + visitorProxy = new FileVisitor<Path>() { + public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) { + if (visitor != null) { + try { + return visitor.preVisitDirectory(path, attrs); + } catch (IOException ex) { + throw new VisitorException(ex); + } + } + return FileVisitResult.CONTINUE; + } + + public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException { + if (visitor != null) { + try { + return visitor.postVisitDirectory(path, exc); + } catch (IOException ex) { + throw new VisitorException(ex); + } + } else if (exc != null) { + throw exc; + } + return FileVisitResult.CONTINUE; + } + + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { + if (visitor != null) { + try { + return visitor.visitFile(path, attrs); + } catch (IOException ex) { + throw new VisitorException(ex); + } + } + return FileVisitResult.CONTINUE; + } + + public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException { + if (visitor != null) { + try { + return visitor.visitFileFailed(path, exc); + } catch (IOException ex) { + throw new VisitorException(ex); + } + } else if (exc != null) { + throw exc; + } + return FileVisitResult.CONTINUE; + } + }; + + // Setup first visit for directory + visitNext(); + + return this; + } + + public static FileTreeIterator iterator(Path start, int maxDepth, + FileVisitOption... options) throws IOException { + return new FileTreeIterator(maxDepth, options).init(start, null); + } + + public static void walkThrough(Path start, int maxDepth, + FileVisitor visitor, + FileVisitOption... options) throws IOException { + Objects.requireNonNull(visitor); + FileTreeIterator itor = new FileTreeIterator(maxDepth, options).init(start, visitor); + try { + while (itor.hasNext()) { + itor.next(); + } + } catch (VisitorException ex) { + // Only VisitorException is processed here as others should be + // handled by FileVisitor already. + throw ex.getCause(); + } + } + + private boolean detectLoop(Path dir, BasicFileAttributes attrs) { + Object key = attrs.fileKey(); + for (Context ctx : stack) { + Object ancestorKey = ctx.attrs.fileKey(); + if (key != null && ancestorKey != null) { + if (key.equals(ancestorKey)) { + return true; + } + } else { + boolean isSameFile = false; + try { + isSameFile = Files.isSameFile(dir, ctx.file); + } catch (IOException x) { + // ignore + } catch (SecurityException x) { + // ignore + } + if (isSameFile) { + return true; + } + } + } + + return false; + } + + private void evalVisitorResult(FileVisitResult result) { + Objects.requireNonNull(result); + switch (result) { + case TERMINATE: + try { + close(); + } catch (IOException ioe) { + // ignore + } + break; + case SKIP_SIBLINGS: + case SKIP_SUBTREE: + // stop iterate in the containing folder + if (! stack.isEmpty()) { + exitDirectory(null); + } + break; + case CONTINUE: + break; + } + } + + private void enteringDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + // Detect loop when follow links + if (followLinks && detectLoop(dir, attrs)) { + // Loop detected + throw new FileSystemLoopException(dir.toString()); + // ?? skip is better ?? + // return; + } + + DirectoryStream<Path> ds = Files.newDirectoryStream(dir); + stack.push(new Context(dir, attrs, ds, ds.iterator())); + } + + private void exitDirectory(DirectoryIteratorException die) { + Context ctx = stack.pop(); + IOException failure = (die == null) ? null : die.getCause(); + + try { + ctx.ds.close(); + } catch (IOException ioe) { + if (failure != null) { + failure = ioe; + } + } + + try { + evalVisitorResult(visitorProxy.postVisitDirectory(ctx.file, failure)); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + // retain DirectoryIteratorException information ? + // throw (die == null) ? new UncheckedIOException(ex) : die; + } + } + + private void visitNext() { + Path p = next.file; + try { + BasicFileAttributes attrs; + try { + attrs = next.getFileAttributes(); + } catch (UncheckedIOException uioe) { + throw uioe.getCause(); + } + if (attrs.isDirectory() && stack.size() < maxDepth) { + enteringDirectory(p, attrs); + FileVisitResult result = visitorProxy.preVisitDirectory(p, attrs); + // Simply undo enter, not calling postVisitDirectory + if (FileVisitResult.CONTINUE != result) { + Context ctx = stack.pop(); + try { + ctx.ds.close(); + } catch (IOException ioe) { + // ignore + } + } + // deal result from containing folder + evalVisitorResult(result); + } else { + evalVisitorResult(visitorProxy.visitFile(p, attrs)); + } + } catch (IOException ioe) { + try { + evalVisitorResult(visitorProxy.visitFileFailed(p, ioe)); + } catch (IOException ioe2) { + throw new UncheckedIOException(ioe2); + } + } + } + + /** + * When there is an exception occurred, we will try to resume the iteration + * to next element. So the exception is thrown, and next call to hasNext() + * will continue the iteration. + */ + public boolean hasNext() { + // next was read-ahead, not yet fetched. + if (next != null) { + return true; + } + + // Check if iterator had been closed. + if (stack.isEmpty()) { + return false; + } + + Iterator<Path> itor = stack.peek().itor; + try { + Path p = itor.next(); + next = Entry.make(p, followLinks); + visitNext(); + } catch (SecurityException se) { + // ignore and skip this file + next = null; + return hasNext(); + } catch (DirectoryIteratorException die) { + // try to resume from level above + exitDirectory(die); + } catch (NoSuchElementException nsee) { + // nothing left at this level + exitDirectory(null); + } + return stack.isEmpty() ? false : hasNext(); + } + + public Entry next() { + if (next != null || hasNext()) { + try { + return next; + } finally { + next = null; + } + } else { + throw new NoSuchElementException(); + } + } + + public void close() throws IOException { + IOException ioe = null; + + for (Context ctx : stack) { + try { + ctx.ds.close(); + } catch (IOException ex) { + // ignore so we try to close all DirectoryStream + // keep the last exception to throw later + ioe = ex; + } + } + + next = null; + stack.clear(); + + if (ioe != null) { + // Throw at least one if there is any + throw ioe; + } + } +} \ No newline at end of file
--- a/src/share/classes/java/nio/file/Files.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/nio/file/Files.java Tue Apr 16 13:13:13 2013 -0700 @@ -29,6 +29,7 @@ import java.nio.file.spi.FileSystemProvider; import java.nio.file.spi.FileTypeDetector; import java.nio.channels.SeekableByteChannel; +import java.io.Closeable; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -38,7 +39,13 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.*; +import java.util.function.BiPredicate; +import java.util.stream.CloseableStream; +import java.util.stream.DelegatingStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import java.security.AccessController; import java.security.PrivilegedAction; import java.nio.charset.Charset; @@ -2589,7 +2596,8 @@ { if (maxDepth < 0) throw new IllegalArgumentException("'maxDepth' is negative"); - new FileTreeWalker(options, visitor, maxDepth).walk(start); + //new FileTreeWalker(options, visitor, maxDepth).walk(start); + FileTreeIterator.walkThrough(start, maxDepth, visitor, options.toArray(new FileVisitOption[0])); return start; } @@ -3124,4 +3132,337 @@ } return path; } + + // -- Stream APIs -- + /** + * Implementation of CloseableStream + */ + static class DelegatingCloseableStream<T> extends DelegatingStream<T> + implements CloseableStream<T> + { + private final Closeable closeable; + + DelegatingCloseableStream(Closeable c, Stream<T> delegate) { + super(delegate); + this.closeable = c; + } + + public void close() { + try { + closeable.close(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + /** + * Return a lazily populated {@code CloseableStream}, the elements of + * which are the entries in the directory. The listing is not recursive. + * + * <p> The elements of the stream are {@link Path} objects that are obtained + * as if by {@link Path#resolve(Path) resolving} the name of the directory + * entry against {@code dir}. Some file systems maintain special links to + * the directory itself and the directory's parent directory. Entries + * representing these links are not included. + * + * <p> The stream is <i>weakly consistent</i>. It is thread safe but does + * not freeze the directory while iterating, so it may (or may not) + * reflect updates to the directory that occur after returned from this + * method. + * + * <p> When not using the try-with-resources construct, then stream's + * {@link CloseableStream#close close} method should be invoked after + * operation is completed so as to free any resources held for the open + * directory. + * + * <p> If an {@link IOException} is thrown when accessing the directory + * after returned from this method, it is wrapped in an {@link + * UncheckedIOException} which will be thrown from the method that caused + * the access to take place. + * + * @param dir The path to the directory + * @return The {@link CloseableStream} describing the content of the + * directory + * + * @throws NotDirectoryException + * if the file could not otherwise be opened because it is not + * a directory <i>(optional specific exception)</i> + * @throws IOException + * if an I/O error occurs when opening the directory + * @throws SecurityException + * In the case of the default provider, and a security manager is + * installed, the {@link SecurityManager#checkRead(String) checkRead} + * method is invoked to check read access to the directory. + * + * @see #newDirectoryStream(Path) + * @see DirectoryStream#entries() + * @since 1.8 + */ + public static CloseableStream<Path> list(Path dir) throws IOException { + DirectoryStream<Path> ds = Files.newDirectoryStream(dir); + final Iterator<Path> delegate = ds.iterator(); + // Re-wrap DirectoryIteratorException to UncheckedIOException + Iterator<Path> it = new Iterator<Path>() { + public boolean hasNext() { + try { + return delegate.hasNext(); + } catch (DirectoryIteratorException die) { + throw new UncheckedIOException(die.getCause()); + } + } + + public Path next() { + try { + return delegate.next(); + } catch (DirectoryIteratorException die) { + throw new UncheckedIOException(die.getCause()); + } + } + }; + + return new DelegatingCloseableStream<>(ds, + StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, + Spliterator.DISTINCT))); + } + + /** + * Return a {@code CloseableStream} that is lazily populated with {@code + * Path} by walking the file tree rooted at a given starting file. The + * file tree is traversed <em>depth-first</em>, the elements in the stream + * are {@link Path} objects that are obtained as if by {@link + * Path#resolve(Path) resolving} the relative path against {@code start}. + * + * <p> The {@code stream} walks the file tree as elements are consumed. + * The {@code CloseableStream} returned is guaranteed to have at least one + * element, the starting file itself. For each file visited, the stream + * attempts to read its {@link BasicFileAttributes}. If the file is a + * directory and can be opened successfully, entries in the directory, and + * their <em>descendants</em> will follow the directory in the stream as + * they are encountered. When all entries have been visited, then the + * directory is closed. The file tree walk then continues at the next + * <em>sibling</em> of the directory. + * + * <p> The stream is <i>weakly consistent</i>. It does not freeze the + * file tree while iterating, so it may (or may not) reflect updates to + * the file tree that occur after returned from this method. + * + * <p> By default, symbolic links are not automatically followed by this + * method. If the {@code options} parameter contains the {@link + * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then symbolic links are + * followed. When following links, and the attributes of the target cannot + * be read, then this method attempts to get the {@code BasicFileAttributes} + * of the link. + * + * <p> If the {@code options} parameter contains the {@link + * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then the stream keeps + * track of directories visited so that cycles can be detected. A cycle + * arises when there is an entry in a directory that is an ancestor of the + * directory. Cycle detection is done by recording the {@link + * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories, + * or if file keys are not available, by invoking the {@link #isSameFile + * isSameFile} method to test if a directory is the same file as an + * ancestor. When a cycle is detected it is treated as an I/O error with + * an instance of {@link FileSystemLoopException}. + * + * <p> The {@code maxDepth} parameter is the maximum number of levels of + * directories to visit. A value of {@code 0} means that only the starting + * file is visited, unless denied by the security manager. A value of + * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all + * levels should be visited. + * + * <p> When a security manager is installed and it denies access to a file + * (or directory), then it is ignored and not included in the stream. + * + * <p> When not using the try-with-resources construct, then stream's + * {@link CloseableStream#close close} method should be invoked after + * operation is completed so as to free any resources held for the open + * directory. + * + * <p> If an {@link IOException} is thrown when accessing the directory + * after returned from this method, it is wrapped in an {@link + * UncheckedIOException} which will be thrown from the method that caused + * the access to take place. + * + * @param start + * the starting file + * @param maxDepth + * the maximum number of directory levels to visit + * @param options + * options to configure the traversal + * + * @return the {@link CloseableStream} of {@link Path} + * + * @throws IllegalArgumentException + * if the {@code maxDepth} parameter is negative + * @throws SecurityException + * If the security manager denies access to the starting file. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String) checkRead} method is invoked + * to check read access to the directory. + * @throws IOException + * if an I/O error is thrown when accessing the starting file. + * @since 1.8 + */ + public static CloseableStream<Path> walk(Path start, int maxDepth, + FileVisitOption... options) + throws IOException + { + if (maxDepth < 0) { + throw new IllegalArgumentException("'maxDepth' is negative"); + } + + FileTreeIterator itor = FileTreeIterator.iterator(start, maxDepth, options); + return new DelegatingCloseableStream<>(itor, + StreamSupport.stream(Spliterators.spliteratorUnknownSize(itor, Spliterator.DISTINCT)) + .map(entry -> entry.getPath())); + } + + /** + * Return a {@code CloseableStream} that is lazily populated with {@code + * Path} by walking the file tree rooted at a given starting file. The + * file tree is traversed <em>depth-first</em>, the elements in the stream + * are {@link Path} objects that are obtained as if by {@link + * Path#resolve(Path) resolving} the relative path against {@code start}. + * + * <p> This method works as if invoking it were equivalent to evaluating the + * expression: + * <blockquote><pre> + * walk(start, Integer.MAX_VALUE, options) + * </pre></blockquote> + * In other words, it visits all levels of the file tree. + * + * @param start + * the starting file + * @param options + * options to configure the traversal + * + * @return the {@link CloseableStream} of {@link Path} + * + * @throws SecurityException + * If the security manager denies access to the starting file. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String) checkRead} method is invoked + * to check read access to the directory. + * @throws IOException + * if an I/O error is thrown when accessing the starting file. + * + * @see #walk(Path, int, FileVisitOption...) + * @since 1.8 + */ + public static CloseableStream<Path> walk(Path start, + FileVisitOption... options) + throws IOException + { + return walk(start, Integer.MAX_VALUE, options); + } + + /** + * Return a {@code CloseableStream} that is lazily populated with {@code + * Path} by searching for files in a file tree rooted at a given starting + * file. + * + * <p> This method walks the file tree in exactly the manner specified by + * the {@link #walk walk} method. For each file encountered, the given + * {@link BiPredicate} is invoked with its {@link Path} and {@link + * BasicFileAttributes}. The {@code Path} object is obtained as if by + * {@link Path#resolve(Path) resolving} the relative path against {@code + * start} and is only included in the returned {@link CloseableStream} if + * the {@code BiPredicate} returns true. + * + * <p> If an {@link IOException} is thrown when accessing the directory + * after returned from this method, it is wrapped in an {@link + * UncheckedIOException} which will be thrown from the method that caused + * the access to take place. + * + * @param start + * the starting file + * @param maxDepth + * the maximum number of directory levels to search + * @param matcher + * the function used to decide whether a file should be included + * in the returned stream + * @param options + * options to configure the traversal + * + * @return the {@link CloseableStream} of {@link Path} + * + * @throws IllegalArgumentException + * if the {@code maxDepth} parameter is negative + * @throws SecurityException + * If the security manager denies access to the starting file. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String) checkRead} method is invoked + * to check read access to the directory. + * @throws IOException + * if an I/O error is thrown when accessing the starting file. + * + * @see #walk(Path, int, FileVisitOption...) + * @since 1.8 + */ + public static CloseableStream<Path> find(Path start, + int maxDepth, + BiPredicate<Path, BasicFileAttributes> matcher, + FileVisitOption... options) + throws IOException + { + if (maxDepth < 0) { + throw new IllegalArgumentException("'maxDepth' is negative"); + } + FileTreeIterator itor = FileTreeIterator.iterator(start, maxDepth, options); + return new DelegatingCloseableStream<>(itor, + StreamSupport.stream(Spliterators.spliteratorUnknownSize(itor, Spliterator.DISTINCT)) + .filter(entry -> matcher.test(entry.getPath(), entry.getFileAttributes())) + .map(entry -> entry.getPath())); + } + + /** + * Read all lines from a file as a {@code CloseableStream}. Unlike {@link + * #readAllLines(Path, Charset) readAllLines}, this method does not read + * all lines into a {@code List}, but populate lazily as the stream is + * consumed. + * + * <p> Bytes from the file are decoded into characters using the specified + * charset and the same line terminators as specified by {@code + * readAllLines} are supported. + * + * This method would throw an {@link java.io.IOException} if an error + * occurs when opening the file. After returned from this method, if an + * I/O error occurs while reading from the file or a malformed or + * unmappable byte sequence is read, the {@code IOException} is wrapped in + * an {@link java.io.UncheckedIOException} which will be thrown from the + * {@link java.util.stream.Stream} method that caused the read to take + * place. In case an {@code IOException} is thrown when closing the file, + * it is also wrapped as an {@code UncheckedIOException}. + * + * <p> When not using the try-with-resources construct, then stream's + * {@link CloseableStream#close close} method should be invoked after + * operation is completed so as to free any resources held for the open + * file. + * + * @param path + * the path to the file + * @param cs + * the charset to use for decoding + * + * @return the lines from the file as a {@code CloseableStream} + * + * @throws IOException + * if an I/O error occurs opening the file + * @throws SecurityException + * In the case of the default provider, and a security manager is + * installed, the {@link SecurityManager#checkRead(String) checkRead} + * method is invoked to check read access to the file. + * + * @see #readAllLines(Path, Charset) + * @see #newBufferedReader(Path, Charset) + * @see java.io.BufferedReader#lines() + * @since 1.8 + */ + public static CloseableStream<String> lines(Path path, Charset cs) + throws IOException + { + BufferedReader br = Files.newBufferedReader(path, cs); + return new DelegatingCloseableStream<>(br, br.lines()); + } }
--- a/src/share/classes/java/util/ArrayDeque.java Thu Apr 11 09:40:13 2013 -0700 +++ b/src/share/classes/java/util/ArrayDeque.java Tue Apr 16 13:13:13 2013 -0700 @@ -33,7 +33,9 @@ */ package java.util; -import java.io.*; + +import java.io.Serializable; +import java.util.function.Consumer; /** * Resizable-array implementation of the {@link Deque} interface. Array @@ -44,16 +46,16 @@ * {@link Stack} when used as a stack, and faster than {@link LinkedList} * when used as a queue. * - * <p>Most <tt>ArrayDeque</tt> operations run in amortized constant time. + * <p>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. * - * <p>The iterators returned by this class's <tt>iterator</tt> method are + * <p>The iterators returned by this class's {@code iterator} method are * <i>fail-fast</i>: If the deque is modified at any time after the iterator - * is created, in any way except through the iterator's own <tt>remove</tt> + * 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 +65,7 @@ * <p>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 <tt>ConcurrentModificationException</tt> 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: <i>the fail-fast behavior of iterators * should be used only to detect bugs.</i> @@ -93,20 +95,20 @@ * other. We also guarantee that all array cells not holding * deque elements are always null. */ - private transient E[] elements; + transient Object[] elements; // non-private to simplify nested class access /** * The index of the element at the head of the deque (which is the * element that would be removed by remove() or pop()); or an * arbitrary number equal to tail if the deque is empty. */ - private transient int head; + transient int head; /** * The index at which the next element would be added to the tail * of the deque (via addLast(E), add(E), or push(E)). */ - private transient int tail; + transient int tail; /** * The minimum capacity that we'll use for a newly created deque. @@ -117,11 +119,10 @@ // ****** Array allocation and resizing utilities ****** /** - * Allocate empty array to hold the given number of elements. + * Allocates empty array to hold the given number of elements. * * @param numElements the number of elements to hold */ - @SuppressWarnings("unchecked") private void allocateElements(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. @@ -138,11 +139,11 @@ if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } - elements = (E[]) new Object[initialCapacity]; + elements = new Object[initialCapacity]; } /** - * Double the capacity of this deque. Call only when full, i.e., + * 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() { @@ -153,8 +154,7 @@ int newCapacity = n << 1; if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); - @SuppressWarnings("unchecked") - E[] a = (E[]) new Object[newCapacity]; + Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p); elements = a; @@ -184,9 +184,8 @@ * Constructs an empty array deque with an initial capacity * sufficient to hold 16 elements. */ - @SuppressWarnings("unchecked") public ArrayDeque() { - elements = (E[]) new Object[16]; + elements = new Object[16]; } /** @@ -252,7 +251,7 @@ * Inserts the specified element at the front of this deque. * * @param e the element to add - * @return <tt>true</tt> (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) { @@ -264,7 +263,7 @@ * Inserts the specified element at the end of this deque. * * @param e the element to add - * @return <tt>true</tt> (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) { @@ -294,7 +293,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 @@ -304,7 +305,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; @@ -316,48 +318,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 <tt>e</tt> such that - * <tt>o.equals(e)</tt> (if such an element exists). - * Returns <tt>true</tt> 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 <tt>true</tt> 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); @@ -372,20 +379,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 <tt>e</tt> such that - * <tt>o.equals(e)</tt> (if such an element exists). - * Returns <tt>true</tt> 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 <tt>true</tt> 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); @@ -404,7 +411,7 @@ * <p>This method is equivalent to {@link #addLast}. * * @param e the element to add - * @return <tt>true</tt> (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) { @@ -418,7 +425,7 @@ * <p>This method is equivalent to {@link #offerLast}. * * @param e the element to add - * @return <tt>true</tt> (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) { @@ -443,12 +450,12 @@ /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns - * <tt>null</tt> if this deque is empty. + * {@code null} if this deque is empty. * * <p>This method is equivalent to {@link #pollFirst}. * * @return the head of the queue represented by this deque, or - * <tt>null</tt> if this deque is empty + * {@code null} if this deque is empty */ public E poll() { return pollFirst(); @@ -470,12 +477,12 @@ /** * Retrieves, but does not remove, the head of the queue represented by - * this deque, or returns <tt>null</tt> if this deque is empty. + * this deque, or returns {@code null} if this deque is empty. * * <p>This method is equivalent to {@link #peekFirst}. * * @return the head of the queue represented by this deque, or - * <tt>null</tt> if this deque is empty + * {@code null} if this deque is empty */ public E peek() { return peekFirst(); @@ -530,7 +537,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; @@ -579,9 +586,9 @@ } /** - * Returns <tt>true</tt> if this deque contains no elements. + * Returns {@code true} if this deque contains no elements. * - * @return <tt>true</tt> if this deque contains no elements + * @return {@code true} if this deque contains no elements */ public boolean isEmpty() { return head == tail; @@ -628,7 +635,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