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