8200701: jdk/jshell/ExceptionsTest.java fails on Windows, after JDK-8198801
8159740: JShell: corralled declarations do not have correct source to wrapper mapping
8212167: JShell : Stack trace of exception has wrong line number
Summary: Build corralled (recoverable undeclared definitions) declarations from position translating wraps....
Reviewed-by: jlahoda
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jshell;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.Visitor;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import static com.sun.tools.javac.code.Flags.FINAL;
import static com.sun.tools.javac.code.Flags.PUBLIC;
import static com.sun.tools.javac.code.Flags.STATIC;
import static com.sun.tools.javac.code.Flags.INTERFACE;
import static com.sun.tools.javac.code.Flags.ENUM;
import jdk.jshell.Wrap.CompoundWrap;
import jdk.jshell.Wrap.Range;
import jdk.jshell.Wrap.RangeWrap;
/**
* Produce a corralled version of the Wrap for a snippet.
*/
class Corraller extends Visitor {
/** Visitor result field: a Wrap
*/
protected Wrap result;
private final TreeDissector dis;
private final String resolutionExceptionBlock;
private final String source;
public Corraller(TreeDissector dis, int keyIndex, String source) {
this.dis = dis;
this.resolutionExceptionBlock = "\n { throw new jdk.jshell.spi.SPIResolutionException(" + keyIndex + "); }";
this.source = source;
}
public Wrap corralType(ClassTree tree) {
return corralToWrap(tree);
}
public Wrap corralMethod(MethodTree tree) {
return corralToWrap(tree);
}
private Wrap corralToWrap(Tree tree) {
try {
JCTree jct = (JCTree) tree;
Wrap w = new CompoundWrap(
" public static\n ",
corral(jct));
debugWrap("corralToWrap SUCCESS source: %s -- wrap:\n %s\n", tree, w.wrapped());
return w;
} catch (Exception ex) {
debugWrap("corralToWrap FAIL: %s - %s\n", tree, ex);
//ex.printStackTrace(System.err);
return null;
}
}
// Corral a single node.
// @SuppressWarnings("unchecked")
private <T extends JCTree> Wrap corral(T tree) {
if (tree == null) {
return null;
} else {
tree.accept(this);
Wrap tmpResult = this.result;
this.result = null;
return tmpResult;
}
}
private String defaultConstructor(JCClassDecl tree) {
return " public " + tree.name.toString() + "() " +
resolutionExceptionBlock;
}
/* ***************************************************************************
* Visitor methods
****************************************************************************/
@Override
public void visitClassDef(JCClassDecl tree) {
boolean isEnum = (tree.mods.flags & ENUM) != 0;
boolean isInterface = (tree.mods.flags & INTERFACE ) != 0;
int classBegin = dis.getStartPosition(tree);
int classEnd = dis.getEndPosition(tree);
//debugWrap("visitClassDef: %d-%d = %s\n", classBegin, classEnd, source.substring(classBegin, classEnd));
ListBuffer<Object> wrappedDefs = new ListBuffer<>();
int bodyBegin = -1;
if (tree.defs != null && !tree.defs.isEmpty()) {
if (isEnum) {
// copy the enum constants verbatim
int enumBegin = dis.getStartPosition(tree.defs.head);
JCTree t = null; // null to shut-up compiler, always set because non-empty
List<? extends JCTree> l = tree.defs;
for (; l.nonEmpty(); l = l.tail) {
t = l.head;
if (t.getKind() == Kind.VARIABLE) {
if ((((JCVariableDecl)t).mods.flags & (PUBLIC | STATIC | FINAL)) != (PUBLIC | STATIC | FINAL)) {
// non-enum constant, process normally
break;
}
} else {
// non-variable, process normally
break;
}
}
int constEnd = l.nonEmpty() // end of constants
? dis.getStartPosition(l.head) - 1 // is one before next defs, if there is one
: dis.getEndPosition(t); // and otherwise end of the last constant
wrappedDefs.append(new RangeWrap(source, new Range(enumBegin, constEnd)));
// handle any other defs
for (; l.nonEmpty(); l = l.tail) {
wrappedDefs.append("\n");
t = l.head;
wrappedDefs.append(corral(t));
}
} else {
// non-enum
boolean constructorSeen = false;
for (List<? extends JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
wrappedDefs.append("\n ");
JCTree t = l.head;
switch (t.getKind()) {
case METHOD:
constructorSeen = constructorSeen || ((MethodTree)t).getName() == tree.name.table.names.init;
break;
case BLOCK:
// throw exception in instance initializer too -- inline because String not Wrap
wrappedDefs.append((((JCBlock)t).flags & STATIC) != 0
? new RangeWrap(source, dis.treeToRange(t))
: resolutionExceptionBlock);
continue; // already appended, skip append below
}
wrappedDefs.append(corral(t));
}
if (!constructorSeen && !isInterface && !isEnum) {
// Generate a default constructor, since
// this is a regular class and there are no constructors
if (wrappedDefs.length() > 0) {
wrappedDefs.append("\n ");
}
wrappedDefs.append(defaultConstructor(tree));
}
}
bodyBegin = dis.getStartPosition(tree.defs.head);
}
Object defs = wrappedDefs.length() == 1
? wrappedDefs.first()
: new CompoundWrap(wrappedDefs.toArray());
if (bodyBegin < 0) {
int brace = source.indexOf('{', classBegin);
if (brace < 0 || brace >= classEnd) {
throw new IllegalArgumentException("No brace found: " + source.substring(classBegin, classEnd));
}
bodyBegin = brace + 1;
}
// body includes openning brace
result = new CompoundWrap(
new RangeWrap(source, new Range(classBegin, bodyBegin)),
defs,
"\n}"
);
}
// Corral the body
@Override
public void visitMethodDef(JCMethodDecl tree) {
int methodBegin = dis.getStartPosition(tree);
int methodEnd = dis.getEndPosition(tree);
//debugWrap("+visitMethodDef: %d-%d = %s\n", methodBegin, methodEnd,
// source.substring(methodBegin, methodEnd));
int bodyBegin = dis.getStartPosition(tree.getBody());
if (bodyBegin < 0) {
bodyBegin = source.indexOf('{', methodBegin);
if (bodyBegin > methodEnd) {
bodyBegin = -1;
}
}
if (bodyBegin > 0) {
//debugWrap("-visitMethodDef BEGIN: %d = '%s'\n", bodyBegin,
// source.substring(methodBegin, bodyBegin));
Range noBodyRange = new Range(methodBegin, bodyBegin);
result = new CompoundWrap(
new RangeWrap(source, noBodyRange),
resolutionExceptionBlock);
} else {
Range range = new Range(methodBegin, methodEnd);
result = new RangeWrap(source, range);
}
}
// Remove initializer, if present
@Override
public void visitVarDef(JCVariableDecl tree) {
int begin = dis.getStartPosition(tree);
int end = dis.getEndPosition(tree);
if (tree.init == null) {
result = new RangeWrap(source, new Range(begin, end));
} else {
int sinit = dis.getStartPosition(tree.init);
int eq = source.lastIndexOf('=', sinit);
if (eq < begin) {
throw new IllegalArgumentException("Equals not found before init: " + source + " @" + sinit);
}
result = new CompoundWrap(new RangeWrap(source, new Range(begin, eq - 1)), ";");
}
}
@Override
public void visitTree(JCTree tree) {
throw new IllegalArgumentException("Unexpected tree: " + tree);
}
void debugWrap(String format, Object... args) {
//state.debug(this, InternalDebugControl.DBG_WRAP, format, args);
}
}