This commit is contained in:
Lana Steuck 2015-09-03 16:13:49 -07:00
commit e405645f6f
38 changed files with 4007 additions and 3334 deletions

@ -76,7 +76,7 @@ public class JavacTaskImpl extends BasicJavacTask {
private final AtomicBoolean used = new AtomicBoolean();
private Iterable<? extends Processor> processors;
JavacTaskImpl(Context context) {
protected JavacTaskImpl(Context context) {
super(context, true);
args = Arguments.instance(context);
fileManager = context.get(JavaFileManager.class);

@ -47,6 +47,9 @@ public class MultiTaskListener implements TaskListener {
/** The context key for the MultiTaskListener. */
public static final Context.Key<MultiTaskListener> taskListenerKey = new Context.Key<>();
/** Empty array of task listeners */
private static final TaskListener[] EMPTY_LISTENERS = new TaskListener[0];
/** Get the MultiTaskListener instance for this context. */
public static MultiTaskListener instance(Context context) {
MultiTaskListener instance = context.get(taskListenerKey);
@ -64,7 +67,7 @@ public class MultiTaskListener implements TaskListener {
* The current set of registered listeners.
* This is a mutable reference to an immutable array.
*/
TaskListener[] listeners = { };
TaskListener[] listeners = EMPTY_LISTENERS;
ClientCodeWrapper ccw;
@ -73,7 +76,7 @@ public class MultiTaskListener implements TaskListener {
}
public boolean isEmpty() {
return (listeners.length == 0);
return listeners == EMPTY_LISTENERS;
}
public void add(TaskListener listener) {
@ -88,10 +91,14 @@ public class MultiTaskListener implements TaskListener {
public void remove(TaskListener listener) {
for (int i = 0; i < listeners.length; i++) {
if (ccw.unwrap(listeners[i]) == listener) {
TaskListener[] newListeners = new TaskListener[listeners.length - 1];
System.arraycopy(listeners, 0, newListeners, 0, i);
System.arraycopy(listeners, i + 1, newListeners, i, newListeners.length - i);
listeners = newListeners;
if (listeners.length == 1) {
listeners = EMPTY_LISTENERS;
} else {
TaskListener[] newListeners = new TaskListener[listeners.length - 1];
System.arraycopy(listeners, 0, newListeners, 0, i);
System.arraycopy(listeners, i + 1, newListeners, i, newListeners.length - i);
listeners = newListeners;
}
break;
}
}
@ -117,4 +124,8 @@ public class MultiTaskListener implements TaskListener {
public String toString() {
return Arrays.toString(listeners);
}
public void clear() {
listeners = EMPTY_LISTENERS;
}
}

@ -26,6 +26,7 @@
package com.sun.tools.javac.code;
import com.sun.tools.javac.code.Kinds.Kind;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
@ -162,15 +163,49 @@ public abstract class Scope {
/** A list of scopes to be notified if items are to be removed from this scope.
*/
List<ScopeListener> listeners = List.nil();
public void addScopeListener(ScopeListener sl) {
listeners = listeners.prepend(sl);
}
ScopeListenerList listeners = new ScopeListenerList();
public interface ScopeListener {
public void symbolAdded(Symbol sym, Scope s);
public void symbolRemoved(Symbol sym, Scope s);
void symbolAdded(Symbol sym, Scope s);
void symbolRemoved(Symbol sym, Scope s);
}
/**
* A list of scope listeners; listeners are stored in weak references, to avoid memory leaks.
* When the listener list is scanned (upon notification), elements corresponding to GC-ed
* listeners are removed so that the listener list size is kept in check.
*/
public static class ScopeListenerList {
List<WeakReference<ScopeListener>> listeners = List.nil();
void add(ScopeListener sl) {
listeners = listeners.prepend(new WeakReference<>(sl));
}
void symbolAdded(Symbol sym, Scope scope) {
walkReferences(sym, scope, false);
}
void symbolRemoved(Symbol sym, Scope scope) {
walkReferences(sym, scope, true);
}
private void walkReferences(Symbol sym, Scope scope, boolean isRemove) {
ListBuffer<WeakReference<ScopeListener>> newListeners = new ListBuffer<>();
for (WeakReference<ScopeListener> wsl : listeners) {
ScopeListener sl = wsl.get();
if (sl != null) {
if (isRemove) {
sl.symbolRemoved(sym, scope);
} else {
sl.symbolAdded(sym, scope);
}
newListeners.add(wsl);
}
}
listeners = newListeners.toList();
}
}
public enum LookupKind {
@ -404,9 +439,7 @@ public abstract class Scope {
elems = e;
//notify listeners
for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
l.head.symbolAdded(sym, this);
}
listeners.symbolAdded(sym, this);
}
/** Remove symbol from this scope.
@ -442,9 +475,7 @@ public abstract class Scope {
}
//notify listeners
for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
l.head.symbolRemoved(sym, this);
}
listeners.symbolRemoved(sym, this);
}
/** Enter symbol sym in this scope if not already there.
@ -698,11 +729,12 @@ public abstract class Scope {
finalized.enter(sym);
}
finalized.addScopeListener(new ScopeListener() {
finalized.listeners.add(new ScopeListener() {
@Override
public void symbolAdded(Symbol sym, Scope s) {
Assert.error("The scope is sealed.");
}
@Override
public void symbolRemoved(Symbol sym, Scope s) {
Assert.error("The scope is sealed.");
@ -929,26 +961,20 @@ public abstract class Scope {
public void prependSubScope(Scope that) {
if (that != null) {
subScopes = subScopes.prepend(that);
that.addScopeListener(this);
that.listeners.add(this);
mark++;
for (ScopeListener sl : listeners) {
sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes
}
listeners.symbolAdded(null, this);
}
}
public void symbolAdded(Symbol sym, Scope s) {
mark++;
for (ScopeListener sl : listeners) {
sl.symbolAdded(sym, s);
}
listeners.symbolAdded(sym, s);
}
public void symbolRemoved(Symbol sym, Scope s) {
mark++;
for (ScopeListener sl : listeners) {
sl.symbolRemoved(sym, s);
}
listeners.symbolRemoved(sym, s);
}
public int getMark() {

@ -68,7 +68,7 @@ public class Arguments {
/**
* The context key for the arguments.
*/
protected static final Context.Key<Arguments> argsKey = new Context.Key<>();
public static final Context.Key<Arguments> argsKey = new Context.Key<>();
private String ownName;
private Set<String> classNames;

@ -87,7 +87,7 @@ import static javax.tools.StandardLocation.CLASS_OUTPUT;
*/
public class JavaCompiler {
/** The context key for the compiler. */
protected static final Context.Key<JavaCompiler> compilerKey = new Context.Key<>();
public static final Context.Key<JavaCompiler> compilerKey = new Context.Key<>();
/** Get the JavaCompiler instance for this context. */
public static JavaCompiler instance(Context context) {
@ -821,7 +821,7 @@ public class JavaCompiler {
// as a JavaCompiler can only be used once, throw an exception if
// it has been used before.
if (hasBeenUsed)
throw new AssertionError("attempt to reuse JavaCompiler");
checkReusable();
hasBeenUsed = true;
// forcibly set the equivalent of -Xlint:-options, so that no further
@ -898,6 +898,10 @@ public class JavaCompiler {
}
}
protected void checkReusable() {
throw new AssertionError("attempt to reuse JavaCompiler");
}
/**
* Set needRootClasses to true, in JavaCompiler subclass constructor
* that want to collect public apis of classes supplied on the command line.

@ -26,6 +26,7 @@
package com.sun.tools.javac.parser;
import java.util.*;
import java.util.stream.Collectors;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
@ -510,7 +511,7 @@ public class JavacParser implements Parser {
if (mods != 0) {
long lowestMod = mods & -mods;
error(token.pos, "mod.not.allowed.here",
Flags.asFlagSet(lowestMod));
Flags.asFlagSet(lowestMod));
}
}
@ -946,10 +947,7 @@ public class JavacParser implements Parser {
t = odStack[0];
if (t.hasTag(JCTree.Tag.PLUS)) {
StringBuilder buf = foldStrings(t);
if (buf != null) {
t = toP(F.at(startPos).Literal(TypeTag.CLASS, buf.toString()));
}
t = foldStrings(t);
}
odStackSupply.add(odStack);
@ -973,37 +971,76 @@ public class JavacParser implements Parser {
/** If tree is a concatenation of string literals, replace it
* by a single literal representing the concatenated string.
*/
protected StringBuilder foldStrings(JCTree tree) {
protected JCExpression foldStrings(JCExpression tree) {
if (!allowStringFolding)
return null;
List<String> buf = List.nil();
ListBuffer<JCExpression> opStack = new ListBuffer<>();
ListBuffer<JCLiteral> litBuf = new ListBuffer<>();
boolean needsFolding = false;
JCExpression curr = tree;
while (true) {
if (tree.hasTag(LITERAL)) {
JCLiteral lit = (JCLiteral) tree;
if (lit.typetag == TypeTag.CLASS) {
StringBuilder sbuf =
new StringBuilder((String)lit.value);
while (buf.nonEmpty()) {
sbuf.append(buf.head);
buf = buf.tail;
}
return sbuf;
}
} else if (tree.hasTag(JCTree.Tag.PLUS)) {
JCBinary op = (JCBinary)tree;
if (op.rhs.hasTag(LITERAL)) {
JCLiteral lit = (JCLiteral) op.rhs;
if (lit.typetag == TypeTag.CLASS) {
buf = buf.prepend((String) lit.value);
tree = op.lhs;
continue;
}
}
if (curr.hasTag(JCTree.Tag.PLUS)) {
JCBinary op = (JCBinary)curr;
needsFolding |= foldIfNeeded(op.rhs, litBuf, opStack, false);
curr = op.lhs;
} else {
needsFolding |= foldIfNeeded(curr, litBuf, opStack, true);
break; //last one!
}
return null;
}
if (needsFolding) {
List<JCExpression> ops = opStack.toList();
JCExpression res = ops.head;
for (JCExpression op : ops.tail) {
res = F.at(op.getStartPosition()).Binary(optag(TokenKind.PLUS), res, op);
storeEnd(res, getEndPos(op));
}
return res;
} else {
return tree;
}
}
private boolean foldIfNeeded(JCExpression tree, ListBuffer<JCLiteral> litBuf,
ListBuffer<JCExpression> opStack, boolean last) {
JCLiteral str = stringLiteral(tree);
if (str != null) {
litBuf.prepend(str);
return last && merge(litBuf, opStack);
} else {
boolean res = merge(litBuf, opStack);
litBuf.clear();
opStack.prepend(tree);
return res;
}
}
boolean merge(ListBuffer<JCLiteral> litBuf, ListBuffer<JCExpression> opStack) {
if (litBuf.isEmpty()) {
return false;
} else if (litBuf.size() == 1) {
opStack.prepend(litBuf.first());
return false;
} else {
JCExpression t = F.at(litBuf.first().getStartPosition()).Literal(TypeTag.CLASS,
litBuf.stream().map(lit -> (String)lit.getValue()).collect(Collectors.joining()));
storeEnd(t, litBuf.last().getEndPosition(endPosTable));
opStack.prepend(t);
return true;
}
}
private JCLiteral stringLiteral(JCTree tree) {
if (tree.hasTag(LITERAL)) {
JCLiteral lit = (JCLiteral)tree;
if (lit.typetag == TypeTag.CLASS) {
return lit;
}
}
return null;
}
/** optimization: To save allocating a new operand/operator stack
* for every binary operation, we use supplys.
*/

@ -119,7 +119,7 @@ public class Context {
* or
* {@literal Key<T> -> Factory<T> }
*/
private final Map<Key<?>,Object> ht = new HashMap<>();
protected final Map<Key<?>,Object> ht = new HashMap<>();
/** Set the factory for the key in this context. */
public <T> void put(Key<T> key, Factory<T> fac) {
@ -173,7 +173,7 @@ public class Context {
*/
private final Map<Class<?>, Key<?>> kt = new HashMap<>();
private <T> Key<T> key(Class<T> clss) {
protected <T> Key<T> key(Class<T> clss) {
checkState(kt);
Key<T> k = uncheckedCast(kt.get(clss));
if (k == null) {

@ -334,7 +334,7 @@ public class Log extends AbstractLog {
* error message more than once. For each error, a pair consisting of the
* source file name and source code position of the error is added to the set.
*/
private Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<>();
protected Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<>();
public boolean hasDiagnosticListener() {
return diagListener != null;

@ -25,26 +25,19 @@
* @test
* @bug 6769027 8006694
* @summary Source line should be displayed immediately after the first diagnostic line
* temporarily workaround combo tests are causing time out in several platforms
* @author Maurizio Cimadamore
* @library ../../lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm T6769027
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
// use /othervm to avoid locale issues
import java.net.URI;
import java.util.regex.Matcher;
import javax.tools.*;
import com.sun.tools.javac.util.*;
public class T6769027
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class T6769027 {
enum OutputKind {
RAW("rawDiagnostics","rawDiagnostics"),
@ -323,11 +316,6 @@ public class T6769027
super(ctx);
}
@Override
protected java.io.PrintWriter getWriterForDiagnosticType(JCDiagnostic.DiagnosticType dt) {
return outWriter;
}
@Override
protected boolean shouldReport(JavaFileObject jfo, int pos) {
return true;
@ -368,7 +356,6 @@ public class T6769027
this.subdiagsIndent = subdiagsIndent;
}
@Override
public void run() {
Context ctx = new Context();
Options options = Options.instance(ctx);
@ -419,7 +406,7 @@ public class T6769027
for (IndentKind detailsIndent : IndentKind.values()) {
for (IndentKind sourceIndent : IndentKind.values()) {
for (IndentKind subdiagsIndent : IndentKind.values()) {
pool.execute(new T6769027(outputKind,
new T6769027(outputKind,
errKind,
multiKind,
multiPolicy,
@ -431,7 +418,7 @@ public class T6769027
summaryIndent,
detailsIndent,
sourceIndent,
subdiagsIndent));
subdiagsIndent).run();
}
}
}
@ -445,8 +432,6 @@ public class T6769027
}
}
}
checkAfterExec(false);
}
void printInfo(String msg, String errorLine) {
@ -457,11 +442,11 @@ public class T6769027
" caret=" + caretKind + " sourcePosition=" + sourceLineKind +
" summaryIndent=" + summaryIndent + " detailsIndent=" + detailsIndent +
" sourceIndent=" + sourceIndent + " subdiagsIndent=" + subdiagsIndent;
errWriter.println(sep);
errWriter.println(desc);
errWriter.println(sep);
errWriter.println(msg);
errWriter.println("Diagnostic formatting problem - expected diagnostic...\n" + errorLine);
System.err.println(sep);
System.err.println(desc);
System.err.println(sep);
System.err.println(msg);
System.err.println("Diagnostic formatting problem - expected diagnostic...\n" + errorLine);
}
void checkOutput(String msg) {
@ -494,8 +479,8 @@ public class T6769027
}
if (!msg.equals(errorLine)) {
// printInfo(msg, errorLine);
errCount.incrementAndGet();
printInfo(msg, errorLine);
throw new AssertionError("errors were found");
}
}

@ -23,38 +23,41 @@
/*
* @test
* @bug 7093325 8006694
* @bug 7093325 8006694 8129962
* @summary Redundant entry in bytecode exception table
* temporarily workaround combo tests are causing time out in several platforms
* @library lib
* @library /tools/javac/lib
* @modules jdk.jdeps/com.sun.tools.classfile
* @build JavacTestingAbstractThreadedTest
* @run main/othervm T7093325
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main T7093325
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.net.URI;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.ConstantPool.*;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Method;
public class T7093325
extends JavacTestingAbstractThreadedTest
implements Runnable {
import javax.tools.JavaFileObject;
enum StatementKind {
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class T7093325 extends ComboInstance<T7093325> {
enum StatementKind implements ComboParameter {
NONE(null, false, false),
THROW("throw new RuntimeException();", false, false),
RETURN_NONEMPTY("System.out.println(); return;", true, false),
RETURN_EMPTY("return;", true, true),
@ -64,107 +67,79 @@ public class T7093325
boolean canInline;
boolean empty;
private StatementKind(String stmt, boolean canInline, boolean empty) {
StatementKind(String stmt, boolean canInline, boolean empty) {
this.stmt = stmt;
this.canInline = canInline;
this.empty = empty;
}
@Override
public String expand(String optParameter) {
return stmt;
}
}
enum CatchArity {
enum CatchArity implements ComboParameter {
NONE(""),
ONE("catch (A a) { #S1 }"),
TWO("catch (B b) { #S2 }"),
THREE("catch (C c) { #S3 }"),
FOUR("catch (D d) { #S4 }");
ONE("catch (A a) { #{STMT[1]} }"),
TWO("catch (B b) { #{STMT[2]} }"),
THREE("catch (C c) { #{STMT[3]} }"),
FOUR("catch (D d) { #{STMT[4]} }");
String catchStr;
private CatchArity(String catchStr) {
CatchArity(String catchStr) {
this.catchStr = catchStr;
}
String catchers() {
@Override
public String expand(String optParameter) {
if (this.ordinal() == 0) {
return catchStr;
} else {
return CatchArity.values()[this.ordinal() - 1].catchers() +
return CatchArity.values()[this.ordinal() - 1].expand(optParameter) +
catchStr;
}
}
}
public static void main(String... args) throws Exception {
for (CatchArity ca : CatchArity.values()) {
for (StatementKind stmt0 : StatementKind.values()) {
if (ca.ordinal() == 0) {
pool.execute(new T7093325(ca, stmt0));
continue;
}
for (StatementKind stmt1 : StatementKind.values()) {
if (ca.ordinal() == 1) {
pool.execute(new T7093325(ca, stmt0, stmt1));
continue;
}
for (StatementKind stmt2 : StatementKind.values()) {
if (ca.ordinal() == 2) {
pool.execute(new T7093325(ca, stmt0, stmt1, stmt2));
continue;
}
for (StatementKind stmt3 : StatementKind.values()) {
if (ca.ordinal() == 3) {
pool.execute(
new T7093325(ca, stmt0, stmt1, stmt2, stmt3));
continue;
}
for (StatementKind stmt4 : StatementKind.values()) {
if (ca.ordinal() == 4) {
pool.execute(
new T7093325(ca, stmt0, stmt1,
stmt2, stmt3, stmt4));
continue;
}
for (StatementKind stmt5 : StatementKind.values()) {
pool.execute(
new T7093325(ca, stmt0, stmt1, stmt2,
stmt3, stmt4, stmt5));
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<T7093325>()
.withFilter(T7093325::testFilter)
.withDimension("CATCH", (x, ca) -> x.ca = ca, CatchArity.values())
.withArrayDimension("STMT", (x, stmt, idx) -> x.stmts[idx] = stmt, 5, StatementKind.values())
.run(T7093325::new);
}
/** instance decls **/
CatchArity ca;
StatementKind[] stmts;
StatementKind[] stmts = new StatementKind[5];
public T7093325(CatchArity ca, StatementKind... stmts) {
this.ca = ca;
this.stmts = stmts;
boolean testFilter() {
int lastPos = ca.ordinal() + 1;
for (int i = 0; i < stmts.length ; i++) {
boolean shouldBeSet = i < lastPos;
boolean isSet = stmts[i] != StatementKind.NONE;
if (shouldBeSet != isSet) {
return false;
}
}
return true;
}
@Override
public void run() {
int id = checkCount.incrementAndGet();
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
JavaSource source = new JavaSource(id);
JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), null,
null, null, Arrays.asList(source));
ct.call();
verifyBytecode(source, id);
public void doWork() throws IOException {
verifyBytecode(newCompilationTask()
.withSourceFromTemplate(source_template)
.generate());
}
void verifyBytecode(JavaSource source, int id) {
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> result) {
boolean lastInlined = false;
boolean hasCode = false;
int gapsCount = 0;
for (int i = 0; i < stmts.length ; i++) {
for (int i = 0; i < ca.ordinal() + 1 ; i++) {
lastInlined = stmts[i].canInline;
hasCode = hasCode || !stmts[i].empty;
if (lastInlined && hasCode) {
@ -176,12 +151,11 @@ public class T7093325
gapsCount++;
}
File compiledTest = new File(String.format("Test%s.class", id));
try {
ClassFile cf = ClassFile.read(compiledTest);
try (InputStream is = result.get().iterator().next().openInputStream()) {
ClassFile cf = ClassFile.read(is);
if (cf == null) {
throw new Error("Classfile not found: " +
compiledTest.getName());
fail("Classfile not found: " + result.compilationInfo());
return;
}
Method test_method = null;
@ -193,7 +167,8 @@ public class T7093325
}
if (test_method == null) {
throw new Error("Method test() not found in class Test");
fail("Method test() not found in class Test" + result.compilationInfo());
return;
}
Code_attribute code = null;
@ -205,7 +180,8 @@ public class T7093325
}
if (code == null) {
throw new Error("Code attribute not found in method test()");
fail("Code attribute not found in method test()");
return;
}
int actualGapsCount = 0;
@ -217,54 +193,28 @@ public class T7093325
}
if (actualGapsCount != gapsCount) {
throw new Error("Bad exception table for test()\n" +
fail("Bad exception table for test()\n" +
"expected gaps: " + gapsCount + "\n" +
"found gaps: " + actualGapsCount + "\n" +
source);
result.compilationInfo());
return;
}
} catch (Exception e) {
} catch (IOException | ConstantPoolException e) {
e.printStackTrace();
throw new Error("error reading " + compiledTest +": " + e);
fail("error reading classfile: " + e);
}
}
class JavaSource extends SimpleJavaFileObject {
static final String source_template =
static final String source_template =
"class Test {\n" +
" void test() {\n" +
" try { #{STMT[0]} } #{CATCH} finally { System.out.println(); }\n" +
" }\n" +
"}\n" +
"class A extends RuntimeException {} \n" +
"class B extends RuntimeException {} \n" +
"class C extends RuntimeException {} \n" +
"class D extends RuntimeException {} \n" +
"class E extends RuntimeException {} \n" +
"class Test#ID {\n" +
" void test() {\n" +
" try { #S0 } #C finally { System.out.println(); }\n" +
" }\n" +
"}";
String source;
public JavaSource(int id) {
super(URI.create(String.format("myfo:/Test%s.java", id)),
JavaFileObject.Kind.SOURCE);
source = source_template.replace("#C", ca.catchers());
source = source.replace("#S0", stmts[0].stmt);
source = source.replace("#ID", String.valueOf(id));
for (int i = 1; i < ca.ordinal() + 1; i++) {
source = source.replace("#S" + i, stmts[i].stmt);
}
}
@Override
public String toString() {
return source;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
"class E extends RuntimeException {}";
}

@ -23,53 +23,47 @@
/*
* @test
* @bug 8002099 8006694
* @bug 8002099 8006694 8129962
* @summary Add support for intersection types in cast expression
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../lib
* @modules jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm/timeout=360 IntersectionTypeCastTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main IntersectionTypeCastTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
public class IntersectionTypeCastTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
interface Type {
import java.io.IOException;
public class IntersectionTypeCastTest extends ComboInstance<IntersectionTypeCastTest> {
interface Type extends ComboParameter {
boolean subtypeOf(Type that);
String asString();
boolean isClass();
boolean isInterface();
}
enum InterfaceKind implements Type {
A("interface A { }\n", "A", null),
B("interface B { }\n", "B", null),
C("interface C extends A { }\n", "C", A);
A("A", null),
B("B", null),
C("C", A);
String declStr;
String typeStr;
InterfaceKind superInterface;
InterfaceKind(String declStr, String typeStr,
InterfaceKind superInterface) {
this.declStr = declStr;
InterfaceKind(String typeStr, InterfaceKind superInterface) {
this.typeStr = typeStr;
this.superInterface = superInterface;
}
@ -80,11 +74,6 @@ public class IntersectionTypeCastTest
that == ClassKind.OBJECT;
}
@Override
public String asString() {
return typeStr;
}
@Override
public boolean isClass() {
return false;
@ -94,53 +83,37 @@ public class IntersectionTypeCastTest
public boolean isInterface() {
return true;
}
@Override
public String expand(String optParameter) {
return typeStr;
}
}
enum ClassKind implements Type {
OBJECT(null, "Object"),
CA("#M class CA implements A { }\n", "CA",
InterfaceKind.A),
CB("#M class CB implements B { }\n", "CB",
InterfaceKind.B),
CAB("#M class CAB implements A, B { }\n", "CAB",
InterfaceKind.A, InterfaceKind.B),
CC("#M class CC implements C { }\n", "CC",
InterfaceKind.C, InterfaceKind.A),
CCA("#M class CCA implements C, A { }\n", "CCA",
InterfaceKind.C, InterfaceKind.A),
CCB("#M class CCB implements C, B { }\n", "CCB",
InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
CCAB("#M class CCAB implements C, A, B { }\n", "CCAB",
InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
OBJECT("Object"),
CA("CA", InterfaceKind.A),
CB("CB", InterfaceKind.B),
CAB("CAB", InterfaceKind.A, InterfaceKind.B),
CC("CC", InterfaceKind.C, InterfaceKind.A),
CCA("CCA", InterfaceKind.C, InterfaceKind.A),
CCB("CCB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
CCAB("CCAB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
String declTemplate;
String typeStr;
List<InterfaceKind> superInterfaces;
ClassKind(String declTemplate, String typeStr,
InterfaceKind... superInterfaces) {
this.declTemplate = declTemplate;
ClassKind(String typeStr, InterfaceKind... superInterfaces) {
this.typeStr = typeStr;
this.superInterfaces = List.from(superInterfaces);
}
String getDecl(ModifierKind mod) {
return declTemplate != null ?
declTemplate.replaceAll("#M", mod.modStr) :
"";
}
@Override
public boolean subtypeOf(Type that) {
return this == that || superInterfaces.contains(that) ||
that == OBJECT;
}
@Override
public String asString() {
return typeStr;
}
@Override
public boolean isClass() {
return true;
@ -150,9 +123,14 @@ public class IntersectionTypeCastTest
public boolean isInterface() {
return false;
}
@Override
public String expand(String optParameter) {
return typeStr;
}
}
enum ModifierKind {
enum ModifierKind implements ComboParameter {
NONE(""),
FINAL("final");
@ -161,14 +139,18 @@ public class IntersectionTypeCastTest
ModifierKind(String modStr) {
this.modStr = modStr;
}
@Override
public String expand(String optParameter) {
return modStr;
}
}
enum CastKind {
CLASS("(#C)", 0),
INTERFACE("(#I0)", 1),
INTERSECTION2("(#C & #I0)", 1),
INTERSECTION3("(#C & #I0 & #I1)", 2);
//INTERSECTION4("(#C & #I0 & #I1 & #I2)", 3);
enum CastKind implements ComboParameter {
CLASS("(#{CLAZZ#IDX})", 0),
INTERFACE("(#{INTF1#IDX})", 1),
INTERSECTION2("(#{CLAZZ#IDX} & #{INTF1#IDX})", 1),
INTERSECTION3("(#{CLAZZ#IDX} & #{INTF1#IDX} & #{INTF2#IDX})", 2);
String castTemplate;
int interfaceBounds;
@ -177,6 +159,11 @@ public class IntersectionTypeCastTest
this.castTemplate = castTemplate;
this.interfaceBounds = interfaceBounds;
}
@Override
public String expand(String optParameter) {
return castTemplate.replaceAll("#IDX", optParameter);
}
}
static class CastInfo {
@ -188,19 +175,9 @@ public class IntersectionTypeCastTest
this.types = types;
}
String getCast() {
String temp = kind.castTemplate.replaceAll("#C",
types[0].asString());
for (int i = 0; i < kind.interfaceBounds ; i++) {
temp = temp.replace(String.format("#I%d", i),
types[i + 1].asString());
}
return temp;
}
boolean hasDuplicateTypes() {
for (int i = 0 ; i < types.length ; i++) {
for (int j = 0 ; j < types.length ; j++) {
for (int i = 0 ; i < arity() ; i++) {
for (int j = 0 ; j < arity() ; j++) {
if (i != j && types[i] == types[j]) {
return true;
}
@ -210,8 +187,10 @@ public class IntersectionTypeCastTest
}
boolean compatibleWith(ModifierKind mod, CastInfo that) {
for (Type t1 : types) {
for (Type t2 : that.types) {
for (int i = 0 ; i < arity() ; i++) {
Type t1 = types[i];
for (int j = 0 ; j < that.arity() ; j++) {
Type t2 = that.types[j];
boolean compat =
t1.subtypeOf(t2) ||
t2.subtypeOf(t1) ||
@ -223,138 +202,92 @@ public class IntersectionTypeCastTest
}
return true;
}
private int arity() {
return kind.interfaceBounds + 1;
}
}
public static void main(String... args) throws Exception {
for (ModifierKind mod : ModifierKind.values()) {
for (CastInfo cast1 : allCastInfo()) {
for (CastInfo cast2 : allCastInfo()) {
pool.execute(
new IntersectionTypeCastTest(mod, cast1, cast2));
}
}
}
checkAfterExec();
new ComboTestHelper<IntersectionTypeCastTest>()
.withFilter(IntersectionTypeCastTest::isRedundantCast)
.withFilter(IntersectionTypeCastTest::arityFilter)
.withArrayDimension("CAST", (x, ck, idx) -> x.castKinds[idx] = ck, 2, CastKind.values())
.withDimension("CLAZZ1", (x, ty) -> x.types1[0] = ty, ClassKind.values())
.withDimension("INTF11", (x, ty) -> x.types1[1] = ty, InterfaceKind.values())
.withDimension("INTF21", (x, ty) -> x.types1[2] = ty, InterfaceKind.values())
.withDimension("CLAZZ2", (x, ty) -> x.types2[0] = ty, ClassKind.values())
.withDimension("INTF12", (x, ty) -> x.types2[1] = ty, InterfaceKind.values())
.withDimension("INTF22", (x, ty) -> x.types2[2] = ty, InterfaceKind.values())
.withDimension("MOD", (x, mod) -> x.mod = mod, ModifierKind.values())
.run(IntersectionTypeCastTest::new);
}
static List<CastInfo> allCastInfo() {
ListBuffer<CastInfo> buf = new ListBuffer<>();
for (CastKind kind : CastKind.values()) {
for (ClassKind clazz : ClassKind.values()) {
if (kind == CastKind.INTERFACE && clazz != ClassKind.OBJECT) {
continue;
} else if (kind.interfaceBounds == 0) {
buf.append(new CastInfo(kind, clazz));
continue;
} else {
for (InterfaceKind intf1 : InterfaceKind.values()) {
if (kind.interfaceBounds == 1) {
buf.append(new CastInfo(kind, clazz, intf1));
continue;
} else {
for (InterfaceKind intf2 : InterfaceKind.values()) {
if (kind.interfaceBounds == 2) {
buf.append(
new CastInfo(kind, clazz, intf1, intf2));
continue;
} else {
for (InterfaceKind intf3 : InterfaceKind.values()) {
buf.append(
new CastInfo(kind, clazz, intf1,
intf2, intf3));
continue;
}
}
}
}
}
boolean isRedundantCast() {
for (int i = 0 ; i < 2 ; i++) {
Type[] types = i == 0 ? types1 : types2;
if (castKinds[i] == CastKind.INTERFACE && types[0] != ClassKind.OBJECT) {
return false;
}
}
return true;
}
boolean arityFilter() {
for (int i = 0 ; i < 2 ; i++) {
int lastPos = castKinds[i].interfaceBounds + 1;
Type[] types = i == 0 ? types1 : types2;
for (int j = 1; j < types.length; j++) {
boolean shouldBeSet = j < lastPos;
if (!shouldBeSet && (types[j] != InterfaceKind.A)) {
return false;
}
}
}
return buf.toList();
return true;
}
ModifierKind mod;
CastInfo cast1, cast2;
JavaSource source;
DiagnosticChecker diagChecker;
IntersectionTypeCastTest(ModifierKind mod, CastInfo cast1, CastInfo cast2) {
this.mod = mod;
this.cast1 = cast1;
this.cast2 = cast2;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
CastKind[] castKinds = new CastKind[2];
Type[] types1 = new Type[3];
Type[] types2 = new Type[3];
@Override
public void run() {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker,
null, null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new AssertionError("Error thrown when compiling the following code:\n" +
source.getCharContent(true));
}
check();
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate(bodyTemplate)
.analyze());
}
class JavaSource extends SimpleJavaFileObject {
String bodyTemplate = "class Test {\n" +
" void test() {\n" +
" Object o = #C1#C2null;\n" +
" } }";
String source = "";
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
for (ClassKind ck : ClassKind.values()) {
source += ck.getDecl(mod);
}
for (InterfaceKind ik : InterfaceKind.values()) {
source += ik.declStr;
}
source += bodyTemplate.replaceAll("#C1", cast1.getCast()).
replaceAll("#C2", cast2.getCast());
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
void check() {
checkCount.incrementAndGet();
String bodyTemplate = "class Test {\n" +
" void test() {\n" +
" Object o = #{CAST[0].1}#{CAST[1].2}null;\n" +
" } }\n" +
"interface A { }\n" +
"interface B { }\n" +
"interface C extends A { }\n" +
"#{MOD} class CA implements A { }\n" +
"#{MOD} class CB implements B { }\n" +
"#{MOD} class CAB implements A, B { }\n" +
"#{MOD} class CC implements C { }\n" +
"#{MOD} class CCA implements C, A { }\n" +
"#{MOD} class CCB implements C, B { }\n" +
"#{MOD} class CCAB implements C, A, B { }";
void check(Result<?> res) {
CastInfo cast1 = new CastInfo(castKinds[0], types1);
CastInfo cast2 = new CastInfo(castKinds[1], types2);
boolean errorExpected = cast1.hasDuplicateTypes() ||
cast2.hasDuplicateTypes();
errorExpected |= !cast2.compatibleWith(mod, cast1);
if (errorExpected != diagChecker.errorFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
boolean errorsFound = res.hasErrors();
if (errorExpected != errorsFound) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nFound error: " + errorsFound +
"\nExpected error: " + errorExpected);
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -23,40 +23,41 @@
/*
* @test
* @bug 8005166
* @bug 8005166 8129962
* @summary Add support for static interface methods
* Smoke test for static interface method hiding
* @modules jdk.compiler
* @run main/timeout=600 InterfaceMethodHidingTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main InterfaceMethodHidingTest
*/
import com.sun.source.util.JavacTask;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.IOException;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class InterfaceMethodHidingTest {
public class InterfaceMethodHidingTest extends ComboInstance<InterfaceMethodHidingTest> {
static int checkCount = 0;
enum SignatureKind {
VOID_INTEGER("void m(Integer s)", "return;"),
STRING_INTEGER("String m(Integer s)", "return null;"),
VOID_STRING("void m(String s)", "return;"),
STRING_STRING("String m(String s)", "return null;");
enum SignatureKind implements ComboParameter {
VOID_INTEGER("void m(Integer s)", false),
STRING_INTEGER("String m(Integer s)", true),
VOID_STRING("void m(String s)", false),
STRING_STRING("String m(String s)", true);
String sigStr;
String retStr;
boolean needsReturn;
SignatureKind(String sigStr, String retStr) {
SignatureKind(String sigStr, boolean needsReturn) {
this.sigStr = sigStr;
this.retStr = retStr;
this.needsReturn = needsReturn;
}
boolean overrideEquivalentWith(SignatureKind s2) {
@ -71,18 +72,21 @@ public class InterfaceMethodHidingTest {
throw new AssertionError("bad signature kind");
}
}
@Override
public String expand(String optParameter) {
return sigStr;
}
}
enum MethodKind {
VIRTUAL("", "#M #S;"),
STATIC("static", "#M #S { #BE; #R }"),
DEFAULT("default", "#M #S { #BE; #R }");
enum MethodKind implements ComboParameter {
VIRTUAL("#{SIG[#IDX]};"),
STATIC("static #{SIG[#IDX]} { #{BODY[#IDX]}; #{RET.#IDX} }"),
DEFAULT("default #{SIG[#IDX]} { #{BODY[#IDX]}; #{RET.#IDX} }");
String modStr;
String methTemplate;
MethodKind(String modStr, String methTemplate) {
this.modStr = modStr;
MethodKind(String methTemplate) {
this.methTemplate = methTemplate;
}
@ -96,15 +100,13 @@ public class InterfaceMethodHidingTest {
mk1 != STATIC;
}
String getBody(BodyExpr be, SignatureKind sk) {
return methTemplate.replaceAll("#BE", be.bodyExprStr)
.replaceAll("#R", sk.retStr)
.replaceAll("#M", modStr)
.replaceAll("#S", sk.sigStr);
@Override
public String expand(String optParameter) {
return methTemplate.replaceAll("#IDX", optParameter);
}
}
enum BodyExpr {
enum BodyExpr implements ComboParameter {
NONE(""),
THIS("Object o = this");
@ -118,129 +120,78 @@ public class InterfaceMethodHidingTest {
return this == NONE ||
mk != MethodKind.STATIC;
}
@Override
public String expand(String optParameter) {
return bodyExprStr;
}
}
public static void main(String... args) throws Exception {
//create default shared JavaCompiler - reused across multiple compilations
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
for (MethodKind mk1 : MethodKind.values()) {
for (SignatureKind sk1 : SignatureKind.values()) {
for (BodyExpr be1 : BodyExpr.values()) {
for (MethodKind mk2 : MethodKind.values()) {
for (SignatureKind sk2 : SignatureKind.values()) {
for (BodyExpr be2 : BodyExpr.values()) {
for (MethodKind mk3 : MethodKind.values()) {
for (SignatureKind sk3 : SignatureKind.values()) {
for (BodyExpr be3 : BodyExpr.values()) {
new InterfaceMethodHidingTest(mk1, mk2, mk3, sk1, sk2, sk3, be1, be2, be3).run(comp, fm);
}
}
}
}
}
}
}
}
}
System.out.println("Total check executed: " + checkCount);
}
new ComboTestHelper<InterfaceMethodHidingTest>()
.withArrayDimension("SIG", (x, sig, idx) -> x.signatureKinds[idx] = sig, 3, SignatureKind.values())
.withArrayDimension("BODY", (x, body, idx) -> x.bodyExprs[idx] = body, 3, BodyExpr.values())
.withArrayDimension("MET", (x, meth, idx) -> x.methodKinds[idx] = meth, 3, MethodKind.values())
.run(InterfaceMethodHidingTest::new);
}
MethodKind mk1, mk2, mk3;
SignatureKind sk1, sk2, sk3;
BodyExpr be1, be2, be3;
JavaSource source;
DiagnosticChecker diagChecker;
MethodKind[] methodKinds = new MethodKind[3];
SignatureKind[] signatureKinds = new SignatureKind[3];
BodyExpr[] bodyExprs = new BodyExpr[3];
InterfaceMethodHidingTest(MethodKind mk1, MethodKind mk2, MethodKind mk3,
SignatureKind sk1, SignatureKind sk2, SignatureKind sk3, BodyExpr be1, BodyExpr be2, BodyExpr be3) {
this.mk1 = mk1;
this.mk2 = mk2;
this.mk3 = mk3;
this.sk1 = sk1;
this.sk2 = sk2;
this.sk3 = sk3;
this.be1 = be1;
this.be2 = be2;
this.be3 = be3;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
class JavaSource extends SimpleJavaFileObject {
String template = "interface Sup {\n" +
String template = "interface Sup {\n" +
" default void sup() { }\n" +
"}\n" +
"interface A extends Sup {\n" +
" #M1\n" +
" #{MET[0].0}\n" +
"}\n" +
"interface B extends A, Sup {\n" +
" #M2\n" +
" #{MET[1].1}\n" +
"}\n" +
"interface C extends B, Sup {\n" +
" #M3\n" +
" #{MET[2].2}\n" +
"}\n";
String source;
@Override
public void doWork() throws IOException {
check(newCompilationTask()
.withOption("-XDallowStaticInterfaceMethods")
.withSourceFromTemplate(template, this::returnExpr)
.analyze());
}
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#M1", mk1.getBody(be1, sk1))
.replaceAll("#M2", mk2.getBody(be2, sk2))
.replaceAll("#M3", mk3.getBody(be3, sk3));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
ComboParameter returnExpr(String name) {
switch (name) {
case "RET":
return optParameter -> {
int idx = new Integer(optParameter);
return signatureKinds[idx].needsReturn ? "return null;" : "return;";
};
default:
return null;
}
}
void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
Arrays.asList("-XDallowStaticInterfaceMethods"), null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new AssertionError("Error thrown when analyzing the following source:\n" + source.getCharContent(true));
}
check();
}
void check(Result<?> res) {
boolean errorExpected = !bodyExprs[0].allowed(methodKinds[0]) ||
!bodyExprs[1].allowed(methodKinds[1]) ||
!bodyExprs[2].allowed(methodKinds[2]);
void check() {
boolean errorExpected =
!be1.allowed(mk1) || !be2.allowed(mk2) || !be3.allowed(mk3);
if (mk1.inherithed()) {
errorExpected |=
sk2.overrideEquivalentWith(sk1) && !MethodKind.overrides(mk2, sk2, mk1, sk1) ||
sk3.overrideEquivalentWith(sk1) && !MethodKind.overrides(mk3, sk3, mk1, sk1);
if (methodKinds[0].inherithed()) {
errorExpected |= signatureKinds[1].overrideEquivalentWith(signatureKinds[0]) &&
!MethodKind.overrides(methodKinds[1], signatureKinds[1], methodKinds[0], signatureKinds[0]) ||
signatureKinds[2].overrideEquivalentWith(signatureKinds[0]) &&
!MethodKind.overrides(methodKinds[2], signatureKinds[2], methodKinds[0], signatureKinds[0]);
}
if (mk2.inherithed()) {
errorExpected |=
sk3.overrideEquivalentWith(sk2) && !MethodKind.overrides(mk3, sk3, mk2, sk2);
if (methodKinds[1].inherithed()) {
errorExpected |= signatureKinds[2].overrideEquivalentWith(signatureKinds[1]) &&
!MethodKind.overrides(methodKinds[2], signatureKinds[2], methodKinds[1], signatureKinds[1]);
}
checkCount++;
if (diagChecker.errorFound != errorExpected) {
throw new AssertionError("Problem when compiling source:\n" + source.getCharContent(true) +
"\nfound error: " + diagChecker.errorFound);
}
}
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
if (res.hasErrors() != errorExpected) {
fail("Problem when compiling source:\n" + res.compilationInfo() +
"\nfound error: " + res.hasErrors());
}
}
}

@ -23,33 +23,32 @@
/*
* @test
* @bug 7192246 8006694
* @bug 7192246 8006694 8129962
* @summary Automatic test for checking correctness of default super/this resolution
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm TestDefaultSuperCall
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main TestDefaultSuperCall
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class TestDefaultSuperCall
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class TestDefaultSuperCall extends ComboInstance<TestDefaultSuperCall> {
enum InterfaceKind {
enum InterfaceKind implements ComboParameter {
DEFAULT("interface A extends B { default void m() { } }"),
ABSTRACT("interface A extends B { void m(); }"),
NONE("interface A extends B { }");
@ -63,9 +62,14 @@ public class TestDefaultSuperCall
boolean methodDefined() {
return this == DEFAULT;
}
@Override
public String expand(String optParameter) {
return interfaceStr;
}
}
enum PruneKind {
enum PruneKind implements ComboParameter {
NO_PRUNE("interface C { }"),
PRUNE("interface C extends A { }");
@ -79,15 +83,20 @@ public class TestDefaultSuperCall
PruneKind(String interfaceStr) {
this.interfaceStr = interfaceStr;
}
@Override
public String expand(String optParameter) {
return interfaceStr;
}
}
enum QualifierKind {
enum QualifierKind implements ComboParameter {
DIRECT_1("C"),
DIRECT_2("A"),
INDIRECT("B"),
UNRELATED("E"),
ENCLOSING_1(null),
ENCLOSING_2(null);
ENCLOSING_1("name0"),
ENCLOSING_2("name1");
String qualifierStr;
@ -95,15 +104,6 @@ public class TestDefaultSuperCall
this.qualifierStr = qualifierStr;
}
String getQualifier(Shape sh) {
switch (this) {
case ENCLOSING_1: return sh.enclosingAt(0);
case ENCLOSING_2: return sh.enclosingAt(1);
default:
return qualifierStr;
}
}
boolean isEnclosing() {
return this == ENCLOSING_1 ||
this == ENCLOSING_2;
@ -119,9 +119,14 @@ public class TestDefaultSuperCall
return false;
}
}
@Override
public String expand(String optParameter) {
return qualifierStr;
}
}
enum ExprKind {
enum ExprKind implements ComboParameter {
THIS("this"),
SUPER("super");
@ -130,19 +135,24 @@ public class TestDefaultSuperCall
ExprKind(String exprStr) {
this.exprStr = exprStr;
}
@Override
public String expand(String optParameter) {
return exprStr;
}
}
enum ElementKind {
INTERFACE("interface #N { #B }", true),
INTERFACE_EXTENDS("interface #N extends A, C { #B }", true),
CLASS("class #N { #B }", false),
CLASS_EXTENDS("abstract class #N implements A, C { #B }", false),
STATIC_CLASS("static class #N { #B }", true),
STATIC_CLASS_EXTENDS("abstract static class #N implements A, C { #B }", true),
ANON_CLASS("new Object() { #B };", false),
METHOD("void test() { #B }", false),
STATIC_METHOD("static void test() { #B }", true),
DEFAULT_METHOD("default void test() { #B }", false);
enum ElementKind implements ComboParameter {
INTERFACE("interface name#CURR { #BODY }", true),
INTERFACE_EXTENDS("interface name#CURR extends A, C { #BODY }", true),
CLASS("class name#CURR { #BODY }", false),
CLASS_EXTENDS("abstract class name#CURR implements A, C { #BODY }", false),
STATIC_CLASS("static class name#CURR { #BODY }", true),
STATIC_CLASS_EXTENDS("abstract static class name#CURR implements A, C { #BODY }", true),
ANON_CLASS("new Object() { #BODY };", false),
METHOD("void test() { #BODY }", false),
STATIC_METHOD("static void test() { #BODY }", true),
DEFAULT_METHOD("default void test() { #BODY }", false);
String templateDecl;
boolean isStatic;
@ -207,11 +217,21 @@ public class TestDefaultSuperCall
this == STATIC_CLASS_EXTENDS ||
this == CLASS_EXTENDS;
}
@Override
public String expand(String optParameter) {
int nextDepth = new Integer(optParameter) + 1;
String replStr = (nextDepth <= 4) ?
String.format("#{ELEM[%d].%d}", nextDepth, nextDepth) :
"#{QUAL}.#{EXPR}.#{METH}();";
return templateDecl
.replaceAll("#CURR", optParameter)
.replaceAll("#BODY", replStr);
}
}
static class Shape {
String shapeStr;
List<ElementKind> enclosingElements;
List<String> enclosingNames;
List<String> elementsWithMethod;
@ -234,114 +254,73 @@ public class TestDefaultSuperCall
} else {
elementsWithMethod.add(prevName);
}
String element = ek.templateDecl.replaceAll("#N", name);
shapeStr = shapeStr ==
null ? element : shapeStr.replaceAll("#B", element);
prevName = name;
}
}
String getShape(QualifierKind qk, ExprKind ek) {
String methName = ek == ExprKind.THIS ? "test" : "m";
String call = qk.getQualifier(this) + "." +
ek.exprStr + "." + methName + "();";
return shapeStr.replaceAll("#B", call);
}
String enclosingAt(int index) {
return index < enclosingNames.size() ?
enclosingNames.get(index) : "BAD";
}
}
public static void main(String... args) throws Exception {
for (InterfaceKind ik : InterfaceKind.values()) {
for (PruneKind pk : PruneKind.values()) {
for (ElementKind ek1 : ElementKind.values()) {
if (!ek1.isAllowedTop()) continue;
for (ElementKind ek2 : ElementKind.values()) {
if (!ek2.isAllowedEnclosing(ek1, true)) continue;
for (ElementKind ek3 : ElementKind.values()) {
if (!ek3.isAllowedEnclosing(ek2, false)) continue;
for (ElementKind ek4 : ElementKind.values()) {
if (!ek4.isAllowedEnclosing(ek3, false)) continue;
for (ElementKind ek5 : ElementKind.values()) {
if (!ek5.isAllowedEnclosing(ek4, false) ||
ek5.isClassDecl()) continue;
for (QualifierKind qk : QualifierKind.values()) {
for (ExprKind ek : ExprKind.values()) {
pool.execute(
new TestDefaultSuperCall(ik, pk,
new Shape(ek1, ek2, ek3,
ek4, ek5), qk, ek));
}
}
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<TestDefaultSuperCall>()
.withFilter(TestDefaultSuperCall::filterBadTopElement)
.withFilter(TestDefaultSuperCall::filterBadIntermediateElement)
.withFilter(TestDefaultSuperCall::filterBadTerminalElement)
.withDimension("INTF1", (x, ik) -> x.ik = ik, InterfaceKind.values())
.withDimension("INTF2", (x, pk) -> x.pk = pk, PruneKind.values())
.withArrayDimension("ELEM", (x, elem, idx) -> x.elements[idx] = elem, 5, ElementKind.values())
.withDimension("QUAL", (x, qk) -> x.qk = qk, QualifierKind.values())
.withDimension("EXPR", (x, ek) -> x.ek = ek, ExprKind.values())
.run(TestDefaultSuperCall::new);
}
InterfaceKind ik;
PruneKind pk;
Shape sh;
ElementKind[] elements = new ElementKind[5];
QualifierKind qk;
ExprKind ek;
JavaSource source;
DiagnosticChecker diagChecker;
TestDefaultSuperCall(InterfaceKind ik, PruneKind pk, Shape sh,
QualifierKind qk, ExprKind ek) {
this.ik = ik;
this.pk = pk;
this.sh = sh;
this.qk = qk;
this.ek = ek;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
boolean filterBadTopElement() {
return elements[0].isAllowedTop();
}
class JavaSource extends SimpleJavaFileObject {
String template = "interface E {}\n" +
"interface B { }\n" +
"#I\n" +
"#P\n" +
"#C";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#I", ik.interfaceStr)
.replaceAll("#P", pk.interfaceStr)
.replaceAll("#C", sh.getShape(qk, ek));
boolean filterBadIntermediateElement() {
for (int i = 1 ; i < 4 ; i++) {
if (!elements[i].isAllowedEnclosing(elements[i - 1], i == 1)) {
return false;
}
}
return true;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
boolean filterBadTerminalElement() {
return elements[4].isAllowedEnclosing(elements[3], false) && !elements[4].isClassDecl();
}
String template = "interface E {}\n" +
"interface B { }\n" +
"#{INTF1}\n" +
"#{INTF2}\n" +
"#{ELEM[0].0}";
@Override
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate(template, this::methodName)
.analyze());
}
ComboParameter methodName(String parameterName) {
switch (parameterName) {
case "METH":
String methodName = ek == ExprKind.THIS ? "test" : "m";
return new ComboParameter.Constant<String>(methodName);
default:
return null;
}
}
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
null, null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
processException(ex);
return;
}
check();
}
void check(Result<?> res) {
Shape sh = new Shape(elements);
void check() {
boolean errorExpected = false;
boolean badEnclosing = false;
@ -364,7 +343,7 @@ public class TestDefaultSuperCall
boolean found = false;
for (int i = 0; i < sh.enclosingElements.size(); i++) {
if (sh.enclosingElements.get(i) == ElementKind.ANON_CLASS) continue;
if (sh.enclosingNames.get(i).equals(qk.getQualifier(sh))) {
if (sh.enclosingNames.get(i).equals(qk.qualifierStr)) {
found = sh.elementsWithMethod.contains(sh.enclosingNames.get(i));
break;
}
@ -388,10 +367,9 @@ public class TestDefaultSuperCall
}
}
checkCount.incrementAndGet();
if (diagChecker.errorFound != errorExpected) {
throw new AssertionError("Problem when compiling source:\n" +
source.getCharContent(true) +
if (res.hasErrors() != errorExpected) {
fail("Problem when compiling source:\n" +
res.compilationInfo() +
"\nenclosingElems: " + sh.enclosingElements +
"\nenclosingNames: " + sh.enclosingNames +
"\nelementsWithMethod: " + sh.elementsWithMethod +
@ -399,20 +377,7 @@ public class TestDefaultSuperCall
"\nbad this: " + badThis +
"\nbad super: " + badSuper +
"\nqual kind: " + qk +
"\nfound error: " + diagChecker.errorFound);
"\nfound error: " + res.hasErrors());
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -23,7 +23,7 @@
/*
* @test
* @bug 6970584 8006694 8062373
* @bug 6970584 8006694 8062373 8129962
* @summary assorted position errors in compiler syntax trees
* temporarily workaround combo tests are causing time out in several platforms
* @library ../lib
@ -31,13 +31,10 @@
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm CheckAttributedTree -q -r -et ERRONEOUS .
* @build combo.ComboTestHelper
* @run main CheckAttributedTree -q -r -et ERRONEOUS .
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
@ -56,6 +53,11 @@ import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@ -79,18 +81,14 @@ import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.EndPosTable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@ -101,6 +99,10 @@ import com.sun.tools.javac.util.Pair;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import combo.ComboTestHelper;
import combo.ComboInstance;
import combo.ComboTestHelper.IgnoreMode;
/**
* Utility and test program to check validity of tree positions for tree nodes.
* The program can be run standalone, or as a jtreg test. In standalone mode,
@ -113,7 +115,7 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
* covering any new language features that may be tested in this test suite.
*/
public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
public class CheckAttributedTree {
/**
* Main entry point.
* If test.src is set, program runs in jtreg mode, and will throw an Error
@ -125,7 +127,6 @@ public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
public static void main(String... args) throws Exception {
String testSrc = System.getProperty("test.src");
File baseDir = (testSrc == null) ? null : new File(testSrc);
throwAssertionOnError = false;
boolean ok = new CheckAttributedTree().run(baseDir, args);
if (!ok) {
if (testSrc != null) // jtreg mode
@ -160,7 +161,6 @@ public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
quiet = true;
else if (arg.equals("-v")) {
verbose = true;
printAll = true;
}
else if (arg.equals("-t") && i + 1 < args.length)
tags.add(args[++i]);
@ -187,18 +187,37 @@ public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
}
}
for (File file: files) {
if (file.exists())
test(file);
else
error("File not found: " + file);
}
ComboTestHelper<FileChecker> cth = new ComboTestHelper<>();
cth.withIgnoreMode(IgnoreMode.IGNORE_ALL)
.withFilter(FileChecker::checkFile)
.withDimension("FILE", (x, file) -> x.file = file, getAllFiles(files))
.run(FileChecker::new);
if (fileCount.get() != 1)
errWriter.println(fileCount + " files read");
checkAfterExec(false);
return (gui || errCount.get() == 0);
if (verbose) {
System.out.println(errSWriter.toString());
}
return (gui || !cth.info().hasFailures());
}
File[] getAllFiles(List<File> roots) throws IOException {
long now = System.currentTimeMillis();
ArrayList<File> buf = new ArrayList<>();
for (File file : roots) {
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
buf.add(file.toFile());
return FileVisitResult.CONTINUE;
}
});
}
long delta = System.currentTimeMillis() - now;
System.err.println("All files = " + buf.size() + " " + delta);
return buf.toArray(new File[buf.size()]);
}
/**
@ -224,116 +243,217 @@ public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
out.println("");
}
/**
* Test a file. If the file is a directory, it will be recursively scanned
* for java files.
* @param file the file or directory to test
*/
void test(final File file) {
if (excludeFiles.contains(file)) {
if (!quiet)
error("File " + file + " excluded");
return;
}
class FileChecker extends ComboInstance<FileChecker> {
if (file.isDirectory()) {
for (File f: file.listFiles()) {
test(f);
File file;
boolean checkFile() {
if (!file.exists()) {
error("File not found: " + file);
return false;
}
return;
if (excludeFiles.contains(file)) {
if (!quiet)
error("File " + file + " excluded");
return false;
}
if (!file.getName().endsWith(".java")) {
if (!quiet)
error("File " + file + " ignored");
return false;
}
return true;
}
if (file.isFile() && file.getName().endsWith(".java")) {
pool.execute(new Runnable() {
@Override
public void run() {
try {
if (verbose)
errWriter.println(file);
fileCount.incrementAndGet();
NPETester p = new NPETester();
p.test(read(file));
} catch (AttributionException e) {
if (!quiet) {
error("Error attributing " + file + "\n" + e.getMessage());
public void doWork() {
if (!file.exists()) {
error("File not found: " + file);
}
if (excludeFiles.contains(file)) {
if (!quiet)
error("File " + file + " excluded");
return;
}
if (!file.getName().endsWith(".java")) {
if (!quiet)
error("File " + file + " ignored");
}
try {
if (verbose)
errWriter.println(file);
fileCount.incrementAndGet();
NPETester p = new NPETester();
p.test(read(file));
} catch (AttributionException e) {
if (!quiet) {
error("Error attributing " + file + "\n" + e.getMessage());
}
} catch (IOException e) {
error("Error reading " + file + ": " + e);
}
}
/**
* Read a file.
* @param file the file to be read
* @return the tree for the content of the file
* @throws IOException if any IO errors occur
* @throws AttributionException if any errors occur while analyzing the file
*/
List<Pair<JCCompilationUnit, JCTree>> read(File file) throws IOException, AttributionException {
try {
Iterable<? extends JavaFileObject> files = fileManager().getJavaFileObjects(file);
final List<Element> analyzedElems = new ArrayList<>();
final List<CompilationUnitTree> trees = new ArrayList<>();
Iterable<? extends Element> elems = newCompilationTask()
.withWriter(pw)
.withOption("-XDshouldStopPolicy=ATTR")
.withOption("-XDverboseCompilePolicy")
.withSource(files.iterator().next())
.withListener(new TaskListener() {
public void started(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ANALYZE)
analyzedElems.add(e.getTypeElement());
}
} catch (IOException e) {
error("Error reading " + file + ": " + e);
public void finished(TaskEvent e) {
if (e.getKind() == Kind.PARSE)
trees.add(e.getCompilationUnit());
}
}).analyze().get();
if (!elems.iterator().hasNext())
throw new AttributionException("No results from analyze");
List<Pair<JCCompilationUnit, JCTree>> res = new ArrayList<>();
for (CompilationUnitTree t : trees) {
JCCompilationUnit cu = (JCCompilationUnit)t;
for (JCTree def : cu.defs) {
if (def.hasTag(CLASSDEF) &&
analyzedElems.contains(((JCTree.JCClassDecl)def).sym)) {
res.add(new Pair<>(cu, def));
}
}
}
return res;
}
catch (Throwable t) {
throw new AttributionException("Exception while attributing file: " + file);
}
}
/**
* Report an error. When the program is complete, the program will either
* exit or throw an Error if any errors have been reported.
* @param msg the error message
*/
void error(String msg) {
System.err.println();
System.err.println(msg);
System.err.println();
fail(msg);
}
/**
* Main class for testing assertions concerning types/symbol
* left uninitialized after attribution
*/
private class NPETester extends TreeScanner {
void test(List<Pair<JCCompilationUnit, JCTree>> trees) {
for (Pair<JCCompilationUnit, JCTree> p : trees) {
sourcefile = p.fst.sourcefile;
endPosTable = p.fst.endPositions;
encl = new Info(p.snd, endPosTable);
p.snd.accept(this);
}
}
@Override
public void scan(JCTree tree) {
if (tree == null ||
excludeTags.contains(treeUtil.nameFromTag(tree.getTag()))) {
return;
}
Info self = new Info(tree, endPosTable);
if (mandatoryType(tree)) {
check(tree.type != null,
"'null' field 'type' found in tree ", self);
if (tree.type==null)
Thread.dumpStack();
}
Field errField = checkFields(tree);
if (errField!=null) {
check(false,
"'null' field '" + errField.getName() + "' found in tree ", self);
}
Info prevEncl = encl;
encl = self;
tree.accept(this);
encl = prevEncl;
}
private boolean mandatoryType(JCTree that) {
return that instanceof JCTree.JCExpression ||
that.hasTag(VARDEF) ||
that.hasTag(METHODDEF) ||
that.hasTag(CLASSDEF);
}
private final List<String> excludedFields = Arrays.asList("varargsElement", "targetType");
void check(boolean ok, String label, Info self) {
if (!ok) {
if (gui) {
if (viewer == null)
viewer = new Viewer();
viewer.addEntry(sourcefile, label, encl, self);
}
error(label + self.toString() + " encl: " + encl.toString() +
" in file: " + sourcefile + " " + self.tree);
}
}
Field checkFields(JCTree t) {
List<Field> fieldsToCheck = treeUtil.getFieldsOfType(t,
excludedFields,
Symbol.class,
Type.class);
for (Field f : fieldsToCheck) {
try {
if (f.get(t) == null) {
return f;
}
}
catch (IllegalAccessException e) {
System.err.println("Cannot read field: " + f);
//swallow it
}
}
});
return;
}
return null;
}
if (!quiet)
error("File " + file + " ignored");
@Override
public void visitImport(JCImport tree) { }
@Override
public void visitTopLevel(JCCompilationUnit tree) {
scan(tree.defs);
}
JavaFileObject sourcefile;
EndPosTable endPosTable;
Info encl;
}
}
// See CR: 6982992 Tests CheckAttributedTree.java, JavacTreeScannerTest.java, and SourceTreeeScannerTest.java timeout
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
Reporter r = new Reporter(pw);
/**
* Read a file.
* @param file the file to be read
* @return the tree for the content of the file
* @throws IOException if any IO errors occur
* @throws AttributionException if any errors occur while analyzing the file
*/
List<Pair<JCCompilationUnit, JCTree>> read(File file) throws IOException, AttributionException {
r.errors = 0;
Iterable<? extends JavaFileObject> files = fm.get().getJavaFileObjects(file);
String[] opts = { "-XDshouldStopPolicy=ATTR", "-XDverboseCompilePolicy" };
JavacTask task = (JavacTask)comp.getTask(pw, fm.get(), r, Arrays.asList(opts), null, files);
final List<Element> analyzedElems = new ArrayList<>();
task.setTaskListener(new TaskListener() {
public void started(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ANALYZE)
analyzedElems.add(e.getTypeElement());
}
public void finished(TaskEvent e) { }
});
int i = 0;
try {
Iterable<? extends CompilationUnitTree> trees = task.parse();
// JavaCompiler c = JavaCompiler.instance(((JavacTaskImpl) task).getContext());
// System.err.println("verboseCompilePolicy: " + c.verboseCompilePolicy);
// System.err.println("shouldStopIfError: " + c.shouldStopPolicyIfError);
// System.err.println("shouldStopIfNoError: " + c.shouldStopPolicyIfNoError);
Iterable<? extends Element> elems = task.analyze();
if (!elems.iterator().hasNext())
throw new AttributionException("No results from analyze");
List<Pair<JCCompilationUnit, JCTree>> res = new ArrayList<>();
//System.err.println("Try to add pairs. Elems are " + analyzedElems);
for (CompilationUnitTree t : trees) {
JCCompilationUnit cu = (JCCompilationUnit)t;
for (JCTree def : cu.defs) {
if (def.hasTag(CLASSDEF) &&
analyzedElems.contains(((JCTree.JCClassDecl)def).sym)) {
//System.err.println("Adding pair..." + cu.sourcefile + " " + ((JCTree.JCClassDecl) def).name);
res.add((i++ % 2) == 0 ? new Pair<>(cu, def) {} : new Pair<>(cu, def));
}
}
}
return res;
}
catch (Throwable t) {
throw new AttributionException("Exception while attributing file: " + file);
}
}
/**
* Report an error. When the program is complete, the program will either
* exit or throw an Error if any errors have been reported.
* @param msg the error message
*/
void error(String msg) {
System.err.println();
System.err.println(msg);
System.err.println();
errCount.incrementAndGet();
}
StringWriter errSWriter = new StringWriter();
PrintWriter errWriter = new PrintWriter(errSWriter);
/** Flag: don't report irrelevant files. */
boolean quiet;
@ -355,101 +475,6 @@ public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
/** Utility class for trees */
TreeUtil treeUtil = new TreeUtil();
/**
* Main class for testing assertions concerning types/symbol
* left uninitialized after attribution
*/
private class NPETester extends TreeScanner {
void test(List<Pair<JCCompilationUnit, JCTree>> trees) {
for (Pair<JCCompilationUnit, JCTree> p : trees) {
// System.err.println("checking " + p.fst.sourcefile);
sourcefile = p.fst.sourcefile;
endPosTable = p.fst.endPositions;
encl = new Info(p.snd, endPosTable);
p.snd.accept(this);
}
}
@Override
public void scan(JCTree tree) {
if (tree == null ||
excludeTags.contains(treeUtil.nameFromTag(tree.getTag()))) {
return;
}
Info self = new Info(tree, endPosTable);
if (mandatoryType(tree)) {
check(tree.type != null,
"'null' field 'type' found in tree ", self);
if (tree.type==null)
Thread.dumpStack();
}
Field errField = checkFields(tree);
if (errField!=null) {
check(false,
"'null' field '" + errField.getName() + "' found in tree ", self);
}
Info prevEncl = encl;
encl = self;
tree.accept(this);
encl = prevEncl;
}
private boolean mandatoryType(JCTree that) {
return that instanceof JCTree.JCExpression ||
that.hasTag(VARDEF) ||
that.hasTag(METHODDEF) ||
that.hasTag(CLASSDEF);
}
private final List<String> excludedFields = Arrays.asList("varargsElement", "targetType");
void check(boolean ok, String label, Info self) {
if (!ok) {
if (gui) {
if (viewer == null)
viewer = new Viewer();
viewer.addEntry(sourcefile, label, encl, self);
}
error(label + self.toString() + " encl: " + encl.toString() +
" in file: " + sourcefile + " " + self.tree);
}
}
Field checkFields(JCTree t) {
List<Field> fieldsToCheck = treeUtil.getFieldsOfType(t,
excludedFields,
Symbol.class,
Type.class);
for (Field f : fieldsToCheck) {
try {
if (f.get(t) == null) {
return f;
}
}
catch (IllegalAccessException e) {
System.err.println("Cannot read field: " + f);
//swallow it
}
}
return null;
}
@Override
public void visitImport(JCImport tree) { }
@Override
public void visitTopLevel(JCCompilationUnit tree) {
scan(tree.defs);
}
JavaFileObject sourcefile;
EndPosTable endPosTable;
Info encl;
}
/**
* Utility class providing easy access to position and other info for a tree node.
*/
@ -523,25 +548,6 @@ public class CheckAttributedTree extends JavacTestingAbstractThreadedTest {
}
}
/**
* DiagnosticListener to report diagnostics and count any errors that occur.
*/
private static class Reporter implements DiagnosticListener<JavaFileObject> {
Reporter(PrintWriter out) {
this.out = out;
}
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
//out.println(diagnostic);
switch (diagnostic.getKind()) {
case ERROR:
errors++;
}
}
int errors;
PrintWriter out;
}
/**
* GUI viewer for issues found by TreePosTester. The viewer provides a drop
* down list for selecting error conditions, a header area providing details

@ -23,30 +23,32 @@
/*
* @test
* @bug 7046778 8006694
* @bug 7046778 8006694 8129962
* @summary Project Coin: problem with diamond and member inner classes
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm DiamondAndInnerClassTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @compile -Xlint:all DiamondAndInnerClassTest.java
* @run main DiamondAndInnerClassTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import com.sun.source.util.JavacTask;
import java.net.URI;
import java.io.IOException;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
public class DiamondAndInnerClassTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
import combo.ComboTestHelper;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
enum TypeArgumentKind {
public class DiamondAndInnerClassTest extends ComboInstance<DiamondAndInnerClassTest> {
enum TypeArgumentKind implements ComboParameter {
NONE(""),
STRING("<String>"),
INTEGER("<Integer>"),
@ -54,7 +56,7 @@ public class DiamondAndInnerClassTest
String typeargStr;
private TypeArgumentKind(String typeargStr) {
TypeArgumentKind(String typeargStr) {
this.typeargStr = typeargStr;
}
@ -75,252 +77,173 @@ public class DiamondAndInnerClassTest
default: throw new AssertionError("Unexpected decl kind: " + this);
}
}
@Override
public String expand(String optParameter) {
return typeargStr;
}
}
enum ArgumentKind {
enum ArgumentKind implements ComboParameter {
OBJECT("(Object)null"),
STRING("(String)null"),
INTEGER("(Integer)null");
String argStr;
private ArgumentKind(String argStr) {
ArgumentKind(String argStr) {
this.argStr = argStr;
}
@Override
public String expand(String optParameter) {
return argStr;
}
}
enum TypeQualifierArity {
ONE(1, "A1#TA1"),
TWO(2, "A1#TA1.A2#TA2"),
THREE(3, "A1#TA1.A2#TA2.A3#TA3");
enum TypeQualifierArity implements ComboParameter {
ONE(1, "A1#{TA#IDX[0]}"),
TWO(2, "A1#{TA#IDX[0]}.A2#{TA#IDX[1]}"),
THREE(3, "A1#{TA#IDX[0]}.A2#{TA#IDX[1]}.A3#{TA#IDX[2]}");
int n;
String qualifierStr;
private TypeQualifierArity(int n, String qualifierStr) {
TypeQualifierArity(int n, String qualifierStr) {
this.n = n;
this.qualifierStr = qualifierStr;
}
String getType(TypeArgumentKind... typeArgumentKinds) {
String res = qualifierStr;
for (int i = 1 ; i <= typeArgumentKinds.length ; i++) {
res = res.replace("#TA" + i, typeArgumentKinds[i-1].typeargStr);
}
return res;
}
boolean matches(InnerClassDeclArity innerClassDeclArity) {
return n ==innerClassDeclArity.n;
@Override
public String expand(String optParameter) {
return qualifierStr.replaceAll("#IDX", optParameter);
}
}
enum InnerClassDeclArity {
ONE(1, "class A1<X> { A1(X x1) { } #B }"),
TWO(2, "class A1<X1> { class A2<X2> { A2(X1 x1, X2 x2) { } #B } }"),
THREE(3, "class A1<X1> { class A2<X2> { class A3<X3> { A3(X1 x1, X2 x2, X3 x3) { } #B } } }");
enum InnerClassDeclArity implements ComboParameter {
ONE(1, "class A1<X> { A1(X x1) { } #{BODY} }"),
TWO(2, "class A1<X1> { class A2<X2> { A2(X1 x1, X2 x2) { } #{BODY} } }"),
THREE(3, "class A1<X1> { class A2<X2> { class A3<X3> { A3(X1 x1, X2 x2, X3 x3) { } #{BODY} } } }");
int n;
String classDeclStr;
private InnerClassDeclArity(int n, String classDeclStr) {
InnerClassDeclArity(int n, String classDeclStr) {
this.n = n;
this.classDeclStr = classDeclStr;
}
@Override
public String expand(String optParameter) {
return classDeclStr;
}
}
enum ArgumentListArity {
ONE(1, "(#A1)"),
TWO(2, "(#A1,#A2)"),
THREE(3, "(#A1,#A2,#A3)");
enum ArgumentListArity implements ComboParameter {
ONE(1, "(#{A[0]})"),
TWO(2, "(#{A[0]},#{A[1]})"),
THREE(3, "(#{A[0]},#{A[1]},#{A[2]})");
int n;
String argListStr;
private ArgumentListArity(int n, String argListStr) {
ArgumentListArity(int n, String argListStr) {
this.n = n;
this.argListStr = argListStr;
}
String getArgs(ArgumentKind... argumentKinds) {
String res = argListStr;
for (int i = 1 ; i <= argumentKinds.length ; i++) {
res = res.replace("#A" + i, argumentKinds[i-1].argStr);
}
return res;
}
boolean matches(InnerClassDeclArity innerClassDeclArity) {
return n ==innerClassDeclArity.n;
@Override
public String expand(String optParameter) {
return argListStr.replaceAll("#IDX", optParameter);
}
}
public static void main(String... args) throws Exception {
for (InnerClassDeclArity innerClassDeclArity : InnerClassDeclArity.values()) {
for (TypeQualifierArity declType : TypeQualifierArity.values()) {
if (!declType.matches(innerClassDeclArity)) continue;
for (TypeQualifierArity newClassType : TypeQualifierArity.values()) {
if (!newClassType.matches(innerClassDeclArity)) continue;
for (ArgumentListArity argList : ArgumentListArity.values()) {
if (!argList.matches(innerClassDeclArity)) continue;
for (TypeArgumentKind taDecl1 : TypeArgumentKind.values()) {
boolean isDeclRaw = taDecl1 == TypeArgumentKind.NONE;
//no diamond on decl site
if (taDecl1 == TypeArgumentKind.DIAMOND) continue;
for (TypeArgumentKind taSite1 : TypeArgumentKind.values()) {
boolean isSiteRaw =
taSite1 == TypeArgumentKind.NONE;
//diamond only allowed on the last type qualifier
if (taSite1 == TypeArgumentKind.DIAMOND &&
innerClassDeclArity !=
InnerClassDeclArity.ONE)
continue;
for (ArgumentKind arg1 : ArgumentKind.values()) {
if (innerClassDeclArity == innerClassDeclArity.ONE) {
pool.execute(
new DiamondAndInnerClassTest(
innerClassDeclArity, declType,
newClassType, argList,
new TypeArgumentKind[] {taDecl1},
new TypeArgumentKind[] {taSite1},
new ArgumentKind[] {arg1}));
continue;
}
for (TypeArgumentKind taDecl2 : TypeArgumentKind.values()) {
//no rare types
if (isDeclRaw != (taDecl2 == TypeArgumentKind.NONE))
continue;
//no diamond on decl site
if (taDecl2 == TypeArgumentKind.DIAMOND)
continue;
for (TypeArgumentKind taSite2 : TypeArgumentKind.values()) {
//no rare types
if (isSiteRaw != (taSite2 == TypeArgumentKind.NONE))
continue;
//diamond only allowed on the last type qualifier
if (taSite2 == TypeArgumentKind.DIAMOND &&
innerClassDeclArity != InnerClassDeclArity.TWO)
continue;
for (ArgumentKind arg2 : ArgumentKind.values()) {
if (innerClassDeclArity == innerClassDeclArity.TWO) {
pool.execute(
new DiamondAndInnerClassTest(
innerClassDeclArity,
declType,
newClassType,
argList,
new TypeArgumentKind[] {taDecl1, taDecl2},
new TypeArgumentKind[] {taSite1, taSite2},
new ArgumentKind[] {arg1, arg2}));
continue;
}
for (TypeArgumentKind taDecl3 : TypeArgumentKind.values()) {
//no rare types
if (isDeclRaw != (taDecl3 == TypeArgumentKind.NONE))
continue;
//no diamond on decl site
if (taDecl3 == TypeArgumentKind.DIAMOND)
continue;
for (TypeArgumentKind taSite3 : TypeArgumentKind.values()) {
//no rare types
if (isSiteRaw != (taSite3 == TypeArgumentKind.NONE))
continue;
//diamond only allowed on the last type qualifier
if (taSite3 == TypeArgumentKind.DIAMOND &&
innerClassDeclArity != InnerClassDeclArity.THREE)
continue;
for (ArgumentKind arg3 : ArgumentKind.values()) {
if (innerClassDeclArity ==
innerClassDeclArity.THREE) {
pool.execute(
new DiamondAndInnerClassTest(
innerClassDeclArity,
declType,
newClassType,
argList,
new TypeArgumentKind[] {taDecl1, taDecl2, taDecl3},
new TypeArgumentKind[] {taSite1, taSite2, taSite3},
new ArgumentKind[] {arg1, arg2, arg3}));
continue;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<DiamondAndInnerClassTest>()
.withFilter(DiamondAndInnerClassTest::rareTypesFilter)
.withFilter(DiamondAndInnerClassTest::noDiamondOnDecl)
.withFilter(DiamondAndInnerClassTest::noDiamondOnIntermediateTypes)
.withFilter(DiamondAndInnerClassTest::arityMismatch)
.withFilter(DiamondAndInnerClassTest::redundantFilter)
.withDimension("BODY", new ComboParameter.Constant<>("#{D.1} res = new #{S.2}#{AL};"))
.withDimension("DECL", (x, arity) -> x.innerClassDeclArity = arity, InnerClassDeclArity.values())
.withDimension("D", (x, arity) -> x.declArity = arity, TypeQualifierArity.values())
.withDimension("S", (x, arity) -> x.siteArity = arity, TypeQualifierArity.values())
.withDimension("AL", (x, alist) -> x.argumentListArity = alist, ArgumentListArity.values())
.withArrayDimension("TA1", (x, targs, idx) -> x.declTypeArgumentKinds[idx] = targs, 3, TypeArgumentKind.values())
.withArrayDimension("TA2", (x, targs, idx) -> x.siteTypeArgumentKinds[idx] = targs, 3, TypeArgumentKind.values())
.withArrayDimension("A", (x, argsk, idx) -> x.argumentKinds[idx] = argsk, 3, ArgumentKind.values())
.run(DiamondAndInnerClassTest::new);
}
InnerClassDeclArity innerClassDeclArity;
TypeQualifierArity declType;
TypeQualifierArity siteType;
ArgumentListArity argList;
TypeArgumentKind[] declTypeArgumentKinds;
TypeArgumentKind[] siteTypeArgumentKinds;
ArgumentKind[] argumentKinds;
JavaSource source;
DiagnosticChecker diagChecker;
TypeQualifierArity declArity;
TypeQualifierArity siteArity;
TypeArgumentKind[] declTypeArgumentKinds = new TypeArgumentKind[3];
TypeArgumentKind[] siteTypeArgumentKinds = new TypeArgumentKind[3];
ArgumentKind[] argumentKinds = new ArgumentKind[3];
ArgumentListArity argumentListArity;
DiamondAndInnerClassTest(InnerClassDeclArity innerClassDeclArity,
TypeQualifierArity declType, TypeQualifierArity siteType,
ArgumentListArity argList, TypeArgumentKind[] declTypeArgumentKinds,
TypeArgumentKind[] siteTypeArgumentKinds, ArgumentKind[] argumentKinds) {
this.innerClassDeclArity = innerClassDeclArity;
this.declType = declType;
this.siteType = siteType;
this.argList = argList;
this.declTypeArgumentKinds = declTypeArgumentKinds;
this.siteTypeArgumentKinds = siteTypeArgumentKinds;
this.argumentKinds = argumentKinds;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
boolean rareTypesFilter() {
for (TypeArgumentKind[] types : Arrays.asList(declTypeArgumentKinds, siteTypeArgumentKinds)) {
boolean isRaw = types[0] == TypeArgumentKind.NONE;
for (int i = 1; i < innerClassDeclArity.n; i++) {
if (isRaw != (types[i] == TypeArgumentKind.NONE)) {
return false;
}
}
}
return true;
}
class JavaSource extends SimpleJavaFileObject {
String bodyTemplate = "#D res = new #S#AL;";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = innerClassDeclArity.classDeclStr.replace("#B", bodyTemplate)
.replace("#D", declType.getType(declTypeArgumentKinds))
.replace("#S", siteType.getType(siteTypeArgumentKinds))
.replace("#AL", argList.getArgs(argumentKinds));
boolean noDiamondOnDecl() {
for (int i = 0; i < innerClassDeclArity.n; i++) {
if (declTypeArgumentKinds[i] == TypeArgumentKind.DIAMOND) {
return false;
}
}
return true;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
boolean noDiamondOnIntermediateTypes() {
for (int i = 0; i < (innerClassDeclArity.n - 1); i++) {
if (siteTypeArgumentKinds[i] == TypeArgumentKind.DIAMOND) {
return false;
}
}
return true;
}
boolean redundantFilter() {
for (TypeArgumentKind[] types : Arrays.asList(declTypeArgumentKinds, siteTypeArgumentKinds)) {
for (int i = innerClassDeclArity.n; i < types.length; i++) {
if (types[i].ordinal() != 0) {
return false;
}
}
}
for (int i = innerClassDeclArity.n; i < argumentKinds.length; i++) {
if (argumentKinds[i].ordinal() != 0) {
return false;
}
}
return true;
}
boolean arityMismatch() {
return argumentListArity.n == innerClassDeclArity.n &&
siteArity.n == innerClassDeclArity.n &&
declArity.n == innerClassDeclArity.n;
}
@Override
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
null, null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new AssertionError("Error thrown when compiling the following code:\n" +
source.getCharContent(true));
}
check();
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate("#{DECL}")
.analyze());
}
void check() {
checkCount.incrementAndGet();
void check(Result<?> res) {
boolean errorExpected = false;
TypeArgumentKind[] expectedArgKinds =
@ -345,24 +268,11 @@ public class DiamondAndInnerClassTest
}
}
if (errorExpected != diagChecker.errorFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
if (errorExpected != res.hasErrors()) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nFound error: " + res.hasErrors() +
"\nExpected error: " + errorExpected);
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -23,29 +23,29 @@
/*
* @test
* @bug 7062745 8006694
* @bug 7062745 8006694 8129962
* @summary Regression: difference in overload resolution when two methods
* are maximally specific
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm GenericOverrideTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main GenericOverrideTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class GenericOverrideTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class GenericOverrideTest extends ComboInstance<GenericOverrideTest> {
enum SourceLevel {
SOURCE_7("-source", "7"),
@ -58,24 +58,29 @@ public class GenericOverrideTest
}
}
enum SignatureKind {
enum SignatureKind implements ComboParameter {
NON_GENERIC(""),
GENERIC("<X>");
String paramStr;
private SignatureKind(String paramStr) {
SignatureKind(String paramStr) {
this.paramStr = paramStr;
}
@Override
public String expand(String optParameter) {
return paramStr;
}
}
enum ReturnTypeKind {
enum ReturnTypeKind implements ComboParameter {
LIST("List"),
ARRAYLIST("ArrayList");
String retStr;
private ReturnTypeKind(String retStr) {
ReturnTypeKind(String retStr) {
this.retStr = retStr;
}
@ -88,9 +93,14 @@ public class GenericOverrideTest
default: throw new AssertionError("Unexpected ret kind: " + this);
}
}
@Override
public String expand(String optParameter) {
return retStr;
}
}
enum TypeArgumentKind {
enum TypeArgumentKind implements ComboParameter {
NONE(""),
UNBOUND("<?>"),
INTEGER("<Number>"),
@ -99,7 +109,7 @@ public class GenericOverrideTest
String typeargStr;
private TypeArgumentKind(String typeargStr) {
TypeArgumentKind(String typeargStr) {
this.typeargStr = typeargStr;
}
@ -141,136 +151,79 @@ public class GenericOverrideTest
default: throw new AssertionError("Unexpected typearg kind: " + this);
}
}
@Override
public String expand(String optParameter) {
return typeargStr;
}
}
public static void main(String... args) throws Exception {
for (SignatureKind sig1 : SignatureKind.values()) {
for (ReturnTypeKind rt1 : ReturnTypeKind.values()) {
for (TypeArgumentKind ta1 : TypeArgumentKind.values()) {
if (!ta1.compatibleWith(sig1)) continue;
for (SignatureKind sig2 : SignatureKind.values()) {
for (ReturnTypeKind rt2 : ReturnTypeKind.values()) {
for (TypeArgumentKind ta2 : TypeArgumentKind.values()) {
if (!ta2.compatibleWith(sig2)) continue;
for (ReturnTypeKind rt3 : ReturnTypeKind.values()) {
for (TypeArgumentKind ta3 : TypeArgumentKind.values()) {
if (!ta3.compatibleWith(SignatureKind.NON_GENERIC))
continue;
for (SourceLevel level : SourceLevel.values()) {
pool.execute(
new GenericOverrideTest(sig1,
rt1, ta1, sig2, rt2,
ta2, rt3, ta3, level));
}
}
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<GenericOverrideTest>()
.withFilter(GenericOverrideTest::argMismatchFilter)
.withDimension("SOURCE", (x, level) -> x.level = level, SourceLevel.values())
.withArrayDimension("SIG", (x, sig, idx) -> x.sigs[idx] = sig, 2, SignatureKind.values())
.withArrayDimension("TARG", (x, targ, idx) -> x.targs[idx] = targ, 3, TypeArgumentKind.values())
.withArrayDimension("RET", (x, ret, idx) -> x.rets[idx] = ret, 3, ReturnTypeKind.values())
.run(GenericOverrideTest::new);
}
SignatureKind sig1, sig2;
ReturnTypeKind rt1, rt2, rt3;
TypeArgumentKind ta1, ta2, ta3;
SignatureKind[] sigs = new SignatureKind[2];
ReturnTypeKind[] rets = new ReturnTypeKind[3];
TypeArgumentKind[] targs = new TypeArgumentKind[3];
SourceLevel level;
JavaSource source;
DiagnosticChecker diagChecker;
GenericOverrideTest(SignatureKind sig1, ReturnTypeKind rt1, TypeArgumentKind ta1,
SignatureKind sig2, ReturnTypeKind rt2, TypeArgumentKind ta2,
ReturnTypeKind rt3, TypeArgumentKind ta3, SourceLevel level) {
this.sig1 = sig1;
this.sig2 = sig2;
this.rt1 = rt1;
this.rt2 = rt2;
this.rt3 = rt3;
this.ta1 = ta1;
this.ta2 = ta2;
this.ta3 = ta3;
this.level = level;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
boolean argMismatchFilter() {
return targs[0].compatibleWith(sigs[0]) &&
targs[1].compatibleWith(sigs[1]) &&
targs[2].compatibleWith(SignatureKind.NON_GENERIC);
}
class JavaSource extends SimpleJavaFileObject {
String template = "import java.util.*;\n" +
"interface A { #S1 #R1#TA1 m(); }\n" +
"interface B { #S2 #R2#TA2 m(); }\n" +
"interface AB extends A, B {}\n" +
"class Test {\n" +
" void test(AB ab) { #R3#TA3 n = ab.m(); }\n" +
"}";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replace("#S1", sig1.paramStr).
replace("#S2", sig2.paramStr).
replace("#R1", rt1.retStr).
replace("#R2", rt2.retStr).
replace("#R3", rt3.retStr).
replace("#TA1", ta1.typeargStr).
replace("#TA2", ta2.typeargStr).
replace("#TA3", ta3.typeargStr);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
String template = "import java.util.*;\n" +
"interface A { #{SIG[0]} #{RET[0]}#{TARG[0]} m(); }\n" +
"interface B { #{SIG[1]} #{RET[1]}#{TARG[1]} m(); }\n" +
"interface AB extends A, B {}\n" +
"class Test {\n" +
" void test(AB ab) { #{RET[2]}#{TARG[2]} n = ab.m(); }\n" +
"}";
@Override
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
level.opts != null ? Arrays.asList(level.opts) : null,
null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new AssertionError("Error thrown when compiling the following code:\n" +
source.getCharContent(true));
}
check();
public void doWork() throws IOException {
check(newCompilationTask()
.withOption("-XDuseUnsharedTable") //this test relies on predictable name indexes!
.withOptions(level.opts)
.withSourceFromTemplate(template)
.analyze());
}
void check() {
checkCount.incrementAndGet();
void check(Result<?> res) {
boolean errorExpected = false;
int mostSpecific = 0;
//first check that either |R1| <: |R2| or |R2| <: |R1|
if (rt1 != rt2) {
if (!rt1.moreSpecificThan(rt2) &&
!rt2.moreSpecificThan(rt1)) {
if (rets[0] != rets[1]) {
if (!rets[0].moreSpecificThan(rets[1]) &&
!rets[1].moreSpecificThan(rets[0])) {
errorExpected = true;
} else {
mostSpecific = rt1.moreSpecificThan(rt2) ? 1 : 2;
mostSpecific = rets[0].moreSpecificThan(rets[1]) ? 1 : 2;
}
}
//check that either TA1 <= TA2 or TA2 <= TA1 (unless most specific return found above is raw)
if (!errorExpected) {
if (ta1 != ta2) {
boolean useStrictCheck = ta1.moreSpecificThan(ta2, true) ||
ta2.moreSpecificThan(ta1, true);
if (!ta1.moreSpecificThan(ta2, useStrictCheck) &&
!ta2.moreSpecificThan(ta1, useStrictCheck)) {
if (targs[0] != targs[1]) {
boolean useStrictCheck = targs[0].moreSpecificThan(targs[1], true) ||
targs[1].moreSpecificThan(targs[0], true);
if (!targs[0].moreSpecificThan(targs[1], useStrictCheck) &&
!targs[1].moreSpecificThan(targs[0], useStrictCheck)) {
errorExpected = true;
} else {
int mostSpecific2 = ta1.moreSpecificThan(ta2, useStrictCheck) ? 1 : 2;
int mostSpecific2 = targs[0].moreSpecificThan(targs[1], useStrictCheck) ? 1 : 2;
if (mostSpecific != 0 && mostSpecific2 != mostSpecific) {
errorExpected = mostSpecific == 1 ?
ta1 != TypeArgumentKind.NONE :
ta2 != TypeArgumentKind.NONE;
targs[0] != TypeArgumentKind.NONE :
targs[1] != TypeArgumentKind.NONE;
} else {
mostSpecific = mostSpecific2;
}
@ -284,34 +237,21 @@ public class GenericOverrideTest
//finally, check that most specific return type is compatible with expected type
if (!errorExpected) {
ReturnTypeKind msrt = mostSpecific == 1 ? rt1 : rt2;
TypeArgumentKind msta = mostSpecific == 1 ? ta1 : ta2;
SignatureKind mssig = mostSpecific == 1 ? sig1 : sig2;
ReturnTypeKind msrt = mostSpecific == 1 ? rets[0] : rets[1];
TypeArgumentKind msta = mostSpecific == 1 ? targs[0] : targs[1];
SignatureKind mssig = mostSpecific == 1 ? sigs[0] : sigs[1];
if (!msrt.moreSpecificThan(rt3) ||
!msta.assignableTo(ta3, mssig, level)) {
if (!msrt.moreSpecificThan(rets[2]) ||
!msta.assignableTo(targs[2], mssig, level)) {
errorExpected = true;
}
}
if (errorExpected != diagChecker.errorFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
if (errorExpected != res.hasErrors()) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nFound error: " + res.hasErrors() +
"\nExpected error: " + errorExpected);
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -23,35 +23,32 @@
/**
* @test
* @bug 8003280 8004102 8006694
* @bug 8003280 8004102 8006694 8129962
* @summary Add lambda tests
* perform several automated checks in lambda conversion, esp. around accessibility
* temporarily workaround combo tests are causing time out in several platforms
* @author Maurizio Cimadamore
* @library ../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/timeout=600/othervm FunctionalInterfaceConversionTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main FunctionalInterfaceConversionTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
public class FunctionalInterfaceConversionTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
enum PackageKind {
public class FunctionalInterfaceConversionTest extends ComboInstance<FunctionalInterfaceConversionTest> {
enum PackageKind implements ComboParameter {
NO_PKG(""),
PKG_A("a");
@ -61,7 +58,8 @@ public class FunctionalInterfaceConversionTest
this.pkg = pkg;
}
String getPkgDecl() {
@Override
public String expand(String optParameter) {
return this == NO_PKG ?
"" :
"package " + pkg + ";";
@ -74,12 +72,12 @@ public class FunctionalInterfaceConversionTest
}
}
enum SamKind {
enum SamKind implements ComboParameter {
CLASS("public class Sam { }"),
ABSTACT_CLASS("public abstract class Sam { }"),
ANNOTATION("public @interface Sam { }"),
ENUM("public enum Sam { }"),
INTERFACE("public interface Sam { \n #METH; \n }");
INTERFACE("public interface Sam { \n #{METH1}; \n }");
String sam_str;
@ -87,12 +85,13 @@ public class FunctionalInterfaceConversionTest
this.sam_str = sam_str;
}
String getSam(String methStr) {
return sam_str.replaceAll("#METH", methStr);
@Override
public String expand(String optParameter) {
return sam_str;
}
}
enum ModifierKind {
enum ModifierKind implements ComboParameter {
PUBLIC("public"),
PACKAGE("");
@ -102,77 +101,73 @@ public class FunctionalInterfaceConversionTest
this.modifier_str = modifier_str;
}
boolean stricterThan(ModifierKind that) {
return this.ordinal() > that.ordinal();
@Override
public String expand(String optParameter) {
return modifier_str;
}
}
enum TypeKind {
enum TypeKind implements ComboParameter {
EXCEPTION("Exception"),
PKG_CLASS("PackageClass");
String typeStr;
private TypeKind(String typeStr) {
TypeKind(String typeStr) {
this.typeStr = typeStr;
}
@Override
public String expand(String optParameter) {
return typeStr;
}
}
enum ExprKind {
enum ExprKind implements ComboParameter {
LAMBDA("x -> null"),
MREF("this::m");
String exprStr;
private ExprKind(String exprStr) {
ExprKind(String exprStr) {
this.exprStr = exprStr;
}
@Override
public String expand(String optParameter) {
return exprStr;
}
}
enum MethodKind {
enum MethodKind implements ComboParameter {
NONE(""),
NON_GENERIC("public abstract #R m(#ARG s) throws #T;"),
GENERIC("public abstract <X> #R m(#ARG s) throws #T;");
NON_GENERIC("public abstract #{RET} m(#{ARG} s) throws #{THROWN};"),
GENERIC("public abstract <X> #{RET} m(#{ARG} s) throws #{THROWN};");
String methodTemplate;
private MethodKind(String methodTemplate) {
MethodKind(String methodTemplate) {
this.methodTemplate = methodTemplate;
}
String getMethod(TypeKind retType, TypeKind argType, TypeKind thrownType) {
return methodTemplate.replaceAll("#R", retType.typeStr).
replaceAll("#ARG", argType.typeStr).
replaceAll("#T", thrownType.typeStr);
@Override
public String expand(String optParameter) {
return methodTemplate;
}
}
public static void main(String[] args) throws Exception {
for (PackageKind samPkg : PackageKind.values()) {
for (ModifierKind modKind : ModifierKind.values()) {
for (SamKind samKind : SamKind.values()) {
for (MethodKind samMeth : MethodKind.values()) {
for (MethodKind clientMeth : MethodKind.values()) {
for (TypeKind retType : TypeKind.values()) {
for (TypeKind argType : TypeKind.values()) {
for (TypeKind thrownType : TypeKind.values()) {
for (ExprKind exprKind : ExprKind.values()) {
pool.execute(
new FunctionalInterfaceConversionTest(
samPkg, modKind, samKind,
samMeth, clientMeth, retType,
argType, thrownType, exprKind));
}
}
}
}
}
}
}
}
}
checkAfterExec(false);
new ComboTestHelper<FunctionalInterfaceConversionTest>()
.withDimension("PKG", (x, pkg) -> x.samPkg = pkg, PackageKind.values())
.withDimension("MOD", (x, mod) -> x.modKind = mod, ModifierKind.values())
.withDimension("CLAZZ", (x, sam) -> x.samKind = sam, SamKind.values())
.withDimension("METH1", (x, meth) -> x.samMeth = meth, MethodKind.values())
.withDimension("METH2", (x, meth) -> x.clientMeth = meth, MethodKind.values())
.withDimension("RET", (x, ret) -> x.retType = ret, TypeKind.values())
.withDimension("ARG", (x, arg) -> x.argType = arg, TypeKind.values())
.withDimension("THROWN", (x, thrown) -> x.thrownType = thrown, TypeKind.values())
.withDimension("EXPR", (x, expr) -> x.exprKind = expr, ExprKind.values())
.run(FunctionalInterfaceConversionTest::new);
}
PackageKind samPkg;
@ -184,70 +179,32 @@ public class FunctionalInterfaceConversionTest
TypeKind argType;
TypeKind thrownType;
ExprKind exprKind;
DiagnosticChecker dc;
SourceFile samSourceFile = new SourceFile("Sam.java", "#P \n #C") {
@Override
public String toString() {
return template.replaceAll("#P", samPkg.getPkgDecl()).
replaceAll("#C", samKind.getSam(
samMeth.getMethod(retType, argType, thrownType)));
}
};
SourceFile pkgClassSourceFile =
new SourceFile("PackageClass.java",
"#P\n #M class PackageClass extends Exception { }") {
@Override
public String toString() {
return template.replaceAll("#P", samPkg.getPkgDecl()).
replaceAll("#M", modKind.modifier_str);
}
};
SourceFile clientSourceFile =
new SourceFile("Client.java",
"#I\n abstract class Client { \n" +
" Sam s = #E;\n" +
" #M \n }") {
@Override
public String toString() {
return template.replaceAll("#I", samPkg.getImportStat())
.replaceAll("#E", exprKind.exprStr)
.replaceAll("#M", clientMeth.getMethod(retType, argType, thrownType));
}
};
FunctionalInterfaceConversionTest(PackageKind samPkg, ModifierKind modKind,
SamKind samKind, MethodKind samMeth, MethodKind clientMeth,
TypeKind retType, TypeKind argType, TypeKind thrownType,
ExprKind exprKind) {
this.samPkg = samPkg;
this.modKind = modKind;
this.samKind = samKind;
this.samMeth = samMeth;
this.clientMeth = clientMeth;
this.retType = retType;
this.argType = argType;
this.thrownType = thrownType;
this.exprKind = exprKind;
this.dc = new DiagnosticChecker();
}
String samSource = "#{PKG} \n #{CLAZZ}";
String pkgClassSource = "#{PKG}\n #{MOD} class PackageClass extends Exception { }";
String clientSource = "#{IMP}\n abstract class Client { \n" +
" Sam s = #{EXPR};\n" +
" #{METH2} \n }";
@Override
public void run() {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate("Sam", samSource)
.withSourceFromTemplate("PackageClass", pkgClassSource)
.withSourceFromTemplate("Client", clientSource, this::importStmt)
.analyze());
}
JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), dc, null, null,
Arrays.asList(samSourceFile, pkgClassSourceFile, clientSourceFile));
try {
ct.analyze();
} catch (IOException ex) {
throw new AssertionError("Test failing with cause", ex.getCause());
ComboParameter importStmt(String name) {
switch (name) {
case "IMP": return new ComboParameter.Constant<>(samPkg.getImportStat());
default: return null;
}
if (dc.errorFound == checkSamConversion()) {
throw new AssertionError(samSourceFile + "\n\n" +
pkgClassSourceFile + "\n\n" + clientSourceFile);
}
void check(Result<?> res) {
if (res.hasErrors() == checkSamConversion()) {
fail("Unexpected compilation result; " + res.compilationInfo());
}
}
@ -276,35 +233,4 @@ public class FunctionalInterfaceConversionTest
return true;
}
}
abstract class SourceFile extends SimpleJavaFileObject {
protected String template;
public SourceFile(String filename, String template) {
super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
this.template = template;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return toString();
}
@Override
public abstract String toString();
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound = false;
@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -23,39 +23,40 @@
/*
* @test
* @bug 7115050 8003280 8005852 8006694
* @bug 7115050 8003280 8005852 8006694 8129962
* @summary Add lambda tests
* Add parser support for lambda expressions
* temporarily workaround combo tests are causing time out in several platforms
* @library ../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm LambdaParserTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main LambdaParserTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class LambdaParserTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class LambdaParserTest extends ComboInstance<LambdaParserTest> {
enum LambdaKind {
enum LambdaKind implements ComboParameter {
NILARY_EXPR("()->x"),
NILARY_STMT("()->{ return x; }"),
ONEARY_SHORT_EXPR("#PN->x"),
ONEARY_SHORT_STMT("#PN->{ return x; }"),
ONEARY_EXPR("(#M1 #T1 #PN)->x"),
ONEARY_STMT("(#M1 #T1 #PN)->{ return x; }"),
TWOARY_EXPR("(#M1 #T1 #PN, #M2 #T2 y)->x"),
TWOARY_STMT("(#M1 #T1 #PN, #M2 #T2 y)->{ return x; }");
ONEARY_SHORT_EXPR("#{NAME}->x"),
ONEARY_SHORT_STMT("#{NAME}->{ return x; }"),
ONEARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME})->x"),
ONEARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME})->{ return x; }"),
TWOARY_EXPR("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->x"),
TWOARY_STMT("(#{MOD[0]} #{TYPE[0]} #{NAME}, #{MOD[1]} #{TYPE[1]} y)->{ return x; }");
String lambdaTemplate;
@ -63,13 +64,9 @@ public class LambdaParserTest
this.lambdaTemplate = lambdaTemplate;
}
String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2,
ModifierKind mk1, ModifierKind mk2, LambdaParameterName pn) {
return lambdaTemplate.replaceAll("#M1", mk1.modifier)
.replaceAll("#M2", mk2.modifier)
.replaceAll("#T1", pk1.parameterType)
.replaceAll("#T2", pk2.parameterType)
.replaceAll("#PN", pn.nameStr);
@Override
public String expand(String optParameter) {
return lambdaTemplate;
}
int arity() {
@ -92,7 +89,7 @@ public class LambdaParserTest
}
}
enum LambdaParameterName {
enum LambdaParameterName implements ComboParameter {
IDENT("x"),
UNDERSCORE("_");
@ -101,9 +98,14 @@ public class LambdaParserTest
LambdaParameterName(String nameStr) {
this.nameStr = nameStr;
}
@Override
public String expand(String optParameter) {
return nameStr;
}
}
enum LambdaParameterKind {
enum LambdaParameterKind implements ComboParameter {
IMPLICIT(""),
EXPLIICT_SIMPLE("A"),
EXPLIICT_SIMPLE_ARR1("A[]"),
@ -129,9 +131,14 @@ public class LambdaParserTest
return this == EXPLICIT_VARARGS ||
this == EXPLICIT_GENERIC2_VARARGS;
}
@Override
public String expand(String optParameter) {
return parameterType;
}
}
enum ModifierKind {
enum ModifierKind implements ComboParameter {
NONE(""),
FINAL("final"),
PUBLIC("public");
@ -150,15 +157,20 @@ public class LambdaParserTest
default: throw new AssertionError("Invalid modifier kind " + this);
}
}
@Override
public String expand(String optParameter) {
return modifier;
}
}
enum ExprKind {
NONE("#L#S"),
SINGLE_PAREN1("(#L#S)"),
SINGLE_PAREN2("(#L)#S"),
DOUBLE_PAREN1("((#L#S))"),
DOUBLE_PAREN2("((#L)#S)"),
DOUBLE_PAREN3("((#L))#S");
enum ExprKind implements ComboParameter {
NONE("#{LAMBDA}#{SUBEXPR}"),
SINGLE_PAREN1("(#{LAMBDA}#{SUBEXPR})"),
SINGLE_PAREN2("(#{LAMBDA})#{SUBEXPR}"),
DOUBLE_PAREN1("((#{LAMBDA}#{SUBEXPR}))"),
DOUBLE_PAREN2("((#{LAMBDA})#{SUBEXPR})"),
DOUBLE_PAREN3("((#{LAMBDA}))#{SUBEXPR}");
String expressionTemplate;
@ -166,14 +178,13 @@ public class LambdaParserTest
this.expressionTemplate = expressionTemplate;
}
String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2,
ModifierKind mk1, ModifierKind mk2, LambdaKind lk, LambdaParameterName pn, SubExprKind sk) {
return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, mk2, pn))
.replaceAll("#S", sk.subExpression);
@Override
public String expand(String optParameter) {
return expressionTemplate;
}
}
enum SubExprKind {
enum SubExprKind implements ComboParameter {
NONE(""),
SELECT_FIELD(".f"),
SELECT_METHOD(".f()"),
@ -186,133 +197,78 @@ public class LambdaParserTest
SubExprKind(String subExpression) {
this.subExpression = subExpression;
}
@Override
public String expand(String optParameter) {
return subExpression;
}
}
public static void main(String... args) throws Exception {
for (LambdaKind lk : LambdaKind.values()) {
for (LambdaParameterName pn : LambdaParameterName.values()) {
for (LambdaParameterKind pk1 : LambdaParameterKind.values()) {
if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT)
continue;
for (LambdaParameterKind pk2 : LambdaParameterKind.values()) {
if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT)
continue;
for (ModifierKind mk1 : ModifierKind.values()) {
if (mk1 != ModifierKind.NONE && lk.isShort())
continue;
if (lk.arity() < 1 && mk1 != ModifierKind.NONE)
continue;
for (ModifierKind mk2 : ModifierKind.values()) {
if (lk.arity() < 2 && mk2 != ModifierKind.NONE)
continue;
for (SubExprKind sk : SubExprKind.values()) {
for (ExprKind ek : ExprKind.values()) {
pool.execute(
new LambdaParserTest(pk1, pk2, mk1,
mk2, lk, sk, ek, pn));
}
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<LambdaParserTest>()
.withFilter(LambdaParserTest::redundantTestFilter)
.withFilter(LambdaParserTest::badImplicitFilter)
.withDimension("LAMBDA", (x, lk) -> x.lk = lk, LambdaKind.values())
.withDimension("NAME", (x, name) -> x.pn = name, LambdaParameterName.values())
.withArrayDimension("TYPE", (x, type, idx) -> x.pks[idx] = type, 2, LambdaParameterKind.values())
.withArrayDimension("MOD", (x, mod, idx) -> x.mks[idx] = mod, 2, ModifierKind.values())
.withDimension("EXPR", ExprKind.values())
.withDimension("SUBEXPR", SubExprKind.values())
.run(LambdaParserTest::new);
}
LambdaParameterKind pk1;
LambdaParameterKind pk2;
ModifierKind mk1;
ModifierKind mk2;
LambdaParameterKind[] pks = new LambdaParameterKind[2];
ModifierKind[] mks = new ModifierKind[2];
LambdaKind lk;
LambdaParameterName pn;
SubExprKind sk;
ExprKind ek;
JavaSource source;
DiagnosticChecker diagChecker;
LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2,
ModifierKind mk1, ModifierKind mk2, LambdaKind lk,
SubExprKind sk, ExprKind ek, LambdaParameterName pn) {
this.pk1 = pk1;
this.pk2 = pk2;
this.mk1 = mk1;
this.mk2 = mk2;
this.lk = lk;
this.pn = pn;
this.sk = sk;
this.ek = ek;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
boolean badImplicitFilter() {
return !(mks[0] != ModifierKind.NONE && lk.isShort());
}
class JavaSource extends SimpleJavaFileObject {
String template = "class Test {\n" +
" SAM s = #E;\n" +
"}";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#E",
ek.expressionString(pk1, pk2, mk1, mk2, lk, pn, sk));
boolean redundantTestFilter() {
for (int i = lk.arity(); i < mks.length ; i++) {
if (mks[i].ordinal() != 0) {
return false;
}
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
for (int i = lk.arity(); i < pks.length ; i++) {
if (pks[i].ordinal() != 0) {
return false;
}
}
return true;
}
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
null, null, Arrays.asList(source));
try {
ct.parse();
} catch (Throwable ex) {
processException(ex);
return;
}
check();
String template = "class Test {\n" +
" SAM s = #{EXPR};\n" +
"}";
@Override
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate(template)
.parse());
}
void check() {
checkCount.incrementAndGet();
boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) ||
(lk.arity() > 1 && !mk2.compatibleWith(pk2));
void check(Result<?> res) {
boolean errorExpected = (lk.arity() > 0 && !mks[0].compatibleWith(pks[0])) ||
(lk.arity() > 1 && !mks[1].compatibleWith(pks[1]));
if (lk.arity() == 2 &&
(pk1.explicit() != pk2.explicit() ||
pk1.isVarargs())) {
(pks[0].explicit() != pks[1].explicit() ||
pks[0].isVarargs())) {
errorExpected = true;
}
errorExpected |= pn == LambdaParameterName.UNDERSCORE &&
lk.arity() > 0;
if (errorExpected != diagChecker.errorFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
if (errorExpected != res.hasErrors()) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nFound error: " + res.hasErrors() +
"\nExpected error: " + errorExpected);
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -23,39 +23,39 @@
/*
* @test
* @bug 7115052 8003280 8006694
* @bug 7115052 8003280 8006694 8129962
* @summary Add lambda tests
* Add parser support for method references
* temporarily workaround combo tests are causing time out in several platforms
* @library ../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm MethodReferenceParserTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main MethodReferenceParserTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class MethodReferenceParserTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class MethodReferenceParserTest extends ComboInstance<MethodReferenceParserTest> {
enum ReferenceKind {
METHOD_REF("#Q::#Gm"),
CONSTRUCTOR_REF("#Q::#Gnew"),
enum ReferenceKind implements ComboParameter {
METHOD_REF("#{QUAL}::#{TARGS}m"),
CONSTRUCTOR_REF("#{QUAL}::#{TARGS}new"),
FALSE_REF("min < max"),
ERR_SUPER("#Q::#Gsuper"),
ERR_METH0("#Q::#Gm()"),
ERR_METH1("#Q::#Gm(X)"),
ERR_CONSTR0("#Q::#Gnew()"),
ERR_CONSTR1("#Q::#Gnew(X)");
ERR_SUPER("#{QUAL}::#{TARGS}super"),
ERR_METH0("#{QUAL}::#{TARGS}m()"),
ERR_METH1("#{QUAL}::#{TARGS}m(X)"),
ERR_CONSTR0("#{QUAL}::#{TARGS}new()"),
ERR_CONSTR1("#{QUAL}::#{TARGS}new(X)");
String referenceTemplate;
@ -63,12 +63,6 @@ public class MethodReferenceParserTest
this.referenceTemplate = referenceTemplate;
}
String getReferenceString(QualifierKind qk, GenericKind gk) {
return referenceTemplate
.replaceAll("#Q", qk.qualifier)
.replaceAll("#G", gk.typeParameters);
}
boolean erroneous() {
switch (this) {
case ERR_SUPER:
@ -80,11 +74,16 @@ public class MethodReferenceParserTest
default: return false;
}
}
@Override
public String expand(String optParameter) {
return referenceTemplate;
}
}
enum ContextKind {
ASSIGN("SAM s = #E;"),
METHOD("m(#E, i);");
enum ContextKind implements ComboParameter {
ASSIGN("SAM s = #{EXPR};"),
METHOD("m(#{EXPR}, i);");
String contextTemplate;
@ -92,13 +91,13 @@ public class MethodReferenceParserTest
this.contextTemplate = contextTemplate;
}
String contextString(ExprKind ek, ReferenceKind rk, QualifierKind qk,
GenericKind gk, SubExprKind sk) {
return contextTemplate.replaceAll("#E", ek.expressionString(rk, qk, gk, sk));
@Override
public String expand(String optParameter) {
return contextTemplate;
}
}
enum GenericKind {
enum GenericKind implements ComboParameter {
NONE(""),
ONE("<X>"),
TWO("<X,Y>");
@ -108,9 +107,14 @@ public class MethodReferenceParserTest
GenericKind(String typeParameters) {
this.typeParameters = typeParameters;
}
@Override
public String expand(String optParameter) {
return typeParameters;
}
}
enum QualifierKind {
enum QualifierKind implements ComboParameter {
THIS("this"),
SUPER("super"),
NEW("new Foo()"),
@ -131,15 +135,20 @@ public class MethodReferenceParserTest
QualifierKind(String qualifier) {
this.qualifier = qualifier;
}
@Override
public String expand(String optParameter) {
return qualifier;
}
}
enum ExprKind {
NONE("#R::S"),
SINGLE_PAREN1("(#R#S)"),
SINGLE_PAREN2("(#R)#S"),
DOUBLE_PAREN1("((#R#S))"),
DOUBLE_PAREN2("((#R)#S)"),
DOUBLE_PAREN3("((#R))#S");
enum ExprKind implements ComboParameter {
NONE("#{MREF}"),
SINGLE_PAREN1("(#{MREF}#{SUBEXPR})"),
SINGLE_PAREN2("(#{MREF})#{SUBEXPR}"),
DOUBLE_PAREN1("((#{MREF}#{SUBEXPR}))"),
DOUBLE_PAREN2("((#{MREF})#{SUBEXPR})"),
DOUBLE_PAREN3("((#{MREF}))#{SUBEXPR}");
String expressionTemplate;
@ -147,14 +156,13 @@ public class MethodReferenceParserTest
this.expressionTemplate = expressionTemplate;
}
String expressionString(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk) {
return expressionTemplate
.replaceAll("#R", rk.getReferenceString(qk, gk))
.replaceAll("#S", sk.subExpression);
@Override
public String expand(String optParameter) {
return expressionTemplate;
}
}
enum SubExprKind {
enum SubExprKind implements ComboParameter {
NONE(""),
SELECT_FIELD(".f"),
SELECT_METHOD(".f()"),
@ -167,100 +175,45 @@ public class MethodReferenceParserTest
SubExprKind(String subExpression) {
this.subExpression = subExpression;
}
@Override
public String expand(String optParameter) {
return subExpression;
}
}
public static void main(String... args) throws Exception {
for (ReferenceKind rk : ReferenceKind.values()) {
for (QualifierKind qk : QualifierKind.values()) {
for (GenericKind gk : GenericKind.values()) {
for (SubExprKind sk : SubExprKind.values()) {
for (ExprKind ek : ExprKind.values()) {
for (ContextKind ck : ContextKind.values()) {
pool.execute(new MethodReferenceParserTest(rk, qk, gk, sk, ek, ck));
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<MethodReferenceParserTest>()
.withDimension("MREF", (x, ref) -> x.rk = ref, ReferenceKind.values())
.withDimension("QUAL", QualifierKind.values())
.withDimension("TARGS", GenericKind.values())
.withDimension("EXPR", ExprKind.values())
.withDimension("SUBEXPR", SubExprKind.values())
.withDimension("CTX", ContextKind.values())
.run(MethodReferenceParserTest::new);
}
ReferenceKind rk;
QualifierKind qk;
GenericKind gk;
SubExprKind sk;
ExprKind ek;
ContextKind ck;
JavaSource source;
DiagnosticChecker diagChecker;
MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek, ContextKind ck) {
this.rk = rk;
this.qk = qk;
this.gk = gk;
this.sk = sk;
this.ek = ek;
this.ck = ck;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
class JavaSource extends SimpleJavaFileObject {
String template = "class Test {\n" +
" void test() {\n" +
" #C\n" +
" }" +
"}";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#C", ck.contextString(ek, rk, qk, gk, sk));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
String template = "class Test {\n" +
" void test() {\n" +
" #{CTX}\n" +
" }" +
"}";
@Override
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
null, null, Arrays.asList(source));
try {
ct.parse();
} catch (Throwable ex) {
processException(ex);
return;
}
check();
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate(template)
.parse());
}
void check() {
checkCount.incrementAndGet();
if (diagChecker.errorFound != rk.erroneous()) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
void check(Result<?> res) {
if (res.hasErrors() != rk.erroneous()) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nFound error: " + res.hasErrors() +
"\nExpected error: " + rk.erroneous());
}
}
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

@ -25,15 +25,12 @@
* @test
* @bug 8129547
* @summary Excess entries in BootstrapMethods with the same (bsm, bsmKind, bsmStaticArgs), but different dynamicArgs
* @library lib
* @library /tools/javac/lib
* @modules jdk.jdeps/com.sun.tools.classfile
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.jvm
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm TestBootstrapMethodsCount
*/
import java.io.File;
@ -43,8 +40,10 @@ import java.util.Arrays;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
@ -69,14 +68,11 @@ import com.sun.tools.javac.util.Names;
import static com.sun.tools.javac.jvm.ClassFile.*;
public class TestBootstrapMethodsCount
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class TestBootstrapMethodsCount {
public static void main(String... args) throws Exception {
pool.execute(new TestBootstrapMethodsCount());
checkAfterExec();
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
new TestBootstrapMethodsCount().run(comp);
}
DiagChecker dc;
@ -85,10 +81,9 @@ public class TestBootstrapMethodsCount
dc = new DiagChecker();
}
public void run() {
int id = checkCount.incrementAndGet();
JavaSource source = new JavaSource(id);
JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
public void run(JavaCompiler comp) {
JavaSource source = new JavaSource();
JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, null, dc,
Arrays.asList("-g"), null, Arrays.asList(source));
Context context = ct.getContext();
Symtab syms = Symtab.instance(context);
@ -108,11 +103,11 @@ public class TestBootstrapMethodsCount
String.format("Diags found when compiling following code\n%s\n\n%s",
source.source, dc.printDiags()));
}
verifyBytecode(id);
verifyBytecode();
}
void verifyBytecode(int id) {
File compiledTest = new File(String.format("Test%d.class", id));
void verifyBytecode() {
File compiledTest = new File("Test.class");
try {
ClassFile cf = ClassFile.read(compiledTest);
BootstrapMethods_attribute bsm_attr =
@ -131,14 +126,14 @@ public class TestBootstrapMethodsCount
class JavaSource extends SimpleJavaFileObject {
static final String source_template = "import java.lang.invoke.*;\n" +
static final String source = "import java.lang.invoke.*;\n" +
"class Bootstrap {\n" +
" public static CallSite bsm(MethodHandles.Lookup lookup, " +
"String name, MethodType methodType) {\n" +
" return null;\n" +
" }\n" +
"}\n" +
"class Test#ID {\n" +
"class Test {\n" +
" void m1() { }\n" +
" void m2(Object arg1) { }\n" +
" void test1() {\n" +
@ -151,11 +146,8 @@ public class TestBootstrapMethodsCount
" }\n" +
"}";
String source;
JavaSource(int id) {
JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = source_template.replace("#ID", String.valueOf(id));
}
@Override

@ -23,33 +23,27 @@
/*
* @test
* @bug 7194586 8003280 8006694 8010404
* @bug 7194586 8003280 8006694 8010404 8129962
* @summary Add lambda tests
* Add back-end support for invokedynamic
* temporarily workaround combo tests are causing time out in several platforms
* @library ../lib
* @library /tools/javac/lib
* @modules jdk.jdeps/com.sun.tools.classfile
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.jvm
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm TestInvokeDynamic
* @build combo.ComboTestHelper
* @run main TestInvokeDynamic
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
@ -78,13 +72,17 @@ import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import combo.ComboParameter;
import combo.ComboTask;
import combo.ComboTestHelper;
import combo.ComboInstance;
import combo.ComboTask.Result;
import static com.sun.tools.javac.jvm.ClassFile.*;
public class TestInvokeDynamic
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class TestInvokeDynamic extends ComboInstance<TestInvokeDynamic> {
enum StaticArgumentKind {
enum StaticArgumentKind implements ComboParameter {
STRING("Hello!", "String", "Ljava/lang/String;") {
@Override
boolean check(CPInfo cpInfo) throws Exception {
@ -189,88 +187,91 @@ public class TestInvokeDynamic
throw new AssertionError();
}
}
@Override
public String expand(String optParameter) {
return sourceTypeStr;
}
}
enum StaticArgumentsArity {
ZERO(0),
ONE(1),
TWO(2),
THREE(3);
enum StaticArgumentsArity implements ComboParameter {
ZERO(0, ""),
ONE(1, ",#{SARG[0]} s1"),
TWO(2, ",#{SARG[0]} s1, #{SARG[1]} s2"),
THREE(3, ",#{SARG[0]} s1, #{SARG[1]} s2, #{SARG[2]} s3");
int arity;
String argsTemplate;
StaticArgumentsArity(int arity) {
StaticArgumentsArity(int arity, String argsTemplate) {
this.arity = arity;
this.argsTemplate = argsTemplate;
}
@Override
public String expand(String optParameter) {
return argsTemplate;
}
}
public static void main(String... args) throws Exception {
for (StaticArgumentsArity arity : StaticArgumentsArity.values()) {
if (arity.arity == 0) {
pool.execute(new TestInvokeDynamic(arity));
} else {
for (StaticArgumentKind sak1 : StaticArgumentKind.values()) {
if (arity.arity == 1) {
pool.execute(new TestInvokeDynamic(arity, sak1));
} else {
for (StaticArgumentKind sak2 : StaticArgumentKind.values()) {
if (arity.arity == 2) {
pool.execute(new TestInvokeDynamic(arity, sak1, sak2));
} else {
for (StaticArgumentKind sak3 : StaticArgumentKind.values()) {
pool.execute(
new TestInvokeDynamic(arity, sak1, sak2, sak3));
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<TestInvokeDynamic>()
.withFilter(TestInvokeDynamic::redundantTestFilter)
.withDimension("SARGS", (x, arity) -> x.arity = arity, StaticArgumentsArity.values())
.withArrayDimension("SARG", (x, arg, idx) -> x.saks[idx] = arg, 3, StaticArgumentKind.values())
.run(TestInvokeDynamic::new);
}
StaticArgumentsArity arity;
StaticArgumentKind[] saks;
DiagChecker dc;
StaticArgumentKind[] saks = new StaticArgumentKind[3];
TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) {
this.arity = arity;
this.saks = saks;
dc = new DiagChecker();
boolean redundantTestFilter() {
for (int i = arity.arity ; i < saks.length ; i++) {
if (saks[i].ordinal() != 0) {
return false;
}
}
return true;
}
public void run() {
int id = checkCount.incrementAndGet();
JavaSource source = new JavaSource(id);
JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
Arrays.asList("-g"), null, Arrays.asList(source));
final String source_template =
"import java.lang.invoke.*;\n" +
"class Test {\n" +
" void m() { }\n" +
" void test() {\n" +
" Object o = this; // marker statement \n" +
" m();\n" +
" }\n" +
"}\n" +
"class Bootstrap {\n" +
" public static CallSite bsm(MethodHandles.Lookup lookup, " +
"String name, MethodType methodType #{SARGS}) {\n" +
" return null;\n" +
" }\n" +
"}";
@Override
public void doWork() throws IOException {
ComboTask comboTask = newCompilationTask()
.withOption("-g")
.withSourceFromTemplate(source_template);
JavacTaskImpl ct = (JavacTaskImpl)comboTask.getTask();
Context context = ct.getContext();
Symtab syms = Symtab.instance(context);
Names names = Names.instance(context);
Types types = Types.instance(context);
ct.addTaskListener(new Indifier(syms, names, types));
try {
ct.generate();
} catch (Throwable t) {
t.printStackTrace();
throw new AssertionError(
String.format("Error thrown when compiling following code\n%s",
source.source));
}
if (dc.diagFound) {
throw new AssertionError(
String.format("Diags found when compiling following code\n%s\n\n%s",
source.source, dc.printDiags()));
}
verifyBytecode(id);
verifyBytecode(comboTask.generate());
}
void verifyBytecode(int id) {
File compiledTest = new File(String.format("Test%d.class", id));
try {
ClassFile cf = ClassFile.read(compiledTest);
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
if (res.hasErrors()) {
fail("Diags found when compiling instance: " + res.compilationInfo());
return;
}
try (InputStream is = res.get().iterator().next().openInputStream()){
ClassFile cf = ClassFile.read(is);
Method testMethod = null;
for (Method m : cf.methods) {
if (m.getName(cf.constant_pool).equals("test")) {
@ -279,12 +280,14 @@ public class TestInvokeDynamic
}
}
if (testMethod == null) {
throw new Error("Test method not found");
fail("Test method not found");
return;
}
Code_attribute ea =
(Code_attribute)testMethod.attributes.get(Attribute.Code);
if (testMethod == null) {
throw new Error("Code attribute for test() method not found");
fail("Code attribute for test() method not found");
return;
}
int bsmIdx = -1;
@ -296,37 +299,39 @@ public class TestInvokeDynamic
.constant_pool.get(i.getShort(1));
bsmIdx = indyInfo.bootstrap_method_attr_index;
if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) {
throw new
AssertionError("type mismatch for CONSTANT_InvokeDynamic_info");
fail("type mismatch for CONSTANT_InvokeDynamic_info");
return;
}
}
}
if (bsmIdx == -1) {
throw new Error("Missing invokedynamic in generated code");
fail("Missing invokedynamic in generated code");
return;
}
BootstrapMethods_attribute bsm_attr =
(BootstrapMethods_attribute)cf
.getAttribute(Attribute.BootstrapMethods);
if (bsm_attr.bootstrap_method_specifiers.length != 1) {
throw new Error("Bad number of method specifiers " +
fail("Bad number of method specifiers " +
"in BootstrapMethods attribute");
return;
}
BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
bsm_attr.bootstrap_method_specifiers[0];
if (bsm_spec.bootstrap_arguments.length != arity.arity) {
throw new Error("Bad number of static invokedynamic args " +
fail("Bad number of static invokedynamic args " +
"in BootstrapMethod attribute");
return;
}
int count = 0;
for (StaticArgumentKind sak : saks) {
if (!sak.check(cf.constant_pool
.get(bsm_spec.bootstrap_arguments[count]))) {
throw new Error("Bad static argument value " + sak);
for (int i = 0 ; i < arity.arity ; i++) {
if (!saks[i].check(cf.constant_pool
.get(bsm_spec.bootstrap_arguments[i]))) {
fail("Bad static argument value " + saks[i]);
return;
}
count++;
}
CONSTANT_MethodHandle_info bsm_handle =
@ -334,7 +339,8 @@ public class TestInvokeDynamic
.get(bsm_spec.bootstrap_method_ref);
if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) {
throw new Error("Bad kind on boostrap method handle");
fail("Bad kind on boostrap method handle");
return;
}
CONSTANT_Methodref_info bsm_ref =
@ -342,88 +348,51 @@ public class TestInvokeDynamic
.get(bsm_handle.reference_index);
if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) {
throw new Error("Bad owner of boostrap method");
fail("Bad owner of boostrap method");
return;
}
if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) {
throw new Error("Bad boostrap method name");
fail("Bad boostrap method name");
return;
}
if (!bsm_ref.getNameAndTypeInfo()
.getType().equals(asBSMSignatureString())) {
throw new Error("Bad boostrap method type" +
fail("Bad boostrap method type" +
bsm_ref.getNameAndTypeInfo().getType() + " " +
asBSMSignatureString());
return;
}
LineNumberTable_attribute lnt =
(LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable);
if (lnt == null) {
throw new Error("No LineNumberTable attribute");
fail("No LineNumberTable attribute");
return;
}
if (lnt.line_number_table_length != 3) {
throw new Error("Wrong number of entries in LineNumberTable");
fail("Wrong number of entries in LineNumberTable");
return;
}
} catch (Exception e) {
e.printStackTrace();
throw new Error("error reading " + compiledTest +": " + e);
fail("error reading classfile: " + res.compilationInfo());
return;
}
}
String asBSMSignatureString() {
StringBuilder buf = new StringBuilder();
buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;");
for (StaticArgumentKind sak : saks) {
buf.append(sak.bytecodeTypeStr);
for (int i = 0 ; i < arity.arity ; i++) {
buf.append(saks[i].bytecodeTypeStr);
}
buf.append(")Ljava/lang/invoke/CallSite;");
return buf.toString();
}
class JavaSource extends SimpleJavaFileObject {
static final String source_template = "import java.lang.invoke.*;\n" +
"class Bootstrap {\n" +
" public static CallSite bsm(MethodHandles.Lookup lookup, " +
"String name, MethodType methodType #SARGS) {\n" +
" return null;\n" +
" }\n" +
"}\n" +
"class Test#ID {\n" +
" void m() { }\n" +
" void test() {\n" +
" Object o = this; // marker statement \n" +
" m();\n" +
" }\n" +
"}";
String source;
JavaSource(int id) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = source_template.replace("#SARGS", asSignatureString())
.replace("#ID", String.valueOf(id));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
String asSignatureString() {
int count = 0;
StringBuilder buf = new StringBuilder();
for (StaticArgumentKind sak : saks) {
buf.append(",");
buf.append(sak.sourceTypeStr);
buf.append(' ');
buf.append(String.format("x%d", count++));
}
return buf.toString();
}
}
class Indifier extends TreeScanner<Void, Void> implements TaskListener {
MethodSymbol bsm;
@ -475,26 +444,4 @@ public class TestInvokeDynamic
return null;
}
}
static class DiagChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean diagFound;
ArrayList<String> diags = new ArrayList<>();
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
diags.add(diagnostic.getMessage(Locale.getDefault()));
diagFound = true;
}
String printDiags() {
StringBuilder buf = new StringBuilder();
for (String s : diags) {
buf.append(s);
buf.append("\n");
}
return buf.toString();
}
}
}

@ -23,34 +23,35 @@
/*
* @test
* @bug 8013576
* @bug 8013576 8129962
* @summary Add stat support to LambdaToMethod
* @library ../lib
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm TestLambdaToMethodStats
* @build combo.ComboTestHelper
* @run main TestLambdaToMethodStats
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import java.io.IOException;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.ClientCodeWrapper;
import com.sun.tools.javac.util.JCDiagnostic;
public class TestLambdaToMethodStats
extends JavacTestingAbstractThreadedTest
implements Runnable {
import com.sun.tools.javac.util.List;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
enum ExprKind {
public class TestLambdaToMethodStats extends ComboInstance<TestLambdaToMethodStats> {
enum ExprKind implements ComboParameter {
LAMBDA("()->null"),
MREF1("this::g"),
MREF2("this::h");
@ -60,9 +61,14 @@ public class TestLambdaToMethodStats
ExprKind(String exprStr) {
this.exprStr = exprStr;
}
@Override
public String expand(String optParameter) {
return exprStr;
}
}
enum TargetKind {
enum TargetKind implements ComboParameter {
IMPLICIT(""),
SERIALIZABLE("(A & java.io.Serializable)");
@ -71,124 +77,89 @@ public class TestLambdaToMethodStats
TargetKind(String targetStr) {
this.targetStr = targetStr;
}
@Override
public String expand(String optParameter) {
return targetStr;
}
}
enum DiagnosticKind {
LAMBDA_STAT("compiler.note.lambda.stat", true, false),
MREF_STAT("compiler.note.mref.stat", false, false),
MREF_STAT1("compiler.note.mref.stat.1", false, true);
String code;
boolean lambda;
boolean bridge;
DiagnosticKind(String code, boolean lambda, boolean bridge) {
this.code = code;
this.lambda = lambda;
this.bridge = bridge;
}
}
public static void main(String... args) throws Exception {
for (ExprKind ek : ExprKind.values()) {
for (TargetKind tk : TargetKind.values()) {
pool.execute(new TestLambdaToMethodStats(ek, tk));
}
}
checkAfterExec(true);
new ComboTestHelper<TestLambdaToMethodStats>()
.withDimension("EXPR", (x, expr) -> x.ek = expr, ExprKind.values())
.withDimension("CAST", (x, target) -> x.tk = target, TargetKind.values())
.run(TestLambdaToMethodStats::new);
}
ExprKind ek;
TargetKind tk;
JavaSource source;
DiagnosticChecker diagChecker;
String template = "interface A {\n" +
" Object o();\n" +
"}\n" +
"class Test {\n" +
" A a = #{CAST}#{EXPR};\n" +
" Object g() { return null; }\n" +
" Object h(Object... o) { return null; }\n" +
"}";
TestLambdaToMethodStats(ExprKind ek, TargetKind tk) {
this.ek = ek;
this.tk = tk;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
@Override
public void doWork() throws IOException {
check(newCompilationTask()
.withOption("-XDdumpLambdaToMethodStats")
.withSourceFromTemplate(template)
.generate());
}
class JavaSource extends SimpleJavaFileObject {
String template = "interface A {\n" +
" Object o();\n" +
"}\n" +
"class Test {\n" +
" A a = #C#E;\n" +
" Object g() { return null; }\n" +
" Object h(Object... o) { return null; }\n" +
"}";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#E", ek.exprStr)
.replaceAll("#C", tk.targetStr);
void check(Result<?> res) {
DiagnosticKind diag = null;
boolean altMetafactory = false;
for (DiagnosticKind dk : DiagnosticKind.values()) {
List<Diagnostic<? extends JavaFileObject>> jcDiag = res.diagnosticsForKey(dk.code);
if (jcDiag.nonEmpty()) {
diag = dk;
ClientCodeWrapper.DiagnosticSourceUnwrapper dsu =
(ClientCodeWrapper.DiagnosticSourceUnwrapper)jcDiag.head;
altMetafactory = (Boolean)dsu.d.getArgs()[0];
break;
}
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
if (diag == null) {
fail("No diagnostic found; " + res.compilationInfo());
}
}
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
Arrays.asList("-XDdumpLambdaToMethodStats"),
null, Arrays.asList(source));
try {
ct.generate();
} catch (Throwable ex) {
throw new
AssertionError("Error thron when analyzing the following source:\n" +
source.getCharContent(true));
}
check();
}
void check() {
checkCount.incrementAndGet();
boolean error = diagChecker.lambda !=
boolean error = diag.lambda !=
(ek == ExprKind.LAMBDA);
error |= diagChecker.bridge !=
error |= diag.bridge !=
(ek == ExprKind.MREF2);
error |= diagChecker.altMetafactory !=
error |= altMetafactory !=
(tk == TargetKind.SERIALIZABLE);
if (error) {
throw new AssertionError("Bad stat diagnostic found for source\n" +
"lambda = " + diagChecker.lambda + "\n" +
"bridge = " + diagChecker.bridge + "\n" +
"altMF = " + diagChecker.altMetafactory + "\n" +
source.source);
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean altMetafactory;
boolean bridge;
boolean lambda;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
try {
if (diagnostic.getKind() == Diagnostic.Kind.NOTE) {
switch (diagnostic.getCode()) {
case "compiler.note.lambda.stat":
lambda = true;
break;
case "compiler.note.mref.stat":
lambda = false;
bridge = false;
break;
case "compiler.note.mref.stat.1":
lambda = false;
bridge = true;
break;
default:
throw new AssertionError("unexpected note: " + diagnostic.getCode());
}
ClientCodeWrapper.DiagnosticSourceUnwrapper dsu =
(ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic;
altMetafactory = (Boolean)dsu.d.getArgs()[0];
}
} catch (RuntimeException t) {
t.printStackTrace();
throw t;
}
fail("Bad stat diagnostic found for source\n" +
"lambda = " + diag.lambda + "\n" +
"bridge = " + diag.bridge + "\n" +
"altMF = " + altMetafactory + "\n" +
res.compilationInfo());
}
}
}

@ -23,18 +23,20 @@
/*
* @test
* @bug 8009649
* @bug 8009649 8129962
* @summary Lambda back-end should generate invokespecial for method handles referring to private instance methods
* @library ../../lib
* @library /tools/javac/lib
* @modules jdk.jdeps/com.sun.tools.classfile
* jdk.compiler/com.sun.tools.javac.api
* @build JavacTestingAbstractThreadedTest
* @run main/othervm TestLambdaBytecode
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main TestLambdaBytecode
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.BootstrapMethods_attribute;
import com.sun.tools.classfile.ClassFile;
@ -43,26 +45,22 @@ import com.sun.tools.classfile.ConstantPool.*;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.Method;
import com.sun.tools.javac.api.JavacTaskImpl;
import java.io.IOException;
import java.io.InputStream;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import static com.sun.tools.javac.jvm.ClassFile.*;
public class TestLambdaBytecode extends ComboInstance<TestLambdaBytecode> {
public class TestLambdaBytecode
extends JavacTestingAbstractThreadedTest
implements Runnable {
static final int MF_ARITY = 3;
static final String MH_SIG = "()V";
enum ClassKind {
enum ClassKind implements ComboParameter {
CLASS("class"),
INTERFACE("interface");
@ -71,9 +69,14 @@ public class TestLambdaBytecode
ClassKind(String classStr) {
this.classStr = classStr;
}
@Override
public String expand(String optParameter) {
return classStr;
}
}
enum AccessKind {
enum AccessKind implements ComboParameter {
PUBLIC("public"),
PRIVATE("private");
@ -82,9 +85,14 @@ public class TestLambdaBytecode
AccessKind(String accessStr) {
this.accessStr = accessStr;
}
@Override
public String expand(String optParameter) {
return accessStr;
}
}
enum StaticKind {
enum StaticKind implements ComboParameter {
STATIC("static"),
INSTANCE("");
@ -93,9 +101,14 @@ public class TestLambdaBytecode
StaticKind(String staticStr) {
this.staticStr = staticStr;
}
@Override
public String expand(String optParameter) {
return staticStr;
}
}
enum DefaultKind {
enum DefaultKind implements ComboParameter {
DEFAULT("default"),
NO_DEFAULT("");
@ -104,15 +117,10 @@ public class TestLambdaBytecode
DefaultKind(String defaultStr) {
this.defaultStr = defaultStr;
}
}
enum ExprKind {
LAMBDA("Runnable r = ()->{ target(); };");
String exprString;
ExprKind(String exprString) {
this.exprString = exprString;
@Override
public String expand(String optParameter) {
return defaultStr;
}
}
@ -155,83 +163,53 @@ public class TestLambdaBytecode
return true;
}
}
String mods() {
StringBuilder buf = new StringBuilder();
buf.append(ak.accessStr);
buf.append(' ');
buf.append(sk.staticStr);
buf.append(' ');
buf.append(dk.defaultStr);
return buf.toString();
}
}
public static void main(String... args) throws Exception {
for (ClassKind ck : ClassKind.values()) {
for (AccessKind ak1 : AccessKind.values()) {
for (StaticKind sk1 : StaticKind.values()) {
for (DefaultKind dk1 : DefaultKind.values()) {
for (AccessKind ak2 : AccessKind.values()) {
for (StaticKind sk2 : StaticKind.values()) {
for (DefaultKind dk2 : DefaultKind.values()) {
for (ExprKind ek : ExprKind.values()) {
pool.execute(new TestLambdaBytecode(ck, ak1, ak2, sk1, sk2, dk1, dk2, ek));
}
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<TestLambdaBytecode>()
.withDimension("CLASSKIND", (x, ck) -> x.ck = ck, ClassKind.values())
.withArrayDimension("ACCESS", (x, acc, idx) -> x.accessKinds[idx] = acc, 2, AccessKind.values())
.withArrayDimension("STATIC", (x, sk, idx) -> x.staticKinds[idx] = sk, 2, StaticKind.values())
.withArrayDimension("DEFAULT", (x, dk, idx) -> x.defaultKinds[idx] = dk, 2, DefaultKind.values())
.run(TestLambdaBytecode::new, TestLambdaBytecode::init);
}
ClassKind ck;
AccessKind[] accessKinds = new AccessKind[2];
StaticKind[] staticKinds = new StaticKind[2];
DefaultKind[] defaultKinds = new DefaultKind[2];
MethodKind mk1, mk2;
ExprKind ek;
DiagChecker dc;
TestLambdaBytecode(ClassKind ck, AccessKind ak1, AccessKind ak2, StaticKind sk1,
StaticKind sk2, DefaultKind dk1, DefaultKind dk2, ExprKind ek) {
mk1 = new MethodKind(ck, ak1, sk1, dk1);
mk2 = new MethodKind(ck, ak2, sk2, dk2);
this.ek = ek;
dc = new DiagChecker();
void init() {
mk1 = new MethodKind(ck, accessKinds[0], staticKinds[0], defaultKinds[0]);
mk2 = new MethodKind(ck, accessKinds[1], staticKinds[1], defaultKinds[1]);
}
public void run() {
int id = checkCount.incrementAndGet();
JavaSource source = new JavaSource(id);
JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
null, null, Arrays.asList(source));
try {
ct.generate();
} catch (Throwable t) {
t.printStackTrace();
throw new AssertionError(
String.format("Error thrown when compiling following code\n%s",
source.source));
}
if (dc.diagFound) {
String source_template =
"#{CLASSKIND} Test {\n" +
" #{ACCESS[0]} #{STATIC[0]} #{DEFAULT[0]} void test() { Runnable r = ()->{ target(); }; }\n" +
" #{ACCESS[1]} #{STATIC[1]} #{DEFAULT[1]} void target() { }\n" +
"}\n";
@Override
public void doWork() throws IOException {
verifyBytecode(newCompilationTask()
.withSourceFromTemplate(source_template)
.generate());
}
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
if (res.hasErrors()) {
boolean errorExpected = !mk1.isOK() || !mk2.isOK();
errorExpected |= mk1.isStatic() && !mk2.isStatic();
if (!errorExpected) {
throw new AssertionError(
String.format("Diags found when compiling following code\n%s\n\n%s",
source.source, dc.printDiags()));
fail("Diags found when compiling instance; " + res.compilationInfo());
}
return;
}
verifyBytecode(id, source);
}
void verifyBytecode(int id, JavaSource source) {
File compiledTest = new File(String.format("Test%d.class", id));
try {
ClassFile cf = ClassFile.read(compiledTest);
try (InputStream is = res.get().iterator().next().openInputStream()) {
ClassFile cf = ClassFile.read(is);
Method testMethod = null;
for (Method m : cf.methods) {
if (m.getName(cf.constant_pool).equals("test")) {
@ -240,12 +218,14 @@ public class TestLambdaBytecode
}
}
if (testMethod == null) {
throw new Error("Test method not found");
fail("Test method not found");
return;
}
Code_attribute ea =
(Code_attribute)testMethod.attributes.get(Attribute.Code);
if (testMethod == null) {
throw new Error("Code attribute for test() method not found");
fail("Code attribute for test() method not found");
return;
}
int bsmIdx = -1;
@ -256,29 +236,34 @@ public class TestLambdaBytecode
(CONSTANT_InvokeDynamic_info)cf
.constant_pool.get(i.getShort(1));
bsmIdx = indyInfo.bootstrap_method_attr_index;
if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType(id))) {
throw new
AssertionError("type mismatch for CONSTANT_InvokeDynamic_info " + source.source + "\n" + indyInfo.getNameAndTypeInfo().getType() + "\n" + makeIndyType(id));
if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType())) {
fail("type mismatch for CONSTANT_InvokeDynamic_info " +
res.compilationInfo() + "\n" + indyInfo.getNameAndTypeInfo().getType() +
"\n" + makeIndyType());
return;
}
}
}
if (bsmIdx == -1) {
throw new Error("Missing invokedynamic in generated code");
fail("Missing invokedynamic in generated code");
return;
}
BootstrapMethods_attribute bsm_attr =
(BootstrapMethods_attribute)cf
.getAttribute(Attribute.BootstrapMethods);
if (bsm_attr.bootstrap_method_specifiers.length != 1) {
throw new Error("Bad number of method specifiers " +
fail("Bad number of method specifiers " +
"in BootstrapMethods attribute");
return;
}
BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
bsm_attr.bootstrap_method_specifiers[0];
if (bsm_spec.bootstrap_arguments.length != MF_ARITY) {
throw new Error("Bad number of static invokedynamic args " +
fail("Bad number of static invokedynamic args " +
"in BootstrapMethod attribute");
return;
}
CONSTANT_MethodHandle_info mh =
@ -294,74 +279,27 @@ public class TestLambdaBytecode
}
if (!kindOK) {
throw new Error("Bad invoke kind in implementation method handle");
fail("Bad invoke kind in implementation method handle");
return;
}
if (!mh.getCPRefInfo().getNameAndTypeInfo().getType().toString().equals(MH_SIG)) {
throw new Error("Type mismatch in implementation method handle");
fail("Type mismatch in implementation method handle");
return;
}
} catch (Exception e) {
e.printStackTrace();
throw new Error("error reading " + compiledTest +": " + e);
fail("error reading " + res.compilationInfo() + ": " + e);
}
}
String makeIndyType(int id) {
String makeIndyType() {
StringBuilder buf = new StringBuilder();
buf.append("(");
if (!mk2.isStatic()) {
buf.append(String.format("LTest%d;", id));
buf.append("LTest;");
}
buf.append(")Ljava/lang/Runnable;");
return buf.toString();
}
static final int MF_ARITY = 3;
static final String MH_SIG = "()V";
class JavaSource extends SimpleJavaFileObject {
static final String source_template =
"#CK Test#ID {\n" +
" #MOD1 void test() { #EK }\n" +
" #MOD2 void target() { }\n" +
"}\n";
String source;
JavaSource(int id) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = source_template.replace("#CK", mk1.ck.classStr)
.replace("#MOD1", mk1.mods())
.replace("#MOD2", mk2.mods())
.replace("#EK", ek.exprString)
.replace("#ID", String.valueOf(id));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
static class DiagChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean diagFound;
ArrayList<String> diags = new ArrayList<>();
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
diags.add(diagnostic.getMessage(Locale.getDefault()));
diagFound = true;
}
String printDiags() {
StringBuilder buf = new StringBuilder();
for (String s : diags) {
buf.append(s);
buf.append("\n");
}
return buf.toString();
}
}
}

@ -23,34 +23,36 @@
/*
* @test
* @bug 8003280 8006694
* @bug 8003280 8006694 8129962
* @summary Add lambda tests
* Automatic test for checking correctness of structural most specific test routine
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../lib
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm/timeout=600 StructuralMostSpecificTest
* @build combo.ComboTestHelper
* @run main StructuralMostSpecificTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.ClientCodeWrapper;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.List;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class StructuralMostSpecificTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class StructuralMostSpecificTest extends ComboInstance<StructuralMostSpecificTest> {
enum RetTypeKind {
enum RetTypeKind implements ComboParameter {
SHORT("short"),
INT("int"),
OBJECT("Object"),
@ -76,9 +78,13 @@ public class StructuralMostSpecificTest
/* INTEGER */ { false , false , true , true , false , false },
/* VOID */ { false , false , false , false , true , true },
/* J_L_VOID */{ false , false , true , false , false , true } };
public String expand(String optParameter) {
return retTypeStr;
}
}
enum ArgTypeKind {
enum ArgTypeKind implements ComboParameter {
SHORT("short"),
INT("int"),
BOOLEAN("boolean"),
@ -91,9 +97,13 @@ public class StructuralMostSpecificTest
ArgTypeKind(String typeStr) {
this.argTypeStr = typeStr;
}
public String expand(String optParameter) {
return argTypeStr;
}
}
enum ExceptionKind {
enum ExceptionKind implements ComboParameter {
NONE(""),
EXCEPTION("throws Exception"),
SQL_EXCEPTION("throws java.sql.SQLException"),
@ -104,9 +114,13 @@ public class StructuralMostSpecificTest
ExceptionKind(String exceptionStr) {
this.exceptionStr = exceptionStr;
}
public String expand(String optParameter) {
return exceptionStr;
}
}
enum LambdaReturnKind {
enum LambdaReturnKind implements ComboParameter {
VOID("return;"),
SHORT("return (short)0;"),
INT("return 0;"),
@ -144,118 +158,72 @@ public class StructuralMostSpecificTest
/* INTEGER */ { false , false , true , false , false },
/* VOID */ { false , false , false , false , false },
/* J_L_VOID */{ true , false , false , false , false } };
public String expand(String optParameter) {
return retStr;
}
}
static final String sourceTemplate =
"interface SAM1 {\n" +
" #{RET[0]} m(#{ARG[0]} a1) #{EX[0]};\n" +
"}\n" +
"interface SAM2 {\n" +
" #{RET[1]} m(#{ARG[1]} a1) #{EX[1]};\n" +
"}\n" +
"class Test {\n" +
" void m(SAM1 s) { }\n" +
" void m(SAM2 s) { }\n" +
" { m((#{ARG[0]} x)->{ #{EXPR} }); }\n" +
"}\n";
public static void main(String... args) throws Exception {
for (LambdaReturnKind lrk : LambdaReturnKind.values()) {
for (RetTypeKind rk1 : RetTypeKind.values()) {
for (RetTypeKind rk2 : RetTypeKind.values()) {
for (ExceptionKind ek1 : ExceptionKind.values()) {
for (ExceptionKind ek2 : ExceptionKind.values()) {
for (ArgTypeKind ak11 : ArgTypeKind.values()) {
for (ArgTypeKind ak12 : ArgTypeKind.values()) {
pool.execute(
new StructuralMostSpecificTest(lrk, rk1,
rk2, ek1, ek2, ak11, ak12));
}
}
}
}
}
}
}
checkAfterExec();
new ComboTestHelper<StructuralMostSpecificTest>()
.withFilter(StructuralMostSpecificTest::hasSameArguments)
.withFilter(StructuralMostSpecificTest::hasCompatibleReturns)
.withFilter(StructuralMostSpecificTest::hasSameOverloadPhase)
.withDimension("EXPR", (x, expr) -> x.lambdaReturnKind = expr, LambdaReturnKind.values())
.withArrayDimension("RET", (x, ret, idx) -> x.returnType[idx] = ret, 2, RetTypeKind.values())
.withArrayDimension("EX", 2, ExceptionKind.values())
.withArrayDimension("ARG", (x, arg, idx) -> x.argumentKind[idx] = arg, 2, ArgTypeKind.values())
.run(StructuralMostSpecificTest::new);
}
LambdaReturnKind lrk;
RetTypeKind rt1, rt2;
ArgTypeKind ak1, ak2;
ExceptionKind ek1, ek2;
JavaSource source;
DiagnosticChecker diagChecker;
LambdaReturnKind lambdaReturnKind;
RetTypeKind[] returnType = new RetTypeKind[2];
ArgTypeKind[] argumentKind = new ArgTypeKind[2];
StructuralMostSpecificTest(LambdaReturnKind lrk, RetTypeKind rt1, RetTypeKind rt2,
ExceptionKind ek1, ExceptionKind ek2, ArgTypeKind ak1, ArgTypeKind ak2) {
this.lrk = lrk;
this.rt1 = rt1;
this.rt2 = rt2;
this.ek1 = ek1;
this.ek2 = ek2;
this.ak1 = ak1;
this.ak2 = ak2;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
boolean hasSameArguments() {
return argumentKind[0] == argumentKind[1];
}
class JavaSource extends SimpleJavaFileObject {
String template = "interface SAM1 {\n" +
" #R1 m(#A1 a1) #E1;\n" +
"}\n" +
"interface SAM2 {\n" +
" #R2 m(#A2 a1) #E2;\n" +
"}\n" +
"class Test {\n" +
" void m(SAM1 s) { }\n" +
" void m(SAM2 s) { }\n" +
" { m((#A1 x)->{ #LR }); }\n" +
"}\n";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#LR", lrk.retStr)
.replaceAll("#R1", rt1.retTypeStr)
.replaceAll("#R2", rt2.retTypeStr)
.replaceAll("#A1", ak1.argTypeStr)
.replaceAll("#A2", ak2.argTypeStr)
.replaceAll("#E1", ek1.exceptionStr)
.replaceAll("#E2", ek2.exceptionStr);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
boolean hasCompatibleReturns() {
return lambdaReturnKind.compatibleWith(returnType[0]) &&
lambdaReturnKind.compatibleWith(returnType[1]);
}
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
Arrays.asList("-XDverboseResolution=all,-predef,-internal,-object-init"),
null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new
AssertionError("Error thron when analyzing the following source:\n" +
source.getCharContent(true));
}
check();
boolean hasSameOverloadPhase() {
return lambdaReturnKind.needsConversion(returnType[0]) == lambdaReturnKind.needsConversion(returnType[1]);
}
void check() {
checkCount.incrementAndGet();
@Override
public void doWork() throws Throwable {
check(newCompilationTask()
.withSourceFromTemplate(sourceTemplate)
.withOption("-XDverboseResolution=all,-predef,-internal,-object-init")
.analyze());
}
if (ak1 != ak2)
return;
if (!lrk.compatibleWith(rt1) || !lrk.compatibleWith(rt2))
return;
if (lrk.needsConversion(rt1) != lrk.needsConversion(rt2))
return;
boolean m1MoreSpecific = rt1.moreSpecificThan(rt2);
boolean m2MoreSpecific = rt2.moreSpecificThan(rt1);
void check(Result<Iterable<? extends Element>> result) {
boolean m1MoreSpecific = returnType[0].moreSpecificThan(returnType[1]);
boolean m2MoreSpecific = returnType[1].moreSpecificThan(returnType[0]);
boolean ambiguous = (m1MoreSpecific == m2MoreSpecific);
if (ambiguous != diagChecker.ambiguityFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nAmbiguity found: " + diagChecker.ambiguityFound +
if (ambiguous != ambiguityFound(result)) {
fail("invalid diagnostics for combo:\n" +
result.compilationInfo() + "\n" +
"\nAmbiguity found: " + ambiguityFound(result) +
"\nm1 more specific: " + m1MoreSpecific +
"\nm2 more specific: " + m2MoreSpecific +
"\nexpected ambiguity: " + ambiguous);
@ -263,44 +231,32 @@ public class StructuralMostSpecificTest
if (!ambiguous) {
String sigToCheck = m1MoreSpecific ? "m(SAM1)" : "m(SAM2)";
if (!sigToCheck.equals(diagChecker.mostSpecificSig)) {
throw new Error("invalid most specific method selected:\n" +
source.getCharContent(true) +
"\nMost specific found: " + diagChecker.mostSpecificSig +
"\nm1 more specific: " + m1MoreSpecific +
"\nm2 more specific: " + m2MoreSpecific);
if (!sigToCheck.equals(mostSpecificSignature(result))) {
fail("invalid most specific method selected:\n" +
result.compilationInfo() + "\n" +
"\nMost specific found: " + mostSpecificSignature(result) +
"\nm1 more specific: " + m1MoreSpecific +
"\nm2 more specific: " + m2MoreSpecific);
}
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean ambiguityFound;
String mostSpecificSig;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
try {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
diagnostic.getCode().equals("compiler.err.ref.ambiguous")) {
ambiguityFound = true;
} else if (diagnostic.getKind() == Diagnostic.Kind.NOTE &&
diagnostic.getCode()
.equals("compiler.note.verbose.resolve.multi")) {
ClientCodeWrapper.DiagnosticSourceUnwrapper dsu =
(ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic;
JCDiagnostic.MultilineDiagnostic mdiag =
(JCDiagnostic.MultilineDiagnostic)dsu.d;
int mostSpecificIndex = (Integer)mdiag.getArgs()[2];
mostSpecificSig =
((JCDiagnostic)mdiag.getSubdiagnostics()
.get(mostSpecificIndex)).getArgs()[1].toString();
}
} catch (RuntimeException t) {
t.printStackTrace();
throw t;
}
}
boolean ambiguityFound(Result<Iterable<? extends Element>> result) {
return result.containsKey("compiler.err.ref.ambiguous");
}
String mostSpecificSignature(Result<Iterable<? extends Element>> result) {
List<Diagnostic<? extends JavaFileObject>> rsDiag =
result.diagnosticsForKey("compiler.note.verbose.resolve.multi");
if (rsDiag.nonEmpty()) {
ClientCodeWrapper.DiagnosticSourceUnwrapper dsu =
(ClientCodeWrapper.DiagnosticSourceUnwrapper)rsDiag.head;
JCDiagnostic.MultilineDiagnostic mdiag =
(JCDiagnostic.MultilineDiagnostic)dsu.d;
int mostSpecificIndex = (Integer)mdiag.getArgs()[2];
return mdiag.getSubdiagnostics().get(mostSpecificIndex).getArgs()[1].toString();
} else {
return null;
}
}
}

@ -23,31 +23,31 @@
/**
* @test
* @bug 8003280 8006694
* @bug 8003280 8006694 8129962
* @summary Add lambda tests
* perform automated checks in type inference in lambda expressions
* in different contexts
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @compile TypeInferenceComboTest.java
* @run main/othervm/timeout=360 TypeInferenceComboTest
* @run main TypeInferenceComboTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class TypeInferenceComboTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
public class TypeInferenceComboTest extends ComboInstance<TypeInferenceComboTest> {
enum Context {
ASSIGNMENT("SAM#Type s = #LBody;"),
METHOD_CALL("#GenericDeclKind void method1(SAM#Type s) { }\n" +
@ -221,82 +221,21 @@ public class TypeInferenceComboTest
}
}
boolean checkTypeInference() {
if (parameterType == TypeKind.VOID) {
if (lambdaBodyType != LambdaBody.RETURN_VOID)
return false;
}
else if (lambdaBodyType != LambdaBody.RETURN_ARG)
return false;
return true;
}
String templateStr = "#C\n" +
"interface SAM2 {\n" +
" SAM m();\n" +
"}\n";
SourceFile samSourceFile = new SourceFile("Sam.java", templateStr) {
public String toString() {
return template.replaceAll("#C",
samKind.getSam(parameterType, returnType));
}
};
SourceFile clientSourceFile = new SourceFile("Client.java",
"class Client { \n" +
" #Context\n" +
"}") {
public String toString() {
return template.replaceAll("#Context",
context.getContext(samKind, samTargetType, keyword,
parameterType, returnType, lambdaKind, parameterKind,
genericDeclKind, lambdaBodyType));
}
};
public void run() {
DiagnosticChecker dc = new DiagnosticChecker();
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), dc,
null, null, Arrays.asList(samSourceFile, clientSourceFile));
try {
ct.analyze();
} catch (Throwable t) {
processException(t);
}
if (dc.errorFound == checkTypeInference()) {
throw new AssertionError(samSourceFile + "\n\n" +
clientSourceFile + "\n" + parameterType + " " + returnType);
}
}
abstract class SourceFile extends SimpleJavaFileObject {
protected String template;
public SourceFile(String filename, String template) {
super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
this.template = template;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return toString();
}
public abstract String toString();
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound = false;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
public static void main(String[] args) {
new ComboTestHelper<TypeInferenceComboTest>()
.withFilter(TypeInferenceComboTest::badTestFilter)
.withFilter(TypeInferenceComboTest::redundantTestFilter)
.withDimension("SAM", (x, sam) -> x.samKind = sam, SamKind.values())
.withDimension("SAMTARGET", (x, target) -> x.samTargetType = target, TypeKind.values())
.withDimension("PARAMTYPE", (x, param) -> x.parameterType = param, TypeKind.values())
.withDimension("RETTYPE", (x, ret) -> x.returnType = ret, TypeKind.values())
.withDimension("CTX", (x, ctx) -> x.context = ctx, Context.values())
.withDimension("LAMBDABODY", (x, body) -> x.lambdaBodyType = body, LambdaBody.values())
.withDimension("LAMBDAKIND", (x, lambda) -> x.lambdaKind = lambda, LambdaKind.values())
.withDimension("PARAMKIND", (x, param) -> x.parameterKind = param, ParameterKind.values())
.withDimension("KEYWORD", (x, kw) -> x.keyword = kw, Keyword.values())
.withDimension("GENDECL", (x, gk) -> x.genericDeclKind = gk, GenericDeclKind.values())
.run(TypeInferenceComboTest::new);
}
SamKind samKind;
@ -310,84 +249,75 @@ public class TypeInferenceComboTest
Keyword keyword;
GenericDeclKind genericDeclKind;
TypeInferenceComboTest(SamKind sk, TypeKind samTargetT, TypeKind parameterT,
TypeKind returnT, LambdaBody lb, Context c, LambdaKind lk,
ParameterKind pk, Keyword kw, GenericDeclKind gdk) {
samKind = sk;
samTargetType = samTargetT;
parameterType = parameterT;
returnType = returnT;
context = c;
lambdaKind = lk;
parameterKind = pk;
keyword = kw;
lambdaBodyType = lb;
genericDeclKind = gdk;
}
public static void main(String[] args) throws Exception {
for(Context ct : Context.values()) {
for (TypeKind returnT : TypeKind.values()) {
for (TypeKind parameterT : TypeKind.values()) {
for(LambdaBody lb : LambdaBody.values()) {
for (ParameterKind parameterK : ParameterKind.values()) {
for(LambdaKind lambdaK : LambdaKind.values()) {
for (SamKind sk : SamKind.values()) {
if (sk == SamKind.NON_GENERIC) {
generateNonGenericSAM(ct, returnT,
parameterT, lb, parameterK,
lambdaK, sk);
}
else if (sk == SamKind.GENERIC) {
generateGenericSAM(ct, returnT,
parameterT, lb, parameterK,
lambdaK, sk);
}
}
}
}
}
}
}
}
checkAfterExec(false);
}
static void generateNonGenericSAM(Context ct, TypeKind returnT,
TypeKind parameterT, LambdaBody lb, ParameterKind parameterK,
LambdaKind lambdaK, SamKind sk) {
if(parameterT != TypeKind.GENERIC && returnT != TypeKind.GENERIC ) {
pool.execute(new TypeInferenceComboTest(sk, null, parameterT,
returnT, lb, ct, lambdaK, parameterK, null, null));
boolean badTestFilter() {
if (samKind == SamKind.NON_GENERIC) {
return (parameterType != TypeKind.GENERIC && returnType != TypeKind.GENERIC);
} else {
return (samTargetType != TypeKind.VOID &&
samTargetType != TypeKind.INT &&
samTargetType != TypeKind.GENERIC &&
(parameterType == TypeKind.GENERIC ||
returnType == TypeKind.GENERIC));
}
}
static void generateGenericSAM(Context ct, TypeKind returnT,
TypeKind parameterT, LambdaBody lb, ParameterKind parameterK,
LambdaKind lambdaK, SamKind sk) {
for (Keyword kw : Keyword.values()) {
for (TypeKind samTargetT : TypeKind.values()) {
if(samTargetT != TypeKind.VOID &&
samTargetT != TypeKind.INT &&
samTargetT != TypeKind.GENERIC &&
(parameterT == TypeKind.GENERIC ||
returnT == TypeKind.GENERIC)) {
if(ct != Context.METHOD_CALL) {
pool.execute(
new TypeInferenceComboTest(sk, samTargetT, parameterT,
returnT, lb, ct, lambdaK, parameterK, kw, null));
} else {//Context.METHOD_CALL
for (GenericDeclKind gdk :
GenericDeclKind.values())
pool.execute(
new TypeInferenceComboTest(sk, samTargetT,
parameterT, returnT, lb, ct, lambdaK,
parameterK, kw, gdk));
}
}
}
}
boolean redundantTestFilter() {
if (samKind == SamKind.NON_GENERIC) {
return keyword.ordinal() == 0 && samTargetType.ordinal() == 0 && genericDeclKind.ordinal() == 0;
} else {
return context == Context.METHOD_CALL || genericDeclKind.ordinal() == 0;
}
}
String sam_template = "#{SAM}\n" +
"interface SAM2 {\n" +
" SAM m();\n" +
"}\n";
String client_template = "class Client { \n" +
" #{CONTEXT}\n" +
"}";
@Override
public void doWork() throws IOException {
Result<?> res = newCompilationTask()
.withSourceFromTemplate("Sam", sam_template, this::samClass)
.withSourceFromTemplate("Client", client_template, this::clientContext)
.analyze();
if (res.hasErrors() == checkTypeInference()) {
fail("Unexpected compilation output when compiling instance: " + res.compilationInfo());
}
}
ComboParameter samClass(String parameterName) {
switch (parameterName) {
case "SAM":
return new ComboParameter.Constant<>(samKind.getSam(parameterType, returnType));
default:
return null;
}
}
ComboParameter clientContext(String parameterName) {
switch (parameterName) {
case "CONTEXT":
return new ComboParameter.Constant<>(context.getContext(samKind, samTargetType,
keyword, parameterType, returnType, lambdaKind, parameterKind, genericDeclKind, lambdaBodyType));
default:
return null;
}
}
boolean checkTypeInference() {
if (parameterType == TypeKind.VOID) {
if (lambdaBodyType != LambdaBody.RETURN_VOID)
return false;
}
else if (lambdaBodyType != LambdaBody.RETURN_ARG)
return false;
return true;
}
}

@ -1,154 +0,0 @@
/*
* Copyright (c) 2010, 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.
*
* 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.
*/
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
* An abstract superclass for threaded tests.
*
* This class will try to read a property named test.concurrency.
* The property can be provided by passing this option to jtreg:
* -javaoption:-Dtest.concurrency=#
*
* If the property is not set the class will use a heuristic to determine the
* maximum number of threads that can be fired to execute a given test.
*
* This code will have to be revisited if jprt starts using concurrency for
* for running jtreg tests.
*/
public abstract class JavacTestingAbstractThreadedTest {
protected static AtomicInteger numberOfThreads = new AtomicInteger();
protected static int getThreadPoolSize() {
Integer testConc = Integer.getInteger("test.concurrency");
if (testConc != null) return testConc;
int cores = Runtime.getRuntime().availableProcessors();
numberOfThreads.set(Math.max(2, Math.min(8, cores / 2)));
return numberOfThreads.get();
}
protected static void checkAfterExec() throws InterruptedException {
checkAfterExec(true);
};
protected static boolean throwAssertionOnError = true;
protected static boolean printAll = false;
protected static StringWriter errSWriter = new StringWriter();
protected static PrintWriter errWriter = new PrintWriter(errSWriter);
protected static StringWriter outSWriter = new StringWriter();
protected static PrintWriter outWriter = new PrintWriter(outSWriter);
protected static void checkAfterExec(boolean printCheckCount)
throws InterruptedException {
pool.shutdown();
pool.awaitTermination(15, TimeUnit.MINUTES);
if (errCount.get() > 0) {
if (throwAssertionOnError) {
closePrinters();
System.err.println(errSWriter.toString());
throw new AssertionError(
String.format("%d errors found", errCount.get()));
} else {
System.err.println(
String.format("%d errors found", errCount.get()));
}
} else if (printCheckCount) {
outWriter.println("Total check executed: " + checkCount.get());
}
/*
* This output is for supporting debugging. It does not mean that a given
* test had executed that number of threads concurrently. The value printed
* here is the maximum possible amount.
*/
closePrinters();
if (printAll) {
System.out.println(errSWriter.toString());
System.out.println(outSWriter.toString());
}
System.out.println("Total number of threads in thread pool: " +
numberOfThreads.get());
}
protected static void closePrinters() {
errWriter.close();
outWriter.close();
}
protected static void processException(Throwable t) {
errCount.incrementAndGet();
t.printStackTrace(errWriter);
pool.shutdown();
}
//number of checks executed
protected static AtomicInteger checkCount = new AtomicInteger();
//number of errors found while running combo tests
protected static AtomicInteger errCount = new AtomicInteger();
//create default shared JavaCompiler - reused across multiple compilations
protected static JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
protected static ExecutorService pool = Executors.newFixedThreadPool(
getThreadPoolSize(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
pool.shutdown();
errCount.incrementAndGet();
e.printStackTrace(System.err);
}
});
return t;
}
});
/*
* File manager is not thread-safe so it cannot be re-used across multiple
* threads. However we cache per-thread FileManager to avoid excessive
* object creation
*/
protected static final ThreadLocal<StandardJavaFileManager> fm =
new ThreadLocal<StandardJavaFileManager>() {
@Override protected StandardJavaFileManager initialValue() {
return comp.getStandardFileManager(null, null, null);
}
};
}

@ -0,0 +1,128 @@
/*
* Copyright (c) 2015, 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.
*
* 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 combo;
import javax.tools.StandardJavaFileManager;
import java.util.Optional;
/**
* This class is the common superclass of all combo test instances. It defines few helper methods
* to build compilation tasks using the shared context object, as well as entry points for
* signalling test failures.
*/
public abstract class ComboInstance<X extends ComboInstance<X>> {
/** The test instance result status. */
private ResultStatus resultStatus = ResultStatus.PASSED;
/** The test instance execution environment. */
private ComboTestHelper<X>.Env env;
/**
* Entry point for executing a combo test instance; first, the test environment is saved
* in the corresponding field, then the instance is run (see {@link ComboInstance#doWork()}.
* During execution, the result status will be updated to match the test outcome.
*/
final void run(ComboTestHelper<X>.Env env) {
try {
this.env = env;
doWork();
if (resultStatus.isSuccess()) {
env.info().passCount++;
}
} catch (Throwable ex) {
resultStatus = ResultStatus.ERROR;
env.info().errCount++;
env.info().lastError = Optional.of(ex);
} finally {
this.env = null;
}
}
/**
* Retrieve a unique ID associated with this test instance.
*/
public int id() {
return env.info().comboCount;
}
/**
* Retrieve shared file manager.
*/
public StandardJavaFileManager fileManager() {
return env.fileManager();
}
/**
* Create a new compilation task using shared compilation context.
*/
protected ComboTask newCompilationTask() {
return new ComboTask(env);
}
/**
* Main test execution entry point; subclasses must implement this method to define the test
* logic.
*/
protected abstract void doWork() throws Throwable;
/**
* Report a test failure.
*/
protected void fail() {
//dump some default info (such as dimension bindings)
fail("Combo instance failed; " + env.bindings);
}
/**
* Report a test failure with corresponding failure message.
*/
protected void fail(String msg) {
resultStatus = ResultStatus.FAILED;
env.info().failCount++;
env.info().lastFailure = Optional.of(msg);
}
/**
* The status associated with this test instance execution.
*/
enum ResultStatus {
/** Test passed. */
PASSED(true),
/** Test failed. */
FAILED(false),
/** Test thrown unexpected error/exception. */
ERROR(false);
boolean success;
ResultStatus(boolean success) {
this.success = success;
}
boolean isSuccess() {
return success;
}
}
}

@ -0,0 +1,112 @@
/*
* Copyright (c) 2015, 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.
*
* 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 combo;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A combo parameter represents an 'hole' in a template that can be replaced with a given string.
* The schema of such holes is defined in {@link ComboParameter#pattern}; the main routine for
* replacing holes in a template scheme is {@link ComboParameter#expandTemplate(String, Resolver)}.
*/
public interface ComboParameter {
/**
* A combo parameter can take the form:
* <p>
* #{MAJOR}
* #{MAJOR.}
* #{MAJOR.MINOR}
* <p>
* where MAJOR can be IDENTIFIER or IDENTIFIER[NUMERIC_INDEX]
* and MINOR can be an identifier.
*/
Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z0-9_]*))?\\}");
/**
* Entry point for the customizable replacement logic. Subclasses must implement this method to
* specify how a given template hole should be expanded. An optional contextual argument is passed
* in as parameter, to make expansion more flexible.
*/
String expand(String optParameter);
/**
* Helper class for defining 'constant' combo parameters - i.e. parameters that always expand
* as a given string value - regardless of the context.
*/
class Constant<D> implements ComboParameter {
D data;
public Constant(D data) {
this.data = data;
}
@Override
public String expand(String _unused) {
return String.valueOf(data);
}
}
/**
* Helper interface used to lookup parameters given a parameter name.
*/
interface Resolver {
ComboParameter lookup(String name);
}
/**
* Main routine for replacing holes in a template string. Holes are repeatedly searches, their
* corresponding parameters retrieved, and replaced through expansion; since an expansion can
* lead to more holes, the process has to be applied until a fixed point is reached.
*/
static String expandTemplate(String template, Resolver resolver) {
CharSequence in = template;
StringBuffer out = new StringBuffer();
while (true) {
boolean more = false;
Matcher m = pattern.matcher(in);
while (m.find()) {
String parameterName = m.group(1);
String minor = m.group(2);
ComboParameter parameter = resolver.lookup(parameterName);
if (parameter == null) {
throw new IllegalStateException("Unhandled parameter name " + parameterName);
}
String replacement = parameter.expand(minor);
more |= pattern.matcher(replacement).find();
m.appendReplacement(out, replacement);
}
m.appendTail(out);
if (!more)
return out.toString();
else {
in = out;
out = new StringBuffer();
}
}
}
}

@ -0,0 +1,359 @@
/*
* Copyright (c) 2015, 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.
*
* 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 combo;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.util.List;
import combo.ComboParameter.Resolver;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* This class represents a compilation task associated with a combo test instance. This is a small
* wrapper around {@link JavacTask} which allows for fluent setup style and which makes use of
* the shared compilation context to speedup performances.
*/
public class ComboTask {
/** Sources to be compiled in this task. */
private List<JavaFileObject> sources = List.nil();
/** Options associated with this task. */
private List<String> options = List.nil();
/** Diagnostic collector. */
private DiagnosticCollector diagsCollector = new DiagnosticCollector();
/** Output writer. */
private Writer out;
/** Listeners associated with this task. */
private List<TaskListener> listeners = List.nil();
/** Underlying javac task object. */
private JavacTask task;
/** Combo execution environment. */
private ComboTestHelper<?>.Env env;
ComboTask(ComboTestHelper<?>.Env env) {
this.env = env;
}
/**
* Add a new source to this task.
*/
public ComboTask withSource(JavaFileObject comboSource) {
sources = sources.prepend(comboSource);
return this;
}
/**
* Add a new template source with given name to this task; the template is replaced with
* corresponding combo parameters (as defined in the combo test environment).
*/
public ComboTask withSourceFromTemplate(String name, String template) {
return withSource(new ComboTemplateSource(name, template));
}
/**
* Add a new template source with default name ("Test") to this task; the template is replaced with
* corresponding combo parameters (as defined in the combo test environment).
*/
public ComboTask withSourceFromTemplate(String template) {
return withSource(new ComboTemplateSource("Test", template));
}
/**
* Add a new template source with given name to this task; the template is replaced with
* corresponding combo parameters (as defined in the combo test environment). A custom resolver
* is used to add combo parameter mappings to the current combo test environment.
*/
public ComboTask withSourceFromTemplate(String name, String template, Resolver resolver) {
return withSource(new ComboTemplateSource(name, template, resolver));
}
/**
* Add a new template source with default name ("Test") to this task; the template is replaced with
* corresponding combo parameters (as defined in the combo test environment). A custom resolver
* is used to add combo parameter mappings to the current combo test environment.
*/
public ComboTask withSourceFromTemplate(String template, Resolver resolver) {
return withSource(new ComboTemplateSource("Test", template, resolver));
}
/**
* Add a new option to this task.
*/
public ComboTask withOption(String opt) {
options = options.append(opt);
return this;
}
/**
* Add a set of options to this task.
*/
public ComboTask withOptions(String[] opts) {
for (String opt : opts) {
options = options.append(opt);
}
return this;
}
/**
* Add a set of options to this task.
*/
public ComboTask withOptions(Iterable<? extends String> opts) {
for (String opt : opts) {
options = options.append(opt);
}
return this;
}
/**
* Set the output writer associated with this task.
*/
public ComboTask withWriter(Writer out) {
this.out = out;
return this;
}
/**
* Add a task listener to this task.
*/
public ComboTask withListener(TaskListener listener) {
listeners = listeners.prepend(listener);
return this;
}
/**
* Parse the sources associated with this task.
*/
public Result<Iterable<? extends CompilationUnitTree>> parse() throws IOException {
return new Result<>(getTask().parse());
}
/**
* Parse and analyzes the sources associated with this task.
*/
public Result<Iterable<? extends Element>> analyze() throws IOException {
return new Result<>(getTask().analyze());
}
/**
* Parse, analyze and perform code generation for the sources associated with this task.
*/
public Result<Iterable<? extends JavaFileObject>> generate() throws IOException {
return new Result<>(getTask().generate());
}
/**
* Fork a new compilation task; if possible the compilation context from previous executions is
* retained (see comments in ReusableContext as to when it's safe to do so); otherwise a brand
* new context is created.
*/
public JavacTask getTask() {
if (task == null) {
ReusableContext context = env.context();
String opts = options == null ? "" :
StreamSupport.stream(options.spliterator(), false).collect(Collectors.joining());
context.clear();
if (!context.polluted && (context.opts == null || context.opts.equals(opts))) {
//we can reuse former context
env.info().ctxReusedCount++;
} else {
env.info().ctxDroppedCount++;
//it's not safe to reuse context - create a new one
context = env.setContext(new ReusableContext());
}
context.opts = opts;
JavacTask javacTask = ((JavacTool)env.javaCompiler()).getTask(out, env.fileManager(),
diagsCollector, options, null, sources, context);
javacTask.setTaskListener(context);
for (TaskListener l : listeners) {
javacTask.addTaskListener(l);
}
task = javacTask;
}
return task;
}
/**
* This class is used to help clients accessing the results of a given compilation task.
* Contains several helper methods to inspect diagnostics generated during the task execution.
*/
public class Result<D> {
/** The underlying compilation results. */
private final D data;
public Result(D data) {
this.data = data;
}
public D get() {
return data;
}
/**
* Did this task generate any error diagnostics?
*/
public boolean hasErrors() {
return diagsCollector.diagsByKind.containsKey(Diagnostic.Kind.ERROR);
}
/**
* Did this task generate any warning diagnostics?
*/
public boolean hasWarnings() {
return diagsCollector.diagsByKind.containsKey(Diagnostic.Kind.WARNING);
}
/**
* Did this task generate any note diagnostics?
*/
public boolean hasNotes() {
return diagsCollector.diagsByKind.containsKey(Diagnostic.Kind.NOTE);
}
/**
* Did this task generate any diagnostic with given key?
*/
public boolean containsKey(String key) {
return diagsCollector.diagsByKeys.containsKey(key);
}
/**
* Retrieve the list of diagnostics of a given kind.
*/
public List<Diagnostic<? extends JavaFileObject>> diagnosticsForKind(Diagnostic.Kind kind) {
List<Diagnostic<? extends JavaFileObject>> diags = diagsCollector.diagsByKind.get(kind);
return diags != null ? diags : List.nil();
}
/**
* Retrieve the list of diagnostics with given key.
*/
public List<Diagnostic<? extends JavaFileObject>> diagnosticsForKey(String key) {
List<Diagnostic<? extends JavaFileObject>> diags = diagsCollector.diagsByKeys.get(key);
return diags != null ? diags : List.nil();
}
/**
* Dump useful info associated with this task.
*/
public String compilationInfo() {
return "instance#" + env.info().comboCount + ":[ options = " + options
+ ", diagnostics = " + diagsCollector.diagsByKeys.keySet()
+ ", dimensions = " + env.bindings
+ ", sources = \n" + sources.stream().map(s -> {
try {
return s.getCharContent(true);
} catch (IOException ex) {
return "";
}
}).collect(Collectors.joining(",")) + "]";
}
}
/**
* This class represents a Java source file whose contents are defined in terms of a template
* string. The holes in such template are expanded using corresponding combo parameter
* instances which can be retrieved using a resolver object.
*/
class ComboTemplateSource extends SimpleJavaFileObject {
String source;
Map<String, ComboParameter> localParametersCache = new HashMap<>();
protected ComboTemplateSource(String name, String template) {
this(name, template, null);
}
protected ComboTemplateSource(String name, String template, Resolver resolver) {
super(URI.create("myfo:/" + env.info().comboCount + "/" + name + ".java"), Kind.SOURCE);
source = ComboParameter.expandTemplate(template, pname -> resolveParameter(pname, resolver));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
/**
* Combo parameter resolver function. First parameters are looked up in the global environment,
* then the local environment is looked up as a fallback.
*/
ComboParameter resolveParameter(String pname, Resolver resolver) {
//first search the env
ComboParameter parameter = env.parametersCache.get(pname);
if (parameter == null) {
//then lookup local cache
parameter = localParametersCache.get(pname);
if (parameter == null && resolver != null) {
//if still null and we have a custom resolution function, try that
parameter = resolver.lookup(pname);
if (parameter != null) {
//if a match was found, store it in the local cache to aviod redundant recomputation
localParametersCache.put(pname, parameter);
}
}
}
return parameter;
}
}
/**
* Helper class to collect all diagnostic generated during the execution of a given compilation task.
*/
class DiagnosticCollector implements DiagnosticListener<JavaFileObject> {
Map<Diagnostic.Kind, List<Diagnostic<? extends JavaFileObject>>> diagsByKind = new HashMap<>();
Map<String, List<Diagnostic<? extends JavaFileObject>>> diagsByKeys = new HashMap<>();
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
List<Diagnostic<? extends JavaFileObject>> diags =
diagsByKeys.getOrDefault(diagnostic.getCode(), List.nil());
diagsByKeys.put(diagnostic.getCode(), diags.prepend(diagnostic));
Diagnostic.Kind kind = diagnostic.getKind();
diags = diagsByKind.getOrDefault(kind, List.nil());
diagsByKind.put(kind, diags.prepend(diagnostic));
}
}
}

@ -0,0 +1,444 @@
/*
* Copyright (c) 2015, 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.
*
* 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 combo;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* An helper class for defining combinatorial (aka "combo" tests). A combo test is made up of one
* or more 'dimensions' - each of which represent a different axis of the test space. For instance,
* if we wanted to test class/interface declaration, one dimension could be the keyword used for
* the declaration (i.e. 'class' vs. 'interface') while another dimension could be the class/interface
* modifiers (i.e. 'public', 'pachake-private' etc.). A combo test consists in running a test instance
* for each point in the test space; that is, for any combination of the combo test dimension:
* <p>
* 'public' 'class'
* 'public' interface'
* 'package-private' 'class'
* 'package-private' 'interface'
* ...
* <p>
* A new test instance {@link ComboInstance} is created, and executed, after its dimensions have been
* initialized accordingly. Each instance can either pass, fail or throw an unexpected error; this helper
* class defines several policies for how failures should be handled during a combo test execution
* (i.e. should errors be ignored? Do we want the first failure to result in a failure of the whole
* combo test?).
* <p>
* Additionally, this helper class allows to specify filter methods that can be used to throw out
* illegal combinations of dimensions - for instance, in the example above, we might want to exclude
* all combinations involving 'protected' and 'private' modifiers, which are disallowed for toplevel
* declarations.
* <p>
* While combo tests can be used for a variety of workloads, typically their main task will consist
* in performing some kind of javac compilation. For this purpose, this framework defines an optimized
* javac context {@link ReusableContext} which can be shared across multiple combo instances,
* when the framework detects it's safe to do so. This allows to reduce the overhead associated with
* compiler initialization when the test space is big.
*/
public class ComboTestHelper<X extends ComboInstance<X>> {
/** Failure mode. */
FailMode failMode = FailMode.FAIL_FAST;
/** Ignore mode. */
IgnoreMode ignoreMode = IgnoreMode.IGNORE_NONE;
/** Combo test instance filter. */
Optional<Predicate<X>> optFilter = Optional.empty();
/** Combo test dimensions. */
List<DimensionInfo<?>> dimensionInfos = new ArrayList<>();
/** Combo test stats. */
Info info = new Info();
/** Shared JavaCompiler used across all combo test instances. */
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
/** Shared file manager used across all combo test instances. */
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
/** Shared context used across all combo instances. */
ReusableContext context = new ReusableContext();
/**
* Set failure mode for this combo test.
*/
public ComboTestHelper<X> withFailMode(FailMode failMode) {
this.failMode = failMode;
return this;
}
/**
* Set ignore mode for this combo test.
*/
public ComboTestHelper<X> withIgnoreMode(IgnoreMode ignoreMode) {
this.ignoreMode = ignoreMode;
return this;
}
/**
* Set a filter for combo test instances to be ignored.
*/
public ComboTestHelper<X> withFilter(Predicate<X> filter) {
optFilter = Optional.of(optFilter.map(filter::and).orElse(filter));
return this;
}
/**
* Adds a new dimension to this combo test, with a given name an array of values.
*/
@SafeVarargs
public final <D> ComboTestHelper<X> withDimension(String name, D... dims) {
return withDimension(name, null, dims);
}
/**
* Adds a new dimension to this combo test, with a given name, an array of values and a
* coresponding setter to be called in order to set the dimension value on the combo test instance
* (before test execution).
*/
@SuppressWarnings("unchecked")
@SafeVarargs
public final <D> ComboTestHelper<X> withDimension(String name, DimensionSetter<X, D> setter, D... dims) {
dimensionInfos.add(new DimensionInfo<>(name, dims, setter));
return this;
}
/**
* Adds a new array dimension to this combo test, with a given base name. This allows to specify
* multiple dimensions at once; the names of the underlying dimensions will be generated from the
* base name, using standard array bracket notation - i.e. "DIM[0]", "DIM[1]", etc.
*/
@SafeVarargs
public final <D> ComboTestHelper<X> withArrayDimension(String name, int size, D... dims) {
return withArrayDimension(name, null, size, dims);
}
/**
* Adds a new array dimension to this combo test, with a given base name, an array of values and a
* coresponding array setter to be called in order to set the dimension value on the combo test
* instance (before test execution). This allows to specify multiple dimensions at once; the names
* of the underlying dimensions will be generated from the base name, using standard array bracket
* notation - i.e. "DIM[0]", "DIM[1]", etc.
*/
@SafeVarargs
public final <D> ComboTestHelper<X> withArrayDimension(String name, ArrayDimensionSetter<X, D> setter, int size, D... dims) {
for (int i = 0 ; i < size ; i++) {
dimensionInfos.add(new ArrayDimensionInfo<>(name, dims, i, setter));
}
return this;
}
/**
* Returns the stat object associated with this combo test.
*/
public Info info() {
return info;
}
/**
* Runs this combo test. This will generate the combinatorial explosion of all dimensions, and
* execute a new test instance (built using given supplier) for each such combination.
*/
public void run(Supplier<X> instanceBuilder) {
run(instanceBuilder, null);
}
/**
* Runs this combo test. This will generate the combinatorial explosion of all dimensions, and
* execute a new test instance (built using given supplier) for each such combination. Before
* executing the test instance entry point, the supplied initialization method is called on
* the test instance; this is useful for ad-hoc test instance initialization once all the dimension
* values have been set.
*/
public void run(Supplier<X> instanceBuilder, Consumer<X> initAction) {
runInternal(0, new Stack<>(), instanceBuilder, Optional.ofNullable(initAction));
end();
}
/**
* Generate combinatorial explosion of all dimension values and create a new test instance
* for each combination.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void runInternal(int index, Stack<DimensionBinding<?>> bindings, Supplier<X> instanceBuilder, Optional<Consumer<X>> initAction) {
if (index == dimensionInfos.size()) {
runCombo(instanceBuilder, initAction, bindings);
} else {
DimensionInfo<?> dinfo = dimensionInfos.get(index);
for (Object d : dinfo.dims) {
bindings.push(new DimensionBinding(d, dinfo));
runInternal(index + 1, bindings, instanceBuilder, initAction);
bindings.pop();
}
}
}
/**
* Run a new test instance using supplied dimension bindings. All required setters and initialization
* method are executed before calling the instance main entry point. Also checks if the instance
* is compatible with the specified test filters; if not, the test is simply skipped.
*/
@SuppressWarnings("unchecked")
private void runCombo(Supplier<X> instanceBuilder, Optional<Consumer<X>> initAction, List<DimensionBinding<?>> bindings) {
X x = instanceBuilder.get();
for (DimensionBinding<?> binding : bindings) {
binding.init(x);
}
initAction.ifPresent(action -> action.accept(x));
info.comboCount++;
if (!optFilter.isPresent() || optFilter.get().test(x)) {
x.run(new Env(bindings));
if (failMode.shouldStop(ignoreMode, info)) {
end();
}
} else {
info.skippedCount++;
}
}
/**
* This method is executed upon combo test completion (either normal or erroneous). Closes down
* all pending resources and dumps useful stats info.
*/
private void end() {
try {
fm.close();
if (info.hasFailures()) {
throw new AssertionError("Failure when executing combo:" + info.lastFailure.orElse(""));
} else if (info.hasErrors()) {
throw new AssertionError("Unexpected exception while executing combo", info.lastError.get());
}
} catch (IOException ex) {
throw new AssertionError("Failure when closing down shared file manager; ", ex);
} finally {
info.dump();
}
}
/**
* Functional interface for specifying combo test instance setters.
*/
public interface DimensionSetter<X extends ComboInstance<X>, D> {
void set(X x, D d);
}
/**
* Functional interface for specifying combo test instance array setters. The setter method
* receives an extra argument for the index of the array element to be set.
*/
public interface ArrayDimensionSetter<X extends ComboInstance<X>, D> {
void set(X x, D d, int index);
}
/**
* Dimension descriptor; each dimension has a name, an array of value and an optional setter
* to be called on the associated combo test instance.
*/
class DimensionInfo<D> {
String name;
D[] dims;
boolean isParameter;
Optional<DimensionSetter<X, D>> optSetter;
DimensionInfo(String name, D[] dims, DimensionSetter<X, D> setter) {
this.name = name;
this.dims = dims;
this.optSetter = Optional.ofNullable(setter);
this.isParameter = dims[0] instanceof ComboParameter;
}
}
/**
* Array dimension descriptor. The dimension name is derived from a base name and an index using
* standard bracket notation; ; the setter accepts an additional 'index' argument to point
* to the array element to be initialized.
*/
class ArrayDimensionInfo<D> extends DimensionInfo<D> {
public ArrayDimensionInfo(String name, D[] dims, int index, ArrayDimensionSetter<X, D> setter) {
super(String.format("%s[%d]", name, index), dims,
setter != null ? (x, d) -> setter.set(x, d, index) : null);
}
}
/**
* Failure policies for a combo test run.
*/
public enum FailMode {
/** Combo test fails when first failure is detected. */
FAIL_FAST,
/** Combo test fails after all instances have been executed. */
FAIL_AFTER;
boolean shouldStop(IgnoreMode ignoreMode, Info info) {
switch (this) {
case FAIL_FAST:
return !ignoreMode.canIgnore(info);
default:
return false;
}
}
}
/**
* Ignore policies for a combo test run.
*/
public enum IgnoreMode {
/** No error or failure is ignored. */
IGNORE_NONE,
/** Only errors are ignored. */
IGNORE_ERRORS,
/** Only failures are ignored. */
IGNORE_FAILURES,
/** Both errors and failures are ignored. */
IGNORE_ALL;
boolean canIgnore(Info info) {
switch (this) {
case IGNORE_ERRORS:
return info.failCount == 0;
case IGNORE_FAILURES:
return info.errCount == 0;
case IGNORE_ALL:
return true;
default:
return info.failCount == 0 && info.errCount == 0;
}
}
}
/**
* A dimension binding. This is essentially a pair of a dimension value and its corresponding
* dimension info.
*/
class DimensionBinding<D> {
D d;
DimensionInfo<D> info;
DimensionBinding(D d, DimensionInfo<D> info) {
this.d = d;
this.info = info;
}
void init(X x) {
info.optSetter.ifPresent(setter -> setter.set(x, d));
}
public String toString() {
return String.format("(%s -> %s)", info.name, d);
}
}
/**
* This class is used to keep track of combo tests stats; info such as numbero of failures/errors,
* number of times a context has been shared/dropped are all recorder here.
*/
public static class Info {
int failCount;
int errCount;
int passCount;
int comboCount;
int skippedCount;
int ctxReusedCount;
int ctxDroppedCount;
Optional<String> lastFailure = Optional.empty();
Optional<Throwable> lastError = Optional.empty();
void dump() {
System.err.println(String.format("%d total checks executed", comboCount));
System.err.println(String.format("%d successes found", passCount));
System.err.println(String.format("%d failures found", failCount));
System.err.println(String.format("%d errors found", errCount));
System.err.println(String.format("%d skips found", skippedCount));
System.err.println(String.format("%d contexts shared", ctxReusedCount));
System.err.println(String.format("%d contexts dropped", ctxDroppedCount));
}
public boolean hasFailures() {
return failCount != 0;
}
public boolean hasErrors() {
return errCount != 0;
}
}
/**
* THe execution environment for a given combo test instance. An environment contains the
* bindings for all the dimensions, along with the combo parameter cache (this is non-empty
* only if one or more dimensions are subclasses of the {@code ComboParameter} interface).
*/
class Env {
List<DimensionBinding<?>> bindings;
Map<String, ComboParameter> parametersCache = new HashMap<>();
@SuppressWarnings({"Unchecked", "rawtypes"})
Env(List<DimensionBinding<?>> bindings) {
this.bindings = bindings;
for (DimensionBinding<?> binding : bindings) {
if (binding.info.isParameter) {
parametersCache.put(binding.info.name, (ComboParameter)binding.d);
};
}
}
Info info() {
return ComboTestHelper.this.info();
}
StandardJavaFileManager fileManager() {
return fm;
}
JavaCompiler javaCompiler() {
return comp;
}
ReusableContext context() {
return context;
}
ReusableContext setContext(ReusableContext context) {
return ComboTestHelper.this.context = context;
}
}
}

@ -0,0 +1,197 @@
/*
* Copyright (c) 2015, 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.
*
* 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 combo;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.api.MultiTaskListener;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.comp.Check;
import com.sun.tools.javac.comp.CompileStates;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.main.Arguments;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.util.HashSet;
import java.util.Set;
/**
* A reusable context is a context that can be used safely across multiple compilation rounds
* arising from execution of a combo test. It achieves reuse by replacing some components
* (most notably JavaCompiler and Log) with reusable counterparts, and by exposing a method
* to cleanup leftovers from previous compilation.
* <p>
* There are, however, situations in which reusing the context is not safe: (i) when different
* compilations are using different sets of compiler options (as most option values are cached
* inside components themselves) and (ii) when the compilation unit happens to redefine classes
* in the java.* packages.
*/
class ReusableContext extends Context implements TaskListener {
Set<CompilationUnitTree> roots = new HashSet<>();
String opts;
boolean polluted = false;
ReusableContext() {
super();
put(Log.logKey, ReusableLog.factory);
put(JavaCompiler.compilerKey, ReusableJavaCompiler.factory);
}
void clear() {
drop(Arguments.argsKey);
drop(DiagnosticListener.class);
drop(Log.outKey);
drop(JavaFileManager.class);
drop(JavacTask.class);
if (ht.get(Log.logKey) instanceof ReusableLog) {
//log already inited - not first round
((ReusableLog)Log.instance(this)).clear();
Enter.instance(this).newRound();
((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear();
Types.instance(this).newRound();
Check.instance(this).newRound();
CompileStates.instance(this).clear();
MultiTaskListener.instance(this).clear();
//find if any of the roots have redefined java.* classes
Symtab syms = Symtab.instance(this);
new TreeScanner<Void, Void>() {
@Override
public Void visitClass(ClassTree node, Void aVoid) {
Symbol sym = ((JCClassDecl)node).sym;
if (sym != null) {
syms.classes.remove(sym.flatName());
if (sym.flatName().toString().startsWith("java.")) {
polluted = true;
}
}
return super.visitClass(node, aVoid);
}
}.scan(roots, null);
roots.clear();
}
}
@Override
public void finished(TaskEvent e) {
if (e.getKind() == Kind.PARSE) {
roots.add(e.getCompilationUnit());
}
}
@Override
public void started(TaskEvent e) {
//do nothing
}
<T> void drop(Key<T> k) {
ht.remove(k);
}
<T> void drop(Class<T> c) {
ht.remove(key(c));
}
/**
* Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with
* previous compilations.
*/
static class ReusableJavaCompiler extends JavaCompiler {
static Factory<JavaCompiler> factory = ReusableJavaCompiler::new;
ReusableJavaCompiler(Context context) {
super(context);
}
@Override
public void close() {
//do nothing
}
void clear() {
newRound();
}
@Override
protected void checkReusable() {
//do nothing - it's ok to reuse the compiler
}
}
/**
* Reusable Log; exposes a method to clean up the component from leftovers associated with
* previous compilations.
*/
static class ReusableLog extends Log {
static Factory<Log> factory = ReusableLog::new;
Context context;
ReusableLog(Context context) {
super(context);
this.context = context;
}
void clear() {
recorded.clear();
sourceMap.clear();
nerrors = 0;
nwarnings = 0;
//Set a fake listener that will lazily lookup the context for the 'real' listener. Since
//this field is never updated when a new task is created, we cannot simply reset the field
//or keep old value. This is a hack to workaround the limitations in the current infrastructure.
diagListener = new DiagnosticListener<JavaFileObject>() {
DiagnosticListener<JavaFileObject> cachedListener;
@Override
@SuppressWarnings("unchecked")
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (cachedListener == null) {
cachedListener = context.get(DiagnosticListener.class);
}
cachedListener.report(diagnostic);
}
};
}
}
}

@ -23,30 +23,31 @@
/*
* @test
* @bug 7030606 8006694
* @bug 7030606 8006694 8129962
* @summary Project-coin: multi-catch types should be pairwise disjoint
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm DisjunctiveTypeWellFormednessTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main DisjunctiveTypeWellFormednessTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class DisjunctiveTypeWellFormednessTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
enum Alternative {
public class DisjunctiveTypeWellFormednessTest extends ComboInstance<DisjunctiveTypeWellFormednessTest> {
enum Alternative implements ComboParameter {
EXCEPTION("Exception"),
RUNTIME_EXCEPTION("RuntimeException"),
IO_EXCEPTION("java.io.IOException"),
@ -55,21 +56,10 @@ public class DisjunctiveTypeWellFormednessTest
String exceptionStr;
private Alternative(String exceptionStr) {
Alternative(String exceptionStr) {
this.exceptionStr = exceptionStr;
}
static String makeDisjunctiveType(Alternative... alternatives) {
StringBuilder buf = new StringBuilder();
String sep = "";
for (Alternative alternative : alternatives) {
buf.append(sep);
buf.append(alternative.exceptionStr);
sep = "|";
}
return buf.toString();
}
boolean disjoint(Alternative that) {
return disjoint[this.ordinal()][that.ordinal()];
}
@ -82,135 +72,85 @@ public class DisjunctiveTypeWellFormednessTest
/*FileNotFoundException*/ { false, true, false, false, true },
/*IllegalArgumentException*/ { false, false, true, true, false }
};
@Override
public String expand(String optParameter) {
return exceptionStr;
}
}
enum Arity {
ONE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5);
enum Arity implements ComboParameter {
ONE(1, "#{TYPE[0]}"),
TWO(2, "#{TYPE[0]} | #{TYPE[1]}"),
THREE(3, "#{TYPE[0]} | #{TYPE[1]} | #{TYPE[2]}"),
FOUR(4, "#{TYPE[0]} | #{TYPE[1]} | #{TYPE[2]} | #{TYPE[3]}"),
FIVE(5, "#{TYPE[0]} | #{TYPE[1]} | #{TYPE[2]} | #{TYPE[3]} | #{TYPE[4]}");
int n;
String arityTemplate;
private Arity(int n) {
Arity(int n, String arityTemplate) {
this.n = n;
this.arityTemplate = arityTemplate;
}
@Override
public String expand(String optParameter) {
return arityTemplate;
}
}
public static void main(String... args) throws Exception {
for (Arity arity : Arity.values()) {
for (Alternative a1 : Alternative.values()) {
if (arity == Arity.ONE) {
pool.execute(new DisjunctiveTypeWellFormednessTest(a1));
continue;
}
for (Alternative a2 : Alternative.values()) {
if (arity == Arity.TWO) {
pool.execute(new DisjunctiveTypeWellFormednessTest(a1, a2));
continue;
}
for (Alternative a3 : Alternative.values()) {
if (arity == Arity.THREE) {
pool.execute(new DisjunctiveTypeWellFormednessTest(a1, a2, a3));
continue;
}
for (Alternative a4 : Alternative.values()) {
if (arity == Arity.FOUR) {
pool.execute(new DisjunctiveTypeWellFormednessTest(a1, a2, a3, a4));
continue;
}
for (Alternative a5 : Alternative.values()) {
pool.execute(new DisjunctiveTypeWellFormednessTest(a1, a2, a3, a4, a5));
}
}
}
}
new ComboTestHelper<DisjunctiveTypeWellFormednessTest>()
.withFilter(DisjunctiveTypeWellFormednessTest::arityFilter)
.withDimension("CTYPE", (x, arity) -> x.arity = arity, Arity.values())
.withArrayDimension("TYPE", (x, type, idx) -> x.alternatives[idx] = type, 5, Alternative.values())
.run(DisjunctiveTypeWellFormednessTest::new);
}
Arity arity;
Alternative[] alternatives = new Alternative[5];
boolean arityFilter() {
for (int i = arity.n; i < alternatives.length ; i++) {
if (alternatives[i].ordinal() != 0) {
return false;
}
}
checkAfterExec(false);
return true;
}
Alternative[] alternatives;
JavaSource source;
DiagnosticChecker diagChecker;
DisjunctiveTypeWellFormednessTest(Alternative... alternatives) {
this.alternatives = alternatives;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
class JavaSource extends SimpleJavaFileObject {
String template = "class Test {\n" +
"void test() {\n" +
"try {} catch (#T e) {}\n" +
"}\n" +
"}\n";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replace("#T", Alternative.makeDisjunctiveType(alternatives));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
String template = "class Test {\n" +
"void test() {\n" +
"try {} catch (#{CTYPE} e) {}\n" +
"}\n" +
"}\n";
@Override
public void run() {
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
null, null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable t) {
processException(t);
return;
}
check();
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate(template)
.analyze());
}
void check() {
void check(Result<?> res) {
int non_disjoint = 0;
int i = 0;
for (Alternative a1 : alternatives) {
int j = 0;
for (Alternative a2 : alternatives) {
if (i == j) continue;
if (!a1.disjoint(a2)) {
for (int i = 0 ; i < arity.n ; i++) {
for (int j = 0 ; j < i ; j++) {
if (!alternatives[i].disjoint(alternatives[j])) {
non_disjoint++;
break;
}
j++;
}
i++;
}
if (non_disjoint != diagChecker.errorsFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound errors: " + diagChecker.errorsFound +
"\nExpected errors: " + non_disjoint);
}
}
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
int errorsFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
diagnostic.getCode().startsWith("compiler.err.multicatch.types.must.be.disjoint")) {
errorsFound++;
}
}
}
int foundErrs = res.diagnosticsForKey("compiler.err.multicatch.types.must.be.disjoint").size();
if (non_disjoint != foundErrs) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nFound errors: " + foundErrs +
"\nExpected errors: " + non_disjoint);
}
}
}

@ -0,0 +1,488 @@
/*
* Copyright (c) 2015, 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.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8134007
* @summary Improve string folding
* @compile T8134007.java
*/
class T8134007 {
String v = "";
//interleaved non-literals
String s1 = "Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v;
//heading non-literal
String s2 = v + "Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9";
//trailing non-literal
String s3 = "Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9"
+"Str0" + "Str1" + "Str2" + "Str3" + "Str4" + "Str5" + "Str6" + "Str7" + "Str8" + "Str9" + v;
}

@ -21,47 +21,30 @@
* questions.
*/
/**@test
* @bug 8082311
/*
* @test
* @bug 8082311 8129962
* @summary Verify that bitwise operators don't allow to mix numeric and boolean operands.
* @library ../lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main BitWiseOperators
*/
import com.sun.tools.javac.util.StringUtils;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
public class BitWiseOperators extends JavacTestingAbstractThreadedTest {
public static void main(String... args) {
new BitWiseOperators().run();
}
import java.io.IOException;
void run() {
for (TYPE type1 : TYPE.values()) {
for (OPERATION op : OPERATION.values()) {
for (TYPE type2 : TYPE.values()) {
runTest(type1, op, type2);
}
}
}
}
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
void runTest(TYPE type1, OPERATION op, TYPE type2) {
DiagnosticCollector<JavaFileObject> dc = new DiagnosticCollector<>();
List<JavaSource> files = Arrays.asList(new JavaSource(type1, op, type2));
comp.getTask(null, null, dc, null, null, files).call();
if (dc.getDiagnostics().isEmpty() ^ TYPE.compatible(type1, type2)) {
throw new AssertionError("Unexpected behavior. Type1: " + type1 +
"; type2: " + type2 +
"; diagnostics: " + dc.getDiagnostics());
}
}
enum TYPE {
public class BitWiseOperators extends ComboInstance<BitWiseOperators> {
enum OperandType implements ComboParameter {
BYTE,
CHAR,
SHORT,
@ -69,45 +52,57 @@ public class BitWiseOperators extends JavacTestingAbstractThreadedTest {
LONG,
BOOLEAN;
public static boolean compatible(TYPE op1, TYPE op2) {
public static boolean compatible(OperandType op1, OperandType op2) {
return !(op1 == BOOLEAN ^ op2 == BOOLEAN);
}
@Override
public String expand(String optParameter) {
return StringUtils.toLowerCase(name());
}
}
enum OPERATION {
enum OperatorKind implements ComboParameter {
BITAND("&"),
BITOR("|"),
BITXOR("^");
String op;
private OPERATION(String op) {
OperatorKind(String op) {
this.op = op;
}
}
class JavaSource extends SimpleJavaFileObject {
String template = "class Test {\n" +
" public Object test(#TYPE1 var1, #TYPE2 var2) {\n" +
" return var1 #OP var2;\n" +
" }\n" +
"}";
String source;
public JavaSource(TYPE type1, OPERATION op, TYPE type2) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replaceAll("#TYPE1", StringUtils.toLowerCase(type1.name()))
.replaceAll("#OP", StringUtils.toLowerCase(op.op))
.replaceAll("#TYPE2", StringUtils.toLowerCase(type2.name()));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
public String expand(String optParameter) {
return op;
}
}
public static void main(String... args) {
new ComboTestHelper<BitWiseOperators>()
.withArrayDimension("TYPE", (x, type, idx) -> x.opTypes[idx] = type, 2, OperandType.values())
.withDimension("OP", OperatorKind.values())
.run(BitWiseOperators::new);
}
OperandType[] opTypes = new OperandType[2];
String template = "class Test {\n" +
" public Object test(#{TYPE[0]} var1, #{TYPE[1]} var2) {\n" +
" return var1 #{OP} var2;\n" +
" }\n" +
"}";
@Override
public void doWork() throws IOException {
Result<?> res = newCompilationTask()
.withSourceFromTemplate(template)
.analyze();
if (res.hasErrors() == OperandType.compatible(opTypes[0], opTypes[1])) {
fail("Unexpected behavior. Type1: " + opTypes[0] +
"; type2: " + opTypes[1] +
"; " + res.compilationInfo());
}
}
}

@ -29,6 +29,7 @@
*/
import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.Scope.ScopeListenerList;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Types;
@ -53,20 +54,14 @@ public class ScopeListenerTest {
types.membersClosure(syms.stringType, true);
types.membersClosure(syms.stringType, false);
Field listenersField = Scope.class.getDeclaredField("listeners");
listenersField.setAccessible(true);
int listenerCount =
((Collection) listenersField.get(syms.stringType.tsym.members())).size();
int listenerCount = listenerCount(syms.stringType.tsym.members());
for (int i = 0; i < 100; i++) {
types.membersClosure(syms.stringType, true);
types.membersClosure(syms.stringType, false);
}
int newListenerCount
= ((Collection) listenersField.get(syms.stringType.tsym.members())).size();
int newListenerCount = listenerCount(syms.stringType.tsym.members());
if (listenerCount != newListenerCount) {
throw new AssertionError("Orig listener count: " + listenerCount +
@ -79,4 +74,12 @@ public class ScopeListenerTest {
;
}
int listenerCount(Scope s) throws ReflectiveOperationException {
Field listenersListField = Scope.class.getDeclaredField("listeners");
listenersListField.setAccessible(true);
Field listenersField = ScopeListenerList.class.getDeclaredField("listeners");
listenersField.setAccessible(true);
return ((Collection<?>)listenersField.get(listenersListField.get(s))).size();
}
}

@ -23,31 +23,25 @@
/*
* @test
* @bug 7042566 8006694
* @bug 7042566 8006694 8129962
* @summary Unambiguous varargs method calls flagged as ambiguous
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../lib
* @library /tools/javac/lib
* @modules jdk.jdeps/com.sun.tools.classfile
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build JavacTestingAbstractThreadedTest
* @run main/othervm T7042566
* @build combo.ComboTestHelper
* @run main T7042566
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.io.File;
import java.net.URI;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import java.io.IOException;
import java.io.InputStream;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.ClassFile;
@ -56,145 +50,12 @@ import com.sun.tools.classfile.ConstantPool.*;
import com.sun.tools.classfile.Method;
import com.sun.tools.javac.util.List;
public class T7042566
extends JavacTestingAbstractThreadedTest
implements Runnable {
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
VarargsMethod m1;
VarargsMethod m2;
TypeConfiguration actuals;
T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf,
TypeConfiguration actuals) {
this.m1 = new VarargsMethod(m1_conf);
this.m2 = new VarargsMethod(m2_conf);
this.actuals = actuals;
}
@Override
public void run() {
int id = checkCount.incrementAndGet();
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
JavaSource source = new JavaSource(id);
ErrorChecker ec = new ErrorChecker();
JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), ec,
null, null, Arrays.asList(source));
ct.call();
check(source, ec, id);
}
void check(JavaSource source, ErrorChecker ec, int id) {
boolean resolutionError = false;
VarargsMethod selectedMethod = null;
boolean m1_applicable = m1.isApplicable(actuals);
boolean m2_applicable = m2.isApplicable(actuals);
if (!m1_applicable && !m2_applicable) {
resolutionError = true;
} else if (m1_applicable && m2_applicable) {
//most specific
boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
resolutionError = m1_moreSpecific == m2_moreSpecific;
selectedMethod = m1_moreSpecific ? m1 : m2;
} else {
selectedMethod = m1_applicable ?
m1 : m2;
}
if (ec.errorFound != resolutionError) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nExpected resolution error: " + resolutionError +
"\nFound error: " + ec.errorFound +
"\nCompiler diagnostics:\n" + ec.printDiags());
} else if (!resolutionError) {
verifyBytecode(selectedMethod, source, id);
}
}
void verifyBytecode(VarargsMethod selected, JavaSource source, int id) {
bytecodeCheckCount.incrementAndGet();
File compiledTest = new File(String.format("Test%d.class", id));
try {
ClassFile cf = ClassFile.read(compiledTest);
Method testMethod = null;
for (Method m : cf.methods) {
if (m.getName(cf.constant_pool).equals("test")) {
testMethod = m;
break;
}
}
if (testMethod == null) {
throw new Error("Test method not found");
}
Code_attribute ea =
(Code_attribute)testMethod.attributes.get(Attribute.Code);
if (testMethod == null) {
throw new Error("Code attribute for test() method not found");
}
for (Instruction i : ea.getInstructions()) {
if (i.getMnemonic().equals("invokevirtual")) {
int cp_entry = i.getUnsignedShort(1);
CONSTANT_Methodref_info methRef =
(CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
String type = methRef.getNameAndTypeInfo().getType();
String sig = selected.parameterTypes.bytecodeSigStr;
if (!type.contains(sig)) {
throw new Error("Unexpected type method call: " +
type + "" +
"\nfound: " + sig +
"\n" + source.getCharContent(true));
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
throw new Error("error reading " + compiledTest +": " + e);
}
}
class JavaSource extends SimpleJavaFileObject {
static final String source_template = "class Test#ID {\n" +
" #V1\n" +
" #V2\n" +
" void test() { m(#E); }\n" +
"}";
String source;
public JavaSource(int id) {
super(URI.create(String.format("myfo:/Test%d.java", id)),
JavaFileObject.Kind.SOURCE);
source = source_template.replaceAll("#V1", m1.toString())
.replaceAll("#V2", m2.toString())
.replaceAll("#E", actuals.expressionListStr)
.replaceAll("#ID", String.valueOf(id));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
public static void main(String... args) throws Exception {
for (TypeConfiguration tconf1 : TypeConfiguration.values()) {
for (TypeConfiguration tconf2 : TypeConfiguration.values()) {
for (TypeConfiguration tconf3 : TypeConfiguration.values()) {
pool.execute(new T7042566(tconf1, tconf2, tconf3));
}
}
}
outWriter.println("Bytecode checks made: " + bytecodeCheckCount.get());
checkAfterExec();
}
public class T7042566 extends ComboInstance<T7042566> {
enum TypeKind {
OBJECT("Object", "(Object)null", "Ljava/lang/Object;"),
@ -216,7 +77,7 @@ public class T7042566
}
}
enum TypeConfiguration {
enum TypeConfiguration implements ComboParameter {
A(TypeKind.OBJECT),
B(TypeKind.STRING),
AA(TypeKind.OBJECT, TypeKind.OBJECT),
@ -237,7 +98,7 @@ public class T7042566
String parameterListStr;
String bytecodeSigStr;
private TypeConfiguration(TypeKind... typeKindList) {
TypeConfiguration(TypeKind... typeKindList) {
this.typeKindList = List.from(typeKindList);
expressionListStr = asExpressionList();
parameterListStr = asParameterList();
@ -284,6 +145,11 @@ public class T7042566
}
return buf.toString();
}
@Override
public String expand(String optParameter) {
return expressionListStr;
}
}
static class VarargsMethod {
@ -333,31 +199,119 @@ public class T7042566
}
}
static class ErrorChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
public static void main(String[] args) {
new ComboTestHelper<T7042566>()
.withArrayDimension("SIG", (x, sig, idx) -> x.methodSignatures[idx] = sig, 2, TypeConfiguration.values())
.withDimension("ACTUALS", (x, actuals) -> x.actuals = actuals, TypeConfiguration.values())
.run(T7042566::new, T7042566::setup);
}
boolean errorFound;
List<String> errDiags = List.nil();
VarargsMethod m1;
VarargsMethod m2;
TypeConfiguration[] methodSignatures = new TypeConfiguration[2];
TypeConfiguration actuals;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errDiags = errDiags
.append(diagnostic.getMessage(Locale.getDefault()));
errorFound = true;
}
}
void setup() {
this.m1 = new VarargsMethod(methodSignatures[0]);
this.m2 = new VarargsMethod(methodSignatures[1]);
}
String printDiags() {
StringBuilder buf = new StringBuilder();
for (String s : errDiags) {
buf.append(s);
buf.append("\n");
}
return buf.toString();
final String source_template = "class Test {\n" +
" #{METH.1}\n" +
" #{METH.2}\n" +
" void test() { m(#{ACTUALS}); }\n" +
"}";
@Override
public void doWork() throws IOException {
check(newCompilationTask()
.withSourceFromTemplate(source_template, this::getMethodDecl)
.generate());
}
ComboParameter getMethodDecl(String parameterName) {
switch (parameterName) {
case "METH": return optParameter -> {
return optParameter.equals("1") ?
m1.toString() : m2.toString();
};
default:
return null;
}
}
//number of bytecode checks made while running combo tests
static AtomicInteger bytecodeCheckCount = new AtomicInteger();
void check(Result<Iterable<? extends JavaFileObject>> res) {
boolean resolutionError = false;
VarargsMethod selectedMethod = null;
boolean m1_applicable = m1.isApplicable(actuals);
boolean m2_applicable = m2.isApplicable(actuals);
if (!m1_applicable && !m2_applicable) {
resolutionError = true;
} else if (m1_applicable && m2_applicable) {
//most specific
boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
resolutionError = m1_moreSpecific == m2_moreSpecific;
selectedMethod = m1_moreSpecific ? m1 : m2;
} else {
selectedMethod = m1_applicable ?
m1 : m2;
}
if (res.hasErrors() != resolutionError) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nExpected resolution error: " + resolutionError +
"\nFound error: " + res.hasErrors());
} else if (!resolutionError) {
verifyBytecode(res, selectedMethod);
}
}
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res, VarargsMethod selected) {
try (InputStream is = res.get().iterator().next().openInputStream()) {
ClassFile cf = ClassFile.read(is);
Method testMethod = null;
for (Method m : cf.methods) {
if (m.getName(cf.constant_pool).equals("test")) {
testMethod = m;
break;
}
}
if (testMethod == null) {
fail("Test method not found");
return;
}
Code_attribute ea =
(Code_attribute)testMethod.attributes.get(Attribute.Code);
if (testMethod == null) {
fail("Code attribute for test() method not found");
return;
}
for (Instruction i : ea.getInstructions()) {
if (i.getMnemonic().equals("invokevirtual")) {
int cp_entry = i.getUnsignedShort(1);
CONSTANT_Methodref_info methRef =
(CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
String type = methRef.getNameAndTypeInfo().getType();
String sig = selected.parameterTypes.bytecodeSigStr;
if (!type.contains(sig)) {
fail("Unexpected type method call: " +
type + "" +
"\nfound: " + sig +
"\n" + res.compilationInfo());
return;
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
fail("error reading classfile; " + res.compilationInfo() +": " + e);
}
}
}

@ -23,40 +23,39 @@
/**
* @test
* @bug 6945418 6993978 8006694 7196160
* @bug 6945418 6993978 8006694 7196160 8129962
* @summary Project Coin: Simplified Varargs Method Invocation
* temporarily workaround combo tests are causing time out in several platforms
* @author mcimadamore
* @library ../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @run main/othervm Warn4
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main Warn4
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import java.io.IOException;
import java.util.Set;
import java.util.HashSet;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
public class Warn4
extends JavacTestingAbstractThreadedTest
implements Runnable {
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class Warn4 extends ComboInstance<Warn4> {
final static Warning[] error = null;
final static Warning[] none = new Warning[] {};
final static Warning[] vararg = new Warning[] { Warning.VARARGS };
final static Warning[] unchecked = new Warning[] { Warning.UNCHECKED };
final static Warning[] both =
new Warning[] { Warning.VARARGS, Warning.UNCHECKED };
final static Warning[] both = new Warning[] { Warning.VARARGS, Warning.UNCHECKED };
enum Warning {
UNCHECKED("generic.array.creation"),
@ -105,7 +104,7 @@ public class Warn4
}
}
enum TrustMe {
enum TrustMe implements ComboParameter {
DONT_TRUST(""),
TRUST("@java.lang.SafeVarargs");
@ -114,9 +113,14 @@ public class Warn4
TrustMe(String anno) {
this.anno = anno;
}
@Override
public String expand(String optParameter) {
return anno;
}
}
enum ModifierKind {
enum ModifierKind implements ComboParameter {
NONE(" "),
FINAL("final "),
STATIC("static "),
@ -127,9 +131,14 @@ public class Warn4
ModifierKind(String mod) {
this.mod = mod;
}
@Override
public String expand(String optParameter) {
return mod;
}
}
enum SuppressLevel {
enum SuppressLevel implements ComboParameter {
NONE(""),
UNCHECKED("unchecked");
@ -139,21 +148,22 @@ public class Warn4
this.lint = lint;
}
String getSuppressAnno() {
@Override
public String expand(String optParameter) {
return "@SuppressWarnings(\"" + lint + "\")";
}
}
enum Signature {
UNBOUND("void #name(List<?>#arity arg) { #body }",
enum Signature implements ComboParameter {
UNBOUND("void #NAME(List<?>#ARITY arg) { #BODY }",
new Warning[][] {none, none, none, none, error}),
INVARIANT_TVAR("<Z> void #name(List<Z>#arity arg) { #body }",
INVARIANT_TVAR("<Z> void #NAME(List<Z>#ARITY arg) { #BODY }",
new Warning[][] {both, both, error, both, error}),
TVAR("<Z> void #name(Z#arity arg) { #body }",
TVAR("<Z> void #NAME(Z#ARITY arg) { #BODY }",
new Warning[][] {both, both, both, both, vararg}),
INVARIANT("void #name(List<String>#arity arg) { #body }",
INVARIANT("void #NAME(List<String>#ARITY arg) { #BODY }",
new Warning[][] {error, error, error, both, error}),
UNPARAMETERIZED("void #name(String#arity arg) { #body }",
UNPARAMETERIZED("void #NAME(String#ARITY arg) { #BODY }",
new Warning[][] {error, error, error, error, none});
String template;
@ -177,130 +187,85 @@ public class Warn4
return warnings[other.ordinal()] == vararg ||
warnings[other.ordinal()] == both;
}
}
public static void main(String... args) throws Exception {
for (SourceLevel sourceLevel : SourceLevel.values()) {
for (TrustMe trustMe : TrustMe.values()) {
for (SuppressLevel suppressLevelClient : SuppressLevel.values()) {
for (SuppressLevel suppressLevelDecl : SuppressLevel.values()) {
for (ModifierKind modKind : ModifierKind.values()) {
for (Signature vararg_meth : Signature.values()) {
for (Signature client_meth : Signature.values()) {
if (vararg_meth.isApplicableTo(client_meth)) {
pool.execute(new Warn4(sourceLevel,
trustMe,
suppressLevelClient,
suppressLevelDecl,
modKind,
vararg_meth,
client_meth));
}
}
}
}
}
}
@Override
public String expand(String optParameter) {
if (optParameter.equals("CLIENT")) {
return template.replaceAll("#ARITY", "")
.replaceAll("#NAME", "test")
.replaceAll("#BODY", "m(arg)");
} else {
return template.replaceAll("#ARITY", "...")
.replaceAll("#NAME", "m")
.replaceAll("#BODY", "");
}
}
}
checkAfterExec();
public static void main(String... args) {
new ComboTestHelper<Warn4>()
.withFilter(Warn4::badTestFilter)
.withDimension("SOURCE", (x, level) -> x.sourceLevel = level, SourceLevel.values())
.withDimension("TRUSTME", (x, trustme) -> x.trustMe = trustme, TrustMe.values())
.withArrayDimension("SUPPRESS", (x, suppress, idx) -> x.suppress[idx] = suppress, 2, SuppressLevel.values())
.withDimension("MOD", (x, mod) -> x.modKind = mod, ModifierKind.values())
.withArrayDimension("MTH", (x, sig, idx) -> x.sigs[idx] = sig, 2, Signature.values())
.run(Warn4::new);
}
SourceLevel sourceLevel;
TrustMe trustMe;
SuppressLevel suppressLevelClient;
SuppressLevel suppressLevelDecl;
SuppressLevel[] suppress = new SuppressLevel[2];
ModifierKind modKind;
Signature vararg_meth;
Signature client_meth;
DiagnosticChecker diagChecker;
Signature[] sigs = new Signature[2];
public Warn4(SourceLevel sourceLevel, TrustMe trustMe,
SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl,
ModifierKind modKind, Signature vararg_meth, Signature client_meth) {
this.sourceLevel = sourceLevel;
this.trustMe = trustMe;
this.suppressLevelClient = suppressLevelClient;
this.suppressLevelDecl = suppressLevelDecl;
this.modKind = modKind;
this.vararg_meth = vararg_meth;
this.client_meth = client_meth;
this.diagChecker = new DiagnosticChecker();
boolean badTestFilter() {
return sigs[0].isApplicableTo(sigs[1]);
}
final String template = "import java.util.List;\n" +
"class Test {\n" +
" #{TRUSTME} #{SUPPRESS[0]} #{MOD} #{MTH[0].VARARG}\n" +
" #{SUPPRESS[1]} #{MTH[1].CLIENT}\n" +
"}";
@Override
public void run() {
int id = checkCount.incrementAndGet();
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
JavaSource source = new JavaSource(id);
JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker,
Arrays.asList("-Xlint:unchecked", "-source", sourceLevel.sourceKey),
null, Arrays.asList(source));
ct.call(); //to get mandatory notes
check(source, new boolean[] {vararg_meth.giveUnchecked(client_meth),
vararg_meth.giveVarargs(client_meth)});
public void doWork() throws IOException {
check(newCompilationTask()
.withOption("-Xlint:unchecked")
.withOption("-source")
.withOption(sourceLevel.sourceKey)
.withSourceFromTemplate(template)
.analyze());
}
void check(JavaSource source, boolean[] warnArr) {
void check(Result<?> res) {
boolean[] warnArr = new boolean[] {sigs[0].giveUnchecked(sigs[1]),
sigs[0].giveVarargs(sigs[1])};
Set<Warning> warnings = new HashSet<>();
for (Diagnostic<? extends JavaFileObject> d : res.diagnosticsForKind(Kind.MANDATORY_WARNING)) {
if (d.getCode().contains(Warning.VARARGS.key)) {
warnings.add(Warning.VARARGS);
} else if(d.getCode().contains(Warning.UNCHECKED.key)) {
warnings.add(Warning.UNCHECKED);
}
}
boolean badOutput = false;
for (Warning wkind : Warning.values()) {
boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel,
suppressLevelClient, suppressLevelDecl, modKind);
suppress[1], suppress[0], modKind);
badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) !=
diagChecker.warnings.contains(wkind);
warnings.contains(wkind);
}
if (badOutput) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nExpected unchecked warning: " + warnArr[0] +
"\nExpected unsafe vararg warning: " + warnArr[1] +
"\nWarnings: " + diagChecker.warnings +
"\nWarnings: " + warnings +
"\nSource level: " + sourceLevel);
}
}
class JavaSource extends SimpleJavaFileObject {
String source;
public JavaSource(int id) {
super(URI.create(String.format("myfo:/Test%d.java", id)),
JavaFileObject.Kind.SOURCE);
String meth1 = vararg_meth.template.replace("#arity", "...");
meth1 = meth1.replace("#name", "m");
meth1 = meth1.replace("#body", "");
meth1 = trustMe.anno + "\n" + suppressLevelDecl.getSuppressAnno() +
modKind.mod + meth1;
String meth2 = client_meth.template.replace("#arity", "");
meth2 = meth2.replace("#name", "test");
meth2 = meth2.replace("#body", "m(arg);");
meth2 = suppressLevelClient.getSuppressAnno() + meth2;
source = String.format("import java.util.List;\n" +
"class Test%s {\n %s \n %s \n } \n", id, meth1, meth2);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
Set<Warning> warnings = new HashSet<>();
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING ||
diagnostic.getKind() == Diagnostic.Kind.WARNING) {
if (diagnostic.getCode().contains(Warning.VARARGS.key)) {
warnings.add(Warning.VARARGS);
} else if(diagnostic.getCode().contains(Warning.UNCHECKED.key)) {
warnings.add(Warning.UNCHECKED);
}
}
}
}
}

@ -26,29 +26,30 @@
* @bug 6993978 7097436 8006694 7196160
* @summary Project Coin: Annotation to reduce varargs warnings
* temporarily workaround combo tests are causing time out in several platforms
* @author mcimadamore
* @library ../../lib
* @modules jdk.compiler
* @build JavacTestingAbstractThreadedTest
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @build combo.ComboTestHelper
* @run main/othervm Warn5
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import java.io.IOException;
import java.util.EnumSet;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
public class Warn5
extends JavacTestingAbstractThreadedTest
implements Runnable {
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask.Result;
import combo.ComboTestHelper;
public class Warn5 extends ComboInstance<Warn5> {
enum XlintOption {
NONE("none"),
@ -65,7 +66,7 @@ public class Warn5
}
}
enum TrustMe {
enum TrustMe implements ComboParameter {
DONT_TRUST(""),
TRUST("@java.lang.SafeVarargs");
@ -74,20 +75,26 @@ public class Warn5
TrustMe(String anno) {
this.anno = anno;
}
@Override
public String expand(String optParameter) {
return anno;
}
}
enum SuppressLevel {
enum SuppressLevel implements ComboParameter {
NONE,
VARARGS;
String getSuppressAnno() {
@Override
public String expand(String optParameter) {
return this == VARARGS ?
"@SuppressWarnings(\"varargs\")" :
"";
}
}
enum ModifierKind {
enum ModifierKind implements ComboParameter {
NONE(""),
FINAL("final"),
STATIC("static"),
@ -98,9 +105,14 @@ public class Warn5
ModifierKind(String mod) {
this.mod = mod;
}
@Override
public String expand(String optParameter) {
return mod;
}
}
enum MethodKind {
enum MethodKind implements ComboParameter {
METHOD("void m"),
CONSTRUCTOR("Test");
@ -109,6 +121,11 @@ public class Warn5
MethodKind(String name) {
this.name = name;
}
@Override
public String expand(String optParameter) {
return name;
}
}
enum SourceLevel {
@ -123,11 +140,11 @@ public class Warn5
}
}
enum SignatureKind {
VARARGS_X("#K <X>#N(X... x)", false, true),
VARARGS_STRING("#K #N(String... x)", true, true),
ARRAY_X("#K <X>#N(X[] x)", false, false),
ARRAY_STRING("#K #N(String[] x)", true, false);
enum SignatureKind implements ComboParameter {
VARARGS_X("<X>#{NAME}(X... x)", false, true),
VARARGS_STRING("#{NAME}(String... x)", true, true),
ARRAY_X("<X>#{NAME}(X[] x)", false, false),
ARRAY_STRING("#{NAME}(String[] x)", true, false);
String stub;
boolean isReifiableArg;
@ -139,14 +156,13 @@ public class Warn5
this.isVarargs = isVarargs;
}
String getSignature(ModifierKind modKind, MethodKind methKind) {
return methKind != MethodKind.CONSTRUCTOR ?
stub.replace("#K", modKind.mod).replace("#N", methKind.name) :
stub.replace("#K", "").replace("#N", methKind.name);
@Override
public String expand(String optParameter) {
return stub;
}
}
enum BodyKind {
enum BodyKind implements ComboParameter {
ASSIGN("Object o = x;", true),
CAST("Object o = (Object)x;", true),
METH("test(x);", true),
@ -162,82 +178,84 @@ public class Warn5
this.body = body;
this.hasAliasing = hasAliasing;
}
@Override
public String expand(String optParameter) {
return body;
}
}
enum WarningKind {
UNSAFE_BODY,
UNSAFE_DECL,
MALFORMED_SAFEVARARGS,
REDUNDANT_SAFEVARARGS;
UNSAFE_BODY("compiler.warn.varargs.unsafe.use.varargs.param"),
UNSAFE_DECL("compiler.warn.unchecked.varargs.non.reifiable.type"),
MALFORMED_SAFEVARARGS("compiler.err.varargs.invalid.trustme.anno"),
REDUNDANT_SAFEVARARGS("compiler.warn.varargs.redundant.trustme.anno");
String code;
WarningKind(String code) {
this.code = code;
}
}
public static void main(String... args) throws Exception {
for (SourceLevel sourceLevel : SourceLevel.values()) {
for (XlintOption xlint : XlintOption.values()) {
for (TrustMe trustMe : TrustMe.values()) {
for (SuppressLevel suppressLevel : SuppressLevel.values()) {
for (ModifierKind modKind : ModifierKind.values()) {
for (MethodKind methKind : MethodKind.values()) {
for (SignatureKind sig : SignatureKind.values()) {
for (BodyKind body : BodyKind.values()) {
pool.execute(new Warn5(sourceLevel,
xlint, trustMe, suppressLevel,
modKind, methKind, sig, body));
}
}
}
}
public static void main(String[] args) {
new ComboTestHelper<Warn5>()
.withFilter(Warn5::badTestFilter)
.withDimension("SOURCE", (x, level) -> x.sourceLevel = level, SourceLevel.values())
.withDimension("LINT", (x, lint) -> x.xlint = lint, XlintOption.values())
.withDimension("TRUSTME", (x, trustme) -> x.trustMe = trustme, TrustMe.values())
.withDimension("SUPPRESS", (x, suppress) -> x.suppressLevel = suppress, SuppressLevel.values())
.withDimension("MOD", (x, mod) -> x.modKind = mod, ModifierKind.values())
.withDimension("NAME", (x, name) -> x.methKind = name, MethodKind.values())
.withDimension("SIG", (x, sig) -> x.sig = sig, SignatureKind.values())
.withDimension("BODY", (x, body) -> x.body = body, BodyKind.values())
.run(Warn5::new);
}
SourceLevel sourceLevel;
XlintOption xlint;
TrustMe trustMe;
SuppressLevel suppressLevel;
ModifierKind modKind;
MethodKind methKind;
SignatureKind sig;
BodyKind body;
boolean badTestFilter() {
return (methKind != MethodKind.CONSTRUCTOR || modKind == ModifierKind.NONE);
}
String template = "import com.sun.tools.javac.api.*;\n" +
"import java.util.List;\n" +
"class Test {\n" +
" static void test(Object o) {}\n" +
" static void testArr(Object[] o) {}\n" +
" #{TRUSTME} #{SUPPRESS} #{MOD} #{SIG} { #{BODY} }\n" +
"}\n";
@Override
public void doWork() throws IOException {
check(newCompilationTask()
.withOption(xlint.getXlintOption())
.withOption("-source")
.withOption(sourceLevel.sourceKey)
.withSourceFromTemplate(template)
.analyze());
}
void check(Result<?> res) {
EnumSet<WarningKind> foundWarnings = EnumSet.noneOf(WarningKind.class);
for (Diagnostic.Kind kind : new Kind[] { Kind.ERROR, Kind.MANDATORY_WARNING, Kind.WARNING}) {
for (Diagnostic<? extends JavaFileObject> diag : res.diagnosticsForKind(kind)) {
for (WarningKind wk : WarningKind.values()) {
if (wk.code.equals(diag.getCode())) {
foundWarnings.add(wk);
}
}
}
}
checkAfterExec(false);
}
final SourceLevel sourceLevel;
final XlintOption xlint;
final TrustMe trustMe;
final SuppressLevel suppressLevel;
final ModifierKind modKind;
final MethodKind methKind;
final SignatureKind sig;
final BodyKind body;
final JavaSource source;
final DiagnosticChecker dc;
public Warn5(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe,
SuppressLevel suppressLevel, ModifierKind modKind,
MethodKind methKind, SignatureKind sig, BodyKind body) {
this.sourceLevel = sourceLevel;
this.xlint = xlint;
this.trustMe = trustMe;
this.suppressLevel = suppressLevel;
this.modKind = modKind;
this.methKind = methKind;
this.sig = sig;
this.body = body;
this.source = new JavaSource();
this.dc = new DiagnosticChecker();
}
@Override
public void run() {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), dc,
Arrays.asList(xlint.getXlintOption(),
"-source", sourceLevel.sourceKey),
null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable t) {
processException(t);
}
check();
}
void check() {
EnumSet<WarningKind> expectedWarnings =
EnumSet.noneOf(WarningKind.class);
@ -284,77 +302,14 @@ public class Warn5
expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS);
}
if (!expectedWarnings.containsAll(dc.warnings) ||
!dc.warnings.containsAll(expectedWarnings)) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
if (!expectedWarnings.containsAll(foundWarnings) ||
!foundWarnings.containsAll(expectedWarnings)) {
fail("invalid diagnostics for source:\n" +
res.compilationInfo() +
"\nOptions: " + xlint.getXlintOption() +
"\nSource Level: " + sourceLevel +
"\nExpected warnings: " + expectedWarnings +
"\nFound warnings: " + dc.warnings);
"\nFound warnings: " + foundWarnings);
}
}
class JavaSource extends SimpleJavaFileObject {
String template = "import com.sun.tools.javac.api.*;\n" +
"import java.util.List;\n" +
"class Test {\n" +
" static void test(Object o) {}\n" +
" static void testArr(Object[] o) {}\n" +
" #T \n #S #M { #B }\n" +
"}\n";
String source;
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source = template.replace("#T", trustMe.anno).
replace("#S", suppressLevel.getSuppressAnno()).
replace("#M", sig.getSignature(modKind, methKind)).
replace("#B", body.body);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
EnumSet<WarningKind> warnings = EnumSet.noneOf(WarningKind.class);
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.WARNING) {
if (diagnostic.getCode().
contains("unsafe.use.varargs.param")) {
setWarning(WarningKind.UNSAFE_BODY);
} else if (diagnostic.getCode().
contains("redundant.trustme")) {
setWarning(WarningKind.REDUNDANT_SAFEVARARGS);
}
} else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING &&
diagnostic.getCode().
contains("varargs.non.reifiable.type")) {
setWarning(WarningKind.UNSAFE_DECL);
} else if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
diagnostic.getCode().contains("invalid.trustme")) {
setWarning(WarningKind.MALFORMED_SAFEVARARGS);
}
}
void setWarning(WarningKind wk) {
if (!warnings.add(wk)) {
throw new AssertionError("Duplicate warning of kind " +
wk + " in source:\n" + source);
}
}
boolean hasWarning(WarningKind wk) {
return warnings.contains(wk);
}
}
}