| /* |
| * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.jshell; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import static java.util.stream.Collectors.joining; |
| import static jdk.jshell.Util.DOIT_METHOD_NAME; |
| |
| /** |
| * Wrapping of source into Java methods, fields, etc. All but outer layer |
| * wrapping with imports and class. |
| */ |
| abstract class Wrap implements GeneralWrap { |
| |
| private static Wrap methodWrap(String prefix, String source, String suffix) { |
| Wrap wunit = new NoWrap(source); |
| return new DoitMethodWrap(new CompoundWrap(prefix, wunit, suffix)); |
| } |
| |
| public static Wrap methodWrap(String source) { |
| return methodWrap("", source, semi(source) + " return null;\n"); |
| } |
| |
| public static Wrap methodReturnWrap(String source) { |
| return methodWrap("return ", source, semi(source)); |
| } |
| |
| public static Wrap methodUnreachableSemiWrap(String source) { |
| return methodWrap("", source, semi(source)); |
| } |
| |
| public static Wrap methodUnreachableWrap(String source) { |
| return methodWrap("", source, ""); |
| } |
| |
| private static String indent(int n) { |
| return " ".substring(0, n * 4); |
| } |
| |
| private static String nlindent(int n) { |
| return "\n" + indent(n); |
| } |
| |
| /**Create a stub of a compilable representation of a variable snippet. |
| * The variable is always represented by a field. If the variable |
| * in the snippet has an initializer, the field is initialized by |
| * calling the DOIT_METHOD_NAME method. |
| * |
| * In some cases, the real inferred type of the variable may be non-denotable |
| * (e.g. intersection types). The declared type of the field must always |
| * be denotable (i.e. such that it can be written into the classfile), but |
| * if the real type is potentially non-denotable, the {@code enhanced} parameter |
| * must be true. |
| * |
| * @param source the snippet's masked source code |
| * @param wtype variable's denotable type suitable for field declaration |
| * @param brackets any [] that should be appended to the type |
| * @param rname range in source that denotes the name of the |
| * @param winit Initializer or null |
| * @param enhanced if the real inferred type of the variable is potentially |
| * non-denotable, this must be true |
| * @return a Wrap that declares the given variable, potentially with |
| * an initialization method |
| */ |
| public static Wrap varWrap(String source, Wrap wtype, String brackets, |
| Range rname, Wrap winit, boolean enhanced, |
| Wrap anonDeclareWrap) { |
| RangeWrap wname = new RangeWrap(source, rname); |
| List<Object> components = new ArrayList<>(); |
| components.add(new VarDeclareWrap(wtype, brackets, wname)); |
| Wrap wmeth; |
| |
| if (winit == null) { |
| wmeth = new CompoundWrap(new NoWrap(" "), " return null;\n"); |
| } else { |
| // int x = y |
| if (enhanced) { |
| // private static <Z> Z do_itAux() { |
| // wtype x_ = y; |
| // @SuppressWarnings("unchecked") |
| // Z x__ = (Z) x_; |
| // return x__; |
| // } |
| // in do_it method: |
| //return do_itAux(); |
| Wrap waux = new CompoundWrap( |
| " private static <Z> Z ", DOIT_METHOD_NAME + "Aux", "() throws Throwable {\n", |
| wtype, brackets + " ", wname, "_ =\n ", winit, semi(winit), |
| " @SuppressWarnings(\"unchecked\") Z ", wname, "__ = (Z)", wname, "_;\n", |
| " return ", wname, "__;\n", |
| "}" |
| ); |
| components.add(waux); |
| wmeth = new CompoundWrap( |
| " return ", wname, " = ", DOIT_METHOD_NAME + "Aux", "();\n" |
| ); |
| } else { |
| // int x_ = y; return x = x_; |
| // decl + "_ = " + init ; + "return " + name + "= " + name + "_ ;" |
| wmeth = new CompoundWrap( |
| wtype, brackets + " ", wname, "_ =\n ", winit, semi(winit), |
| " return ", wname, " = ", wname, "_;\n" |
| ); |
| } |
| } |
| components.add(new DoitMethodWrap(wmeth)); |
| if (anonDeclareWrap != null) { |
| components.add(anonDeclareWrap); |
| } |
| return new CompoundWrap(components.toArray()); |
| } |
| |
| public static Wrap tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap) { |
| RangeWrap winit = new NoWrap(source); |
| // y |
| // return $1 = y; |
| // "return " + $1 + "=" + init ; |
| Wrap wmeth = new CompoundWrap("return " + name + " =\n ", winit, semi(winit)); |
| Wrap wInitMeth = new DoitMethodWrap(wmeth); |
| |
| String varDecl = " public static\n " + typename + " " + name + ";\n"; |
| return anonDeclareWrap != null ? new CompoundWrap(varDecl, wInitMeth, anonDeclareWrap) |
| : new CompoundWrap(varDecl, wInitMeth); |
| } |
| |
| public static Wrap simpleWrap(String source) { |
| return new NoWrap(source); |
| } |
| |
| public static Wrap identityWrap(String source) { |
| return new NoWrap(source); |
| } |
| |
| public static Wrap rangeWrap(String source, Range range) { |
| return new RangeWrap(source, range); |
| } |
| |
| public static Wrap classMemberWrap(String source) { |
| Wrap w = new NoWrap(source); |
| return new CompoundWrap(" public static\n ", w); |
| } |
| |
| private static int countLines(String s) { |
| return countLines(s, 0, s.length()); |
| } |
| |
| private static int countLines(String s, int from, int toEx) { |
| int cnt = 0; |
| int idx = from; |
| while ((idx = s.indexOf('\n', idx)) > 0) { |
| if (idx >= toEx) break; |
| ++cnt; |
| ++idx; |
| } |
| return cnt; |
| } |
| |
| public static final class Range { |
| final int begin; |
| final int end; // exclusive |
| |
| Range(int begin, int end) { |
| this.begin = begin; |
| this.end = end; |
| } |
| |
| Range(String s) { |
| this.begin = 0; |
| this.end = s.length(); |
| } |
| |
| String part(String s) { |
| return s.substring(begin, end); |
| } |
| |
| int length() { |
| return end - begin; |
| } |
| |
| boolean isEmpty() { |
| return end == begin; |
| } |
| |
| void verify(String s) { |
| if (begin < 0 || end <= begin || end > s.length()) { |
| throw new InternalError("Bad Range: " + s + "[" + begin + "," + end + "]"); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "Range[" + begin + "," + end + ")"; |
| } |
| } |
| |
| public static class CompoundWrap extends Wrap { |
| |
| final Object[] os; |
| final String wrapped; |
| final int snidxFirst; |
| final int snidxLast; |
| final int snlineFirst; |
| final int snlineLast; |
| |
| CompoundWrap(Object... os) { |
| this.os = os; |
| int sniFirst = Integer.MAX_VALUE; |
| int sniLast = Integer.MIN_VALUE; |
| int snlnFirst = Integer.MAX_VALUE; |
| int snlnLast = Integer.MIN_VALUE; |
| StringBuilder sb = new StringBuilder(); |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| sb.append(s); |
| } else if (o instanceof Wrap) { |
| Wrap w = (Wrap) o; |
| if (w.firstSnippetIndex() < sniFirst) { |
| sniFirst = w.firstSnippetIndex(); |
| } |
| if (w.lastSnippetIndex() > sniLast) { |
| sniLast = w.lastSnippetIndex(); |
| } |
| if (w.firstSnippetLine() < snlnFirst) { |
| snlnFirst = w.firstSnippetLine(); |
| } |
| if (w.lastSnippetLine() > snlnLast) { |
| snlnLast = w.lastSnippetLine(); |
| } |
| sb.append(w.wrapped()); |
| } else { |
| throw new InternalError("Bad object in CommoundWrap: " + o); |
| } |
| } |
| this.wrapped = sb.toString(); |
| this.snidxFirst = sniFirst; |
| this.snidxLast = sniLast; |
| this.snlineFirst = snlnFirst; |
| this.snlineLast = snlnLast; |
| } |
| |
| @Override |
| public String wrapped() { |
| return wrapped; |
| } |
| |
| @Override |
| public int snippetIndexToWrapIndex(int sni) { |
| int before = 0; |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| before += s.length(); |
| } else if (o instanceof Wrap) { |
| Wrap w = (Wrap) o; |
| if (sni >= w.firstSnippetIndex() && sni < w.lastSnippetIndex()) { |
| int wwi = w.snippetIndexToWrapIndex(sni); |
| debugWrap("\nCommoundWrap.snippetIndexToWrapIndex: SnippetIndex(%d) -> WrapIndex(%d + %d = %d)" |
| + "\n === %s", |
| sni, wwi, before, wwi + before, wrapped()); |
| return wwi + before; |
| } |
| before += w.wrapped().length(); |
| } |
| } |
| return 0; |
| } |
| |
| Wrap wrapIndexToWrap(long wi) { |
| int before = 0; |
| Wrap w = null; |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| before += s.length(); |
| } else if (o instanceof Wrap) { |
| w = (Wrap) o; |
| int len = w.wrapped().length(); |
| if ((wi - before) <= len) { |
| debugWrap("CommoundWrap.wrapIndexToWrap: Defer to wrap %s - wi: %d. before; %d >>> %s\n", |
| w, wi, before, w.wrapped()); |
| return w; |
| } |
| before += len; |
| } |
| } |
| return w; |
| } |
| |
| @Override |
| public int wrapIndexToSnippetIndex(int wi) { |
| int before = 0; |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| before += s.length(); |
| } else if (o instanceof Wrap) { |
| Wrap w = (Wrap) o; |
| int len = w.wrapped().length(); |
| if ((wi - before) <= len) { |
| int si = w.wrapIndexToSnippetIndex(wi - before); |
| debugWrap("\nCommoundWrap.wrapIndexToSnippetIndex: WrapIndex(%d) -> SnippetIndex(%d)\n", |
| wi, si); |
| return si; |
| } |
| before += len; |
| } |
| } |
| return lastSnippetIndex(); |
| } |
| |
| @Override |
| public int firstSnippetIndex() { |
| return snidxFirst; |
| } |
| |
| @Override |
| public int lastSnippetIndex() { |
| return snidxLast; |
| } |
| |
| @Override |
| public int snippetLineToWrapLine(int snline) { |
| int before = 0; |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| before += countLines(s); |
| } else if (o instanceof Wrap) { |
| Wrap w = (Wrap) o; |
| if (snline >= w.firstSnippetLine() && snline <= w.lastSnippetLine()) { |
| return w.snippetLineToWrapLine(snline) + before; |
| } |
| before += countLines(w.wrapped()); |
| } |
| } |
| return 0; |
| } |
| |
| Wrap wrapLineToWrap(int wline) { |
| int before = 0; |
| Wrap w = null; |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| before += countLines(s); |
| } else if (o instanceof Wrap) { |
| w = (Wrap) o; |
| int lns = countLines(w.wrapped()); |
| if ((wline - before) <= lns) { |
| return w; |
| } |
| before += lns; |
| } |
| } |
| return w; |
| } |
| |
| @Override |
| public int wrapLineToSnippetLine(int wline) { |
| int before = 0; |
| for (Object o : os) { |
| if (o instanceof String) { |
| String s = (String) o; |
| before += countLines(s); |
| } else if (o instanceof Wrap) { |
| Wrap w = (Wrap) o; |
| int lns = countLines(w.wrapped()); |
| if ((wline - before) <= lns) { |
| return w.wrapLineToSnippetLine(wline - before); |
| } |
| before += lns; |
| } |
| } |
| return 0; |
| } |
| |
| @Override |
| public int firstSnippetLine() { |
| return snlineFirst; |
| } |
| |
| @Override |
| public int lastSnippetLine() { |
| return snlineLast; |
| } |
| |
| @Override |
| public String toString() { |
| return "CompoundWrap(" + Arrays.stream(os) |
| .map(o -> (o instanceof String) |
| ? "\"" + o + "\"" |
| : o.toString()) |
| .collect(joining(",")) |
| + ")"; |
| } |
| } |
| |
| static class RangeWrap extends Wrap { |
| |
| final Range range; |
| final String wrapped; // The snippet portion of the source |
| final int firstSnline; // Line count to start of snippet portion |
| final int lastSnline; // Line count to end of snippet portion |
| |
| RangeWrap(String snippetSource, Range usedWithinSnippet) { |
| this.range = usedWithinSnippet; |
| this.wrapped = usedWithinSnippet.part(snippetSource); |
| usedWithinSnippet.verify(snippetSource); |
| this.firstSnline = countLines(snippetSource, 0, range.begin); |
| this.lastSnline = firstSnline + countLines(snippetSource, range.begin, range.end); |
| } |
| |
| @Override |
| public String wrapped() { |
| return wrapped; |
| } |
| |
| @Override |
| public int snippetIndexToWrapIndex(int sni) { |
| if (sni < range.begin) { |
| debugWrap("\nRangeWrap.snippetIndexToWrapIndex: ERR before SnippetIndex(%d) -> WrapIndex(%d + %d = %d)\n", |
| sni, 0); |
| return 0; |
| } |
| if (sni > range.end) { |
| debugWrap("\nRangeWrap.snippetIndexToWrapIndex: ERR after SnippetIndex(%d) -> WrapIndex(%d + %d = %d)\n", |
| sni, range.length()); |
| return range.length(); |
| } |
| int wi = sni - range.begin; |
| debugWrap("\nRangeWrap.snippetIndexToWrapIndex: SnippetIndex(%d) -> WrapIndex(%d + %d = %d)" |
| + "\n === %s", |
| sni, sni, range.begin, sni - range.begin, wrapped()); |
| return wi; |
| } |
| |
| @Override |
| public int wrapIndexToSnippetIndex(int wi) { |
| if (wi < 0) { |
| debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: ERR before WrapIndex(%d) -> SnippetIndex(%d)\n", |
| wi, 0); |
| return 0; // bad index |
| } |
| int max = range.length(); |
| if (wi > max) { |
| debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: ERR after WrapIndex(%d) -> SnippetIndex(%d)\n", |
| wi, max + range.begin); |
| return max + range.begin; |
| } |
| int sni = wi + range.begin; |
| debugWrap("\nRangeWrap.wrapIndexToSnippetIndex: WrapIndex(%d) -> SnippetIndex(%d)\n", |
| wi, sni); |
| return sni; |
| } |
| |
| @Override |
| public int firstSnippetIndex() { |
| return range.begin; |
| } |
| |
| @Override |
| public int lastSnippetIndex() { |
| return range.end; |
| } |
| |
| @Override |
| public int snippetLineToWrapLine(int snline) { |
| if (snline < firstSnline) { |
| return 0; |
| } |
| if (snline >= lastSnline) { |
| return lastSnline - firstSnline; |
| } |
| return snline - firstSnline; |
| } |
| |
| @Override |
| public int wrapLineToSnippetLine(int wline) { |
| if (wline < 0) { |
| return 0; // bad index |
| } |
| int max = lastSnline - firstSnline; |
| if (wline > max) { |
| wline = max; |
| } |
| return wline + firstSnline; |
| } |
| |
| @Override |
| public int firstSnippetLine() { |
| return firstSnline; |
| } |
| |
| @Override |
| public int lastSnippetLine() { |
| return lastSnline; |
| } |
| |
| @Override |
| public String toString() { |
| return "RangeWrap(" + range + ")"; |
| } |
| } |
| |
| private static class NoWrap extends RangeWrap { |
| |
| NoWrap(String unit) { |
| super(unit, new Range(unit)); |
| } |
| } |
| |
| private static String semi(Wrap w) { |
| return semi(w.wrapped()); |
| } |
| |
| private static String semi(String s) { |
| return ((s.endsWith(";")) ? "\n" : ((s.endsWith(";\n")) ? "" : ";\n")); |
| } |
| |
| private static class DoitMethodWrap extends CompoundWrap { |
| |
| DoitMethodWrap(Wrap w) { |
| super(" public static Object " + DOIT_METHOD_NAME + "() throws Throwable {\n" |
| + " ", w, |
| " }\n"); |
| } |
| } |
| |
| private static class VarDeclareWrap extends CompoundWrap { |
| |
| VarDeclareWrap(Wrap wtype, String brackets, Wrap wname) { |
| super(" public static ", wtype, brackets + " ", wname, semi(wname)); |
| } |
| } |
| |
| void debugWrap(String format, Object... args) { |
| //System.err.printf(format, args); |
| //state.debug(this, InternalDebugControl.DBG_WRAP, format, args); |
| } |
| } |