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