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 }