8152925: JShell: enable corralling of any type declaration, including enum
authorrfield
Fri, 01 Apr 2016 09:20:33 -0700
changeset 36780 6bf2bef08a91
parent 36779 9a030c4d2591
child 36781 4fb5cb5c3fd1
8152925: JShell: enable corralling of any type declaration, including enum Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/jshell/Corraller.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java
langtools/test/jdk/jshell/ReplaceTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Corraller.java	Tue Mar 29 10:24:30 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Corraller.java	Fri Apr 01 09:20:33 2016 -0700
@@ -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);
+    }
+
+    public Wrap corralType(ClassTree ct) {
+        ((JCClassDecl) ct).mods.flags |= Flags.STATIC | Flags.PUBLIC;
+        return corral(ct);
     }
 
-    Wrap corralMethod(MethodTree mt) {
-        return corralMethod(mt, null, 1);
+    public Wrap corralMethod(MethodTree mt) {
+        ((JCMethodDecl) mt).mods.flags |= Flags.STATIC | Flags.PUBLIC;
+        return corral(mt);
     }
 
-    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;
+    private Wrap corral(Tree tree) {
+        try {
+            printStat((JCTree) tree);
+        } catch (IOException e) {
+            throw new AssertionError(e);
         }
-        Range paramRange = dis.treeListToRange(mt.getParameters());
-        Range throwsRange = dis.treeListToRange(mt.getThrows());
-        return Wrap.corralledMethod(compileSource,
-                modRange, tpRange, typeRange, name, paramRange, throwsRange, index, indent);
+        return Wrap.simpleWrap(out.toString());
     }
 
-    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("[]");
-        }
-        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);
+    @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);
     }
 
-    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 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;
     }
 }
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Tue Mar 29 10:24:30 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Fri Apr 01 09:20:33 2016 -0700
@@ -50,6 +50,7 @@
 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 @@
     }
 
     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 @@
 
         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 @@
     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);
     }
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Tue Mar 29 10:24:30 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Fri Apr 01 09:20:33 2016 -0700
@@ -258,7 +258,7 @@
         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));
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Tue Mar 29 10:24:30 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Fri Apr 01 09:20:33 2016 -0700
@@ -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 @@
         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 @@
         return new CompoundWrap(varDecl, wInitMeth);
     }
 
-    public static Wrap importWrap(String source) {
+    public static Wrap simpleWrap(String source) {
         return new NoWrap(source);
     }
 
--- a/langtools/test/jdk/jshell/ReplaceTest.java	Tue Mar 29 10:24:30 2016 -0700
+++ b/langtools/test/jdk/jshell/ReplaceTest.java	Fri Apr 01 09:20:33 2016 -0700
@@ -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 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();
     }