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