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 static com.sun.tools.javac.code.Flags.RECORD;
import static com.sun.tools.javac.code.Flags.SYNTHETIC;
import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.tree.TreeInfo;
import jdk.jshell.Wrap.CompoundWrap;
import jdk.jshell.Wrap.Range;
import jdk.jshell.Wrap.RangeWrap;
import java.util.Set;
class Corraller extends Visitor {
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);
return null;
}
}
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;
}
@Override
public void visitClassDef(JCClassDecl tree) {
boolean isEnum = (tree.mods.flags & ENUM) != 0;
boolean isInterface = (tree.mods.flags & INTERFACE ) != 0;
boolean isRecord = (tree.mods.flags & RECORD ) != 0;
int classBegin = dis.getStartPosition(tree);
int classEnd = dis.getEndPosition(tree);
ListBuffer<Object> wrappedDefs = new ListBuffer<>();
int bodyBegin = -1;
if (tree.defs != null && !tree.defs.isEmpty()) {
if (isEnum) {
int enumBegin = dis.getStartPosition(tree.defs.head);
JCTree t = null;
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)) {
break;
}
} else {
break;
}
}
int constEnd = l.nonEmpty()
? dis.getStartPosition(l.head) - 1
: dis.getEndPosition(t);
wrappedDefs.append(new RangeWrap(source, new Range(enumBegin, constEnd)));
for (; l.nonEmpty(); l = l.tail) {
wrappedDefs.append("\n");
t = l.head;
wrappedDefs.append(corral(t));
}
} else {
boolean constructorSeen = false;
for (List<? extends JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
JCTree t = l.head;
if (isRecord && t.hasTag(Tag.VARDEF) && (TreeInfo.flags(t) & RECORD) != 0) {
continue;
}
wrappedDefs.append("\n ");
switch (t.getKind()) {
case METHOD:
constructorSeen = constructorSeen || ((MethodTree)t).getName() == tree.name.table.names.init;
break;
case BLOCK:
wrappedDefs.append((((JCBlock)t).flags & STATIC) != 0
? new RangeWrap(source, dis.treeToRange(t))
: resolutionExceptionBlock);
continue;
}
wrappedDefs.append(corral(t));
}
if (!constructorSeen && isRecord) {
if (wrappedDefs.length() > 0) {
wrappedDefs.append("\n ");
}
wrappedDefs.append(" public " + tree.name.toString() + " " + resolutionExceptionBlock);
} else if (!constructorSeen && !isInterface && !isEnum) {
if (wrappedDefs.length() > 0) {
wrappedDefs.append("\n ");
}
wrappedDefs.append(defaultConstructor(tree));
}
}
if (!isRecord) {
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;
}
result = new CompoundWrap(
new RangeWrap(source, new Range(classBegin, bodyBegin)),
defs,
"\n}"
);
}
@Override
public void visitMethodDef(JCMethodDecl tree) {
int methodBegin = dis.getStartPosition(tree);
int methodEnd = dis.getEndPosition(tree);
int bodyBegin = dis.getStartPosition(tree.getBody());
if (bodyBegin < 0) {
bodyBegin = source.indexOf('{', methodBegin);
if (bodyBegin > methodEnd) {
bodyBegin = -1;
}
}
String adjustedSource;
if (bodyBegin < 0) {
adjustedSource = new MaskCommentsAndModifiers(source, Set.of("abstract")).cleared();
bodyBegin = adjustedSource.charAt(methodEnd - 1) == ';'
? methodEnd - 1
: methodEnd;
} else {
adjustedSource = source;
}
debugWrap("-visitMethodDef BEGIN: %d = '%s'\n", bodyBegin,
adjustedSource.substring(methodBegin, bodyBegin));
Range noBodyRange = new Range(methodBegin, bodyBegin);
result = new CompoundWrap(
new RangeWrap(adjustedSource, noBodyRange),
resolutionExceptionBlock);
}
@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) {
}
}