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 }