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 } |