8152925: JShell: enable corralling of any type declaration, including enum
Reviewed-by: jlahoda
This commit is contained in:
parent
baf7e598c8
commit
3818cea28d
@ -25,124 +25,125 @@
|
||||
|
||||
package jdk.jshell;
|
||||
|
||||
import java.util.List;
|
||||
import com.sun.source.tree.ArrayTypeTree;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import jdk.jshell.Wrap.Range;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
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.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCNewClass;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.tree.Pretty;
|
||||
import com.sun.tools.javac.tree.TreeMaker;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
import com.sun.tools.javac.util.Names;
|
||||
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.PUBLIC;
|
||||
|
||||
/**
|
||||
* Produce a corralled version of the Wrap for a snippet.
|
||||
* Incoming tree is mutated.
|
||||
*
|
||||
* @author Robert Field
|
||||
*/
|
||||
class Corraller {
|
||||
class Corraller extends Pretty {
|
||||
|
||||
private final int index;
|
||||
private final String compileSource;
|
||||
private final TreeDissector dis;
|
||||
private final StringWriter out;
|
||||
private final int keyIndex;
|
||||
private final TreeMaker make;
|
||||
private final Names names;
|
||||
private JCBlock resolutionExceptionBlock;
|
||||
|
||||
Corraller(int index, String compileSource, TreeDissector dis) {
|
||||
this.index = index;
|
||||
this.compileSource = compileSource;
|
||||
this.dis = dis;
|
||||
public Corraller(int keyIndex, Context context) {
|
||||
this(new StringWriter(), keyIndex, context);
|
||||
}
|
||||
|
||||
Wrap corralTree(Tree tree, String enclosingType, int indent) {
|
||||
switch (tree.getKind()) {
|
||||
case VARIABLE:
|
||||
return corralVariable((VariableTree) tree, indent);
|
||||
case CLASS:
|
||||
case ENUM:
|
||||
case ANNOTATION_TYPE:
|
||||
case INTERFACE:
|
||||
return corralType((ClassTree) tree, indent);
|
||||
case METHOD:
|
||||
return corralMethod((MethodTree) tree, enclosingType, indent);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
private Corraller(StringWriter out, int keyIndex, Context context) {
|
||||
super(out, false);
|
||||
this.out = out;
|
||||
this.keyIndex = keyIndex;
|
||||
this.make = TreeMaker.instance(context);
|
||||
this.names = Names.instance(context);
|
||||
}
|
||||
|
||||
Wrap corralMethod(MethodTree mt) {
|
||||
return corralMethod(mt, null, 1);
|
||||
public Wrap corralType(ClassTree ct) {
|
||||
((JCClassDecl) ct).mods.flags |= Flags.STATIC | Flags.PUBLIC;
|
||||
return corral(ct);
|
||||
}
|
||||
|
||||
Wrap corralMethod(MethodTree mt, String enclosingType, int indent) {
|
||||
Range modRange = dis.treeToRange(mt.getModifiers());
|
||||
Range tpRange = dis.treeListToRange(mt.getTypeParameters());
|
||||
Range typeRange = dis.treeToRange(mt.getReturnType());
|
||||
String name = mt.getName().toString();
|
||||
if ("<init>".equals(name)) {
|
||||
name = enclosingType;
|
||||
}
|
||||
Range paramRange = dis.treeListToRange(mt.getParameters());
|
||||
Range throwsRange = dis.treeListToRange(mt.getThrows());
|
||||
return Wrap.corralledMethod(compileSource,
|
||||
modRange, tpRange, typeRange, name, paramRange, throwsRange, index, indent);
|
||||
public Wrap corralMethod(MethodTree mt) {
|
||||
((JCMethodDecl) mt).mods.flags |= Flags.STATIC | Flags.PUBLIC;
|
||||
return corral(mt);
|
||||
}
|
||||
|
||||
Wrap corralVariable(VariableTree vt, int indent) {
|
||||
String name = vt.getName().toString();
|
||||
Range modRange = dis.treeToRange(vt.getModifiers());
|
||||
Tree baseType = vt.getType();
|
||||
StringBuilder sbBrackets = new StringBuilder();
|
||||
while (baseType instanceof ArrayTypeTree) {
|
||||
//TODO handle annotations too
|
||||
baseType = ((ArrayTypeTree) baseType).getType();
|
||||
sbBrackets.append("[]");
|
||||
private Wrap corral(Tree tree) {
|
||||
try {
|
||||
printStat((JCTree) tree);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
Range rtype = dis.treeToRange(baseType);
|
||||
Range runit = dis.treeToRange(vt);
|
||||
runit = new Range(runit.begin, runit.end - 1);
|
||||
ExpressionTree it = vt.getInitializer();
|
||||
int nameMax;
|
||||
if (it != null) {
|
||||
Range rinit = dis.treeToRange(it);
|
||||
nameMax = rinit.begin - 1;
|
||||
} else {
|
||||
nameMax = runit.end - 1;
|
||||
}
|
||||
int nameStart = compileSource.lastIndexOf(name, nameMax);
|
||||
if (nameStart < 0) {
|
||||
throw new AssertionError("Name '" + name + "' not found");
|
||||
}
|
||||
int nameEnd = nameStart + name.length();
|
||||
Range rname = new Range(nameStart, nameEnd);
|
||||
return Wrap.corralledVar(compileSource, modRange, rtype, sbBrackets.toString(), rname, indent);
|
||||
return Wrap.simpleWrap(out.toString());
|
||||
}
|
||||
|
||||
Wrap corralType(ClassTree ct, int indent) {
|
||||
boolean isClass;
|
||||
switch (ct.getKind()) {
|
||||
case CLASS:
|
||||
isClass = true;
|
||||
break;
|
||||
case INTERFACE:
|
||||
isClass = false;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
@Override
|
||||
public void visitBlock(JCBlock tree) {
|
||||
// Top-level executable blocks (usually method bodies) are corralled
|
||||
super.visitBlock((tree.flags & STATIC) != 0
|
||||
? tree
|
||||
: resolutionExceptionBlock());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarDef(JCVariableDecl tree) {
|
||||
// No field inits in corralled classes
|
||||
tree.init = null;
|
||||
super.visitVarDef(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassDef(JCClassDecl tree) {
|
||||
if ((tree.mods.flags & (INTERFACE | ENUM)) == 0 &&
|
||||
!tree.getMembers().stream()
|
||||
.anyMatch(t -> t.getKind() == Tree.Kind.METHOD &&
|
||||
((MethodTree) t).getName() == tree.name.table.names.init)) {
|
||||
// Generate a default constructor, since
|
||||
// this is a regular class and there are no constructors
|
||||
ListBuffer<JCTree> ndefs = new ListBuffer<>();
|
||||
ndefs.addAll(tree.defs);
|
||||
ndefs.add(make.MethodDef(make.Modifiers(PUBLIC),
|
||||
tree.name.table.names.init,
|
||||
null, List.nil(), List.nil(), List.nil(),
|
||||
resolutionExceptionBlock(), null));
|
||||
tree.defs = ndefs.toList();
|
||||
}
|
||||
Range modRange = dis.treeToRange(ct.getModifiers());
|
||||
String name = ct.getSimpleName().toString();
|
||||
Range tpRange = dis.treeListToRange(ct.getTypeParameters());
|
||||
Range extendsRange = dis.treeToRange(ct.getExtendsClause());
|
||||
List<Range> implementsRanges = ct.getImplementsClause().stream()
|
||||
.map(ic -> dis.treeToRange(ic))
|
||||
.collect(toList());
|
||||
List<Wrap> members = ct.getMembers().stream()
|
||||
.map(t -> corralTree(t, name, indent + 1))
|
||||
.filter(w -> w != null)
|
||||
.collect(toList());
|
||||
boolean hasConstructor = ct.getMembers().stream()
|
||||
.anyMatch(t -> t.getKind() == Tree.Kind.METHOD && ((MethodTree) t).getName().toString().equals("<init>"));
|
||||
Wrap wrap = Wrap.corralledType(compileSource, modRange, ct.getKind(), name, tpRange,
|
||||
extendsRange, implementsRanges, members, isClass && !hasConstructor, index, indent);
|
||||
return wrap;
|
||||
super.visitClassDef(tree);
|
||||
}
|
||||
|
||||
private JCBlock resolutionExceptionBlock() {
|
||||
if (resolutionExceptionBlock == null) {
|
||||
JCExpression expClass
|
||||
= make.Select(make.Select(make.Select(make.Select(
|
||||
make.Ident(names.fromString("jdk")),
|
||||
names.fromString("internal")),
|
||||
names.fromString("jshell")),
|
||||
names.fromString("remote")),
|
||||
names.fromString("RemoteResolutionException")
|
||||
);
|
||||
JCNewClass exp = make.NewClass(null,
|
||||
null, expClass, List.of(make.Literal(keyIndex)), null);
|
||||
resolutionExceptionBlock = make.Block(0L, List.<JCStatement>of(
|
||||
make.Throw(exp)));
|
||||
}
|
||||
return resolutionExceptionBlock;
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import jdk.jshell.ClassTracker.ClassInfo;
|
||||
import jdk.jshell.Key.ErroneousKey;
|
||||
import jdk.jshell.Key.MethodKey;
|
||||
@ -133,7 +134,7 @@ class Eval {
|
||||
}
|
||||
|
||||
private List<SnippetEvent> processImport(String userSource, String compileSource) {
|
||||
Wrap guts = Wrap.importWrap(compileSource);
|
||||
Wrap guts = Wrap.simpleWrap(compileSource);
|
||||
Matcher mat = IMPORT_PATTERN.matcher(compileSource);
|
||||
String fullname;
|
||||
String name;
|
||||
@ -300,13 +301,15 @@ class Eval {
|
||||
|
||||
ClassTree klassTree = (ClassTree) unitTree;
|
||||
String name = klassTree.getSimpleName().toString();
|
||||
Wrap guts = Wrap.classMemberWrap(compileSource);
|
||||
DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
|
||||
TypeDeclKey key = state.keyMap.keyForClass(name);
|
||||
Wrap corralled = new Corraller(key.index(), compileSource, dis).corralType(klassTree, 1);
|
||||
Snippet snip = new TypeDeclSnippet(state.keyMap.keyForClass(name), userSource, guts,
|
||||
// Corralling mutates. Must be last use of pt, unitTree, klassTree
|
||||
Wrap corralled = new Corraller(key.index(), pt.getContext()).corralType(klassTree);
|
||||
|
||||
Wrap guts = Wrap.classMemberWrap(compileSource);
|
||||
Snippet snip = new TypeDeclSnippet(key, userSource, guts,
|
||||
name, snippetKind,
|
||||
corralled, tds.declareReferences(), tds.bodyReferences());
|
||||
DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
|
||||
return declare(snip, modDiag);
|
||||
}
|
||||
|
||||
@ -354,31 +357,30 @@ class Eval {
|
||||
private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
|
||||
TreeDependencyScanner tds = new TreeDependencyScanner();
|
||||
tds.scan(unitTree);
|
||||
TreeDissector dis = TreeDissector.createByFirstClass(pt);
|
||||
|
||||
MethodTree mt = (MethodTree) unitTree;
|
||||
TreeDissector dis = TreeDissector.createByFirstClass(pt);
|
||||
DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
|
||||
if (modDiag.hasErrors()) {
|
||||
return compileFailResult(modDiag, userSource);
|
||||
}
|
||||
String unitName = mt.getName().toString();
|
||||
Wrap guts = Wrap.classMemberWrap(compileSource);
|
||||
|
||||
Range typeRange = dis.treeToRange(mt.getReturnType());
|
||||
String name = mt.getName().toString();
|
||||
|
||||
String parameterTypes
|
||||
= mt.getParameters()
|
||||
.stream()
|
||||
.map(param -> dis.treeToRange(param.getType()).part(compileSource))
|
||||
.collect(Collectors.joining(","));
|
||||
Tree returnType = mt.getReturnType();
|
||||
DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
|
||||
MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
|
||||
// Corralling mutates. Must be last use of pt, unitTree, mt
|
||||
Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt);
|
||||
|
||||
if (modDiag.hasErrors()) {
|
||||
return compileFailResult(modDiag, userSource);
|
||||
}
|
||||
Wrap guts = Wrap.classMemberWrap(compileSource);
|
||||
Range typeRange = dis.treeToRange(returnType);
|
||||
String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
|
||||
|
||||
MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
|
||||
// rewrap with correct Key index
|
||||
Wrap corralled = new Corraller(key.index(), compileSource, dis).corralMethod(mt);
|
||||
Snippet snip = new MethodSnippet(key, userSource, guts,
|
||||
unitName, signature,
|
||||
name, signature,
|
||||
corralled, tds.declareReferences(), tds.bodyReferences());
|
||||
return declare(snip, modDiag);
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||
OuterWrap codeWrap;
|
||||
switch (guessKind(code)) {
|
||||
case IMPORT:
|
||||
codeWrap = OuterWrap.wrapImport(null, Wrap.importWrap(code + "any.any"));
|
||||
codeWrap = OuterWrap.wrapImport(null, Wrap.simpleWrap(code + "any.any"));
|
||||
break;
|
||||
case METHOD:
|
||||
codeWrap = wrapInClass(Wrap.classMemberWrap(code));
|
||||
|
@ -25,9 +25,6 @@
|
||||
|
||||
package jdk.jshell;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.sun.source.tree.Tree;
|
||||
import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
|
||||
|
||||
/**
|
||||
@ -67,97 +64,6 @@ abstract class Wrap implements GeneralWrap {
|
||||
return "\n" + indent(n);
|
||||
}
|
||||
|
||||
public static Wrap corralledMethod(String source, Range modRange, Range tpRange,
|
||||
Range typeRange, String name, Range paramRange, Range throwsRange, int id, int indent) {
|
||||
List<Object> l = new ArrayList<>();
|
||||
l.add(indent(indent) + ((indent == 1) ? "public static" + nlindent(indent) : ""));
|
||||
if (!modRange.isEmpty()) {
|
||||
l.add(new RangeWrap(source, modRange));
|
||||
l.add(" ");
|
||||
}
|
||||
if (tpRange != null) {
|
||||
l.add("<");
|
||||
l.add(new RangeWrap(source, tpRange));
|
||||
l.add("> ");
|
||||
}
|
||||
if (!typeRange.isEmpty()) {
|
||||
l.add(new RangeWrap(source, typeRange));
|
||||
l.add(" ");
|
||||
}
|
||||
l.add(name + "(");
|
||||
if (paramRange != null && !paramRange.isEmpty()) {
|
||||
l.add(nlindent(indent + 1));
|
||||
l.add(new RangeWrap(source, paramRange));
|
||||
}
|
||||
l.add(")");
|
||||
if (throwsRange != null) {
|
||||
l.add(" throws ");
|
||||
l.add(new RangeWrap(source, throwsRange));
|
||||
}
|
||||
l.add(" {"
|
||||
+ nlindent(indent+1)
|
||||
+ "throw new jdk.internal.jshell.remote.RemoteResolutionException(" + id + ");"
|
||||
+ nlindent(indent)
|
||||
+ "}\n");
|
||||
return new CompoundWrap(l.toArray());
|
||||
}
|
||||
|
||||
public static Wrap corralledType(String source, Range modRange, Tree.Kind kind, String name, Range tpRange,
|
||||
Range extendsRange, List<Range> implementsRanges, List<Wrap> members,
|
||||
boolean defaultConstructor, int id, int indent) {
|
||||
boolean isInterface = kind == Tree.Kind.INTERFACE;
|
||||
List<Object> l = new ArrayList<>();
|
||||
l.add(indent(indent) + ((indent == 1) ? "public static" + nlindent(indent) : ""));
|
||||
if (!modRange.isEmpty()) {
|
||||
l.add(new RangeWrap(source, modRange));
|
||||
l.add(" ");
|
||||
}
|
||||
l.add((isInterface ? "interface " : "class ") + name);
|
||||
if (tpRange != null) {
|
||||
l.add("<");
|
||||
l.add(new RangeWrap(source, tpRange));
|
||||
l.add("> ");
|
||||
}
|
||||
if (extendsRange != null && !extendsRange.isEmpty()) {
|
||||
l.add(" extends ");
|
||||
l.add(new RangeWrap(source, extendsRange));
|
||||
}
|
||||
for (int i = 0; i < implementsRanges.size(); ++i) {
|
||||
Range ir = implementsRanges.get(i);
|
||||
l.add(i == 0 ? " implements " : ", ");
|
||||
l.add(new RangeWrap(source, ir));
|
||||
}
|
||||
if (defaultConstructor) {
|
||||
l.add(" {"
|
||||
+ nlindent(indent+1)
|
||||
+ ((indent == 1)? "public " : "") + name + "() {"
|
||||
+ nlindent(indent+2)
|
||||
+ "throw new jdk.internal.jshell.remote.RemoteResolutionException(" + id + ");"
|
||||
+ nlindent(indent+1)
|
||||
+ "}\n");
|
||||
} else {
|
||||
l.add(" {\n");
|
||||
}
|
||||
l.addAll(members);
|
||||
l.add(indent(indent) + "}\n");
|
||||
return new CompoundWrap(l.toArray());
|
||||
}
|
||||
|
||||
public static Wrap corralledVar(String source, Range modRange, Range typeRange, String brackets, Range nameRange, int indent) {
|
||||
RangeWrap wname = new RangeWrap(source, nameRange);
|
||||
List<Object> l = new ArrayList<>();
|
||||
l.add(indent(indent) + ((indent == 1) ? "public static" + nlindent(indent) : ""));
|
||||
if (!modRange.isEmpty()) {
|
||||
l.add(new RangeWrap(source, modRange));
|
||||
l.add(" ");
|
||||
}
|
||||
l.add(new RangeWrap(source, typeRange));
|
||||
l.add(" ");
|
||||
l.add(wname);
|
||||
l.add(semi(wname));
|
||||
return new CompoundWrap(l.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param in
|
||||
@ -200,7 +106,7 @@ abstract class Wrap implements GeneralWrap {
|
||||
return new CompoundWrap(varDecl, wInitMeth);
|
||||
}
|
||||
|
||||
public static Wrap importWrap(String source) {
|
||||
public static Wrap simpleWrap(String source) {
|
||||
return new NoWrap(source);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test 8080069
|
||||
* @test 8080069 8152925
|
||||
* @summary Test of Snippet redefinition and replacement.
|
||||
* @build KullaTesting TestingInputStream
|
||||
* @run testng ReplaceTest
|
||||
@ -374,18 +374,18 @@ public class ReplaceTest extends KullaTesting {
|
||||
}
|
||||
|
||||
public void testForwardVarToEnum() {
|
||||
DeclarationSnippet a = classKey(assertEval("enum E { Q, W, E; float ff() { return fff; } }", added(RECOVERABLE_NOT_DEFINED)));
|
||||
assertUnresolvedDependencies1(a, RECOVERABLE_NOT_DEFINED, "variable fff");
|
||||
DeclarationSnippet a = classKey(assertEval("enum E { Q, W, E; float ff() { return fff; } }", added(RECOVERABLE_DEFINED)));
|
||||
assertUnresolvedDependencies1(a, RECOVERABLE_DEFINED, "variable fff");
|
||||
Snippet g = varKey(assertEval("float fff = 4.5f;", "4.5",
|
||||
added(VALID),
|
||||
ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null)));
|
||||
ste(a, RECOVERABLE_DEFINED, VALID, false, null)));
|
||||
assertEval("E.Q.ff();", "4.5");
|
||||
assertEval("double fff = 3.3;", "3.3", null,
|
||||
DiagCheck.DIAG_OK,
|
||||
DiagCheck.DIAG_ERROR,
|
||||
ste(MAIN_SNIPPET, VALID, VALID, true, null),
|
||||
ste(g, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
|
||||
ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
|
||||
ste(a, VALID, RECOVERABLE_DEFINED, false, MAIN_SNIPPET));
|
||||
assertUnresolvedDependencies(a, 0);
|
||||
assertActiveKeys();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user