--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2014, 2015, 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.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Modifier;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.Pretty;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import jdk.jshell.ClassTracker.ClassInfo;
+import jdk.jshell.Key.ErroneousKey;
+import jdk.jshell.Key.MethodKey;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.TaskFactory.AnalyzeTask;
+import jdk.jshell.TaskFactory.BaseTask;
+import jdk.jshell.TaskFactory.CompileTask;
+import jdk.jshell.TaskFactory.ParseTask;
+import jdk.jshell.TreeDissector.ExpressionInfo;
+import jdk.jshell.Wrap.Range;
+import jdk.jshell.Snippet.Status;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+import static jdk.jshell.Util.*;
+import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
+import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
+import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
+
+/**
+ * The Evaluation Engine. Source internal analysis, wrapping control,
+ * compilation, declaration. redefinition, replacement, and execution.
+ *
+ * @author Robert Field
+ */
+class Eval {
+
+ private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\p{javaWhitespace}+(?<static>static\\p{javaWhitespace}+)?(?<fullname>[\\p{L}\\p{N}_\\$\\.]+\\.(?<name>[\\p{L}\\p{N}_\\$]+|\\*))");
+
+ private int varNumber = 0;
+
+ private final JShell state;
+
+ Eval(JShell state) {
+ this.state = state;
+ }
+
+ List<SnippetEvent> eval(String userSource) throws IllegalStateException {
+ String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
+ if (compileSource.length() == 0) {
+ return Collections.emptyList();
+ }
+ // String folding messes up position information.
+ ParseTask pt = state.taskFactory.new ParseTask(compileSource);
+ if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
+ return compileFailResult(pt, userSource);
+ }
+
+ List<? extends Tree> units = pt.units();
+ if (units.isEmpty()) {
+ return compileFailResult(pt, userSource);
+ }
+ // Erase illegal modifiers
+ compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
+ Tree unitTree = units.get(0);
+ state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
+ switch (unitTree.getKind()) {
+ case IMPORT:
+ return processImport(userSource, compileSource);
+ case VARIABLE:
+ return processVariables(userSource, units, compileSource, pt);
+ case EXPRESSION_STATEMENT:
+ return processExpression(userSource, compileSource);
+ case CLASS:
+ return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
+ case ENUM:
+ return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
+ case ANNOTATION_TYPE:
+ return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
+ case INTERFACE:
+ return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
+ case METHOD:
+ return processMethod(userSource, unitTree, compileSource, pt);
+ default:
+ return processStatement(userSource, compileSource);
+ }
+ }
+
+ private List<SnippetEvent> processImport(String userSource, String compileSource) {
+ Wrap guts = Wrap.importWrap(compileSource);
+ Matcher mat = IMPORT_PATTERN.matcher(compileSource);
+ String fullname;
+ String name;
+ boolean isStatic;
+ if (mat.find()) {
+ isStatic = mat.group("static") != null;
+ name = mat.group("name");
+ fullname = mat.group("fullname");
+ } else {
+ // bad import -- fake it
+ isStatic = compileSource.contains("static");
+ name = fullname = compileSource;
+ }
+ String fullkey = (isStatic ? "static-" : "") + fullname;
+ boolean isStar = name.equals("*");
+ String keyName = isStar
+ ? fullname
+ : name;
+ SubKind snippetKind = isStar
+ ? (isStatic ? STATIC_IMPORT_ON_DEMAND_SUBKIND : TYPE_IMPORT_ON_DEMAND_SUBKIND)
+ : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
+ Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
+ userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
+ return declare(snip);
+ }
+
+ private static class EvalPretty extends Pretty {
+
+ private final Writer out;
+
+ public EvalPretty(Writer writer, boolean bln) {
+ super(writer, bln);
+ this.out = writer;
+ }
+
+ /**
+ * Print string, DO NOT replacing all non-ascii character with unicode
+ * escapes.
+ */
+ @Override
+ public void print(Object o) throws IOException {
+ out.write(o.toString());
+ }
+
+ static String prettyExpr(JCTree tree, boolean bln) {
+ StringWriter out = new StringWriter();
+ try {
+ new EvalPretty(out, bln).printExpr(tree);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ return out.toString();
+ }
+ }
+
+ private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
+ List<SnippetEvent> allEvents = new ArrayList<>();
+ TreeDissector dis = new TreeDissector(pt);
+ for (Tree unitTree : units) {
+ VariableTree vt = (VariableTree) unitTree;
+ String name = vt.getName().toString();
+ String typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
+ Tree baseType = vt.getType();
+ TreeDependencyScanner tds = new TreeDependencyScanner();
+ tds.scan(baseType); // Not dependent on initializer
+ StringBuilder sbBrackets = new StringBuilder();
+ while (baseType instanceof ArrayTypeTree) {
+ //TODO handle annotations too
+ baseType = ((ArrayTypeTree) baseType).getType();
+ sbBrackets.append("[]");
+ }
+ Range rtype = dis.treeToRange(baseType);
+ Range runit = dis.treeToRange(vt);
+ runit = new Range(runit.begin, runit.end - 1);
+ ExpressionTree it = vt.getInitializer();
+ Range rinit = null;
+ int nameMax = runit.end - 1;
+ SubKind subkind;
+ if (it != null) {
+ subkind = SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND;
+ rinit = dis.treeToRange(it);
+ nameMax = rinit.begin - 1;
+ } else {
+ subkind = SubKind.VAR_DECLARATION_SUBKIND;
+ }
+ int nameStart = compileSource.lastIndexOf(name, nameMax);
+ if (nameStart < 0) {
+ throw new AssertionError("Name '" + name + "' not found");
+ }
+ int nameEnd = nameStart + name.length();
+ Range rname = new Range(nameStart, nameEnd);
+ Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit);
+ Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
+ name, subkind, typeName,
+ tds.declareReferences());
+ DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
+ List<SnippetEvent> res1 = declare(snip, modDiag);
+ allEvents.addAll(res1);
+ }
+
+ return allEvents;
+ }
+
+ private List<SnippetEvent> processExpression(String userSource, String compileSource) {
+ String name = null;
+ ExpressionInfo ei = typeOfExpression(compileSource);
+ ExpressionTree assignVar;
+ Wrap guts;
+ Snippet snip;
+ if (ei != null && ei.isNonVoid) {
+ String typeName = ei.typeName;
+ SubKind subkind;
+ if (ei.tree instanceof IdentifierTree) {
+ IdentifierTree id = (IdentifierTree) ei.tree;
+ name = id.getName().toString();
+ subkind = SubKind.VAR_VALUE_SUBKIND;
+
+ } else if (ei.tree instanceof AssignmentTree
+ && (assignVar = ((AssignmentTree) ei.tree).getVariable()) instanceof IdentifierTree) {
+ name = assignVar.toString();
+ subkind = SubKind.ASSIGNMENT_SUBKIND;
+ } else {
+ subkind = SubKind.OTHER_EXPRESSION_SUBKIND;
+ }
+ if (shouldGenTempVar(subkind)) {
+ if (state.tempVariableNameGenerator != null) {
+ name = state.tempVariableNameGenerator.get();
+ }
+ while (name == null || state.keyMap.doesVariableNameExist(name)) {
+ name = "$" + ++varNumber;
+ }
+ guts = Wrap.tempVarWrap(compileSource, typeName, name);
+ Collection<String> declareReferences = null; //TODO
+ snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
+ name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences);
+ } else {
+ guts = Wrap.methodReturnWrap(compileSource);
+ snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
+ name, subkind);
+ }
+ } else {
+ guts = Wrap.methodWrap(compileSource);
+ if (ei == null) {
+ // We got no type info, check for not a statement by trying
+ AnalyzeTask at = trialCompile(guts);
+ if (at.getDiagnostics().hasNotStatement()) {
+ guts = Wrap.methodReturnWrap(compileSource);
+ at = trialCompile(guts);
+ }
+ if (at.hasErrors()) {
+ return compileFailResult(at, userSource);
+ }
+ }
+ snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
+ }
+ return declare(snip);
+ }
+
+ private List<SnippetEvent> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
+ TreeDependencyScanner tds = new TreeDependencyScanner();
+ tds.scan(unitTree);
+
+ TreeDissector dis = new TreeDissector(pt);
+
+ ClassTree klassTree = (ClassTree) unitTree;
+ String name = klassTree.getSimpleName().toString();
+ Wrap guts = Wrap.classMemberWrap(compileSource);
+ Wrap corralled = null; //TODO
+ Snippet snip = new TypeDeclSnippet(state.keyMap.keyForClass(name), userSource, guts,
+ name, snippetKind,
+ corralled, tds.declareReferences(), tds.bodyReferences());
+ DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
+ return declare(snip, modDiag);
+ }
+
+ private List<SnippetEvent> processStatement(String userSource, String compileSource) {
+ Wrap guts = Wrap.methodWrap(compileSource);
+ // Check for unreachable by trying
+ AnalyzeTask at = trialCompile(guts);
+ if (at.hasErrors()) {
+ if (at.getDiagnostics().hasUnreachableError()) {
+ guts = Wrap.methodUnreachableSemiWrap(compileSource);
+ at = trialCompile(guts);
+ if (at.hasErrors()) {
+ if (at.getDiagnostics().hasUnreachableError()) {
+ // Without ending semicolon
+ guts = Wrap.methodUnreachableWrap(compileSource);
+ at = trialCompile(guts);
+ }
+ if (at.hasErrors()) {
+ return compileFailResult(at, userSource);
+ }
+ }
+ } else {
+ return compileFailResult(at, userSource);
+ }
+ }
+ Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
+ return declare(snip);
+ }
+
+ private OuterWrap wrapInClass(String className, Set<Key> except, String userSource, Wrap guts, Collection<Snippet> plus) {
+ String imports = state.maps.packageAndImportsExcept(except, plus);
+ return OuterWrap.wrapInClass(state.maps.packageName(), className, imports, userSource, guts);
+ }
+
+ OuterWrap wrapInClass(Snippet snip, Set<Key> except, Wrap guts, Collection<Snippet> plus) {
+ return wrapInClass(snip.className(), except, snip.source(), guts, plus);
+ }
+
+ private AnalyzeTask trialCompile(Wrap guts) {
+ OuterWrap outer = wrapInClass(REPL_DOESNOTMATTER_CLASS_NAME,
+ Collections.emptySet(), "", guts, null);
+ return state.taskFactory.new AnalyzeTask(outer);
+ }
+
+ private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
+ TreeDependencyScanner tds = new TreeDependencyScanner();
+ tds.scan(unitTree);
+
+ MethodTree mt = (MethodTree) unitTree;
+ TreeDissector dis = new TreeDissector(pt);
+ DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
+ if (modDiag.hasErrors()) {
+ return compileFailResult(modDiag, userSource);
+ }
+ String unitName = mt.getName().toString();
+ Wrap guts = Wrap.classMemberWrap(compileSource);
+
+ Range modRange = dis.treeToRange(mt.getModifiers());
+ Range tpRange = dis.treeListToRange(mt.getTypeParameters());
+ Range typeRange = dis.treeToRange(mt.getReturnType());
+ String name = mt.getName().toString();
+ Range paramRange = dis.treeListToRange(mt.getParameters());
+ Range throwsRange = dis.treeListToRange(mt.getThrows());
+
+ String parameterTypes
+ = mt.getParameters()
+ .stream()
+ .map(param -> dis.treeToRange(param.getType()).part(compileSource))
+ .collect(Collectors.joining(","));
+ String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
+
+ MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
+ // rewrap with correct Key index
+ Wrap corralled = Wrap.corralledMethod(compileSource,
+ modRange, tpRange, typeRange, name, paramRange, throwsRange, key.index());
+ Snippet snip = new MethodSnippet(key, userSource, guts,
+ unitName, signature,
+ corralled, tds.declareReferences(), tds.bodyReferences());
+ return declare(snip, modDiag);
+ }
+
+ /**
+ * The snippet has failed, return with the rejected event
+ *
+ * @param xt the task from which to extract the failure diagnostics
+ * @param userSource the incoming bad user source
+ * @return a rejected snippet event
+ */
+ private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) {
+ return compileFailResult(xt.getDiagnostics(), userSource);
+ }
+
+ /**
+ * The snippet has failed, return with the rejected event
+ *
+ * @param diags the failure diagnostics
+ * @param userSource the incoming bad user source
+ * @return a rejected snippet event
+ */
+ private List<SnippetEvent> compileFailResult(DiagList diags, String userSource) {
+ ErroneousKey key = state.keyMap.keyForErroneous();
+ Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND);
+ snip.setFailed(diags);
+ state.maps.installSnippet(snip);
+ return Collections.singletonList(new SnippetEvent(
+ snip, Status.NONEXISTENT, Status.REJECTED,
+ false, null, null, null)
+ );
+ }
+
+ private ExpressionInfo typeOfExpression(String expression) {
+ Wrap guts = Wrap.methodReturnWrap(expression);
+ TaskFactory.AnalyzeTask at = trialCompile(guts);
+ if (!at.hasErrors() && at.cuTree() != null) {
+ return new TreeDissector(at)
+ .typeOfReturnStatement(at.messages(), state.maps::fullClassNameAndPackageToClass);
+ }
+ return null;
+ }
+
+ /**
+ * Should a temp var wrap the expression. TODO make this user configurable.
+ *
+ * @param snippetKind
+ * @return
+ */
+ private boolean shouldGenTempVar(SubKind snippetKind) {
+ return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND;
+ }
+
+ List<SnippetEvent> drop(Snippet si) {
+ Unit c = new Unit(state, si);
+
+ Set<Unit> ins = c.dependents().collect(toSet());
+ Set<Unit> outs = compileAndLoad(ins);
+
+ return events(c, outs, null, null);
+ }
+
+ private List<SnippetEvent> declare(Snippet si) {
+ return declare(si, new DiagList());
+ }
+
+ private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
+ Unit c = new Unit(state, si, null, generatedDiagnostics);
+
+ // Ignores duplicates
+ //TODO: remove, modify, or move to edit
+ if (c.isRedundant()) {
+ return Collections.emptyList();
+ }
+
+ Set<Unit> ins = new LinkedHashSet<>();
+ ins.add(c);
+ Set<Unit> outs = compileAndLoad(ins);
+
+ if (!si.status().isDefined
+ && si.diagnostics().isEmpty()
+ && si.unresolved().isEmpty()) {
+ // did not succeed, but no record of it, extract from others
+ si.setDiagnostics(outs.stream()
+ .flatMap(u -> u.snippet().diagnostics().stream())
+ .collect(Collectors.toCollection(DiagList::new)));
+ }
+
+ // If appropriate, execute the snippet
+ String value = null;
+ Exception exception = null;
+ if (si.isExecutable() && si.status().isDefined) {
+ try {
+ value = state.executionControl().commandInvoke(state.maps.classFullName(si));
+ value = si.subKind().hasValue()
+ ? expunge(value)
+ : "";
+ } catch (EvalException ex) {
+ exception = translateExecutionException(ex);
+ } catch (UnresolvedReferenceException ex) {
+ exception = ex;
+ }
+ }
+ return events(c, outs, value, exception);
+ }
+
+ private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, Exception exception) {
+ List<SnippetEvent> events = new ArrayList<>();
+ events.add(c.event(value, exception));
+ events.addAll(outs.stream()
+ .filter(u -> u != c)
+ .map(u -> u.event(null, null))
+ .collect(Collectors.toList()));
+ events.addAll(outs.stream()
+ .flatMap(u -> u.secondaryEvents().stream())
+ .collect(Collectors.toList()));
+ //System.err.printf("Events: %s\n", events);
+ return events;
+ }
+
+ private Set<Unit> compileAndLoad(Set<Unit> ins) {
+ if (ins.isEmpty()) {
+ return ins;
+ }
+ Set<Unit> replaced = new LinkedHashSet<>();
+ while (true) {
+ state.debug(DBG_GEN, "compileAndLoad %s\n", ins);
+
+ ins.stream().forEach(u -> u.initialize(ins));
+ AnalyzeTask at = state.taskFactory.new AnalyzeTask(ins);
+ ins.stream().forEach(u -> u.setDiagnostics(at));
+ // corral any Snippets that need it
+ if (ins.stream().filter(u -> u.corralIfNeeded(ins)).count() > 0) {
+ // if any were corralled, re-analyze everything
+ AnalyzeTask cat = state.taskFactory.new AnalyzeTask(ins);
+ ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
+ }
+ ins.stream().forEach(u -> u.setStatus());
+ // compile and load the legit snippets
+ boolean success;
+ while (true) {
+ List<Unit> legit = ins.stream()
+ .filter(u -> u.isDefined())
+ .collect(toList());
+ state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
+ ins, legit);
+ if (legit.isEmpty()) {
+ // no class files can be generated
+ success = true;
+ } else {
+ // re-wrap with legit imports
+ legit.stream().forEach(u -> u.setWrap(ins, legit));
+
+ // generate class files for those capable
+ CompileTask ct = state.taskFactory.new CompileTask(legit);
+ if (!ct.compile()) {
+ // oy! compile failed because of recursive new unresolved
+ if (legit.stream()
+ .filter(u -> u.smashingErrorDiagnostics(ct))
+ .count() > 0) {
+ // try again, with the erroreous removed
+ continue;
+ } else {
+ state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
+ legit);
+ }
+ }
+
+ // load all new classes
+ load(legit.stream()
+ .flatMap(u -> u.classesToLoad(ct.classInfoList(u)))
+ .collect(toList()));
+ // attempt to redefine the remaining classes
+ List<Unit> toReplace = legit.stream()
+ .filter(u -> !u.doRedefines())
+ .collect(toList());
+
+ // prevent alternating redefine/replace cyclic dependency
+ // loop by replacing all that have been replaced
+ if (!toReplace.isEmpty()) {
+ replaced.addAll(toReplace);
+ replaced.stream().forEach(u -> u.markForReplacement());
+ }
+
+ success = toReplace.isEmpty();
+ }
+ break;
+ }
+
+ // add any new dependencies to the working set
+ List<Unit> newDependencies = ins.stream()
+ .flatMap(u -> u.effectedDependents())
+ .collect(toList());
+ state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s success: %s\n",
+ ins, newDependencies, success);
+ if (!ins.addAll(newDependencies) && success) {
+ // all classes that could not be directly loaded (because they
+ // are new) have been redefined, and no new dependnencies were
+ // identified
+ ins.stream().forEach(u -> u.finish());
+ return ins;
+ }
+ }
+ }
+
+ private void load(List<ClassInfo> cil) {
+ if (!cil.isEmpty()) {
+ state.executionControl().commandLoad(cil);
+ }
+ }
+
+ private EvalException translateExecutionException(EvalException ex) {
+ StackTraceElement[] raw = ex.getStackTrace();
+ int last = raw.length;
+ do {
+ if (last == 0) {
+ last = raw.length - 1;
+ break;
+ }
+ } while (!isWrap(raw[--last]));
+ StackTraceElement[] elems = new StackTraceElement[last + 1];
+ for (int i = 0; i <= last; ++i) {
+ StackTraceElement r = raw[i];
+ String rawKlass = r.getClassName();
+ Matcher matcher = prefixPattern.matcher(rawKlass);
+ String num;
+ if (matcher.find() && (num = matcher.group("num")) != null) {
+ int end = matcher.end();
+ if (rawKlass.charAt(end - 1) == '$') {
+ --end;
+ }
+ int id = Integer.parseInt(num);
+ Snippet si = state.maps.getSnippet(id);
+ String klass = expunge(rawKlass);
+ String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName();
+ String file = "#" + id;
+ int line = si.outerWrap().wrapLineToSnippetLine(r.getLineNumber() - 1) + 1;
+ elems[i] = new StackTraceElement(klass, method, file, line);
+ } else if (r.getFileName().equals("<none>")) {
+ elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber());
+ } else {
+ elems[i] = r;
+ }
+ }
+ String msg = ex.getMessage();
+ if (msg.equals("<none>")) {
+ msg = null;
+ }
+ return new EvalException(msg, ex.getExceptionClassName(), elems);
+ }
+
+ private boolean isWrap(StackTraceElement ste) {
+ return prefixPattern.matcher(ste.getClassName()).find();
+ }
+
+ private DiagList modifierDiagnostics(ModifiersTree modtree,
+ final TreeDissector dis, boolean isAbstractProhibited) {
+
+ class ModifierDiagnostic extends Diag {
+
+ final boolean fatal;
+ final String message;
+
+ ModifierDiagnostic(List<Modifier> list, boolean fatal) {
+ this.fatal = fatal;
+ StringBuilder sb = new StringBuilder();
+ sb.append((list.size() > 1) ? "Modifiers " : "Modifier ");
+ for (Modifier mod : list) {
+ sb.append("'");
+ sb.append(mod.toString());
+ sb.append("' ");
+ }
+ sb.append("not permitted in top-level declarations");
+ if (!fatal) {
+ sb.append(", ignored");
+ }
+ this.message = sb.toString();
+ }
+
+ @Override
+ public boolean isError() {
+ return fatal;
+ }
+
+ @Override
+ public long getPosition() {
+ return dis.getStartPosition(modtree);
+ }
+
+ @Override
+ public long getStartPosition() {
+ return dis.getStartPosition(modtree);
+ }
+
+ @Override
+ public long getEndPosition() {
+ return dis.getEndPosition(modtree);
+ }
+
+ @Override
+ public String getCode() {
+ return fatal
+ ? "jdk.eval.error.illegal.modifiers"
+ : "jdk.eval.warn.illegal.modifiers";
+ }
+
+ @Override
+ public String getMessage(Locale locale) {
+ return message;
+ }
+
+ @Override
+ Unit unitOrNull() {
+ return null;
+ }
+ }
+
+ List<Modifier> list = new ArrayList<>();
+ boolean fatal = false;
+ for (Modifier mod : modtree.getFlags()) {
+ switch (mod) {
+ case SYNCHRONIZED:
+ case NATIVE:
+ list.add(mod);
+ fatal = true;
+ break;
+ case ABSTRACT:
+ if (isAbstractProhibited) {
+ list.add(mod);
+ fatal = true;
+ }
+ break;
+ case PUBLIC:
+ case PROTECTED:
+ case PRIVATE:
+ case STATIC:
+ case FINAL:
+ list.add(mod);
+ break;
+ }
+ }
+ return list.isEmpty()
+ ? new DiagList()
+ : new DiagList(new ModifierDiagnostic(list, fatal));
+ }
+
+}