1 /*
   2  * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jshell;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collection;
  30 import java.util.Collections;
  31 import java.util.List;
  32 import java.util.Locale;
  33 import java.util.regex.Matcher;
  34 import java.util.regex.Pattern;
  35 import java.util.stream.Collectors;
  36 import javax.lang.model.element.Modifier;
  37 import com.sun.source.tree.ArrayTypeTree;
  38 import com.sun.source.tree.AssignmentTree;
  39 import com.sun.source.tree.ClassTree;
  40 import com.sun.source.tree.ExpressionTree;
  41 import com.sun.source.tree.IdentifierTree;
  42 import com.sun.source.tree.MethodTree;
  43 import com.sun.source.tree.ModifiersTree;
  44 import com.sun.source.tree.Tree;
  45 import com.sun.source.tree.VariableTree;
  46 import com.sun.tools.javac.tree.JCTree;
  47 import com.sun.tools.javac.tree.Pretty;
  48 import java.io.IOException;
  49 import java.io.StringWriter;
  50 import java.io.Writer;
  51 import java.util.LinkedHashSet;
  52 import java.util.Set;
  53 import jdk.jshell.ClassTracker.ClassInfo;
  54 import jdk.jshell.Key.ErroneousKey;
  55 import jdk.jshell.Key.MethodKey;
  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 static java.util.stream.Collectors.toList;
  65 import static java.util.stream.Collectors.toSet;
  66 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
  67 import static jdk.jshell.Util.*;
  68 import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
  69 import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
  70 import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
  71 import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
  72 import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
  73 import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
  74 
  75 /**
  76  * The Evaluation Engine. Source internal analysis, wrapping control,
  77  * compilation, declaration. redefinition, replacement, and execution.
  78  *
  79  * @author Robert Field
  80  */
  81 class Eval {
  82 
  83     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}_\\$]+|\\*))");
  84 
  85     private int varNumber = 0;
  86 
  87     private final JShell state;
  88 
  89     Eval(JShell state) {
  90         this.state = state;
  91     }
  92 
  93     List<SnippetEvent> eval(String userSource) throws IllegalStateException {
  94         String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
  95         if (compileSource.length() == 0) {
  96             return Collections.emptyList();
  97         }
  98         // String folding messes up position information.
  99         ParseTask pt = state.taskFactory.new ParseTask(compileSource);
 100         if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
 101             return compileFailResult(pt, userSource);
 102         }
 103 
 104         List<? extends Tree> units = pt.units();
 105         if (units.isEmpty()) {
 106             return compileFailResult(pt, userSource);
 107         }
 108         // Erase illegal modifiers
 109         compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
 110         Tree unitTree = units.get(0);
 111         state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
 112         switch (unitTree.getKind()) {
 113             case IMPORT:
 114                 return processImport(userSource, compileSource);
 115             case VARIABLE:
 116                 return processVariables(userSource, units, compileSource, pt);
 117             case EXPRESSION_STATEMENT:
 118                 return processExpression(userSource, compileSource);
 119             case CLASS:
 120                 return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
 121             case ENUM:
 122                 return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
 123             case ANNOTATION_TYPE:
 124                 return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
 125             case INTERFACE:
 126                 return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
 127             case METHOD:
 128                 return processMethod(userSource, unitTree, compileSource, pt);
 129             default:
 130                 return processStatement(userSource, compileSource);
 131         }
 132     }
 133 
 134     private List<SnippetEvent> processImport(String userSource, String compileSource) {
 135         Wrap guts = Wrap.importWrap(compileSource);
 136         Matcher mat = IMPORT_PATTERN.matcher(compileSource);
 137         String fullname;
 138         String name;
 139         boolean isStatic;
 140         if (mat.find()) {
 141             isStatic = mat.group("static") != null;
 142             name = mat.group("name");
 143             fullname = mat.group("fullname");
 144         } else {
 145             // bad import -- fake it
 146             isStatic = compileSource.contains("static");
 147             name = fullname = compileSource;
 148         }
 149         String fullkey = (isStatic ? "static-" : "") + fullname;
 150         boolean isStar = name.equals("*");
 151         String keyName = isStar
 152                 ? fullname
 153                 : name;
 154         SubKind snippetKind = isStar
 155                 ? (isStatic ? STATIC_IMPORT_ON_DEMAND_SUBKIND : TYPE_IMPORT_ON_DEMAND_SUBKIND)
 156                 : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
 157         Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
 158                 userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
 159         return declare(snip);
 160     }
 161 
 162     private static class EvalPretty extends Pretty {
 163 
 164         private final Writer out;
 165 
 166         public EvalPretty(Writer writer, boolean bln) {
 167             super(writer, bln);
 168             this.out = writer;
 169         }
 170 
 171         /**
 172          * Print string, DO NOT replacing all non-ascii character with unicode
 173          * escapes.
 174          */
 175         @Override
 176         public void print(Object o) throws IOException {
 177             out.write(o.toString());
 178         }
 179 
 180         static String prettyExpr(JCTree tree, boolean bln) {
 181             StringWriter out = new StringWriter();
 182             try {
 183                 new EvalPretty(out, bln).printExpr(tree);
 184             } catch (IOException e) {
 185                 throw new AssertionError(e);
 186             }
 187             return out.toString();
 188         }
 189     }
 190 
 191     private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
 192         List<SnippetEvent> allEvents = new ArrayList<>();
 193         TreeDissector dis = TreeDissector.createByFirstClass(pt);
 194         for (Tree unitTree : units) {
 195             VariableTree vt = (VariableTree) unitTree;
 196             String name = vt.getName().toString();
 197             String typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
 198             Tree baseType = vt.getType();
 199             TreeDependencyScanner tds = new TreeDependencyScanner();
 200             tds.scan(baseType); // Not dependent on initializer
 201             StringBuilder sbBrackets = new StringBuilder();
 202             while (baseType instanceof ArrayTypeTree) {
 203                 //TODO handle annotations too
 204                 baseType = ((ArrayTypeTree) baseType).getType();
 205                 sbBrackets.append("[]");
 206             }
 207             Range rtype = dis.treeToRange(baseType);
 208             Range runit = dis.treeToRange(vt);
 209             runit = new Range(runit.begin, runit.end - 1);
 210             ExpressionTree it = vt.getInitializer();
 211             Range rinit = null;
 212             int nameMax = runit.end - 1;
 213             SubKind subkind;
 214             if (it != null) {
 215                 subkind = SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND;
 216                 rinit = dis.treeToRange(it);
 217                 nameMax = rinit.begin - 1;
 218             } else {
 219                 subkind = SubKind.VAR_DECLARATION_SUBKIND;
 220             }
 221             int nameStart = compileSource.lastIndexOf(name, nameMax);
 222             if (nameStart < 0) {
 223                 throw new AssertionError("Name '" + name + "' not found");
 224             }
 225             int nameEnd = nameStart + name.length();
 226             Range rname = new Range(nameStart, nameEnd);
 227             Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit);
 228             Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
 229                     name, subkind, typeName,
 230                     tds.declareReferences());
 231             DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
 232             List<SnippetEvent> res1 = declare(snip, modDiag);
 233             allEvents.addAll(res1);
 234         }
 235 
 236         return allEvents;
 237     }
 238 
 239     private List<SnippetEvent> processExpression(String userSource, String compileSource) {
 240         String name = null;
 241         ExpressionInfo ei = typeOfExpression(compileSource);
 242         ExpressionTree assignVar;
 243         Wrap guts;
 244         Snippet snip;
 245         if (ei != null && ei.isNonVoid) {
 246             String typeName = ei.typeName;
 247             SubKind subkind;
 248             if (ei.tree instanceof IdentifierTree) {
 249                 IdentifierTree id = (IdentifierTree) ei.tree;
 250                 name = id.getName().toString();
 251                 subkind = SubKind.VAR_VALUE_SUBKIND;
 252 
 253             } else if (ei.tree instanceof AssignmentTree
 254                     && (assignVar = ((AssignmentTree) ei.tree).getVariable()) instanceof IdentifierTree) {
 255                 name = assignVar.toString();
 256                 subkind = SubKind.ASSIGNMENT_SUBKIND;
 257             } else {
 258                 subkind = SubKind.OTHER_EXPRESSION_SUBKIND;
 259             }
 260             if (shouldGenTempVar(subkind)) {
 261                 if (state.tempVariableNameGenerator != null) {
 262                     name = state.tempVariableNameGenerator.get();
 263                 }
 264                 while (name == null || state.keyMap.doesVariableNameExist(name)) {
 265                     name = "$" + ++varNumber;
 266                 }
 267                 guts = Wrap.tempVarWrap(compileSource, typeName, name);
 268                 Collection<String> declareReferences = null; //TODO
 269                 snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
 270                         name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences);
 271             } else {
 272                 guts = Wrap.methodReturnWrap(compileSource);
 273                 snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
 274                         name, subkind);
 275             }
 276         } else {
 277             guts = Wrap.methodWrap(compileSource);
 278             if (ei == null) {
 279                 // We got no type info, check for not a statement by trying
 280                 AnalyzeTask at = trialCompile(guts);
 281                 if (at.getDiagnostics().hasNotStatement()) {
 282                     guts = Wrap.methodReturnWrap(compileSource);
 283                     at = trialCompile(guts);
 284                 }
 285                 if (at.hasErrors()) {
 286                     return compileFailResult(at, userSource);
 287                 }
 288             }
 289             snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
 290         }
 291         return declare(snip);
 292     }
 293 
 294     private List<SnippetEvent> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
 295         TreeDependencyScanner tds = new TreeDependencyScanner();
 296         tds.scan(unitTree);
 297 
 298         TreeDissector dis = TreeDissector.createByFirstClass(pt);
 299 
 300         ClassTree klassTree = (ClassTree) unitTree;
 301         String name = klassTree.getSimpleName().toString();
 302         Wrap guts = Wrap.classMemberWrap(compileSource);
 303         Wrap corralled = null; //TODO
 304         Snippet snip = new TypeDeclSnippet(state.keyMap.keyForClass(name), userSource, guts,
 305                 name, snippetKind,
 306                 corralled, tds.declareReferences(), tds.bodyReferences());
 307         DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
 308         return declare(snip, modDiag);
 309     }
 310 
 311     private List<SnippetEvent> processStatement(String userSource, String compileSource) {
 312         Wrap guts = Wrap.methodWrap(compileSource);
 313         // Check for unreachable by trying
 314         AnalyzeTask at = trialCompile(guts);
 315         if (at.hasErrors()) {
 316             if (at.getDiagnostics().hasUnreachableError()) {
 317                 guts = Wrap.methodUnreachableSemiWrap(compileSource);
 318                 at = trialCompile(guts);
 319                 if (at.hasErrors()) {
 320                     if (at.getDiagnostics().hasUnreachableError()) {
 321                         // Without ending semicolon
 322                         guts = Wrap.methodUnreachableWrap(compileSource);
 323                         at = trialCompile(guts);
 324                     }
 325                     if (at.hasErrors()) {
 326                         return compileFailResult(at, userSource);
 327                     }
 328                 }
 329             } else {
 330                 return compileFailResult(at, userSource);
 331             }
 332         }
 333         Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
 334         return declare(snip);
 335     }
 336 
 337     private OuterWrap wrapInClass(String className, Set<Key> except, String userSource, Wrap guts, Collection<Snippet> plus) {
 338         String imports = state.maps.packageAndImportsExcept(except, plus);
 339         return OuterWrap.wrapInClass(state.maps.packageName(), className, imports, userSource, guts);
 340     }
 341 
 342     OuterWrap wrapInClass(Snippet snip, Set<Key> except, Wrap guts, Collection<Snippet> plus) {
 343         return wrapInClass(snip.className(), except, snip.source(), guts, plus);
 344     }
 345 
 346     private AnalyzeTask trialCompile(Wrap guts) {
 347         OuterWrap outer = wrapInClass(REPL_DOESNOTMATTER_CLASS_NAME,
 348                 Collections.emptySet(), "", guts, null);
 349         return state.taskFactory.new AnalyzeTask(outer);
 350     }
 351 
 352     private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
 353         TreeDependencyScanner tds = new TreeDependencyScanner();
 354         tds.scan(unitTree);
 355 
 356         MethodTree mt = (MethodTree) unitTree;
 357         TreeDissector dis = TreeDissector.createByFirstClass(pt);
 358         DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
 359         if (modDiag.hasErrors()) {
 360             return compileFailResult(modDiag, userSource);
 361         }
 362         String unitName = mt.getName().toString();
 363         Wrap guts = Wrap.classMemberWrap(compileSource);
 364 
 365         Range modRange = dis.treeToRange(mt.getModifiers());
 366         Range tpRange = dis.treeListToRange(mt.getTypeParameters());
 367         Range typeRange = dis.treeToRange(mt.getReturnType());
 368         String name = mt.getName().toString();
 369         Range paramRange = dis.treeListToRange(mt.getParameters());
 370         Range throwsRange = dis.treeListToRange(mt.getThrows());
 371 
 372         String parameterTypes
 373                 = mt.getParameters()
 374                 .stream()
 375                 .map(param -> dis.treeToRange(param.getType()).part(compileSource))
 376                 .collect(Collectors.joining(","));
 377         String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
 378 
 379         MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
 380         // rewrap with correct Key index
 381         Wrap corralled = Wrap.corralledMethod(compileSource,
 382                 modRange, tpRange, typeRange, name, paramRange, throwsRange, key.index());
 383         Snippet snip = new MethodSnippet(key, userSource, guts,
 384                 unitName, signature,
 385                 corralled, tds.declareReferences(), tds.bodyReferences());
 386         return declare(snip, modDiag);
 387     }
 388 
 389     /**
 390      * The snippet has failed, return with the rejected event
 391      *
 392      * @param xt the task from which to extract the failure diagnostics
 393      * @param userSource the incoming bad user source
 394      * @return a rejected snippet event
 395      */
 396     private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) {
 397         return compileFailResult(xt.getDiagnostics(), userSource);
 398     }
 399 
 400     /**
 401      * The snippet has failed, return with the rejected event
 402      *
 403      * @param diags the failure diagnostics
 404      * @param userSource the incoming bad user source
 405      * @return a rejected snippet event
 406      */
 407     private List<SnippetEvent> compileFailResult(DiagList diags, String userSource) {
 408         ErroneousKey key = state.keyMap.keyForErroneous();
 409         Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND);
 410         snip.setFailed(diags);
 411         state.maps.installSnippet(snip);
 412         return Collections.singletonList(new SnippetEvent(
 413                 snip, Status.NONEXISTENT, Status.REJECTED,
 414                 false, null, null, null)
 415         );
 416     }
 417 
 418     private ExpressionInfo typeOfExpression(String expression) {
 419         Wrap guts = Wrap.methodReturnWrap(expression);
 420         TaskFactory.AnalyzeTask at = trialCompile(guts);
 421         if (!at.hasErrors() && at.firstCuTree() != null) {
 422             return TreeDissector.createByFirstClass(at)
 423                     .typeOfReturnStatement(at, state);
 424         }
 425         return null;
 426     }
 427 
 428     /**
 429      * Should a temp var wrap the expression. TODO make this user configurable.
 430      *
 431      * @param snippetKind
 432      * @return
 433      */
 434     private boolean shouldGenTempVar(SubKind snippetKind) {
 435         return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND;
 436     }
 437 
 438     List<SnippetEvent> drop(Snippet si) {
 439         Unit c = new Unit(state, si);
 440 
 441         Set<Unit> ins = c.dependents().collect(toSet());
 442         Set<Unit> outs = compileAndLoad(ins);
 443 
 444         return events(c, outs, null, null);
 445     }
 446 
 447     private List<SnippetEvent> declare(Snippet si) {
 448         return declare(si, new DiagList());
 449     }
 450 
 451     private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
 452         Unit c = new Unit(state, si, null, generatedDiagnostics);
 453 
 454         // Ignores duplicates
 455         //TODO: remove, modify, or move to edit
 456         if (c.isRedundant()) {
 457             return Collections.emptyList();
 458         }
 459 
 460         Set<Unit> ins = new LinkedHashSet<>();
 461         ins.add(c);
 462         Set<Unit> outs = compileAndLoad(ins);
 463 
 464         if (!si.status().isDefined
 465                 && si.diagnostics().isEmpty()
 466                 && si.unresolved().isEmpty()) {
 467             // did not succeed, but no record of it, extract from others
 468             si.setDiagnostics(outs.stream()
 469                     .flatMap(u -> u.snippet().diagnostics().stream())
 470                     .collect(Collectors.toCollection(DiagList::new)));
 471         }
 472 
 473         // If appropriate, execute the snippet
 474         String value = null;
 475         Exception exception = null;
 476         if (si.isExecutable() && si.status().isDefined) {
 477             try {
 478                 value = state.executionControl().commandInvoke(state.maps.classFullName(si));
 479                 value = si.subKind().hasValue()
 480                         ? expunge(value)
 481                         : "";
 482             } catch (EvalException ex) {
 483                 exception = translateExecutionException(ex);
 484             } catch (UnresolvedReferenceException ex) {
 485                 exception = ex;
 486             }
 487         }
 488         return events(c, outs, value, exception);
 489     }
 490 
 491     private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, Exception exception) {
 492         List<SnippetEvent> events = new ArrayList<>();
 493         events.add(c.event(value, exception));
 494         events.addAll(outs.stream()
 495                 .filter(u -> u != c)
 496                 .map(u -> u.event(null, null))
 497                 .collect(Collectors.toList()));
 498         events.addAll(outs.stream()
 499                 .flatMap(u -> u.secondaryEvents().stream())
 500                 .collect(Collectors.toList()));
 501         //System.err.printf("Events: %s\n", events);
 502         return events;
 503     }
 504 
 505     private Set<Unit> compileAndLoad(Set<Unit> ins) {
 506         if (ins.isEmpty()) {
 507             return ins;
 508         }
 509         Set<Unit> replaced = new LinkedHashSet<>();
 510         while (true) {
 511             state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
 512 
 513             ins.stream().forEach(u -> u.initialize(ins));
 514             AnalyzeTask at = state.taskFactory.new AnalyzeTask(ins);
 515             ins.stream().forEach(u -> u.setDiagnostics(at));
 516 
 517             // corral any Snippets that need it
 518             AnalyzeTask cat;
 519             if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
 520                 // if any were corralled, re-analyze everything
 521                 cat = state.taskFactory.new AnalyzeTask(ins);
 522                 ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
 523             } else {
 524                 cat = at;
 525             }
 526             ins.stream().forEach(u -> u.setStatus(cat));
 527             // compile and load the legit snippets
 528             boolean success;
 529             while (true) {
 530                 List<Unit> legit = ins.stream()
 531                         .filter(u -> u.isDefined())
 532                         .collect(toList());
 533                 state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
 534                         ins, legit);
 535                 if (legit.isEmpty()) {
 536                     // no class files can be generated
 537                     success = true;
 538                 } else {
 539                     // re-wrap with legit imports
 540                     legit.stream().forEach(u -> u.setWrap(ins, legit));
 541 
 542                     // generate class files for those capable
 543                     CompileTask ct = state.taskFactory.new CompileTask(legit);
 544                     if (!ct.compile()) {
 545                         // oy! compile failed because of recursive new unresolved
 546                         if (legit.stream()
 547                                 .filter(u -> u.smashingErrorDiagnostics(ct))
 548                                 .count() > 0) {
 549                             // try again, with the erroreous removed
 550                             continue;
 551                         } else {
 552                             state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
 553                                     legit);
 554                         }
 555                     }
 556 
 557                     // load all new classes
 558                     load(legit.stream()
 559                             .flatMap(u -> u.classesToLoad(ct.classInfoList(u)))
 560                             .collect(toList()));
 561                     // attempt to redefine the remaining classes
 562                     List<Unit> toReplace = legit.stream()
 563                             .filter(u -> !u.doRedefines())
 564                             .collect(toList());
 565 
 566                     // prevent alternating redefine/replace cyclic dependency
 567                     // loop by replacing all that have been replaced
 568                     if (!toReplace.isEmpty()) {
 569                         replaced.addAll(toReplace);
 570                         replaced.stream().forEach(u -> u.markForReplacement());
 571                     }
 572 
 573                     success = toReplace.isEmpty();
 574                 }
 575                 break;
 576             }
 577 
 578             // add any new dependencies to the working set
 579             List<Unit> newDependencies = ins.stream()
 580                     .flatMap(u -> u.effectedDependents())
 581                     .collect(toList());
 582             state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s  success: %s\n",
 583                     ins, newDependencies, success);
 584             if (!ins.addAll(newDependencies) && success) {
 585                 // all classes that could not be directly loaded (because they
 586                 // are new) have been redefined, and no new dependnencies were
 587                 // identified
 588                 ins.stream().forEach(u -> u.finish());
 589                 return ins;
 590             }
 591         }
 592     }
 593 
 594     private void load(List<ClassInfo> cil) {
 595         if (!cil.isEmpty()) {
 596             state.executionControl().commandLoad(cil);
 597         }
 598     }
 599 
 600     private EvalException translateExecutionException(EvalException ex) {
 601         StackTraceElement[] raw = ex.getStackTrace();
 602         int last = raw.length;
 603         do {
 604             if (last == 0) {
 605                 last = raw.length - 1;
 606                 break;
 607             }
 608         } while (!isWrap(raw[--last]));
 609         StackTraceElement[] elems = new StackTraceElement[last + 1];
 610         for (int i = 0; i <= last; ++i) {
 611             StackTraceElement r = raw[i];
 612             String rawKlass = r.getClassName();
 613             Matcher matcher = prefixPattern.matcher(rawKlass);
 614             String num;
 615             if (matcher.find() && (num = matcher.group("num")) != null) {
 616                 int end = matcher.end();
 617                 if (rawKlass.charAt(end - 1) == '$') {
 618                     --end;
 619                 }
 620                 int id = Integer.parseInt(num);
 621                 Snippet si = state.maps.getSnippet(id);
 622                 String klass = expunge(rawKlass);
 623                 String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName();
 624                 String file = "#" + id;
 625                 int line = si.outerWrap().wrapLineToSnippetLine(r.getLineNumber() - 1) + 1;
 626                 elems[i] = new StackTraceElement(klass, method, file, line);
 627             } else if (r.getFileName().equals("<none>")) {
 628                 elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber());
 629             } else {
 630                 elems[i] = r;
 631             }
 632         }
 633         String msg = ex.getMessage();
 634         if (msg.equals("<none>")) {
 635             msg = null;
 636         }
 637         return new EvalException(msg, ex.getExceptionClassName(), elems);
 638     }
 639 
 640     private boolean isWrap(StackTraceElement ste) {
 641         return prefixPattern.matcher(ste.getClassName()).find();
 642     }
 643 
 644     private DiagList modifierDiagnostics(ModifiersTree modtree,
 645             final TreeDissector dis, boolean isAbstractProhibited) {
 646 
 647         class ModifierDiagnostic extends Diag {
 648 
 649             final boolean fatal;
 650             final String message;
 651 
 652             ModifierDiagnostic(List<Modifier> list, boolean fatal) {
 653                 this.fatal = fatal;
 654                 StringBuilder sb = new StringBuilder();
 655                 sb.append((list.size() > 1) ? "Modifiers " : "Modifier ");
 656                 for (Modifier mod : list) {
 657                     sb.append("'");
 658                     sb.append(mod.toString());
 659                     sb.append("' ");
 660                 }
 661                 sb.append("not permitted in top-level declarations");
 662                 if (!fatal) {
 663                     sb.append(", ignored");
 664                 }
 665                 this.message = sb.toString();
 666             }
 667 
 668             @Override
 669             public boolean isError() {
 670                 return fatal;
 671             }
 672 
 673             @Override
 674             public long getPosition() {
 675                 return dis.getStartPosition(modtree);
 676             }
 677 
 678             @Override
 679             public long getStartPosition() {
 680                 return dis.getStartPosition(modtree);
 681             }
 682 
 683             @Override
 684             public long getEndPosition() {
 685                 return dis.getEndPosition(modtree);
 686             }
 687 
 688             @Override
 689             public String getCode() {
 690                 return fatal
 691                         ? "jdk.eval.error.illegal.modifiers"
 692                         : "jdk.eval.warn.illegal.modifiers";
 693             }
 694 
 695             @Override
 696             public String getMessage(Locale locale) {
 697                 return message;
 698             }
 699 
 700             @Override
 701             Unit unitOrNull() {
 702                 return null;
 703             }
 704         }
 705 
 706         List<Modifier> list = new ArrayList<>();
 707         boolean fatal = false;
 708         for (Modifier mod : modtree.getFlags()) {
 709             switch (mod) {
 710                 case SYNCHRONIZED:
 711                 case NATIVE:
 712                     list.add(mod);
 713                     fatal = true;
 714                     break;
 715                 case ABSTRACT:
 716                     if (isAbstractProhibited) {
 717                         list.add(mod);
 718                         fatal = true;
 719                     }
 720                     break;
 721                 case PUBLIC:
 722                 case PROTECTED:
 723                 case PRIVATE:
 724                 case STATIC:
 725                 case FINAL:
 726                     list.add(mod);
 727                     break;
 728             }
 729         }
 730         return list.isEmpty()
 731                 ? new DiagList()
 732                 : new DiagList(new ModifierDiagnostic(list, fatal));
 733     }
 734 
 735 }