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