1 /*
   2  * Copyright (c) 2014, 2016, 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 package jdk.jshell;
  26 
  27 import java.util.ArrayList;
  28 import java.util.Collection;
  29 import java.util.Collections;
  30 import java.util.List;
  31 import java.util.Locale;
  32 import java.util.regex.Matcher;
  33 import java.util.regex.Pattern;
  34 import java.util.stream.Collectors;
  35 import javax.lang.model.element.Modifier;
  36 import com.sun.source.tree.ArrayTypeTree;
  37 import com.sun.source.tree.AssignmentTree;
  38 import com.sun.source.tree.ClassTree;
  39 import com.sun.source.tree.ExpressionTree;
  40 import com.sun.source.tree.IdentifierTree;
  41 import com.sun.source.tree.MethodTree;
  42 import com.sun.source.tree.ModifiersTree;
  43 import com.sun.source.tree.Tree;
  44 import com.sun.source.tree.VariableTree;
  45 import com.sun.tools.javac.tree.JCTree;
  46 import com.sun.tools.javac.tree.Pretty;
  47 import java.io.IOException;
  48 import java.io.StringWriter;
  49 import java.io.Writer;
  50 import java.util.LinkedHashSet;
  51 import java.util.Set;
  52 import jdk.jshell.Key.ErroneousKey;
  53 import jdk.jshell.Key.MethodKey;
  54 import jdk.jshell.Key.TypeDeclKey;
  55 import jdk.jshell.Snippet.Kind;
  56 import jdk.jshell.Snippet.SubKind;
  57 import jdk.jshell.TaskFactory.AnalyzeTask;
  58 import jdk.jshell.TaskFactory.BaseTask;
  59 import jdk.jshell.TaskFactory.CompileTask;
  60 import jdk.jshell.TaskFactory.ParseTask;
  61 import jdk.jshell.TreeDissector.ExpressionInfo;
  62 import jdk.jshell.Wrap.Range;
  63 import jdk.jshell.Snippet.Status;
  64 import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
  65 import jdk.jshell.spi.ExecutionControl.ClassInstallException;
  66 import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
  67 import jdk.jshell.spi.ExecutionControl.InternalException;
  68 import jdk.jshell.spi.ExecutionControl.NotImplementedException;
  69 import jdk.jshell.spi.ExecutionControl.ResolutionException;
  70 import jdk.jshell.spi.ExecutionControl.RunException;
  71 import jdk.jshell.spi.ExecutionControl.UserException;
  72 import static java.util.stream.Collectors.toList;
  73 import static java.util.stream.Collectors.toSet;
  74 import static java.util.Collections.singletonList;
  75 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
  76 import static jdk.jshell.Util.DOIT_METHOD_NAME;
  77 import static jdk.jshell.Util.PREFIX_PATTERN;
  78 import static jdk.jshell.Util.expunge;
  79 import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
  80 import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
  81 import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
  82 import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
  83 
  84 /**
  85  * The Evaluation Engine. Source internal analysis, wrapping control,
  86  * compilation, declaration. redefinition, replacement, and execution.
  87  *
  88  * @author Robert Field
  89  */
  90 class Eval {
  91 
  92     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}_\\$]+|\\*))");
  93 
  94     private int varNumber = 0;
  95 
  96     private final JShell state;
  97 
  98     Eval(JShell state) {
  99         this.state = state;
 100     }
 101 
 102     /**
 103      * Evaluates a snippet of source.
 104      *
 105      * @param userSource the source of the snippet
 106      * @return the list of primary and update events
 107      * @throws IllegalStateException
 108      */
 109     List<SnippetEvent> eval(String userSource) throws IllegalStateException {
 110         List<SnippetEvent> allEvents = new ArrayList<>();
 111         for (Snippet snip : sourceToSnippets(userSource)) {
 112             if (snip.kind() == Kind.ERRONEOUS) {
 113                 state.maps.installSnippet(snip);
 114                 allEvents.add(new SnippetEvent(
 115                         snip, Status.NONEXISTENT, Status.REJECTED,
 116                         false, null, null, null));
 117             } else {
 118                 allEvents.addAll(declare(snip, snip.syntheticDiags()));
 119             }
 120         }
 121         return allEvents;
 122     }
 123 
 124     /**
 125      * Converts the user source of a snippet into a Snippet list -- Snippet will
 126      * have wrappers.
 127      *
 128      * @param userSource the source of the snippet
 129      * @return usually a singleton list of Snippet, but may be empty or multiple
 130      */
 131     List<Snippet> sourceToSnippetsWithWrappers(String userSource) {
 132         List<Snippet> snippets = sourceToSnippets(userSource);
 133         for (Snippet snip : snippets) {
 134             if (snip.outerWrap() == null) {
 135                 snip.setOuterWrap(
 136                         (snip.kind() == Kind.IMPORT)
 137                                 ? state.outerMap.wrapImport(snip.guts(), snip)
 138                                 : state.outerMap.wrapInTrialClass(snip.guts())
 139                 );
 140             }
 141         }
 142         return snippets;
 143     }
 144 
 145     /**
 146      * Converts the user source of a snippet into a Snippet object (or list of
 147      * objects in the case of: int x, y, z;).  Does not install the Snippets
 148      * or execute them.
 149      *
 150      * @param userSource the source of the snippet
 151      * @return usually a singleton list of Snippet, but may be empty or multiple
 152      */
 153     private List<Snippet> sourceToSnippets(String userSource) {
 154         String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
 155         if (compileSource.length() == 0) {
 156             return Collections.emptyList();
 157         }
 158         ParseTask pt = state.taskFactory.new ParseTask(compileSource);
 159         List<? extends Tree> units = pt.units();
 160         if (units.isEmpty()) {
 161             return compileFailResult(pt, userSource, Kind.ERRONEOUS);
 162         }
 163         Tree unitTree = units.get(0);
 164         if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
 165             return compileFailResult(pt, userSource, kindOfTree(unitTree));
 166         }
 167 
 168         // Erase illegal/ignored modifiers
 169         compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
 170 
 171         state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
 172         switch (unitTree.getKind()) {
 173             case IMPORT:
 174                 return processImport(userSource, compileSource);
 175             case VARIABLE:
 176                 return processVariables(userSource, units, compileSource, pt);
 177             case EXPRESSION_STATEMENT:
 178                 return processExpression(userSource, compileSource);
 179             case CLASS:
 180                 return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
 181             case ENUM:
 182                 return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
 183             case ANNOTATION_TYPE:
 184                 return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
 185             case INTERFACE:
 186                 return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
 187             case METHOD:
 188                 return processMethod(userSource, unitTree, compileSource, pt);
 189             default:
 190                 return processStatement(userSource, compileSource);
 191         }
 192     }
 193 
 194     private List<Snippet> processImport(String userSource, String compileSource) {
 195         Wrap guts = Wrap.simpleWrap(compileSource);
 196         Matcher mat = IMPORT_PATTERN.matcher(compileSource);
 197         String fullname;
 198         String name;
 199         boolean isStatic;
 200         if (mat.find()) {
 201             isStatic = mat.group("static") != null;
 202             name = mat.group("name");
 203             fullname = mat.group("fullname");
 204         } else {
 205             // bad import -- fake it
 206             isStatic = compileSource.contains("static");
 207             name = fullname = compileSource;
 208         }
 209         String fullkey = (isStatic ? "static-" : "") + fullname;
 210         boolean isStar = name.equals("*");
 211         String keyName = isStar
 212                 ? fullname
 213                 : name;
 214         SubKind snippetKind = isStar
 215                 ? (isStatic ? STATIC_IMPORT_ON_DEMAND_SUBKIND : TYPE_IMPORT_ON_DEMAND_SUBKIND)
 216                 : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
 217         Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
 218                 userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
 219         return singletonList(snip);
 220     }
 221 
 222     private static class EvalPretty extends Pretty {
 223 
 224         private final Writer out;
 225 
 226         public EvalPretty(Writer writer, boolean bln) {
 227             super(writer, bln);
 228             this.out = writer;
 229         }
 230 
 231         /**
 232          * Print string, DO NOT replacing all non-ascii character with unicode
 233          * escapes.
 234          */
 235         @Override
 236         public void print(Object o) throws IOException {
 237             out.write(o.toString());
 238         }
 239 
 240         static String prettyExpr(JCTree tree, boolean bln) {
 241             StringWriter out = new StringWriter();
 242             try {
 243                 new EvalPretty(out, bln).printExpr(tree);
 244             } catch (IOException e) {
 245                 throw new AssertionError(e);
 246             }
 247             return out.toString();
 248         }
 249     }
 250 
 251     private List<Snippet> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
 252         List<Snippet> snippets = new ArrayList<>();
 253         TreeDissector dis = TreeDissector.createByFirstClass(pt);
 254         for (Tree unitTree : units) {
 255             VariableTree vt = (VariableTree) unitTree;
 256             String name = vt.getName().toString();
 257             String typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
 258             Tree baseType = vt.getType();
 259             TreeDependencyScanner tds = new TreeDependencyScanner();
 260             tds.scan(baseType); // Not dependent on initializer
 261             StringBuilder sbBrackets = new StringBuilder();
 262             while (baseType instanceof ArrayTypeTree) {
 263                 //TODO handle annotations too
 264                 baseType = ((ArrayTypeTree) baseType).getType();
 265                 sbBrackets.append("[]");
 266             }
 267             Range rtype = dis.treeToRange(baseType);
 268             Range runit = dis.treeToRange(vt);
 269             runit = new Range(runit.begin, runit.end - 1);
 270             ExpressionTree it = vt.getInitializer();
 271             Range rinit = null;
 272             int nameMax = runit.end - 1;
 273             SubKind subkind;
 274             if (it != null) {
 275                 subkind = SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND;
 276                 rinit = dis.treeToRange(it);
 277                 nameMax = rinit.begin - 1;
 278             } else {
 279                 subkind = SubKind.VAR_DECLARATION_SUBKIND;
 280             }
 281             int nameStart = compileSource.lastIndexOf(name, nameMax);
 282             if (nameStart < 0) {
 283                 throw new AssertionError("Name '" + name + "' not found");
 284             }
 285             int nameEnd = nameStart + name.length();
 286             Range rname = new Range(nameStart, nameEnd);
 287             Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit);
 288             DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
 289             Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
 290                     name, subkind, typeName,
 291                     tds.declareReferences(), modDiag);
 292             snippets.add(snip);
 293         }
 294         return snippets;
 295     }
 296 
 297     private List<Snippet> processExpression(String userSource, String compileSource) {
 298         String name = null;
 299         ExpressionInfo ei = typeOfExpression(compileSource);
 300         ExpressionTree assignVar;
 301         Wrap guts;
 302         Snippet snip;
 303         if (ei != null && ei.isNonVoid) {
 304             String typeName = ei.typeName;
 305             SubKind subkind;
 306             if (ei.tree instanceof IdentifierTree) {
 307                 IdentifierTree id = (IdentifierTree) ei.tree;
 308                 name = id.getName().toString();
 309                 subkind = SubKind.VAR_VALUE_SUBKIND;
 310 
 311             } else if (ei.tree instanceof AssignmentTree
 312                     && (assignVar = ((AssignmentTree) ei.tree).getVariable()) instanceof IdentifierTree) {
 313                 name = assignVar.toString();
 314                 subkind = SubKind.ASSIGNMENT_SUBKIND;
 315             } else {
 316                 subkind = SubKind.OTHER_EXPRESSION_SUBKIND;
 317             }
 318             if (shouldGenTempVar(subkind)) {
 319                 if (state.tempVariableNameGenerator != null) {
 320                     name = state.tempVariableNameGenerator.get();
 321                 }
 322                 while (name == null || state.keyMap.doesVariableNameExist(name)) {
 323                     name = "$" + ++varNumber;
 324                 }
 325                 guts = Wrap.tempVarWrap(compileSource, typeName, name);
 326                 Collection<String> declareReferences = null; //TODO
 327                 snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
 328                         name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences, null);
 329             } else {
 330                 guts = Wrap.methodReturnWrap(compileSource);
 331                 snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
 332                         name, subkind);
 333             }
 334         } else {
 335             guts = Wrap.methodWrap(compileSource);
 336             if (ei == null) {
 337                 // We got no type info, check for not a statement by trying
 338                 AnalyzeTask at = trialCompile(guts);
 339                 if (at.getDiagnostics().hasNotStatement()) {
 340                     guts = Wrap.methodReturnWrap(compileSource);
 341                     at = trialCompile(guts);
 342                 }
 343                 if (at.hasErrors()) {
 344                     return compileFailResult(at, userSource, Kind.EXPRESSION);
 345                 }
 346             }
 347             snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
 348         }
 349         return singletonList(snip);
 350     }
 351 
 352     private List<Snippet> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
 353         TreeDependencyScanner tds = new TreeDependencyScanner();
 354         tds.scan(unitTree);
 355 
 356         TreeDissector dis = TreeDissector.createByFirstClass(pt);
 357 
 358         ClassTree klassTree = (ClassTree) unitTree;
 359         String name = klassTree.getSimpleName().toString();
 360         DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
 361         TypeDeclKey key = state.keyMap.keyForClass(name);
 362         // Corralling mutates.  Must be last use of pt, unitTree, klassTree
 363         Wrap corralled = new Corraller(key.index(), pt.getContext()).corralType(klassTree);
 364 
 365         Wrap guts = Wrap.classMemberWrap(compileSource);
 366         Snippet snip = new TypeDeclSnippet(key, userSource, guts,
 367                 name, snippetKind,
 368                 corralled, tds.declareReferences(), tds.bodyReferences(), modDiag);
 369         return singletonList(snip);
 370     }
 371 
 372     private List<Snippet> processStatement(String userSource, String compileSource) {
 373         Wrap guts = Wrap.methodWrap(compileSource);
 374         // Check for unreachable by trying
 375         AnalyzeTask at = trialCompile(guts);
 376         if (at.hasErrors()) {
 377             if (at.getDiagnostics().hasUnreachableError()) {
 378                 guts = Wrap.methodUnreachableSemiWrap(compileSource);
 379                 at = trialCompile(guts);
 380                 if (at.hasErrors()) {
 381                     if (at.getDiagnostics().hasUnreachableError()) {
 382                         // Without ending semicolon
 383                         guts = Wrap.methodUnreachableWrap(compileSource);
 384                         at = trialCompile(guts);
 385                     }
 386                     if (at.hasErrors()) {
 387                         return compileFailResult(at, userSource, Kind.STATEMENT);
 388                     }
 389                 }
 390             } else {
 391                 return compileFailResult(at, userSource, Kind.STATEMENT);
 392             }
 393         }
 394         Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
 395         return singletonList(snip);
 396     }
 397 
 398     private AnalyzeTask trialCompile(Wrap guts) {
 399         OuterWrap outer = state.outerMap.wrapInTrialClass(guts);
 400         return state.taskFactory.new AnalyzeTask(outer);
 401     }
 402 
 403     private List<Snippet> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
 404         TreeDependencyScanner tds = new TreeDependencyScanner();
 405         tds.scan(unitTree);
 406         TreeDissector dis = TreeDissector.createByFirstClass(pt);
 407 
 408         MethodTree mt = (MethodTree) unitTree;
 409         String name = mt.getName().toString();
 410         String parameterTypes
 411                 = mt.getParameters()
 412                 .stream()
 413                 .map(param -> dis.treeToRange(param.getType()).part(compileSource))
 414                 .collect(Collectors.joining(","));
 415         Tree returnType = mt.getReturnType();
 416         DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
 417         MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
 418         // Corralling mutates.  Must be last use of pt, unitTree, mt
 419         Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt);
 420 
 421         if (modDiag.hasErrors()) {
 422             return compileFailResult(modDiag, userSource, Kind.METHOD);
 423         }
 424         Wrap guts = Wrap.classMemberWrap(compileSource);
 425         Range typeRange = dis.treeToRange(returnType);
 426         String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
 427 
 428         Snippet snip = new MethodSnippet(key, userSource, guts,
 429                 name, signature,
 430                 corralled, tds.declareReferences(), tds.bodyReferences(), modDiag);
 431         return singletonList(snip);
 432     }
 433 
 434     private Kind kindOfTree(Tree tree) {
 435         switch (tree.getKind()) {
 436             case IMPORT:
 437                 return Kind.IMPORT;
 438             case VARIABLE:
 439                 return Kind.VAR;
 440             case EXPRESSION_STATEMENT:
 441                 return Kind.EXPRESSION;
 442             case CLASS:
 443             case ENUM:
 444             case ANNOTATION_TYPE:
 445             case INTERFACE:
 446                 return Kind.TYPE_DECL;
 447             case METHOD:
 448                 return Kind.METHOD;
 449             default:
 450                 return Kind.STATEMENT;
 451         }
 452     }
 453 
 454     /**
 455      * The snippet has failed, return with the rejected snippet
 456      *
 457      * @param xt the task from which to extract the failure diagnostics
 458      * @param userSource the incoming bad user source
 459      * @return a rejected snippet
 460      */
 461     private List<Snippet> compileFailResult(BaseTask xt, String userSource, Kind probableKind) {
 462         return compileFailResult(xt.getDiagnostics(), userSource, probableKind);
 463     }
 464 
 465     /**
 466      * The snippet has failed, return with the rejected snippet
 467      *
 468      * @param diags the failure diagnostics
 469      * @param userSource the incoming bad user source
 470      * @return a rejected snippet
 471      */
 472     private List<Snippet> compileFailResult(DiagList diags, String userSource, Kind probableKind) {
 473         ErroneousKey key = state.keyMap.keyForErroneous();
 474         Snippet snip = new ErroneousSnippet(key, userSource, null,
 475                 probableKind, SubKind.UNKNOWN_SUBKIND);
 476         snip.setFailed(diags);
 477 
 478         // Install  wrapper for query by SourceCodeAnalysis.wrapper
 479         String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, true).cleared());
 480         OuterWrap outer;
 481         switch (probableKind) {
 482             case IMPORT:
 483                 outer = state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);
 484                 break;
 485             case EXPRESSION:
 486                 outer = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));
 487                 break;
 488             case VAR:
 489             case TYPE_DECL:
 490             case METHOD:
 491                 outer = state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));
 492                 break;
 493             default:
 494                 outer = state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
 495                 break;
 496         }
 497         snip.setOuterWrap(outer);
 498 
 499         return singletonList(snip);
 500     }
 501 
 502     private ExpressionInfo typeOfExpression(String expression) {
 503         Wrap guts = Wrap.methodReturnWrap(expression);
 504         TaskFactory.AnalyzeTask at = trialCompile(guts);
 505         if (!at.hasErrors() && at.firstCuTree() != null) {
 506             return TreeDissector.createByFirstClass(at)
 507                     .typeOfReturnStatement(at, state);
 508         }
 509         return null;
 510     }
 511 
 512     /**
 513      * Should a temp var wrap the expression. TODO make this user configurable.
 514      *
 515      * @param snippetKind
 516      * @return
 517      */
 518     private boolean shouldGenTempVar(SubKind snippetKind) {
 519         return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND;
 520     }
 521 
 522     List<SnippetEvent> drop(Snippet si) {
 523         Unit c = new Unit(state, si);
 524 
 525         Set<Unit> ins = c.dependents().collect(toSet());
 526         Set<Unit> outs = compileAndLoad(ins);
 527 
 528         return events(c, outs, null, null);
 529     }
 530 
 531     private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
 532         Unit c = new Unit(state, si, null, generatedDiagnostics);
 533         Set<Unit> ins = new LinkedHashSet<>();
 534         ins.add(c);
 535         Set<Unit> outs = compileAndLoad(ins);
 536 
 537         if (!si.status().isDefined()
 538                 && si.diagnostics().isEmpty()
 539                 && si.unresolved().isEmpty()) {
 540             // did not succeed, but no record of it, extract from others
 541             si.setDiagnostics(outs.stream()
 542                     .flatMap(u -> u.snippet().diagnostics().stream())
 543                     .collect(Collectors.toCollection(DiagList::new)));
 544         }
 545 
 546         // If appropriate, execute the snippet
 547         String value = null;
 548         JShellException exception = null;
 549         if (si.status().isDefined()) {
 550             if (si.isExecutable()) {
 551                 try {
 552                     value = state.executionControl().invoke(si.classFullName(), DOIT_METHOD_NAME);
 553                     value = si.subKind().hasValue()
 554                             ? expunge(value)
 555                             : "";
 556                 } catch (ResolutionException ex) {
 557                     DeclarationSnippet sn = (DeclarationSnippet) state.maps.getSnippetDeadOrAlive(ex.id());
 558                     exception = new UnresolvedReferenceException(sn, ex.getStackTrace());
 559                 } catch (UserException ex) {
 560                     exception = translateExecutionException(ex);
 561                 } catch (RunException ex) {
 562                     // StopException - no-op
 563                 } catch (InternalException ex) {
 564                     state.debug(ex, "invoke");
 565                 } catch (EngineTerminationException ex) {
 566                     state.closeDown();
 567                 }
 568             } else if (si.subKind() == SubKind.VAR_DECLARATION_SUBKIND) {
 569                 switch (((VarSnippet) si).typeName()) {
 570                     case "byte":
 571                     case "short":
 572                     case "int":
 573                     case "long":
 574                         value = "0";
 575                         break;
 576                     case "float":
 577                     case "double":
 578                         value = "0.0";
 579                         break;
 580                     case "boolean":
 581                         value = "false";
 582                         break;
 583                     case "char":
 584                         value = "''";
 585                         break;
 586                     default:
 587                         value = "null";
 588                         break;
 589                 }
 590             }
 591         }
 592         return events(c, outs, value, exception);
 593     }
 594 
 595     private boolean interestingEvent(SnippetEvent e) {
 596         return e.isSignatureChange()
 597                     || e.causeSnippet() == null
 598                     || e.status() != e.previousStatus()
 599                     || e.exception() != null;
 600     }
 601 
 602     private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, JShellException exception) {
 603         List<SnippetEvent> events = new ArrayList<>();
 604         events.add(c.event(value, exception));
 605         events.addAll(outs.stream()
 606                 .filter(u -> u != c)
 607                 .map(u -> u.event(null, null))
 608                 .filter(this::interestingEvent)
 609                 .collect(Collectors.toList()));
 610         events.addAll(outs.stream()
 611                 .flatMap(u -> u.secondaryEvents().stream())
 612                 .filter(this::interestingEvent)
 613                 .collect(Collectors.toList()));
 614         //System.err.printf("Events: %s\n", events);
 615         return events;
 616     }
 617 
 618     private Set<OuterWrap> outerWrapSet(Collection<Unit> units) {
 619         return units.stream()
 620                 .map(u -> u.snippet().outerWrap())
 621                 .collect(toSet());
 622     }
 623 
 624     private Set<Unit> compileAndLoad(Set<Unit> ins) {
 625         if (ins.isEmpty()) {
 626             return ins;
 627         }
 628         Set<Unit> replaced = new LinkedHashSet<>();
 629         // Loop until dependencies and errors are stable
 630         while (true) {
 631             state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
 632 
 633             ins.stream().forEach(u -> u.initialize());
 634             ins.stream().forEach(u -> u.setWrap(ins, ins));
 635             AnalyzeTask at = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
 636             ins.stream().forEach(u -> u.setDiagnostics(at));
 637 
 638             // corral any Snippets that need it
 639             AnalyzeTask cat;
 640             if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
 641                 // if any were corralled, re-analyze everything
 642                 cat = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
 643                 ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
 644             } else {
 645                 cat = at;
 646             }
 647             ins.stream().forEach(u -> u.setStatus(cat));
 648             // compile and load the legit snippets
 649             boolean success;
 650             while (true) {
 651                 List<Unit> legit = ins.stream()
 652                         .filter(u -> u.isDefined())
 653                         .collect(toList());
 654                 state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
 655                         ins, legit);
 656                 if (legit.isEmpty()) {
 657                     // no class files can be generated
 658                     success = true;
 659                 } else {
 660                     // re-wrap with legit imports
 661                     legit.stream().forEach(u -> u.setWrap(ins, legit));
 662 
 663                     // generate class files for those capable
 664                     CompileTask ct = state.taskFactory.new CompileTask(outerWrapSet(legit));
 665                     if (!ct.compile()) {
 666                         // oy! compile failed because of recursive new unresolved
 667                         if (legit.stream()
 668                                 .filter(u -> u.smashingErrorDiagnostics(ct))
 669                                 .count() > 0) {
 670                             // try again, with the erroreous removed
 671                             continue;
 672                         } else {
 673                             state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
 674                                     legit);
 675                         }
 676                     }
 677 
 678                     // load all new classes
 679                     load(legit.stream()
 680                             .flatMap(u -> u.classesToLoad(ct.classList(u.snippet().outerWrap())))
 681                             .collect(toSet()));
 682                     // attempt to redefine the remaining classes
 683                     List<Unit> toReplace = legit.stream()
 684                             .filter(u -> !u.doRedefines())
 685                             .collect(toList());
 686 
 687                     // prevent alternating redefine/replace cyclic dependency
 688                     // loop by replacing all that have been replaced
 689                     if (!toReplace.isEmpty()) {
 690                         replaced.addAll(toReplace);
 691                         replaced.stream().forEach(u -> u.markForReplacement());
 692                     }
 693 
 694                     success = toReplace.isEmpty();
 695                 }
 696                 break;
 697             }
 698 
 699             // add any new dependencies to the working set
 700             List<Unit> newDependencies = ins.stream()
 701                     .flatMap(u -> u.effectedDependents())
 702                     .collect(toList());
 703             state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s  success: %s\n",
 704                     ins, newDependencies, success);
 705             if (!ins.addAll(newDependencies) && success) {
 706                 // all classes that could not be directly loaded (because they
 707                 // are new) have been redefined, and no new dependnencies were
 708                 // identified
 709                 ins.stream().forEach(u -> u.finish());
 710                 return ins;
 711             }
 712         }
 713     }
 714 
 715     /**
 716      * If there are classes to load, loads by calling the execution engine.
 717      * @param classbytecoes names of the classes to load.
 718      */
 719     private void load(Collection<ClassBytecodes> classbytecoes) {
 720         if (!classbytecoes.isEmpty()) {
 721             ClassBytecodes[] cbcs = classbytecoes.toArray(new ClassBytecodes[classbytecoes.size()]);
 722             try {
 723                 state.executionControl().load(cbcs);
 724                 state.classTracker.markLoaded(cbcs);
 725             } catch (ClassInstallException ex) {
 726                 state.classTracker.markLoaded(cbcs, ex.installed());
 727             } catch (NotImplementedException ex) {
 728                 state.debug(ex, "Seriously?!? load not implemented");
 729             } catch (EngineTerminationException ex) {
 730                 state.closeDown();
 731             }
 732         }
 733     }
 734 
 735     private EvalException translateExecutionException(UserException ex) {
 736         StackTraceElement[] raw = ex.getStackTrace();
 737         int last = raw.length;
 738         do {
 739             if (last == 0) {
 740                 last = raw.length - 1;
 741                 break;
 742             }
 743         } while (!isWrap(raw[--last]));
 744         StackTraceElement[] elems = new StackTraceElement[last + 1];
 745         for (int i = 0; i <= last; ++i) {
 746             StackTraceElement r = raw[i];
 747             OuterSnippetsClassWrap outer = state.outerMap.getOuter(r.getClassName());
 748             if (outer != null) {
 749                 String klass = expunge(r.getClassName());
 750                 String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName();
 751                 int wln = r.getLineNumber() - 1;
 752                 int line = outer.wrapLineToSnippetLine(wln) + 1;
 753                 Snippet sn = outer.wrapLineToSnippet(wln);
 754                 String file = "#" + sn.id();
 755                 elems[i] = new StackTraceElement(klass, method, file, line);
 756             } else if (r.getFileName().equals("<none>")) {
 757                 elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber());
 758             } else {
 759                 elems[i] = r;
 760             }
 761         }
 762         String msg = ex.getMessage();
 763         if (msg.equals("<none>")) {
 764             msg = null;
 765         }
 766         return new EvalException(msg, ex.causeExceptionClass(), elems);
 767     }
 768 
 769     private boolean isWrap(StackTraceElement ste) {
 770         return PREFIX_PATTERN.matcher(ste.getClassName()).find();
 771     }
 772 
 773     private DiagList modifierDiagnostics(ModifiersTree modtree,
 774             final TreeDissector dis, boolean isAbstractProhibited) {
 775 
 776         class ModifierDiagnostic extends Diag {
 777 
 778             final boolean fatal;
 779             final String message;
 780 
 781             ModifierDiagnostic(List<Modifier> list, boolean fatal) {
 782                 this.fatal = fatal;
 783                 StringBuilder sb = new StringBuilder();
 784                 for (Modifier mod : list) {
 785                     sb.append("'");
 786                     sb.append(mod.toString());
 787                     sb.append("' ");
 788                 }
 789                 String key = (list.size() > 1)
 790                         ? fatal
 791                             ? "jshell.diag.modifier.plural.fatal"
 792                             : "jshell.diag.modifier.plural.ignore"
 793                         : fatal
 794                             ? "jshell.diag.modifier.single.fatal"
 795                             : "jshell.diag.modifier.single.ignore";
 796                 this.message = state.messageFormat(key, sb.toString());
 797             }
 798 
 799             @Override
 800             public boolean isError() {
 801                 return fatal;
 802             }
 803 
 804             @Override
 805             public long getPosition() {
 806                 return dis.getStartPosition(modtree);
 807             }
 808 
 809             @Override
 810             public long getStartPosition() {
 811                 return dis.getStartPosition(modtree);
 812             }
 813 
 814             @Override
 815             public long getEndPosition() {
 816                 return dis.getEndPosition(modtree);
 817             }
 818 
 819             @Override
 820             public String getCode() {
 821                 return fatal
 822                         ? "jdk.eval.error.illegal.modifiers"
 823                         : "jdk.eval.warn.illegal.modifiers";
 824             }
 825 
 826             @Override
 827             public String getMessage(Locale locale) {
 828                 return message;
 829             }
 830         }
 831 
 832         List<Modifier> list = new ArrayList<>();
 833         boolean fatal = false;
 834         for (Modifier mod : modtree.getFlags()) {
 835             switch (mod) {
 836                 case SYNCHRONIZED:
 837                 case NATIVE:
 838                     list.add(mod);
 839                     fatal = true;
 840                     break;
 841                 case ABSTRACT:
 842                     if (isAbstractProhibited) {
 843                         list.add(mod);
 844                         fatal = true;
 845                     }
 846                     break;
 847                 case PUBLIC:
 848                 case PROTECTED:
 849                 case PRIVATE:
 850                 case STATIC:
 851                 case FINAL:
 852                     list.add(mod);
 853                     break;
 854             }
 855         }
 856         return list.isEmpty()
 857                 ? new DiagList()
 858                 : new DiagList(new ModifierDiagnostic(list, fatal));
 859     }
 860 
 861 }