Eval.java revision 3357:3e3553ee39d9
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 */ 25package jdk.jshell; 26 27import java.util.ArrayList; 28import java.util.Collection; 29import java.util.Collections; 30import java.util.List; 31import java.util.Locale; 32import java.util.regex.Matcher; 33import java.util.regex.Pattern; 34import java.util.stream.Collectors; 35import javax.lang.model.element.Modifier; 36import com.sun.source.tree.ArrayTypeTree; 37import com.sun.source.tree.AssignmentTree; 38import com.sun.source.tree.ClassTree; 39import com.sun.source.tree.ExpressionTree; 40import com.sun.source.tree.IdentifierTree; 41import com.sun.source.tree.MethodTree; 42import com.sun.source.tree.ModifiersTree; 43import com.sun.source.tree.Tree; 44import com.sun.source.tree.VariableTree; 45import com.sun.tools.javac.tree.JCTree; 46import com.sun.tools.javac.tree.Pretty; 47import java.io.IOException; 48import java.io.StringWriter; 49import java.io.Writer; 50import java.util.LinkedHashSet; 51import java.util.Set; 52import jdk.jshell.ClassTracker.ClassInfo; 53import jdk.jshell.Key.ErroneousKey; 54import jdk.jshell.Key.MethodKey; 55import jdk.jshell.Key.TypeDeclKey; 56import jdk.jshell.Snippet.SubKind; 57import jdk.jshell.TaskFactory.AnalyzeTask; 58import jdk.jshell.TaskFactory.BaseTask; 59import jdk.jshell.TaskFactory.CompileTask; 60import jdk.jshell.TaskFactory.ParseTask; 61import jdk.jshell.TreeDissector.ExpressionInfo; 62import jdk.jshell.Wrap.Range; 63import jdk.jshell.Snippet.Status; 64import static java.util.stream.Collectors.toList; 65import static java.util.stream.Collectors.toSet; 66import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN; 67import static jdk.jshell.Util.*; 68import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME; 69import static jdk.internal.jshell.remote.RemoteCodes.PREFIX_PATTERN; 70import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND; 71import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND; 72import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND; 73import 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 */ 81class 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.simpleWrap(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 DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false); 303 TypeDeclKey key = state.keyMap.keyForClass(name); 304 // Corralling mutates. Must be last use of pt, unitTree, klassTree 305 Wrap corralled = new Corraller(key.index(), pt.getContext()).corralType(klassTree); 306 307 Wrap guts = Wrap.classMemberWrap(compileSource); 308 Snippet snip = new TypeDeclSnippet(key, userSource, guts, 309 name, snippetKind, 310 corralled, tds.declareReferences(), tds.bodyReferences()); 311 return declare(snip, modDiag); 312 } 313 314 private List<SnippetEvent> processStatement(String userSource, String compileSource) { 315 Wrap guts = Wrap.methodWrap(compileSource); 316 // Check for unreachable by trying 317 AnalyzeTask at = trialCompile(guts); 318 if (at.hasErrors()) { 319 if (at.getDiagnostics().hasUnreachableError()) { 320 guts = Wrap.methodUnreachableSemiWrap(compileSource); 321 at = trialCompile(guts); 322 if (at.hasErrors()) { 323 if (at.getDiagnostics().hasUnreachableError()) { 324 // Without ending semicolon 325 guts = Wrap.methodUnreachableWrap(compileSource); 326 at = trialCompile(guts); 327 } 328 if (at.hasErrors()) { 329 return compileFailResult(at, userSource); 330 } 331 } 332 } else { 333 return compileFailResult(at, userSource); 334 } 335 } 336 Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); 337 return declare(snip); 338 } 339 340 private AnalyzeTask trialCompile(Wrap guts) { 341 OuterWrap outer = state.outerMap.wrapInTrialClass(guts); 342 return state.taskFactory.new AnalyzeTask(outer); 343 } 344 345 private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) { 346 TreeDependencyScanner tds = new TreeDependencyScanner(); 347 tds.scan(unitTree); 348 TreeDissector dis = TreeDissector.createByFirstClass(pt); 349 350 MethodTree mt = (MethodTree) unitTree; 351 String name = mt.getName().toString(); 352 String parameterTypes 353 = mt.getParameters() 354 .stream() 355 .map(param -> dis.treeToRange(param.getType()).part(compileSource)) 356 .collect(Collectors.joining(",")); 357 Tree returnType = mt.getReturnType(); 358 DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true); 359 MethodKey key = state.keyMap.keyForMethod(name, parameterTypes); 360 // Corralling mutates. Must be last use of pt, unitTree, mt 361 Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt); 362 363 if (modDiag.hasErrors()) { 364 return compileFailResult(modDiag, userSource); 365 } 366 Wrap guts = Wrap.classMemberWrap(compileSource); 367 Range typeRange = dis.treeToRange(returnType); 368 String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource); 369 370 Snippet snip = new MethodSnippet(key, userSource, guts, 371 name, signature, 372 corralled, tds.declareReferences(), tds.bodyReferences()); 373 return declare(snip, modDiag); 374 } 375 376 /** 377 * The snippet has failed, return with the rejected event 378 * 379 * @param xt the task from which to extract the failure diagnostics 380 * @param userSource the incoming bad user source 381 * @return a rejected snippet event 382 */ 383 private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) { 384 return compileFailResult(xt.getDiagnostics(), userSource); 385 } 386 387 /** 388 * The snippet has failed, return with the rejected event 389 * 390 * @param diags the failure diagnostics 391 * @param userSource the incoming bad user source 392 * @return a rejected snippet event 393 */ 394 private List<SnippetEvent> compileFailResult(DiagList diags, String userSource) { 395 ErroneousKey key = state.keyMap.keyForErroneous(); 396 Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND); 397 snip.setFailed(diags); 398 state.maps.installSnippet(snip); 399 return Collections.singletonList(new SnippetEvent( 400 snip, Status.NONEXISTENT, Status.REJECTED, 401 false, null, null, null) 402 ); 403 } 404 405 private ExpressionInfo typeOfExpression(String expression) { 406 Wrap guts = Wrap.methodReturnWrap(expression); 407 TaskFactory.AnalyzeTask at = trialCompile(guts); 408 if (!at.hasErrors() && at.firstCuTree() != null) { 409 return TreeDissector.createByFirstClass(at) 410 .typeOfReturnStatement(at, state); 411 } 412 return null; 413 } 414 415 /** 416 * Should a temp var wrap the expression. TODO make this user configurable. 417 * 418 * @param snippetKind 419 * @return 420 */ 421 private boolean shouldGenTempVar(SubKind snippetKind) { 422 return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND; 423 } 424 425 List<SnippetEvent> drop(Snippet si) { 426 Unit c = new Unit(state, si); 427 428 Set<Unit> ins = c.dependents().collect(toSet()); 429 Set<Unit> outs = compileAndLoad(ins); 430 431 return events(c, outs, null, null); 432 } 433 434 private List<SnippetEvent> declare(Snippet si) { 435 return declare(si, new DiagList()); 436 } 437 438 private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) { 439 Unit c = new Unit(state, si, null, generatedDiagnostics); 440 Set<Unit> ins = new LinkedHashSet<>(); 441 ins.add(c); 442 Set<Unit> outs = compileAndLoad(ins); 443 444 if (!si.status().isDefined 445 && si.diagnostics().isEmpty() 446 && si.unresolved().isEmpty()) { 447 // did not succeed, but no record of it, extract from others 448 si.setDiagnostics(outs.stream() 449 .flatMap(u -> u.snippet().diagnostics().stream()) 450 .collect(Collectors.toCollection(DiagList::new))); 451 } 452 453 // If appropriate, execute the snippet 454 String value = null; 455 Exception exception = null; 456 if (si.status().isDefined) { 457 if (si.isExecutable()) { 458 try { 459 value = state.executionControl().commandInvoke(si.classFullName()); 460 value = si.subKind().hasValue() 461 ? expunge(value) 462 : ""; 463 } catch (EvalException ex) { 464 exception = translateExecutionException(ex); 465 } catch (UnresolvedReferenceException ex) { 466 exception = ex; 467 } 468 } else if (si.subKind() == SubKind.VAR_DECLARATION_SUBKIND) { 469 switch (((VarSnippet) si).typeName()) { 470 case "byte": 471 case "short": 472 case "int": 473 case "long": 474 value = "0"; 475 break; 476 case "float": 477 case "double": 478 value = "0.0"; 479 break; 480 case "boolean": 481 value = "false"; 482 break; 483 case "char": 484 value = "''"; 485 break; 486 default: 487 value = "null"; 488 break; 489 } 490 } 491 } 492 return events(c, outs, value, exception); 493 } 494 495 private boolean interestingEvent(SnippetEvent e) { 496 return e.isSignatureChange() 497 || e.causeSnippet() == null 498 || e.status() != e.previousStatus() 499 || e.exception() != null; 500 } 501 502 private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, Exception exception) { 503 List<SnippetEvent> events = new ArrayList<>(); 504 events.add(c.event(value, exception)); 505 events.addAll(outs.stream() 506 .filter(u -> u != c) 507 .map(u -> u.event(null, null)) 508 .filter(this::interestingEvent) 509 .collect(Collectors.toList())); 510 events.addAll(outs.stream() 511 .flatMap(u -> u.secondaryEvents().stream()) 512 .filter(this::interestingEvent) 513 .collect(Collectors.toList())); 514 //System.err.printf("Events: %s\n", events); 515 return events; 516 } 517 518 private Set<OuterWrap> outerWrapSet(Collection<Unit> units) { 519 return units.stream() 520 .map(u -> u.snippet().outerWrap()) 521 .collect(toSet()); 522 } 523 524 private Set<Unit> compileAndLoad(Set<Unit> ins) { 525 if (ins.isEmpty()) { 526 return ins; 527 } 528 Set<Unit> replaced = new LinkedHashSet<>(); 529 // Loop until dependencies and errors are stable 530 while (true) { 531 state.debug(DBG_GEN, "compileAndLoad %s\n", ins); 532 533 ins.stream().forEach(u -> u.initialize()); 534 ins.stream().forEach(u -> u.setWrap(ins, ins)); 535 AnalyzeTask at = state.taskFactory.new AnalyzeTask(outerWrapSet(ins)); 536 ins.stream().forEach(u -> u.setDiagnostics(at)); 537 538 // corral any Snippets that need it 539 AnalyzeTask cat; 540 if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) { 541 // if any were corralled, re-analyze everything 542 cat = state.taskFactory.new AnalyzeTask(outerWrapSet(ins)); 543 ins.stream().forEach(u -> u.setCorralledDiagnostics(cat)); 544 } else { 545 cat = at; 546 } 547 ins.stream().forEach(u -> u.setStatus(cat)); 548 // compile and load the legit snippets 549 boolean success; 550 while (true) { 551 List<Unit> legit = ins.stream() 552 .filter(u -> u.isDefined()) 553 .collect(toList()); 554 state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n", 555 ins, legit); 556 if (legit.isEmpty()) { 557 // no class files can be generated 558 success = true; 559 } else { 560 // re-wrap with legit imports 561 legit.stream().forEach(u -> u.setWrap(ins, legit)); 562 563 // generate class files for those capable 564 CompileTask ct = state.taskFactory.new CompileTask(outerWrapSet(legit)); 565 if (!ct.compile()) { 566 // oy! compile failed because of recursive new unresolved 567 if (legit.stream() 568 .filter(u -> u.smashingErrorDiagnostics(ct)) 569 .count() > 0) { 570 // try again, with the erroreous removed 571 continue; 572 } else { 573 state.debug(DBG_GEN, "Should never happen error-less failure - %s\n", 574 legit); 575 } 576 } 577 578 // load all new classes 579 load(legit.stream() 580 .flatMap(u -> u.classesToLoad(ct.classInfoList(u.snippet().outerWrap()))) 581 .collect(toSet())); 582 // attempt to redefine the remaining classes 583 List<Unit> toReplace = legit.stream() 584 .filter(u -> !u.doRedefines()) 585 .collect(toList()); 586 587 // prevent alternating redefine/replace cyclic dependency 588 // loop by replacing all that have been replaced 589 if (!toReplace.isEmpty()) { 590 replaced.addAll(toReplace); 591 replaced.stream().forEach(u -> u.markForReplacement()); 592 } 593 594 success = toReplace.isEmpty(); 595 } 596 break; 597 } 598 599 // add any new dependencies to the working set 600 List<Unit> newDependencies = ins.stream() 601 .flatMap(u -> u.effectedDependents()) 602 .collect(toList()); 603 state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s success: %s\n", 604 ins, newDependencies, success); 605 if (!ins.addAll(newDependencies) && success) { 606 // all classes that could not be directly loaded (because they 607 // are new) have been redefined, and no new dependnencies were 608 // identified 609 ins.stream().forEach(u -> u.finish()); 610 return ins; 611 } 612 } 613 } 614 615 private void load(Set<ClassInfo> cil) { 616 if (!cil.isEmpty()) { 617 state.executionControl().commandLoad(cil); 618 } 619 } 620 621 private EvalException translateExecutionException(EvalException ex) { 622 StackTraceElement[] raw = ex.getStackTrace(); 623 int last = raw.length; 624 do { 625 if (last == 0) { 626 last = raw.length - 1; 627 break; 628 } 629 } while (!isWrap(raw[--last])); 630 StackTraceElement[] elems = new StackTraceElement[last + 1]; 631 for (int i = 0; i <= last; ++i) { 632 StackTraceElement r = raw[i]; 633 OuterSnippetsClassWrap outer = state.outerMap.getOuter(r.getClassName()); 634 if (outer != null) { 635 String klass = expunge(r.getClassName()); 636 String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName(); 637 int wln = r.getLineNumber() - 1; 638 int line = outer.wrapLineToSnippetLine(wln) + 1; 639 Snippet sn = outer.wrapLineToSnippet(wln); 640 String file = "#" + sn.id(); 641 elems[i] = new StackTraceElement(klass, method, file, line); 642 } else if (r.getFileName().equals("<none>")) { 643 elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber()); 644 } else { 645 elems[i] = r; 646 } 647 } 648 String msg = ex.getMessage(); 649 if (msg.equals("<none>")) { 650 msg = null; 651 } 652 return new EvalException(msg, ex.getExceptionClassName(), elems); 653 } 654 655 private boolean isWrap(StackTraceElement ste) { 656 return PREFIX_PATTERN.matcher(ste.getClassName()).find(); 657 } 658 659 private DiagList modifierDiagnostics(ModifiersTree modtree, 660 final TreeDissector dis, boolean isAbstractProhibited) { 661 662 class ModifierDiagnostic extends Diag { 663 664 final boolean fatal; 665 final String message; 666 667 ModifierDiagnostic(List<Modifier> list, boolean fatal) { 668 this.fatal = fatal; 669 StringBuilder sb = new StringBuilder(); 670 for (Modifier mod : list) { 671 sb.append("'"); 672 sb.append(mod.toString()); 673 sb.append("' "); 674 } 675 String key = (list.size() > 1) 676 ? fatal 677 ? "jshell.diag.modifier.plural.fatal" 678 : "jshell.diag.modifier.plural.ignore" 679 : fatal 680 ? "jshell.diag.modifier.single.fatal" 681 : "jshell.diag.modifier.single.ignore"; 682 this.message = state.messageFormat(key, sb.toString()); 683 } 684 685 @Override 686 public boolean isError() { 687 return fatal; 688 } 689 690 @Override 691 public long getPosition() { 692 return dis.getStartPosition(modtree); 693 } 694 695 @Override 696 public long getStartPosition() { 697 return dis.getStartPosition(modtree); 698 } 699 700 @Override 701 public long getEndPosition() { 702 return dis.getEndPosition(modtree); 703 } 704 705 @Override 706 public String getCode() { 707 return fatal 708 ? "jdk.eval.error.illegal.modifiers" 709 : "jdk.eval.warn.illegal.modifiers"; 710 } 711 712 @Override 713 public String getMessage(Locale locale) { 714 return message; 715 } 716 } 717 718 List<Modifier> list = new ArrayList<>(); 719 boolean fatal = false; 720 for (Modifier mod : modtree.getFlags()) { 721 switch (mod) { 722 case SYNCHRONIZED: 723 case NATIVE: 724 list.add(mod); 725 fatal = true; 726 break; 727 case ABSTRACT: 728 if (isAbstractProhibited) { 729 list.add(mod); 730 fatal = true; 731 } 732 break; 733 case PUBLIC: 734 case PROTECTED: 735 case PRIVATE: 736 case STATIC: 737 case FINAL: 738 list.add(mod); 739 break; 740 } 741 } 742 return list.isEmpty() 743 ? new DiagList() 744 : new DiagList(new ModifierDiagnostic(list, fatal)); 745 } 746 747} 748