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