1 /*
2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
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
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.jshell;
27 
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import static java.util.stream.Collectors.joining;
32 import static jdk.jshell.Util.DOIT_METHOD_NAME;
33 
34 /**
35  * Wrapping of source into Java methods, fields, etc.  All but outer layer
36  * wrapping with imports and class.
37  */
38 abstract class Wrap implements GeneralWrap {
39 
methodWrap(String prefix, String source, String suffix)40     private static Wrap methodWrap(String prefix, String source, String suffix) {
41         Wrap wunit = new NoWrap(source);
42         return new DoitMethodWrap(new CompoundWrap(prefix, wunit, suffix));
43     }
44 
methodWrap(String source)45     public static Wrap methodWrap(String source) {
46         return methodWrap("", source, semi(source) + "        return null;\n");
47     }
48 
methodReturnWrap(String source)49     public static Wrap methodReturnWrap(String source) {
50         return methodWrap("return ", source, semi(source));
51     }
52 
methodUnreachableSemiWrap(String source)53     public static Wrap methodUnreachableSemiWrap(String source) {
54         return methodWrap("", source, semi(source));
55     }
56 
methodUnreachableWrap(String source)57     public static Wrap methodUnreachableWrap(String source) {
58         return methodWrap("", source, "");
59     }
60 
indent(int n)61     private static String indent(int n) {
62         return "                              ".substring(0, n * 4);
63     }
64 
nlindent(int n)65     private static String nlindent(int n) {
66         return "\n" + indent(n);
67     }
68 
69     /**Create a stub of a compilable representation of a variable snippet.
70      * The variable is always represented by a field. If the variable
71      * in the snippet has an initializer, the field is initialized by
72      * calling the DOIT_METHOD_NAME method.
73      *
74      * In some cases, the real inferred type of the variable may be non-denotable
75      * (e.g. intersection types). The declared type of the field must always
76      * be denotable (i.e. such that it can be written into the classfile), but
77      * if the real type is potentially non-denotable, the {@code enhanced} parameter
78      * must be true.
79      *
80      * @param source the snippet's masked source code
81      * @param wtype variable's denotable type suitable for field declaration
82      * @param brackets any [] that should be appended to the type
83      * @param rname range in source that denotes the name of the
84      * @param winit Initializer or null
85      * @param enhanced if the real inferred type of the variable is potentially
86      *                 non-denotable, this must be true
87      * @return a Wrap that declares the given variable, potentially with
88      *         an initialization method
89      */
varWrap(String source, Wrap wtype, String brackets, Range rname, Wrap winit, boolean enhanced, Wrap anonDeclareWrap)90     public static Wrap varWrap(String source, Wrap wtype, String brackets,
91                                Range rname, Wrap winit, boolean enhanced,
92                                Wrap anonDeclareWrap) {
93         RangeWrap wname = new RangeWrap(source, rname);
94         List<Object> components = new ArrayList<>();
95         components.add(new VarDeclareWrap(wtype, brackets, wname));
96         Wrap wmeth;
97 
98         if (winit == null) {
99             wmeth = new CompoundWrap(new NoWrap(" "), "   return null;\n");
100         } else {
101             // int x = y
102             if (enhanced) {
103                 // private static <Z> Z do_itAux() {
104                 //     wtype x_ = y;
105                 //     @SuppressWarnings("unchecked")
106                 //     Z x__ = (Z) x_;
107                 //     return x__;
108                 // }
109                 // in do_it method:
110                 //return do_itAux();
111                 Wrap waux = new CompoundWrap(
112                         "    private static <Z> Z ", DOIT_METHOD_NAME + "Aux", "() throws Throwable {\n",
113                         wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
114                         "        @SuppressWarnings(\"unchecked\") Z ", wname, "__ = (Z)", wname, "_;\n",
115                         "        return ", wname, "__;\n",
116                         "}"
117                 );
118                 components.add(waux);
119                 wmeth = new CompoundWrap(
120                         "        return ", wname, " = ", DOIT_METHOD_NAME + "Aux", "();\n"
121                 );
122             } else {
123                 // int x_ = y; return x = x_;
124                 // decl + "_ = " + init ; + "return " + name + "= " + name + "_ ;"
125                 wmeth = new CompoundWrap(
126                         wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
127                         "        return ", wname, " = ", wname, "_;\n"
128                 );
129             }
130         }
131         components.add(new DoitMethodWrap(wmeth));
132         if (anonDeclareWrap != null) {
133             components.add(anonDeclareWrap);
134         }
135         return new CompoundWrap(components.toArray());
136     }
137 
tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap)138     public static Wrap tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap) {
139         RangeWrap winit = new NoWrap(source);
140         // y
141         // return $1 = y;
142         // "return " + $1 + "=" + init ;
143         Wrap wmeth = new CompoundWrap("return " + name + " =\n        ", winit, semi(winit));
144         Wrap wInitMeth = new DoitMethodWrap(wmeth);
145 
146         String varDecl = "    public static\n    " + typename + " " + name + ";\n";
147         return anonDeclareWrap != null ? new CompoundWrap(varDecl, wInitMeth, anonDeclareWrap)
148                                        : new CompoundWrap(varDecl, wInitMeth);
149     }
150 
simpleWrap(String source)151     public static Wrap simpleWrap(String source) {
152         return new NoWrap(source);
153     }
154 
identityWrap(String source)155     public static Wrap identityWrap(String source) {
156         return new NoWrap(source);
157     }
158 
rangeWrap(String source, Range range)159     public static Wrap rangeWrap(String source, Range range) {
160         return new RangeWrap(source, range);
161     }
162 
classMemberWrap(String source)163     public static Wrap classMemberWrap(String source) {
164         Wrap w = new NoWrap(source);
165         return new CompoundWrap("    public static\n    ", w);
166     }
167 
countLines(String s)168     private static int countLines(String s) {
169         return countLines(s, 0, s.length());
170     }
171 
countLines(String s, int from, int toEx)172     private static int countLines(String s, int from, int toEx) {
173         int cnt = 0;
174         int idx = from;
175         while ((idx = s.indexOf('\n', idx)) > 0) {
176             if (idx >= toEx) break;
177             ++cnt;
178             ++idx;
179         }
180         return cnt;
181     }
182 
183     public static final class Range {
184         final int begin;
185         final int end; // exclusive
186 
Range(int begin, int end)187         Range(int begin, int end) {
188             this.begin = begin;
189             this.end = end;
190         }
191 
Range(String s)192         Range(String s) {
193             this.begin = 0;
194             this.end = s.length();
195         }
196 
part(String s)197         String part(String s) {
198             return s.substring(begin, end);
199         }
200 
length()201         int length() {
202             return end - begin;
203         }
204 
isEmpty()205         boolean isEmpty() {
206             return end == begin;
207         }
208 
verify(String s)209         void verify(String s) {
210             if (begin < 0 || end <= begin || end > s.length()) {
211                 throw new InternalError("Bad Range: " + s + "[" + begin + "," + end + "]");
212             }
213         }
214 
215         @Override
toString()216         public String toString() {
217             return "Range[" + begin + "," + end + ")";
218         }
219     }
220 
221     public static class CompoundWrap extends Wrap {
222 
223         final Object[] os;
224         final String wrapped;
225         final int snidxFirst;
226         final int snidxLast;
227         final int snlineFirst;
228         final int snlineLast;
229 
CompoundWrap(Object... os)230         CompoundWrap(Object... os) {
231             this.os = os;
232             int sniFirst = Integer.MAX_VALUE;
233             int sniLast = Integer.MIN_VALUE;
234             int snlnFirst = Integer.MAX_VALUE;
235             int snlnLast = Integer.MIN_VALUE;
236             StringBuilder sb = new StringBuilder();
237             for (Object o : os) {
238                 if (o instanceof String) {
239                     String s = (String) o;
240                     sb.append(s);
241                 } else if (o instanceof Wrap) {
242                     Wrap w = (Wrap) o;
243                     if (w.firstSnippetIndex() < sniFirst) {
244                         sniFirst = w.firstSnippetIndex();
245                     }
246                     if (w.lastSnippetIndex() > sniLast) {
247                         sniLast = w.lastSnippetIndex();
248                     }
249                     if (w.firstSnippetLine() < snlnFirst) {
250                         snlnFirst = w.firstSnippetLine();
251                     }
252                     if (w.lastSnippetLine() > snlnLast) {
253                         snlnLast = w.lastSnippetLine();
254                     }
255                     sb.append(w.wrapped());
256                 } else {
257                     throw new InternalError("Bad object in CommoundWrap: " + o);
258                 }
259             }
260             this.wrapped = sb.toString();
261             this.snidxFirst = sniFirst;
262             this.snidxLast = sniLast;
263             this.snlineFirst = snlnFirst;
264             this.snlineLast = snlnLast;
265         }
266 
267         @Override
wrapped()268         public String wrapped() {
269             return wrapped;
270         }
271 
272         @Override
snippetIndexToWrapIndex(int sni)273         public int snippetIndexToWrapIndex(int sni) {
274             int before = 0;
275             for (Object o : os) {
276                 if (o instanceof String) {
277                     String s = (String) o;
278                     before += s.length();
279                 } else if (o instanceof Wrap) {
280                     Wrap w = (Wrap) o;
281                     if (sni >= w.firstSnippetIndex() && sni < w.lastSnippetIndex()) {
282                         int wwi = w.snippetIndexToWrapIndex(sni);
283                         debugWrap("\nCommoundWrap.snippetIndexToWrapIndex: SnippetIndex(%d) -> WrapIndex(%d + %d = %d)"
284                                         + "\n   === %s",
285                                 sni, wwi, before, wwi + before, wrapped());
286                         return wwi + before;
287                     }
288                     before += w.wrapped().length();
289                 }
290             }
291             return 0;
292         }
293 
wrapIndexToWrap(long wi)294         Wrap wrapIndexToWrap(long wi) {
295             int before = 0;
296             Wrap w = null;
297             for (Object o : os) {
298                 if (o instanceof String) {
299                     String s = (String) o;
300                     before += s.length();
301                 } else if (o instanceof Wrap) {
302                     w = (Wrap) o;
303                     int len = w.wrapped().length();
304                     if ((wi - before) <= len) {
305                         debugWrap("CommoundWrap.wrapIndexToWrap: Defer to wrap %s - wi: %d. before; %d   >>> %s\n",
306                                 w, wi, before, w.wrapped());
307                         return w;
308                     }
309                     before += len;
310                 }
311             }
312             return w;
313         }
314 
315         @Override
wrapIndexToSnippetIndex(int wi)316         public int wrapIndexToSnippetIndex(int wi) {
317             int before = 0;
318             for (Object o : os) {
319                 if (o instanceof String) {
320                     String s = (String) o;
321                     before += s.length();
322                 } else if (o instanceof Wrap) {
323                     Wrap w = (Wrap) o;
324                     int len = w.wrapped().length();
325                     if ((wi - before) <= len) {
326                         int si = w.wrapIndexToSnippetIndex(wi - before);
327                         debugWrap("\nCommoundWrap.wrapIndexToSnippetIndex: WrapIndex(%d) -> SnippetIndex(%d)\n",
328                                 wi, si);
329                         return si;
330                     }
331                     before += len;
332                 }
333             }
334             return lastSnippetIndex();
335         }
336 
337         @Override
firstSnippetIndex()338         public int firstSnippetIndex() {
339             return snidxFirst;
340         }
341 
342         @Override
lastSnippetIndex()343         public int lastSnippetIndex() {
344             return snidxLast;
345         }
346 
347         @Override
snippetLineToWrapLine(int snline)348         public int snippetLineToWrapLine(int snline) {
349             int before = 0;
350             for (Object o : os) {
351                 if (o instanceof String) {
352                     String s = (String) o;
353                     before += countLines(s);
354                 } else if (o instanceof Wrap) {
355                     Wrap w = (Wrap) o;
356                     if (snline >= w.firstSnippetLine() && snline <= w.lastSnippetLine()) {
357                         return w.snippetLineToWrapLine(snline) + before;
358                     }
359                     before += countLines(w.wrapped());
360                 }
361             }
362             return 0;
363         }
364 
wrapLineToWrap(int wline)365         Wrap wrapLineToWrap(int wline) {
366             int before = 0;
367             Wrap w = null;
368             for (Object o : os) {
369                 if (o instanceof String) {
370                     String s = (String) o;
371                     before += countLines(s);
372                 } else if (o instanceof Wrap) {
373                     w = (Wrap) o;
374                     int lns = countLines(w.wrapped());
375                     if ((wline - before) <= lns) {
376                         return w;
377                     }
378                     before += lns;
379                 }
380             }
381             return w;
382         }
383 
384         @Override
wrapLineToSnippetLine(int wline)385         public int wrapLineToSnippetLine(int wline) {
386             int before = 0;
387             for (Object o : os) {
388                 if (o instanceof String) {
389                     String s = (String) o;
390                     before += countLines(s);
391                 } else if (o instanceof Wrap) {
392                     Wrap w = (Wrap) o;
393                     int lns = countLines(w.wrapped());
394                     if ((wline - before) <= lns) {
395                         return w.wrapLineToSnippetLine(wline - before);
396                     }
397                     before += lns;
398                 }
399             }
400             return 0;
401         }
402 
403         @Override
firstSnippetLine()404         public int firstSnippetLine() {
405             return snlineFirst;
406         }
407 
408         @Override
lastSnippetLine()409         public int lastSnippetLine() {
410             return snlineLast;
411         }
412 
413         @Override
toString()414         public String toString() {
415             return "CompoundWrap(" + Arrays.stream(os)
416                     .map(o -> (o instanceof String)
417                             ? "\"" + o + "\""
418                             : o.toString())
419                     .collect(joining(","))
420                     + ")";
421         }
422     }
423 
424     static class RangeWrap extends Wrap {
425 
426         final Range range;
427         final String wrapped;   // The snippet portion of the source
428         final int firstSnline;  // Line count to start of snippet portion
429         final int lastSnline;   // Line count to end of snippet portion
430 
RangeWrap(String snippetSource, Range usedWithinSnippet)431         RangeWrap(String snippetSource, Range usedWithinSnippet) {
432             this.range = usedWithinSnippet;
433             this.wrapped = usedWithinSnippet.part(snippetSource);
434             usedWithinSnippet.verify(snippetSource);
435             this.firstSnline = countLines(snippetSource, 0, range.begin);
436             this.lastSnline = firstSnline + countLines(snippetSource, range.begin, range.end);
437         }
438 
439         @Override
wrapped()440         public String wrapped() {
441             return wrapped;
442         }
443 
444         @Override
snippetIndexToWrapIndex(int sni)445         public int snippetIndexToWrapIndex(int sni) {
446             if (sni < range.begin) {
447                 debugWrap("\nRangeWrap.snippetIndexToWrapIndex: ERR before SnippetIndex(%d) -> WrapIndex(%d + %d = %d)\n",
448                         sni, 0);
449                 return 0;
450             }
451             if (sni > range.end) {
452                 debugWrap("\nRangeWrap.snippetIndexToWrapIndex: ERR after SnippetIndex(%d) -> WrapIndex(%d + %d = %d)\n",
453                         sni, range.length());
454                 return range.length();
455             }
456             int wi = sni - range.begin;
457             debugWrap("\nRangeWrap.snippetIndexToWrapIndex: SnippetIndex(%d) -> WrapIndex(%d + %d = %d)"
458                             + "\n   === %s",
459                     sni, sni, range.begin, sni - range.begin, wrapped());
460             return wi;
461         }
462 
463         @Override
wrapIndexToSnippetIndex(int wi)464         public int wrapIndexToSnippetIndex(int wi) {
465             if (wi < 0) {
466                 debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: ERR before WrapIndex(%d) -> SnippetIndex(%d)\n",
467                         wi, 0);
468                 return 0; // bad index
469             }
470             int max = range.length();
471             if (wi > max) {
472                 debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: ERR after WrapIndex(%d) -> SnippetIndex(%d)\n",
473                         wi, max + range.begin);
474                 return max + range.begin;
475             }
476             int sni = wi + range.begin;
477             debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: WrapIndex(%d) -> SnippetIndex(%d)\n",
478                     wi, sni);
479             return sni;
480         }
481 
482         @Override
firstSnippetIndex()483         public int firstSnippetIndex() {
484             return range.begin;
485         }
486 
487         @Override
lastSnippetIndex()488         public int lastSnippetIndex() {
489             return range.end;
490         }
491 
492         @Override
snippetLineToWrapLine(int snline)493         public int snippetLineToWrapLine(int snline) {
494             if (snline < firstSnline) {
495                 return 0;
496             }
497             if (snline >= lastSnline) {
498                 return lastSnline - firstSnline;
499             }
500             return snline - firstSnline;
501         }
502 
503         @Override
wrapLineToSnippetLine(int wline)504         public int wrapLineToSnippetLine(int wline) {
505             if (wline < 0) {
506                 return 0; // bad index
507             }
508             int max = lastSnline - firstSnline;
509             if (wline > max) {
510                 wline = max;
511             }
512             return wline + firstSnline;
513         }
514 
515         @Override
firstSnippetLine()516         public int firstSnippetLine() {
517             return firstSnline;
518         }
519 
520         @Override
lastSnippetLine()521         public int lastSnippetLine() {
522             return lastSnline;
523         }
524 
525         @Override
toString()526         public String toString() {
527             return "RangeWrap(" + range + ")";
528         }
529     }
530 
531     private static class NoWrap extends RangeWrap {
532 
NoWrap(String unit)533         NoWrap(String unit) {
534             super(unit, new Range(unit));
535         }
536     }
537 
semi(Wrap w)538     private static String semi(Wrap w) {
539         return semi(w.wrapped());
540     }
541 
semi(String s)542     private static String semi(String s) {
543         return ((s.endsWith(";")) ? "\n" : ((s.endsWith(";\n")) ? "" : ";\n"));
544     }
545 
546     private static class DoitMethodWrap extends CompoundWrap {
547 
DoitMethodWrap(Wrap w)548         DoitMethodWrap(Wrap w) {
549             super("    public static Object " + DOIT_METHOD_NAME + "() throws Throwable {\n"
550                     + "        ", w,
551                     "    }\n");
552         }
553     }
554 
555     private static class VarDeclareWrap extends CompoundWrap {
556 
VarDeclareWrap(Wrap wtype, String brackets, Wrap wname)557         VarDeclareWrap(Wrap wtype, String brackets, Wrap wname) {
558             super("    public static ", wtype, brackets + " ", wname, semi(wname));
559         }
560     }
561 
debugWrap(String format, Object... args)562     void debugWrap(String format, Object... args) {
563         //System.err.printf(format, args);
564         //state.debug(this, InternalDebugControl.DBG_WRAP, format, args);
565     }
566 }
567