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 wname a wrap of the source that denotes the name of the variable
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, Wrap wname, Wrap winit, boolean enhanced, Wrap anonDeclareWrap)90     public static Wrap varWrap(String source, Wrap wtype, String brackets,
91                                Wrap wname, Wrap winit, boolean enhanced,
92                                Wrap anonDeclareWrap) {
93         List<Object> components = new ArrayList<>();
94         components.add(new VarDeclareWrap(wtype, brackets, wname));
95         Wrap wmeth;
96 
97         if (winit == null) {
98             wmeth = new CompoundWrap(new NoWrap(" "), "   return null;\n");
99         } else {
100             // int x = y
101             if (enhanced) {
102                 // private static <Z> Z do_itAux() {
103                 //     wtype x_ = y;
104                 //     @SuppressWarnings("unchecked")
105                 //     Z x__ = (Z) x_;
106                 //     return x__;
107                 // }
108                 // in do_it method:
109                 //return do_itAux();
110                 //find an unused name:
111                 String scratchName = "$";
112                 while (winit.wrapped().contains(scratchName)) {
113                     scratchName += "$";
114                 }
115                 Wrap waux = new CompoundWrap(
116                         "    private static <" + scratchName + "> " + scratchName +" ", DOIT_METHOD_NAME + "Aux", "() throws Throwable {\n",
117                         wtype, brackets + " ", scratchName, "_ =\n        ", winit, semi(winit),
118                         "        @SuppressWarnings(\"unchecked\") ", scratchName, " ", scratchName, "__ = (", scratchName, ")", scratchName, "_;\n",
119                         "        return ", scratchName, "__;\n",
120                         "}"
121                 );
122                 components.add(waux);
123                 wmeth = new CompoundWrap(
124                         "        return ", wname, " = ", DOIT_METHOD_NAME + "Aux", "();\n"
125                 );
126             } else {
127                 // int x_ = y; return x = x_;
128                 // decl + "_ = " + init ; + "return " + name + "= " + name + "_ ;"
129                 wmeth = new CompoundWrap(
130                         wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
131                         "        return ", wname, " = ", wname, "_;\n"
132                 );
133             }
134         }
135         components.add(new DoitMethodWrap(wmeth));
136         if (anonDeclareWrap != null) {
137             components.add(anonDeclareWrap);
138         }
139         return new CompoundWrap(components.toArray());
140     }
141 
tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap)142     public static Wrap tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap) {
143         RangeWrap winit = new NoWrap(source);
144         // y
145         // return $1 = y;
146         // "return " + $1 + "=" + init ;
147         Wrap wmeth = new CompoundWrap("return " + name + " =\n        ", winit, semi(winit));
148         Wrap wInitMeth = new DoitMethodWrap(wmeth);
149 
150         String varDecl = "    public static\n    " + typename + " " + name + ";\n";
151         return anonDeclareWrap != null ? new CompoundWrap(varDecl, wInitMeth, anonDeclareWrap)
152                                        : new CompoundWrap(varDecl, wInitMeth);
153     }
154 
simpleWrap(String source)155     public static Wrap simpleWrap(String source) {
156         return new NoWrap(source);
157     }
158 
identityWrap(String source)159     public static Wrap identityWrap(String source) {
160         return new NoWrap(source);
161     }
162 
rangeWrap(String source, Range range)163     public static Wrap rangeWrap(String source, Range range) {
164         return new RangeWrap(source, range);
165     }
166 
classMemberWrap(String source)167     public static Wrap classMemberWrap(String source) {
168         Wrap w = new NoWrap(source);
169         return new CompoundWrap("    public static\n    ", w);
170     }
171 
countLines(String s)172     private static int countLines(String s) {
173         return countLines(s, 0, s.length());
174     }
175 
countLines(String s, int from, int toEx)176     private static int countLines(String s, int from, int toEx) {
177         int cnt = 0;
178         int idx = from;
179         while ((idx = s.indexOf('\n', idx)) > 0) {
180             if (idx >= toEx) break;
181             ++cnt;
182             ++idx;
183         }
184         return cnt;
185     }
186 
187     public static final class Range {
188         final int begin;
189         final int end; // exclusive
190 
Range(int begin, int end)191         Range(int begin, int end) {
192             this.begin = begin;
193             this.end = end;
194         }
195 
Range(String s)196         Range(String s) {
197             this.begin = 0;
198             this.end = s.length();
199         }
200 
part(String s)201         String part(String s) {
202             return s.substring(begin, end);
203         }
204 
length()205         int length() {
206             return end - begin;
207         }
208 
isEmpty()209         boolean isEmpty() {
210             return end == begin;
211         }
212 
verify(String s)213         void verify(String s) {
214             if (begin < 0 || end <= begin || end > s.length()) {
215                 throw new InternalError("Bad Range: " + s + "[" + begin + "," + end + "]");
216             }
217         }
218 
219         @Override
toString()220         public String toString() {
221             return "Range[" + begin + "," + end + ")";
222         }
223     }
224 
225     public static class CompoundWrap extends Wrap {
226 
227         final Object[] os;
228         final String wrapped;
229         final int snidxFirst;
230         final int snidxLast;
231         final int snlineFirst;
232         final int snlineLast;
233 
CompoundWrap(Object... os)234         CompoundWrap(Object... os) {
235             this.os = os;
236             int sniFirst = Integer.MAX_VALUE;
237             int sniLast = Integer.MIN_VALUE;
238             int snlnFirst = Integer.MAX_VALUE;
239             int snlnLast = Integer.MIN_VALUE;
240             StringBuilder sb = new StringBuilder();
241             for (Object o : os) {
242                 if (o instanceof String) {
243                     String s = (String) o;
244                     sb.append(s);
245                 } else if (o instanceof Wrap) {
246                     Wrap w = (Wrap) o;
247                     if (w.firstSnippetIndex() < sniFirst) {
248                         sniFirst = w.firstSnippetIndex();
249                     }
250                     if (w.lastSnippetIndex() > sniLast) {
251                         sniLast = w.lastSnippetIndex();
252                     }
253                     if (w.firstSnippetLine() < snlnFirst) {
254                         snlnFirst = w.firstSnippetLine();
255                     }
256                     if (w.lastSnippetLine() > snlnLast) {
257                         snlnLast = w.lastSnippetLine();
258                     }
259                     sb.append(w.wrapped());
260                 } else {
261                     throw new InternalError("Bad object in CommoundWrap: " + o);
262                 }
263             }
264             this.wrapped = sb.toString();
265             this.snidxFirst = sniFirst;
266             this.snidxLast = sniLast;
267             this.snlineFirst = snlnFirst;
268             this.snlineLast = snlnLast;
269         }
270 
271         @Override
wrapped()272         public String wrapped() {
273             return wrapped;
274         }
275 
276         @Override
snippetIndexToWrapIndex(int sni)277         public int snippetIndexToWrapIndex(int sni) {
278             int before = 0;
279             for (Object o : os) {
280                 if (o instanceof String) {
281                     String s = (String) o;
282                     before += s.length();
283                 } else if (o instanceof Wrap) {
284                     Wrap w = (Wrap) o;
285                     if (sni >= w.firstSnippetIndex() && sni < w.lastSnippetIndex()) {
286                         int wwi = w.snippetIndexToWrapIndex(sni);
287                         debugWrap("\nCommoundWrap.snippetIndexToWrapIndex: SnippetIndex(%d) -> WrapIndex(%d + %d = %d)"
288                                         + "\n   === %s",
289                                 sni, wwi, before, wwi + before, wrapped());
290                         return wwi + before;
291                     }
292                     before += w.wrapped().length();
293                 }
294             }
295             return 0;
296         }
297 
wrapIndexToWrap(long wi)298         Wrap wrapIndexToWrap(long wi) {
299             int before = 0;
300             Wrap w = null;
301             for (Object o : os) {
302                 if (o instanceof String) {
303                     String s = (String) o;
304                     before += s.length();
305                 } else if (o instanceof Wrap) {
306                     w = (Wrap) o;
307                     int len = w.wrapped().length();
308                     if ((wi - before) <= len) {
309                         debugWrap("CommoundWrap.wrapIndexToWrap: Defer to wrap %s - wi: %d. before; %d   >>> %s\n",
310                                 w, wi, before, w.wrapped());
311                         return w;
312                     }
313                     before += len;
314                 }
315             }
316             return w;
317         }
318 
319         @Override
wrapIndexToSnippetIndex(int wi)320         public int wrapIndexToSnippetIndex(int wi) {
321             int before = 0;
322             for (Object o : os) {
323                 if (o instanceof String) {
324                     String s = (String) o;
325                     before += s.length();
326                 } else if (o instanceof Wrap) {
327                     Wrap w = (Wrap) o;
328                     int len = w.wrapped().length();
329                     if ((wi - before) <= len) {
330                         int si = w.wrapIndexToSnippetIndex(wi - before);
331                         debugWrap("\nCommoundWrap.wrapIndexToSnippetIndex: WrapIndex(%d) -> SnippetIndex(%d)\n",
332                                 wi, si);
333                         return si;
334                     }
335                     before += len;
336                 }
337             }
338             return lastSnippetIndex();
339         }
340 
341         @Override
firstSnippetIndex()342         public int firstSnippetIndex() {
343             return snidxFirst;
344         }
345 
346         @Override
lastSnippetIndex()347         public int lastSnippetIndex() {
348             return snidxLast;
349         }
350 
351         @Override
snippetLineToWrapLine(int snline)352         public int snippetLineToWrapLine(int snline) {
353             int before = 0;
354             for (Object o : os) {
355                 if (o instanceof String) {
356                     String s = (String) o;
357                     before += countLines(s);
358                 } else if (o instanceof Wrap) {
359                     Wrap w = (Wrap) o;
360                     if (snline >= w.firstSnippetLine() && snline <= w.lastSnippetLine()) {
361                         return w.snippetLineToWrapLine(snline) + before;
362                     }
363                     before += countLines(w.wrapped());
364                 }
365             }
366             return 0;
367         }
368 
wrapLineToWrap(int wline)369         Wrap wrapLineToWrap(int wline) {
370             int before = 0;
371             Wrap w = null;
372             for (Object o : os) {
373                 if (o instanceof String) {
374                     String s = (String) o;
375                     before += countLines(s);
376                 } else if (o instanceof Wrap) {
377                     w = (Wrap) o;
378                     int lns = countLines(w.wrapped());
379                     if ((wline - before) <= lns) {
380                         return w;
381                     }
382                     before += lns;
383                 }
384             }
385             return w;
386         }
387 
388         @Override
wrapLineToSnippetLine(int wline)389         public int wrapLineToSnippetLine(int wline) {
390             int before = 0;
391             for (Object o : os) {
392                 if (o instanceof String) {
393                     String s = (String) o;
394                     before += countLines(s);
395                 } else if (o instanceof Wrap) {
396                     Wrap w = (Wrap) o;
397                     int lns = countLines(w.wrapped());
398                     if ((wline - before) <= lns) {
399                         return w.wrapLineToSnippetLine(wline - before);
400                     }
401                     before += lns;
402                 }
403             }
404             return 0;
405         }
406 
407         @Override
firstSnippetLine()408         public int firstSnippetLine() {
409             return snlineFirst;
410         }
411 
412         @Override
lastSnippetLine()413         public int lastSnippetLine() {
414             return snlineLast;
415         }
416 
417         @Override
toString()418         public String toString() {
419             return "CompoundWrap(" + Arrays.stream(os)
420                     .map(o -> (o instanceof String)
421                             ? "\"" + o + "\""
422                             : o.toString())
423                     .collect(joining(","))
424                     + ")";
425         }
426     }
427 
428     static class RangeWrap extends Wrap {
429 
430         final Range range;
431         final String wrapped;   // The snippet portion of the source
432         final int firstSnline;  // Line count to start of snippet portion
433         final int lastSnline;   // Line count to end of snippet portion
434 
RangeWrap(String snippetSource, Range usedWithinSnippet)435         RangeWrap(String snippetSource, Range usedWithinSnippet) {
436             this.range = usedWithinSnippet;
437             this.wrapped = usedWithinSnippet.part(snippetSource);
438             usedWithinSnippet.verify(snippetSource);
439             this.firstSnline = countLines(snippetSource, 0, range.begin);
440             this.lastSnline = firstSnline + countLines(snippetSource, range.begin, range.end);
441         }
442 
443         @Override
wrapped()444         public String wrapped() {
445             return wrapped;
446         }
447 
448         @Override
snippetIndexToWrapIndex(int sni)449         public int snippetIndexToWrapIndex(int sni) {
450             if (sni < range.begin) {
451                 debugWrap("\nRangeWrap.snippetIndexToWrapIndex: ERR before SnippetIndex(%d) -> WrapIndex(%d + %d = %d)\n",
452                         sni, 0);
453                 return 0;
454             }
455             if (sni > range.end) {
456                 debugWrap("\nRangeWrap.snippetIndexToWrapIndex: ERR after SnippetIndex(%d) -> WrapIndex(%d + %d = %d)\n",
457                         sni, range.length());
458                 return range.length();
459             }
460             int wi = sni - range.begin;
461             debugWrap("\nRangeWrap.snippetIndexToWrapIndex: SnippetIndex(%d) -> WrapIndex(%d + %d = %d)"
462                             + "\n   === %s",
463                     sni, sni, range.begin, sni - range.begin, wrapped());
464             return wi;
465         }
466 
467         @Override
wrapIndexToSnippetIndex(int wi)468         public int wrapIndexToSnippetIndex(int wi) {
469             if (wi < 0) {
470                 debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: ERR before WrapIndex(%d) -> SnippetIndex(%d)\n",
471                         wi, 0);
472                 return 0; // bad index
473             }
474             int max = range.length();
475             if (wi > max) {
476                 debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: ERR after WrapIndex(%d) -> SnippetIndex(%d)\n",
477                         wi, max + range.begin);
478                 return max + range.begin;
479             }
480             int sni = wi + range.begin;
481             debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: WrapIndex(%d) -> SnippetIndex(%d)\n",
482                     wi, sni);
483             return sni;
484         }
485 
486         @Override
firstSnippetIndex()487         public int firstSnippetIndex() {
488             return range.begin;
489         }
490 
491         @Override
lastSnippetIndex()492         public int lastSnippetIndex() {
493             return range.end;
494         }
495 
496         @Override
snippetLineToWrapLine(int snline)497         public int snippetLineToWrapLine(int snline) {
498             if (snline < firstSnline) {
499                 return 0;
500             }
501             if (snline >= lastSnline) {
502                 return lastSnline - firstSnline;
503             }
504             return snline - firstSnline;
505         }
506 
507         @Override
wrapLineToSnippetLine(int wline)508         public int wrapLineToSnippetLine(int wline) {
509             if (wline < 0) {
510                 return 0; // bad index
511             }
512             int max = lastSnline - firstSnline;
513             if (wline > max) {
514                 wline = max;
515             }
516             return wline + firstSnline;
517         }
518 
519         @Override
firstSnippetLine()520         public int firstSnippetLine() {
521             return firstSnline;
522         }
523 
524         @Override
lastSnippetLine()525         public int lastSnippetLine() {
526             return lastSnline;
527         }
528 
529         @Override
toString()530         public String toString() {
531             return "RangeWrap(" + range + ")";
532         }
533     }
534 
535     private static class NoWrap extends RangeWrap {
536 
NoWrap(String unit)537         NoWrap(String unit) {
538             super(unit, new Range(unit));
539         }
540     }
541 
semi(Wrap w)542     private static String semi(Wrap w) {
543         return semi(w.wrapped());
544     }
545 
semi(String s)546     private static String semi(String s) {
547         return ((s.endsWith(";")) ? "\n" : ((s.endsWith(";\n")) ? "" : ";\n"));
548     }
549 
550     private static class DoitMethodWrap extends CompoundWrap {
551 
DoitMethodWrap(Wrap w)552         DoitMethodWrap(Wrap w) {
553             super("    public static Object " + DOIT_METHOD_NAME + "() throws Throwable {\n"
554                     + "        ", w,
555                     "    }\n");
556         }
557     }
558 
559     private static class VarDeclareWrap extends CompoundWrap {
560 
VarDeclareWrap(Wrap wtype, String brackets, Wrap wname)561         VarDeclareWrap(Wrap wtype, String brackets, Wrap wname) {
562             super("    public static ", wtype, brackets + " ", wname, semi(wname));
563         }
564     }
565 
debugWrap(String format, Object... args)566     void debugWrap(String format, Object... args) {
567         //System.err.printf(format, args);
568         //state.debug(this, InternalDebugControl.DBG_WRAP, format, args);
569     }
570 }
571