src/jdk.jshell/share/classes/jdk/jshell/Corraller.java
changeset 55417 6c2d53701e34
parent 47216 71c04702a3d5
equal deleted inserted replaced
55416:09ee0bd26bda 55417:6c2d53701e34
     1 /*
     1 /*
     2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package jdk.jshell;
    26 package jdk.jshell;
    27 
    27 
    28 import java.io.IOException;
       
    29 import java.io.StringWriter;
       
    30 import com.sun.source.tree.ClassTree;
    28 import com.sun.source.tree.ClassTree;
    31 import com.sun.source.tree.MethodTree;
    29 import com.sun.source.tree.MethodTree;
    32 import com.sun.source.tree.Tree;
    30 import com.sun.source.tree.Tree;
    33 import com.sun.tools.javac.code.Flags;
    31 import com.sun.source.tree.Tree.Kind;
    34 import com.sun.tools.javac.tree.JCTree;
    32 import com.sun.tools.javac.tree.JCTree;
    35 import com.sun.tools.javac.tree.JCTree.JCBlock;
    33 import com.sun.tools.javac.tree.JCTree.JCBlock;
    36 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
    34 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
    37 import com.sun.tools.javac.tree.JCTree.JCExpression;
       
    38 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    35 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    39 import com.sun.tools.javac.tree.JCTree.JCNewClass;
       
    40 import com.sun.tools.javac.tree.JCTree.JCStatement;
       
    41 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
    36 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
    42 import com.sun.tools.javac.tree.Pretty;
    37 import com.sun.tools.javac.tree.JCTree.Visitor;
    43 import com.sun.tools.javac.tree.TreeMaker;
       
    44 import com.sun.tools.javac.util.Context;
       
    45 import com.sun.tools.javac.util.List;
    38 import com.sun.tools.javac.util.List;
    46 import com.sun.tools.javac.util.ListBuffer;
    39 import com.sun.tools.javac.util.ListBuffer;
    47 import com.sun.tools.javac.util.Names;
    40 import static com.sun.tools.javac.code.Flags.FINAL;
       
    41 import static com.sun.tools.javac.code.Flags.PUBLIC;
    48 import static com.sun.tools.javac.code.Flags.STATIC;
    42 import static com.sun.tools.javac.code.Flags.STATIC;
    49 import static com.sun.tools.javac.code.Flags.INTERFACE;
    43 import static com.sun.tools.javac.code.Flags.INTERFACE;
    50 import static com.sun.tools.javac.code.Flags.ENUM;
    44 import static com.sun.tools.javac.code.Flags.ENUM;
    51 import static com.sun.tools.javac.code.Flags.PUBLIC;
    45 import jdk.jshell.Wrap.CompoundWrap;
    52 import com.sun.tools.javac.util.Name;
    46 import jdk.jshell.Wrap.Range;
    53 import jdk.jshell.spi.SPIResolutionException;
    47 import jdk.jshell.Wrap.RangeWrap;
    54 
    48 
    55 /**
    49 /**
    56  * Produce a corralled version of the Wrap for a snippet.
    50  * Produce a corralled version of the Wrap for a snippet.
    57  * Incoming tree is mutated.
       
    58  *
       
    59  * @author Robert Field
       
    60  */
    51  */
    61 class Corraller extends Pretty {
    52 class Corraller extends Visitor {
    62 
    53 
    63     private final StringWriter out;
    54     /** Visitor result field: a Wrap
    64     private final int keyIndex;
    55      */
    65     private final TreeMaker make;
    56     protected Wrap result;
    66     private final Names names;
    57 
    67     private JCBlock resolutionExceptionBlock;
    58     private final TreeDissector dis;
    68 
    59     private final String resolutionExceptionBlock;
    69     public Corraller(int keyIndex, Context context) {
    60     private final String source;
    70         this(new StringWriter(), keyIndex, context);
    61 
    71     }
    62     public Corraller(TreeDissector dis, int keyIndex, String source) {
    72 
    63         this.dis = dis;
    73     private Corraller(StringWriter out, int keyIndex, Context context) {
    64         this.resolutionExceptionBlock = "\n      { throw new jdk.jshell.spi.SPIResolutionException(" + keyIndex + "); }";
    74         super(out, false);
    65         this.source = source;
    75         this.out = out;
    66     }
    76         this.keyIndex = keyIndex;
    67 
    77         this.make = TreeMaker.instance(context);
    68     public Wrap corralType(ClassTree tree) {
    78         this.names = Names.instance(context);
    69         return corralToWrap(tree);
    79     }
    70     }
    80 
    71 
    81     public Wrap corralType(ClassTree ct) {
    72     public Wrap corralMethod(MethodTree tree) {
    82         ((JCClassDecl) ct).mods.flags |= Flags.STATIC | Flags.PUBLIC;
    73         return corralToWrap(tree);
    83         return corral(ct);
    74     }
    84     }
    75 
    85 
    76     private Wrap corralToWrap(Tree tree) {
    86     public Wrap corralMethod(MethodTree mt) {
       
    87         ((JCMethodDecl) mt).mods.flags |= Flags.STATIC | Flags.PUBLIC;
       
    88         return corral(mt);
       
    89     }
       
    90 
       
    91     private Wrap corral(Tree tree) {
       
    92         try {
    77         try {
    93             printStat((JCTree) tree);
    78             JCTree jct = (JCTree) tree;
    94         } catch (IOException e) {
    79             Wrap w = new CompoundWrap(
    95             throw new AssertionError(e);
    80                     "    public static\n    ",
    96         }
    81                     corral(jct));
    97         return Wrap.simpleWrap(out.toString());
    82             debugWrap("corralToWrap SUCCESS source: %s -- wrap:\n %s\n", tree, w.wrapped());
    98     }
    83             return w;
    99 
    84         } catch (Exception ex) {
   100     @Override
    85             debugWrap("corralToWrap FAIL: %s - %s\n", tree, ex);
   101     public void visitBlock(JCBlock tree) {
    86             //ex.printStackTrace(System.err);
   102         // Top-level executable blocks (usually method bodies) are corralled
    87             return null;
   103         super.visitBlock((tree.flags & STATIC) != 0
    88         }
   104                 ? tree
    89     }
   105                 : resolutionExceptionBlock());
    90 
   106     }
    91     // Corral a single node.
   107 
    92 //    @SuppressWarnings("unchecked")
       
    93     private <T extends JCTree> Wrap corral(T tree) {
       
    94         if (tree == null) {
       
    95             return null;
       
    96         } else {
       
    97             tree.accept(this);
       
    98             Wrap tmpResult = this.result;
       
    99             this.result = null;
       
   100             return tmpResult;
       
   101         }
       
   102     }
       
   103 
       
   104     private String defaultConstructor(JCClassDecl tree) {
       
   105         return "  public " + tree.name.toString() + "() " +
       
   106                 resolutionExceptionBlock;
       
   107     }
       
   108 
       
   109     /* ***************************************************************************
       
   110      * Visitor methods
       
   111      ****************************************************************************/
       
   112 
       
   113     @Override
       
   114     public void visitClassDef(JCClassDecl tree) {
       
   115         boolean isEnum = (tree.mods.flags & ENUM) != 0;
       
   116         boolean isInterface = (tree.mods.flags & INTERFACE ) != 0;
       
   117         int classBegin = dis.getStartPosition(tree);
       
   118         int classEnd = dis.getEndPosition(tree);
       
   119         //debugWrap("visitClassDef: %d-%d = %s\n", classBegin, classEnd, source.substring(classBegin, classEnd));
       
   120         ListBuffer<Object> wrappedDefs = new ListBuffer<>();
       
   121         int bodyBegin = -1;
       
   122         if (tree.defs != null && !tree.defs.isEmpty()) {
       
   123             if (isEnum) {
       
   124                 // copy the enum constants verbatim
       
   125                 int enumBegin = dis.getStartPosition(tree.defs.head);
       
   126                 JCTree t = null; // null to shut-up compiler, always set because non-empty
       
   127                 List<? extends JCTree> l = tree.defs;
       
   128                 for (; l.nonEmpty(); l = l.tail) {
       
   129                     t = l.head;
       
   130                     if (t.getKind() == Kind.VARIABLE) {
       
   131                         if ((((JCVariableDecl)t).mods.flags & (PUBLIC | STATIC | FINAL)) != (PUBLIC | STATIC | FINAL)) {
       
   132                             // non-enum constant, process normally
       
   133                             break;
       
   134                         }
       
   135                     } else {
       
   136                         // non-variable, process normally
       
   137                         break;
       
   138                     }
       
   139                 }
       
   140                 int constEnd = l.nonEmpty()                  // end of constants
       
   141                         ? dis.getStartPosition(l.head) - 1   // is one before next defs, if there is one
       
   142                         : dis.getEndPosition(t);             // and otherwise end of the last constant
       
   143                 wrappedDefs.append(new RangeWrap(source, new Range(enumBegin, constEnd)));
       
   144                 // handle any other defs
       
   145                 for (; l.nonEmpty(); l = l.tail) {
       
   146                     wrappedDefs.append("\n");
       
   147                     t = l.head;
       
   148                     wrappedDefs.append(corral(t));
       
   149                 }
       
   150             } else {
       
   151                 // non-enum
       
   152                 boolean constructorSeen = false;
       
   153                 for (List<? extends JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
       
   154                     wrappedDefs.append("\n   ");
       
   155                     JCTree t = l.head;
       
   156                     switch (t.getKind()) {
       
   157                         case METHOD:
       
   158                             constructorSeen = constructorSeen || ((MethodTree)t).getName() == tree.name.table.names.init;
       
   159                             break;
       
   160                         case BLOCK:
       
   161                             // throw exception in instance initializer too -- inline because String not Wrap
       
   162                             wrappedDefs.append((((JCBlock)t).flags & STATIC) != 0
       
   163                                     ? new RangeWrap(source, dis.treeToRange(t))
       
   164                                     : resolutionExceptionBlock);
       
   165                             continue; // already appended, skip append below
       
   166                     }
       
   167                     wrappedDefs.append(corral(t));
       
   168                 }
       
   169                 if (!constructorSeen && !isInterface && !isEnum) {
       
   170                     // Generate a default constructor, since
       
   171                     // this is a regular class and there are no constructors
       
   172                     if (wrappedDefs.length() > 0) {
       
   173                         wrappedDefs.append("\n ");
       
   174                     }
       
   175                     wrappedDefs.append(defaultConstructor(tree));
       
   176                 }
       
   177             }
       
   178             bodyBegin = dis.getStartPosition(tree.defs.head);
       
   179         }
       
   180         Object defs = wrappedDefs.length() == 1
       
   181             ? wrappedDefs.first()
       
   182             : new CompoundWrap(wrappedDefs.toArray());
       
   183         if (bodyBegin < 0) {
       
   184             int brace = source.indexOf('{', classBegin);
       
   185             if (brace < 0 || brace >= classEnd) {
       
   186                 throw new IllegalArgumentException("No brace found: " + source.substring(classBegin, classEnd));
       
   187             }
       
   188             bodyBegin = brace + 1;
       
   189         }
       
   190         // body includes openning brace
       
   191         result = new CompoundWrap(
       
   192                 new RangeWrap(source, new Range(classBegin, bodyBegin)),
       
   193                 defs,
       
   194                 "\n}"
       
   195         );
       
   196     }
       
   197 
       
   198     // Corral the body
       
   199     @Override
       
   200     public void visitMethodDef(JCMethodDecl tree) {
       
   201         int methodBegin = dis.getStartPosition(tree);
       
   202         int methodEnd = dis.getEndPosition(tree);
       
   203         //debugWrap("+visitMethodDef: %d-%d = %s\n", methodBegin, methodEnd,
       
   204         //        source.substring(methodBegin, methodEnd));
       
   205         int bodyBegin = dis.getStartPosition(tree.getBody());
       
   206         if (bodyBegin < 0) {
       
   207             bodyBegin = source.indexOf('{', methodBegin);
       
   208             if (bodyBegin > methodEnd) {
       
   209                 bodyBegin = -1;
       
   210             }
       
   211         }
       
   212         if (bodyBegin > 0) {
       
   213             //debugWrap("-visitMethodDef BEGIN: %d = '%s'\n", bodyBegin,
       
   214             //        source.substring(methodBegin, bodyBegin));
       
   215             Range noBodyRange = new Range(methodBegin, bodyBegin);
       
   216             result = new CompoundWrap(
       
   217                     new RangeWrap(source, noBodyRange),
       
   218                     resolutionExceptionBlock);
       
   219         } else {
       
   220             Range range = new Range(methodBegin, methodEnd);
       
   221             result = new RangeWrap(source, range);
       
   222         }
       
   223     }
       
   224 
       
   225     // Remove initializer, if present
   108     @Override
   226     @Override
   109     public void visitVarDef(JCVariableDecl tree) {
   227     public void visitVarDef(JCVariableDecl tree) {
   110         // No field inits in corralled classes
   228         int begin = dis.getStartPosition(tree);
   111         tree.init = null;
   229         int end = dis.getEndPosition(tree);
   112         super.visitVarDef(tree);
   230         if (tree.init == null) {
   113     }
   231             result = new RangeWrap(source, new Range(begin, end));
   114 
   232         } else {
   115     @Override
   233             int sinit = dis.getStartPosition(tree.init);
   116     public void visitClassDef(JCClassDecl tree) {
   234             int eq = source.lastIndexOf('=', sinit);
   117         if ((tree.mods.flags & (INTERFACE | ENUM)) == 0 &&
   235             if (eq < begin) {
   118                 !tree.getMembers().stream()
   236                 throw new IllegalArgumentException("Equals not found before init: " + source + " @" + sinit);
   119                 .anyMatch(t -> t.getKind() == Tree.Kind.METHOD &&
   237             }
   120                 ((MethodTree) t).getName() == tree.name.table.names.init)) {
   238             result = new CompoundWrap(new RangeWrap(source, new Range(begin, eq - 1)), ";");
   121             // Generate a default constructor, since
   239         }
   122             // this is a regular class and there are no constructors
   240     }
   123             ListBuffer<JCTree> ndefs = new ListBuffer<>();
   241 
   124             ndefs.addAll(tree.defs);
   242     @Override
   125             ndefs.add(make.MethodDef(make.Modifiers(PUBLIC),
   243     public void visitTree(JCTree tree) {
   126                     tree.name.table.names.init,
   244         throw new IllegalArgumentException("Unexpected tree: " + tree);
   127                     null, List.nil(), List.nil(), List.nil(),
   245     }
   128                     resolutionExceptionBlock(), null));
   246 
   129             tree.defs = ndefs.toList();
   247     void debugWrap(String format, Object... args) {
   130         }
   248         //state.debug(this, InternalDebugControl.DBG_WRAP, format, args);
   131         super.visitClassDef(tree);
       
   132     }
       
   133 
       
   134     // Build a compiler tree for an exception throwing block, e.g.:
       
   135     // {
       
   136     //     throw new jdk.jshell.spi.SPIResolutionException(9);
       
   137     // }
       
   138     private JCBlock resolutionExceptionBlock() {
       
   139         if (resolutionExceptionBlock == null) {
       
   140             JCExpression expClass = null;
       
   141             // Split the exception class name at dots
       
   142             for (String id : SPIResolutionException.class.getName().split("\\.")) {
       
   143                 Name nm = names.fromString(id);
       
   144                 if (expClass == null) {
       
   145                     expClass = make.Ident(nm);
       
   146                 } else {
       
   147                     expClass = make.Select(expClass, nm);
       
   148                 }
       
   149             }
       
   150             JCNewClass exp = make.NewClass(null,
       
   151                     null, expClass, List.of(make.Literal(keyIndex)), null);
       
   152             resolutionExceptionBlock = make.Block(0L, List.of(make.Throw(exp)));
       
   153         }
       
   154         return resolutionExceptionBlock;
       
   155     }
   249     }
   156 }
   250 }