1 /* 2 * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.jshell; 26 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.List; 31 import java.util.Locale; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 import java.util.stream.Collectors; 35 import javax.lang.model.element.Modifier; 36 import com.sun.source.tree.ArrayTypeTree; 37 import com.sun.source.tree.AssignmentTree; 38 import com.sun.source.tree.ClassTree; 39 import com.sun.source.tree.ExpressionStatementTree; 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.NewClassTree; 45 import com.sun.source.tree.Tree; 46 import com.sun.source.tree.VariableTree; 47 import com.sun.tools.javac.tree.JCTree; 48 import com.sun.tools.javac.tree.Pretty; 49 import java.io.IOException; 50 import java.io.StringWriter; 51 import java.io.Writer; 52 import java.util.Arrays; 53 import java.util.LinkedHashSet; 54 import java.util.Set; 55 import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo; 56 import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription; 57 import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription.VariableDesc; 58 import jdk.jshell.Key.ErroneousKey; 59 import jdk.jshell.Key.MethodKey; 60 import jdk.jshell.Key.TypeDeclKey; 61 import jdk.jshell.Snippet.Kind; 62 import jdk.jshell.Snippet.SubKind; 63 import jdk.jshell.TaskFactory.AnalyzeTask; 64 import jdk.jshell.TaskFactory.BaseTask; 65 import jdk.jshell.TaskFactory.ParseTask; 66 import jdk.jshell.Util.Pair; 67 import jdk.jshell.Wrap.CompoundWrap; 68 import jdk.jshell.Wrap.Range; 69 import jdk.jshell.Snippet.Status; 70 import jdk.jshell.spi.ExecutionControl.ClassBytecodes; 71 import jdk.jshell.spi.ExecutionControl.ClassInstallException; 72 import jdk.jshell.spi.ExecutionControl.EngineTerminationException; 73 import jdk.jshell.spi.ExecutionControl.InternalException; 74 import jdk.jshell.spi.ExecutionControl.NotImplementedException; 75 import jdk.jshell.spi.ExecutionControl.ResolutionException; 76 import jdk.jshell.spi.ExecutionControl.RunException; 77 import jdk.jshell.spi.ExecutionControl.UserException; 78 import static java.util.stream.Collectors.toList; 79 import static java.util.stream.Collectors.toSet; 80 import static java.util.Collections.singletonList; 81 import com.sun.tools.javac.code.Symbol.TypeSymbol; 82 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN; 83 import static jdk.jshell.Util.DOIT_METHOD_NAME; 84 import static jdk.jshell.Util.PREFIX_PATTERN; 85 import static jdk.jshell.Util.expunge; 86 import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND; 87 import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND; 88 import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND; 89 import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND; 90 91 /** 92 * The Evaluation Engine. Source internal analysis, wrapping control, 93 * compilation, declaration. redefinition, replacement, and execution. 94 * 95 * @author Robert Field 96 */ 97 class Eval { 98 99 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}_\\$]+|\\*))"); 100 private static final Pattern DEFAULT_PREFIX = Pattern.compile("\\p{javaWhitespace}*(default)\\p{javaWhitespace}+"); 101 102 // for uses that should not change state -- non-evaluations 103 private boolean preserveState = false; 104 105 private int varNumber = 0; 106 107 /* The number of anonymous innerclasses seen so far. Used to generate unique 108 * names of these classes. 109 */ 110 private int anonCount = 0; 111 112 private final JShell state; 113 114 // The set of names of methods on Object 115 private final Set<String> objectMethods = Arrays 116 .stream(Object.class.getMethods()) 117 .map(m -> m.getName()) 118 .collect(toSet()); 119 Eval(JShell state)120 Eval(JShell state) { 121 this.state = state; 122 } 123 124 /** 125 * Evaluates a snippet of source. 126 * 127 * @param userSource the source of the snippet 128 * @return the list of primary and update events 129 * @throws IllegalStateException 130 */ eval(String userSource)131 List<SnippetEvent> eval(String userSource) throws IllegalStateException { 132 List<SnippetEvent> allEvents = new ArrayList<>(); 133 for (Snippet snip : sourceToSnippets(userSource)) { 134 if (snip.kind() == Kind.ERRONEOUS) { 135 state.maps.installSnippet(snip); 136 allEvents.add(new SnippetEvent( 137 snip, Status.NONEXISTENT, Status.REJECTED, 138 false, null, null, null)); 139 } else { 140 allEvents.addAll(declare(snip, snip.syntheticDiags())); 141 } 142 } 143 return allEvents; 144 } 145 146 /** 147 * Converts the user source of a snippet into a Snippet list -- Snippet will 148 * have wrappers. 149 * 150 * @param userSource the source of the snippet 151 * @return usually a singleton list of Snippet, but may be empty or multiple 152 */ sourceToSnippetsWithWrappers(String userSource)153 List<Snippet> sourceToSnippetsWithWrappers(String userSource) { 154 List<Snippet> snippets = sourceToSnippets(userSource); 155 for (Snippet snip : snippets) { 156 if (snip.outerWrap() == null) { 157 snip.setOuterWrap( 158 (snip.kind() == Kind.IMPORT) 159 ? state.outerMap.wrapImport(snip.guts(), snip) 160 : state.outerMap.wrapInTrialClass(snip.guts()) 161 ); 162 } 163 } 164 return snippets; 165 } 166 167 /** 168 * Converts the user source of a snippet into a Snippet object (or list of 169 * objects in the case of: int x, y, z;). Does not install the Snippets 170 * or execute them. Does not change any state. 171 * 172 * @param userSource the source of the snippet 173 * @return usually a singleton list of Snippet, but may be empty or multiple 174 */ toScratchSnippets(String userSource)175 List<Snippet> toScratchSnippets(String userSource) { 176 try { 177 preserveState = true; 178 return sourceToSnippets(userSource); 179 } finally { 180 preserveState = false; 181 } 182 } 183 184 /** 185 * Converts the user source of a snippet into a Snippet object (or list of 186 * objects in the case of: int x, y, z;). Does not install the Snippets 187 * or execute them. 188 * 189 * @param userSource the source of the snippet 190 * @return usually a singleton list of Snippet, but may be empty or multiple 191 */ sourceToSnippets(String userSource)192 private List<Snippet> sourceToSnippets(String userSource) { 193 String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared()); 194 if (compileSource.length() == 0) { 195 return Collections.emptyList(); 196 } 197 return state.taskFactory.parse(compileSource, pt -> { 198 List<? extends Tree> units = pt.units(); 199 if (units.isEmpty()) { 200 return compileFailResult(pt, userSource, Kind.ERRONEOUS); 201 } 202 Tree unitTree = units.get(0); 203 if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) { 204 Matcher matcher = DEFAULT_PREFIX.matcher(compileSource); 205 DiagList dlist = matcher.lookingAt() 206 ? new DiagList(new ModifierDiagnostic(true, 207 state.messageFormat("jshell.diag.modifier.single.fatal", "'default'"), 208 matcher.start(1), matcher.end(1))) 209 : pt.getDiagnostics(); 210 return compileFailResult(dlist, userSource, kindOfTree(unitTree)); 211 } 212 213 // Erase illegal/ignored modifiers 214 String compileSourceInt = new MaskCommentsAndModifiers(compileSource, true).cleared(); 215 216 state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree); 217 switch (unitTree.getKind()) { 218 case IMPORT: 219 return processImport(userSource, compileSourceInt); 220 case VARIABLE: 221 return processVariables(userSource, units, compileSourceInt, pt); 222 case EXPRESSION_STATEMENT: 223 return processExpression(userSource, unitTree, compileSourceInt, pt); 224 case CLASS: 225 return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt); 226 case ENUM: 227 return processClass(userSource, unitTree, compileSourceInt, SubKind.ENUM_SUBKIND, pt); 228 case ANNOTATION_TYPE: 229 return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt); 230 case INTERFACE: 231 return processClass(userSource, unitTree, compileSourceInt, SubKind.INTERFACE_SUBKIND, pt); 232 case METHOD: 233 return processMethod(userSource, unitTree, compileSourceInt, pt); 234 default: 235 return processStatement(userSource, compileSourceInt); 236 } 237 }); 238 } 239 processImport(String userSource, String compileSource)240 private List<Snippet> processImport(String userSource, String compileSource) { 241 Wrap guts = Wrap.simpleWrap(compileSource); 242 Matcher mat = IMPORT_PATTERN.matcher(compileSource); 243 String fullname; 244 String name; 245 boolean isStatic; 246 if (mat.find()) { 247 isStatic = mat.group("static") != null; 248 name = mat.group("name"); 249 fullname = mat.group("fullname"); 250 } else { 251 // bad import -- fake it 252 isStatic = compileSource.contains("static"); 253 name = fullname = compileSource; 254 } 255 String fullkey = (isStatic ? "static-" : "") + fullname; 256 boolean isStar = name.equals("*"); 257 String keyName = isStar 258 ? fullname 259 : name; 260 SubKind snippetKind = isStar 261 ? (isStatic ? STATIC_IMPORT_ON_DEMAND_SUBKIND : TYPE_IMPORT_ON_DEMAND_SUBKIND) 262 : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND); 263 Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind), 264 userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar); 265 return singletonList(snip); 266 } 267 268 private static class EvalPretty extends Pretty { 269 270 private final Writer out; 271 EvalPretty(Writer writer, boolean bln)272 public EvalPretty(Writer writer, boolean bln) { 273 super(writer, bln); 274 this.out = writer; 275 } 276 277 /** 278 * Print string, DO NOT replacing all non-ascii character with unicode 279 * escapes. 280 */ 281 @Override print(Object o)282 public void print(Object o) throws IOException { 283 out.write(o.toString()); 284 } 285 prettyExpr(JCTree tree, boolean bln)286 static String prettyExpr(JCTree tree, boolean bln) { 287 StringWriter out = new StringWriter(); 288 try { 289 new EvalPretty(out, bln).printExpr(tree); 290 } catch (IOException e) { 291 throw new AssertionError(e); 292 } 293 return out.toString(); 294 } 295 } 296 processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt)297 private List<Snippet> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) { 298 List<Snippet> snippets = new ArrayList<>(); 299 TreeDissector dis = TreeDissector.createByFirstClass(pt); 300 for (Tree unitTree : units) { 301 VariableTree vt = (VariableTree) unitTree; 302 String name = vt.getName().toString(); 303 String typeName; 304 String fullTypeName; 305 String displayType; 306 boolean hasEnhancedType = false; 307 TreeDependencyScanner tds = new TreeDependencyScanner(); 308 Wrap typeWrap; 309 Wrap anonDeclareWrap = null; 310 Wrap winit = null; 311 boolean enhancedDesugaring = false; 312 Set<String> anonymousClasses = Collections.emptySet(); 313 StringBuilder sbBrackets = new StringBuilder(); 314 Tree baseType = vt.getType(); 315 if (baseType != null) { 316 tds.scan(baseType); // Not dependent on initializer 317 fullTypeName = displayType = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false); 318 while (baseType instanceof ArrayTypeTree) { 319 //TODO handle annotations too 320 baseType = ((ArrayTypeTree) baseType).getType(); 321 sbBrackets.append("[]"); 322 } 323 Range rtype = dis.treeToRange(baseType); 324 typeWrap = Wrap.rangeWrap(compileSource, rtype); 325 } else { 326 DiagList dl = trialCompile(Wrap.methodWrap(compileSource)); 327 if (dl.hasErrors()) { 328 return compileFailResult(dl, userSource, kindOfTree(unitTree)); 329 } 330 Tree init = vt.getInitializer(); 331 if (init != null) { 332 Range rinit = dis.treeToRange(init); 333 String initCode = rinit.part(compileSource); 334 ExpressionInfo ei = 335 ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state, false); 336 if (ei != null && ei.declareTypeName != null) { 337 typeName = ei.declareTypeName; 338 fullTypeName = ei.fullTypeName; 339 displayType = ei.displayTypeName; 340 341 hasEnhancedType = !typeName.equals(fullTypeName); 342 343 enhancedDesugaring = !ei.isPrimitiveType; 344 345 Pair<Wrap, Wrap> anonymous2Member = 346 anonymous2Member(ei, compileSource, rinit, dis, init); 347 anonDeclareWrap = anonymous2Member.first; 348 winit = anonymous2Member.second; 349 anonymousClasses = ei.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet()); 350 } else { 351 displayType = fullTypeName = typeName = "java.lang.Object"; 352 } 353 tds.scan(init); 354 } else { 355 displayType = fullTypeName = typeName = "java.lang.Object"; 356 } 357 typeWrap = Wrap.identityWrap(typeName); 358 } 359 Range runit = dis.treeToRange(vt); 360 runit = new Range(runit.begin, runit.end - 1); 361 ExpressionTree it = vt.getInitializer(); 362 int nameMax = runit.end - 1; 363 SubKind subkind; 364 if (it != null) { 365 subkind = SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND; 366 Range rinit = dis.treeToRange(it); 367 winit = winit == null ? Wrap.rangeWrap(compileSource, rinit) : winit; 368 nameMax = rinit.begin - 1; 369 } else { 370 String sinit; 371 switch (typeName) { 372 case "byte": 373 case "short": 374 case "int": 375 sinit = "0"; 376 break; 377 case "long": 378 sinit = "0L"; 379 break; 380 case "float": 381 sinit = "0.0f"; 382 break; 383 case "double": 384 sinit = "0.0d"; 385 break; 386 case "boolean": 387 sinit = "false"; 388 break; 389 case "char": 390 sinit = "'\\u0000'"; 391 break; 392 default: 393 sinit = "null"; 394 break; 395 } 396 winit = Wrap.simpleWrap(sinit); 397 subkind = SubKind.VAR_DECLARATION_SUBKIND; 398 } 399 int nameStart = compileSource.lastIndexOf(name, nameMax); 400 if (nameStart < 0) { 401 throw new AssertionError("Name '" + name + "' not found"); 402 } 403 int nameEnd = nameStart + name.length(); 404 Range rname = new Range(nameStart, nameEnd); 405 Wrap guts = Wrap.varWrap(compileSource, typeWrap, sbBrackets.toString(), rname, 406 winit, enhancedDesugaring, anonDeclareWrap); 407 DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true); 408 Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, 409 name, subkind, displayType, hasEnhancedType ? fullTypeName : null, anonymousClasses, 410 tds.declareReferences(), modDiag); 411 snippets.add(snip); 412 } 413 return snippets; 414 } 415 416 /**Convert anonymous classes in "init" to member classes, based 417 * on the additional information from ExpressionInfo.anonymousClasses. 418 * 419 * This means: 420 * -if the code in the anonymous class captures any variables from the 421 * enclosing context, create fields for them 422 * -creating an explicit constructor that: 423 * --if the new class expression has a base/enclosing expression, make it an 424 * explicit constructor parameter "encl" and use "encl.super" when invoking 425 * the supertype constructor 426 * --if the (used) supertype constructor has any parameters, declare them 427 * as explicit parameters of the constructor, and pass them to the super 428 * constructor 429 * --if the code in the anonymous class captures any variables from the 430 * enclosing context, make them an explicit paramters of the constructor 431 * and assign to respective fields. 432 * --if there are any explicit fields with initializers in the anonymous class, 433 * move the initializers at the end of the constructor (after the captured fields 434 * are assigned, so that the initializers of these fields can use them). 435 * -from the captured variables fields, constructor, and existing members 436 * (with cleared field initializers), create an explicit class that extends or 437 * implements the supertype of the anonymous class. 438 * 439 * This method returns two wraps: the first contains the class declarations for the 440 * converted classes, the first one should be used instead of "init" in the variable 441 * declaration. 442 */ anonymous2Member(ExpressionInfo ei, String compileSource, Range rinit, TreeDissector dis, Tree init)443 private Pair<Wrap, Wrap> anonymous2Member(ExpressionInfo ei, 444 String compileSource, 445 Range rinit, 446 TreeDissector dis, 447 Tree init) { 448 List<Wrap> anonymousDeclarations = new ArrayList<>(); 449 List<Wrap> partitionedInit = new ArrayList<>(); 450 int lastPos = rinit.begin; 451 com.sun.tools.javac.util.List<NewClassTree> toConvert = 452 ExpressionToTypeInfo.listAnonymousClassesToConvert(init); 453 com.sun.tools.javac.util.List<AnonymousDescription> descriptions = 454 ei.anonymousClasses; 455 while (toConvert.nonEmpty() && descriptions.nonEmpty()) { 456 NewClassTree node = toConvert.head; 457 AnonymousDescription ad = descriptions.head; 458 459 toConvert = toConvert.tail; 460 descriptions = descriptions.tail; 461 462 List<Object> classBodyParts = new ArrayList<>(); 463 //declarations of the captured variables: 464 for (VariableDesc vd : ad.capturedVariables) { 465 classBodyParts.add(vd.type + " " + vd.name + ";\n"); 466 } 467 468 List<Object> constructorParts = new ArrayList<>(); 469 constructorParts.add(ad.declareTypeName + "("); 470 String sep = ""; 471 //add the parameter for the base/enclosing expression, if any: 472 if (ad.enclosingInstanceType != null) { 473 constructorParts.add(ad.enclosingInstanceType + " encl"); 474 sep = ", "; 475 } 476 int idx = 0; 477 //add parameters of the super constructor, if any: 478 for (String type : ad.parameterTypes) { 479 constructorParts.add(sep); 480 constructorParts.add(type + " " + "arg" + idx++); 481 sep = ", "; 482 } 483 //add parameters for the captured variables: 484 for (VariableDesc vd : ad.capturedVariables) { 485 constructorParts.add(sep); 486 constructorParts.add(vd.type + " " + "cap$" + vd.name); 487 sep = ", "; 488 } 489 //construct super constructor call: 490 if (ad.enclosingInstanceType != null) { 491 //if there's an enclosing instance, call super on it: 492 constructorParts.add(") { encl.super ("); 493 } else { 494 constructorParts.add(") { super ("); 495 } 496 sep = ""; 497 for (int i = 0; i < idx; i++) { 498 constructorParts.add(sep); 499 constructorParts.add("arg" + i); 500 sep = ", "; 501 } 502 constructorParts.add(");"); 503 //initialize the captured variables: 504 for (VariableDesc vd : ad.capturedVariables) { 505 constructorParts.add("this." + vd.name + " = " + "cap$" + vd.name + ";\n"); 506 } 507 List<? extends Tree> members = 508 node.getClassBody().getMembers(); 509 for (Tree member : members) { 510 if (member.getKind() == Tree.Kind.VARIABLE) { 511 VariableTree vt = (VariableTree) member; 512 513 if (vt.getInitializer() != null) { 514 //for variables with initializer, explicitly move the initializer 515 //to the constructor after the captured variables as assigned 516 //(the initializers would otherwise run too early): 517 Range wholeVar = dis.treeToRange(vt); 518 int name = ((JCTree) vt).pos; 519 classBodyParts.add(new CompoundWrap(Wrap.rangeWrap(compileSource, 520 new Range(wholeVar.begin, name)), 521 vt.getName().toString(), 522 ";\n")); 523 constructorParts.add(Wrap.rangeWrap(compileSource, 524 new Range(name, wholeVar.end))); 525 continue; 526 } 527 } 528 classBodyParts.add(Wrap.rangeWrap(compileSource, 529 dis.treeToRange(member))); 530 } 531 532 constructorParts.add("}"); 533 534 //construct the member class: 535 classBodyParts.add(new CompoundWrap(constructorParts.toArray())); 536 537 Wrap classBodyWrap = new CompoundWrap(classBodyParts.toArray()); 538 539 anonymousDeclarations.add(new CompoundWrap("public static class ", ad.declareTypeName, 540 (ad.isClass ? " extends " : " implements "), 541 ad.superTypeName, " { ", classBodyWrap, "}")); 542 543 //change the new class expression to use the newly created member type: 544 Range argRange = dis.treeListToRange(node.getArguments()); 545 Wrap argWrap; 546 547 if (argRange != null) { 548 argWrap = Wrap.rangeWrap(compileSource, argRange); 549 } else { 550 argWrap = Wrap.simpleWrap(" "); 551 } 552 553 if (ad.enclosingInstanceType != null) { 554 //if there's an enclosing expression, set it as the first parameter: 555 Range enclosingRanges = 556 dis.treeToRange(node.getEnclosingExpression()); 557 Wrap enclosingWrap = Wrap.rangeWrap(compileSource, enclosingRanges); 558 argWrap = argRange != null ? new CompoundWrap(enclosingWrap, 559 Wrap.simpleWrap(","), 560 argWrap) 561 : enclosingWrap; 562 } 563 564 Range current = dis.treeToRange(node); 565 String capturedArgs; 566 if (!ad.capturedVariables.isEmpty()) { 567 capturedArgs = (ad.parameterTypes.isEmpty() ? "" : ", ") + 568 ad.capturedVariables.stream() 569 .map(vd -> vd.name) 570 .collect(Collectors.joining(",")); 571 } else { 572 capturedArgs = ""; 573 } 574 if (lastPos < current.begin) 575 partitionedInit.add(Wrap.rangeWrap(compileSource, 576 new Range(lastPos, current.begin))); 577 partitionedInit.add(new CompoundWrap("new " + ad.declareTypeName + "(", 578 argWrap, 579 capturedArgs, 580 ")")); 581 lastPos = current.end; 582 } 583 584 if (lastPos < rinit.end) 585 partitionedInit.add(Wrap.rangeWrap(compileSource, new Range(lastPos, rinit.end))); 586 587 return new Pair<>(new CompoundWrap(anonymousDeclarations.toArray()), 588 new CompoundWrap(partitionedInit.toArray())); 589 } 590 processExpression(String userSource, Tree tree, String compileSource, ParseTask pt)591 private List<Snippet> processExpression(String userSource, Tree tree, String compileSource, ParseTask pt) { 592 ExpressionStatementTree expr = (ExpressionStatementTree) tree; 593 String name = null; 594 ExpressionInfo ei = ExpressionToTypeInfo.expressionInfo(compileSource, state); 595 ExpressionTree assignVar; 596 Wrap guts; 597 Snippet snip; 598 if (ei != null && ei.isNonVoid) { 599 String typeName = ei.typeName; 600 SubKind subkind; 601 if (ei.tree instanceof IdentifierTree) { 602 IdentifierTree id = (IdentifierTree) ei.tree; 603 name = id.getName().toString(); 604 subkind = SubKind.VAR_VALUE_SUBKIND; 605 606 } else if (ei.tree instanceof AssignmentTree 607 && (assignVar = ((AssignmentTree) ei.tree).getVariable()) instanceof IdentifierTree) { 608 name = assignVar.toString(); 609 subkind = SubKind.ASSIGNMENT_SUBKIND; 610 } else { 611 subkind = SubKind.OTHER_EXPRESSION_SUBKIND; 612 } 613 if (shouldGenTempVar(subkind)) { 614 if (preserveState) { 615 name = "$$"; 616 } else { 617 if (state.tempVariableNameGenerator != null) { 618 name = state.tempVariableNameGenerator.get(); 619 } 620 while (name == null || state.keyMap.doesVariableNameExist(name)) { 621 name = "$" + ++varNumber; 622 } 623 } 624 ExpressionInfo varEI = 625 ExpressionToTypeInfo.localVariableTypeForInitializer(compileSource, state, true); 626 String declareTypeName; 627 String fullTypeName; 628 String displayTypeName; 629 Set<String> anonymousClasses; 630 if (varEI != null) { 631 declareTypeName = varEI.declareTypeName; 632 fullTypeName = varEI.fullTypeName; 633 displayTypeName = varEI.displayTypeName; 634 635 TreeDissector dis = TreeDissector.createByFirstClass(pt); 636 Pair<Wrap, Wrap> anonymous2Member = 637 anonymous2Member(varEI, compileSource, new Range(0, compileSource.length()), dis, expr.getExpression()); 638 guts = Wrap.tempVarWrap(anonymous2Member.second.wrapped(), declareTypeName, name, anonymous2Member.first); 639 anonymousClasses = varEI.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet()); 640 } else { 641 declareTypeName = ei.accessibleTypeName; 642 displayTypeName = fullTypeName = typeName; 643 guts = Wrap.tempVarWrap(compileSource, declareTypeName, name, null); 644 anonymousClasses = Collections.emptySet(); 645 } 646 Collection<String> declareReferences = null; //TODO 647 snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, 648 name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, displayTypeName, fullTypeName, anonymousClasses, declareReferences, null); 649 } else { 650 guts = Wrap.methodReturnWrap(compileSource); 651 snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts, 652 name, subkind); 653 } 654 } else { 655 guts = Wrap.methodWrap(compileSource); 656 if (ei == null) { 657 // We got no type info, check for not a statement by trying 658 DiagList dl = trialCompile(guts); 659 if (dl.hasNotStatement()) { 660 guts = Wrap.methodReturnWrap(compileSource); 661 dl = trialCompile(guts); 662 } 663 if (dl.hasErrors()) { 664 return compileFailResult(dl, userSource, Kind.EXPRESSION); 665 } 666 } 667 snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); 668 } 669 return singletonList(snip); 670 } 671 processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt)672 private List<Snippet> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) { 673 TreeDependencyScanner tds = new TreeDependencyScanner(); 674 tds.scan(unitTree); 675 676 TreeDissector dis = TreeDissector.createByFirstClass(pt); 677 678 ClassTree klassTree = (ClassTree) unitTree; 679 String name = klassTree.getSimpleName().toString(); 680 DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false); 681 TypeDeclKey key = state.keyMap.keyForClass(name); 682 // Corralling 683 Wrap corralled = new Corraller(dis, key.index(), compileSource).corralType(klassTree); 684 685 Wrap guts = Wrap.classMemberWrap(compileSource); 686 Snippet snip = new TypeDeclSnippet(key, userSource, guts, 687 name, snippetKind, 688 corralled, tds.declareReferences(), tds.bodyReferences(), modDiag); 689 return singletonList(snip); 690 } 691 processStatement(String userSource, String compileSource)692 private List<Snippet> processStatement(String userSource, String compileSource) { 693 Wrap guts = Wrap.methodWrap(compileSource); 694 // Check for unreachable by trying 695 DiagList dl = trialCompile(guts); 696 if (dl.hasErrors()) { 697 if (dl.hasUnreachableError()) { 698 guts = Wrap.methodUnreachableSemiWrap(compileSource); 699 dl = trialCompile(guts); 700 if (dl.hasErrors()) { 701 if (dl.hasUnreachableError()) { 702 // Without ending semicolon 703 guts = Wrap.methodUnreachableWrap(compileSource); 704 dl = trialCompile(guts); 705 } 706 if (dl.hasErrors()) { 707 return compileFailResult(dl, userSource, Kind.STATEMENT); 708 } 709 } 710 } else { 711 return compileFailResult(dl, userSource, Kind.STATEMENT); 712 } 713 } 714 Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); 715 return singletonList(snip); 716 } 717 trialCompile(Wrap guts)718 private DiagList trialCompile(Wrap guts) { 719 OuterWrap outer = state.outerMap.wrapInTrialClass(guts); 720 return state.taskFactory.analyze(outer, AnalyzeTask::getDiagnostics); 721 } 722 processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt)723 private List<Snippet> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) { 724 TreeDependencyScanner tds = new TreeDependencyScanner(); 725 tds.scan(unitTree); 726 final TreeDissector dis = TreeDissector.createByFirstClass(pt); 727 728 final MethodTree mt = (MethodTree) unitTree; 729 final String name = mt.getName().toString(); 730 if (objectMethods.contains(name)) { 731 // The name matches a method on Object, short of an overhaul, this 732 // fails, see 8187137. Generate a descriptive error message 733 734 // The error position will be the position of the name, find it 735 long possibleStart = dis.getEndPosition(mt.getReturnType()); 736 Range possibleRange = new Range((int) possibleStart, 737 dis.getStartPosition(mt.getBody())); 738 String possibleNameSection = possibleRange.part(compileSource); 739 int offset = possibleNameSection.indexOf(name); 740 long start = offset < 0 741 ? possibleStart // something wrong, punt 742 : possibleStart + offset; 743 744 return compileFailResult(new DiagList(objectMethodNameDiag(name, start)), userSource, Kind.METHOD); 745 } 746 String parameterTypes 747 = mt.getParameters() 748 .stream() 749 .map(param -> dis.treeToRange(param.getType()).part(compileSource)) 750 .collect(Collectors.joining(",")); 751 Tree returnType = mt.getReturnType(); 752 DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true); 753 MethodKey key = state.keyMap.keyForMethod(name, parameterTypes); 754 // Corralling 755 Wrap corralled = new Corraller(dis, key.index(), compileSource).corralMethod(mt); 756 757 if (modDiag.hasErrors()) { 758 return compileFailResult(modDiag, userSource, Kind.METHOD); 759 } 760 Wrap guts = Wrap.classMemberWrap(compileSource); 761 Range typeRange = dis.treeToRange(returnType); 762 String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource); 763 764 Snippet snip = new MethodSnippet(key, userSource, guts, 765 name, signature, 766 corralled, tds.declareReferences(), tds.bodyReferences(), modDiag); 767 return singletonList(snip); 768 } 769 kindOfTree(Tree tree)770 private Kind kindOfTree(Tree tree) { 771 switch (tree.getKind()) { 772 case IMPORT: 773 return Kind.IMPORT; 774 case VARIABLE: 775 return Kind.VAR; 776 case EXPRESSION_STATEMENT: 777 return Kind.EXPRESSION; 778 case CLASS: 779 case ENUM: 780 case ANNOTATION_TYPE: 781 case INTERFACE: 782 return Kind.TYPE_DECL; 783 case METHOD: 784 return Kind.METHOD; 785 default: 786 return Kind.STATEMENT; 787 } 788 } 789 790 /** 791 * The snippet has failed, return with the rejected snippet 792 * 793 * @param xt the task from which to extract the failure diagnostics 794 * @param userSource the incoming bad user source 795 * @return a rejected snippet 796 */ compileFailResult(BaseTask xt, String userSource, Kind probableKind)797 private List<Snippet> compileFailResult(BaseTask xt, String userSource, Kind probableKind) { 798 return compileFailResult(xt.getDiagnostics(), userSource, probableKind); 799 } 800 801 /** 802 * The snippet has failed, return with the rejected snippet 803 * 804 * @param diags the failure diagnostics 805 * @param userSource the incoming bad user source 806 * @return a rejected snippet 807 */ compileFailResult(DiagList diags, String userSource, Kind probableKind)808 private List<Snippet> compileFailResult(DiagList diags, String userSource, Kind probableKind) { 809 ErroneousKey key = state.keyMap.keyForErroneous(); 810 Snippet snip = new ErroneousSnippet(key, userSource, null, 811 probableKind, SubKind.UNKNOWN_SUBKIND); 812 snip.setFailed(diags); 813 814 // Install wrapper for query by SourceCodeAnalysis.wrapper 815 String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, true).cleared()); 816 OuterWrap outer; 817 switch (probableKind) { 818 case IMPORT: 819 outer = state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip); 820 break; 821 case EXPRESSION: 822 outer = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource)); 823 break; 824 case VAR: 825 case TYPE_DECL: 826 case METHOD: 827 outer = state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource)); 828 break; 829 default: 830 outer = state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource)); 831 break; 832 } 833 snip.setOuterWrap(outer); 834 835 return singletonList(snip); 836 } 837 838 /** 839 * Should a temp var wrap the expression. TODO make this user configurable. 840 * 841 * @param snippetKind 842 * @return 843 */ shouldGenTempVar(SubKind snippetKind)844 private boolean shouldGenTempVar(SubKind snippetKind) { 845 return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND; 846 } 847 drop(Snippet si)848 List<SnippetEvent> drop(Snippet si) { 849 Unit c = new Unit(state, si); 850 Set<Unit> outs; 851 if (si instanceof PersistentSnippet) { 852 Set<Unit> ins = c.dependents().collect(toSet()); 853 outs = compileAndLoad(ins); 854 } else { 855 outs = Collections.emptySet(); 856 } 857 return events(c, outs, null, null); 858 } 859 declare(Snippet si, DiagList generatedDiagnostics)860 private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) { 861 Unit c = new Unit(state, si, null, generatedDiagnostics); 862 Set<Unit> ins = new LinkedHashSet<>(); 863 ins.add(c); 864 Set<Unit> outs = compileAndLoad(ins); 865 866 if (!si.status().isDefined() 867 && si.diagnostics().isEmpty() 868 && si.unresolved().isEmpty()) { 869 // did not succeed, but no record of it, extract from others 870 si.setDiagnostics(outs.stream() 871 .flatMap(u -> u.snippet().diagnostics().stream()) 872 .collect(Collectors.toCollection(DiagList::new))); 873 } 874 875 // If appropriate, execute the snippet 876 String value = null; 877 JShellException exception = null; 878 if (si.status().isDefined()) { 879 if (si.isExecutable()) { 880 try { 881 value = state.executionControl().invoke(si.classFullName(), DOIT_METHOD_NAME); 882 value = si.subKind().hasValue() 883 ? expunge(value) 884 : ""; 885 } catch (ResolutionException ex) { 886 exception = asUnresolvedReferenceException(ex); 887 } catch (UserException ex) { 888 exception = asEvalException(ex); 889 } catch (RunException ex) { 890 // StopException - no-op 891 } catch (InternalException ex) { 892 state.debug(ex, "invoke"); 893 } catch (EngineTerminationException ex) { 894 state.debug(ex, "termination"); 895 state.closeDown(); 896 } 897 } 898 } 899 return events(c, outs, value, exception); 900 } 901 902 // Convert an internal UserException to an API EvalException, translating 903 // the stack to snippet form. Convert any chained exceptions asEvalException(UserException ue)904 private EvalException asEvalException(UserException ue) { 905 return new EvalException(ue.getMessage(), 906 ue.causeExceptionClass(), 907 translateExceptionStack(ue), 908 asJShellException(ue.getCause())); 909 } 910 911 // Convert an internal ResolutionException to an API UnresolvedReferenceException, 912 // translating the snippet id to snipper and the stack to snippet form asUnresolvedReferenceException(ResolutionException re)913 private UnresolvedReferenceException asUnresolvedReferenceException(ResolutionException re) { 914 DeclarationSnippet sn = (DeclarationSnippet) state.maps.getSnippetDeadOrAlive(re.id()); 915 return new UnresolvedReferenceException(sn, translateExceptionStack(re)); 916 } 917 918 // Convert an internal UserException/ResolutionException to an API 919 // EvalException/UnresolvedReferenceException asJShellException(Throwable e)920 private JShellException asJShellException(Throwable e) { 921 if (e == null) { 922 return null; 923 } else if (e instanceof UserException) { 924 return asEvalException((UserException) e); 925 } else if (e instanceof ResolutionException) { 926 return asUnresolvedReferenceException((ResolutionException) e); 927 } else { 928 throw new AssertionError(e); 929 } 930 } 931 interestingEvent(SnippetEvent e)932 private boolean interestingEvent(SnippetEvent e) { 933 return e.isSignatureChange() 934 || e.causeSnippet() == null 935 || e.status() != e.previousStatus() 936 || e.exception() != null; 937 } 938 events(Unit c, Collection<Unit> outs, String value, JShellException exception)939 private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, JShellException exception) { 940 List<SnippetEvent> events = new ArrayList<>(); 941 events.add(c.event(value, exception)); 942 events.addAll(outs.stream() 943 .filter(u -> u != c) 944 .map(u -> u.event(null, null)) 945 .filter(this::interestingEvent) 946 .collect(Collectors.toList())); 947 events.addAll(outs.stream() 948 .flatMap(u -> u.secondaryEvents().stream()) 949 .filter(this::interestingEvent) 950 .collect(Collectors.toList())); 951 //System.err.printf("Events: %s\n", events); 952 return events; 953 } 954 outerWrapSet(Collection<Unit> units)955 private Set<OuterWrap> outerWrapSet(Collection<Unit> units) { 956 return units.stream() 957 .map(u -> u.snippet().outerWrap()) 958 .collect(toSet()); 959 } 960 compileAndLoad(Set<Unit> ins)961 private Set<Unit> compileAndLoad(Set<Unit> ins) { 962 if (ins.isEmpty()) { 963 return ins; 964 } 965 Set<Unit> replaced = new LinkedHashSet<>(); 966 // Loop until dependencies and errors are stable 967 while (true) { 968 state.debug(DBG_GEN, "compileAndLoad %s\n", ins); 969 970 ins.stream().forEach(Unit::initialize); 971 ins.stream().forEach(u -> u.setWrap(ins, ins)); 972 state.taskFactory.analyze(outerWrapSet(ins), at -> { 973 ins.stream().forEach(u -> u.setDiagnostics(at)); 974 975 // corral any Snippets that need it 976 if (ins.stream().filter(u -> u.corralIfNeeded(ins)).count() > 0) { 977 // if any were corralled, re-analyze everything 978 state.taskFactory.analyze(outerWrapSet(ins), cat -> { 979 ins.stream().forEach(u -> u.setCorralledDiagnostics(cat)); 980 ins.stream().forEach(u -> u.setStatus(cat)); 981 return null; 982 }); 983 } else { 984 ins.stream().forEach(u -> u.setStatus(at)); 985 } 986 return null; 987 }); 988 // compile and load the legit snippets 989 boolean success; 990 while (true) { 991 List<Unit> legit = ins.stream() 992 .filter(Unit::isDefined) 993 .collect(toList()); 994 state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n", 995 ins, legit); 996 if (legit.isEmpty()) { 997 // no class files can be generated 998 success = true; 999 } else { 1000 // re-wrap with legit imports 1001 legit.stream().forEach(u -> u.setWrap(ins, legit)); 1002 1003 // generate class files for those capable 1004 Result res = state.taskFactory.compile(outerWrapSet(legit), ct -> { 1005 if (!ct.compile()) { 1006 // oy! compile failed because of recursive new unresolved 1007 if (legit.stream() 1008 .filter(u -> u.smashingErrorDiagnostics(ct)) 1009 .count() > 0) { 1010 // try again, with the erroreous removed 1011 return Result.CONTINUE; 1012 } else { 1013 state.debug(DBG_GEN, "Should never happen error-less failure - %s\n", 1014 legit); 1015 } 1016 } 1017 1018 // load all new classes 1019 load(legit.stream() 1020 .flatMap(u -> u.classesToLoad(ct.classList(u.snippet().outerWrap()))) 1021 .collect(toSet())); 1022 // attempt to redefine the remaining classes 1023 List<Unit> toReplace = legit.stream() 1024 .filter(u -> !u.doRedefines()) 1025 .collect(toList()); 1026 1027 // prevent alternating redefine/replace cyclic dependency 1028 // loop by replacing all that have been replaced 1029 if (!toReplace.isEmpty()) { 1030 replaced.addAll(toReplace); 1031 replaced.stream().forEach(Unit::markForReplacement); 1032 //ensure correct classnames are set in the snippets: 1033 replaced.stream().forEach(u -> u.setWrap(ins, legit)); 1034 } 1035 1036 return toReplace.isEmpty() ? Result.SUCESS : Result.FAILURE; 1037 }); 1038 1039 switch (res) { 1040 case CONTINUE: continue; 1041 case SUCESS: success = true; break; 1042 default: 1043 case FAILURE: success = false; break; 1044 } 1045 } 1046 break; 1047 } 1048 1049 // add any new dependencies to the working set 1050 List<Unit> newDependencies = ins.stream() 1051 .flatMap(Unit::effectedDependents) 1052 .collect(toList()); 1053 state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s success: %s\n", 1054 ins, newDependencies, success); 1055 if (!ins.addAll(newDependencies) && success) { 1056 // all classes that could not be directly loaded (because they 1057 // are new) have been redefined, and no new dependnencies were 1058 // identified 1059 ins.stream().forEach(Unit::finish); 1060 return ins; 1061 } 1062 } 1063 } 1064 //where: 1065 enum Result {SUCESS, FAILURE, CONTINUE} 1066 1067 /** 1068 * If there are classes to load, loads by calling the execution engine. 1069 * @param classbytecodes names of the classes to load. 1070 */ load(Collection<ClassBytecodes> classbytecodes)1071 private void load(Collection<ClassBytecodes> classbytecodes) { 1072 if (!classbytecodes.isEmpty()) { 1073 ClassBytecodes[] cbcs = classbytecodes.toArray(new ClassBytecodes[classbytecodes.size()]); 1074 try { 1075 state.executionControl().load(cbcs); 1076 state.classTracker.markLoaded(cbcs); 1077 } catch (ClassInstallException ex) { 1078 state.classTracker.markLoaded(cbcs, ex.installed()); 1079 } catch (NotImplementedException ex) { 1080 state.debug(ex, "Seriously?!? load not implemented"); 1081 state.closeDown(); 1082 } catch (EngineTerminationException ex) { 1083 state.closeDown(); 1084 } 1085 } 1086 } 1087 translateExceptionStack(Exception ex)1088 private StackTraceElement[] translateExceptionStack(Exception ex) { 1089 StackTraceElement[] raw = ex.getStackTrace(); 1090 int last = raw.length; 1091 do { 1092 if (last == 0) { 1093 last = raw.length - 1; 1094 break; 1095 } 1096 } while (!isWrap(raw[--last])); 1097 StackTraceElement[] elems = new StackTraceElement[last + 1]; 1098 for (int i = 0; i <= last; ++i) { 1099 StackTraceElement r = raw[i]; 1100 OuterSnippetsClassWrap outer = state.outerMap.getOuter(r.getClassName()); 1101 if (outer != null) { 1102 String klass = expunge(r.getClassName()); 1103 String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName(); 1104 int wln = r.getLineNumber() - 1; 1105 int line = outer.wrapLineToSnippetLine(wln) + 1; 1106 Snippet sn = outer.wrapLineToSnippet(wln); 1107 String file = "#" + sn.id(); 1108 elems[i] = new StackTraceElement(klass, method, file, line); 1109 } else if ("<none>".equals(r.getFileName())) { 1110 elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber()); 1111 } else { 1112 elems[i] = r; 1113 } 1114 } 1115 return elems; 1116 } 1117 isWrap(StackTraceElement ste)1118 private boolean isWrap(StackTraceElement ste) { 1119 return PREFIX_PATTERN.matcher(ste.getClassName()).find(); 1120 } 1121 1122 /** 1123 * Construct a diagnostic for a method name matching an Object method name 1124 * @param name the method name 1125 * @param nameStart the position within the source of the method name 1126 * @return the generated diagnostic 1127 */ objectMethodNameDiag(String name, long nameStart)1128 private Diag objectMethodNameDiag(String name, long nameStart) { 1129 return new Diag() { 1130 @Override 1131 public boolean isError() { 1132 return true; 1133 } 1134 1135 @Override 1136 public long getPosition() { 1137 return nameStart; 1138 } 1139 1140 @Override 1141 public long getStartPosition() { 1142 return nameStart; 1143 } 1144 1145 @Override 1146 public long getEndPosition() { 1147 return nameStart + name.length(); 1148 } 1149 1150 @Override 1151 public String getCode() { 1152 return "jdk.eval.error.object.method"; 1153 } 1154 1155 @Override 1156 public String getMessage(Locale locale) { 1157 return state.messageFormat("jshell.diag.object.method.fatal", 1158 String.join(" ", objectMethods)); 1159 } 1160 }; 1161 } 1162 1163 private class ModifierDiagnostic extends Diag { 1164 1165 final boolean fatal; 1166 final String message; 1167 final long start; 1168 final long end; 1169 1170 ModifierDiagnostic(boolean fatal, 1171 final String message, 1172 long start, 1173 long end) { 1174 this.fatal = fatal; 1175 this.message = message; 1176 this.start = start; 1177 this.end = end; 1178 } 1179 1180 @Override 1181 public boolean isError() { 1182 return fatal; 1183 } 1184 1185 @Override 1186 public long getPosition() { 1187 return start; 1188 } 1189 1190 @Override 1191 public long getStartPosition() { 1192 return start; 1193 } 1194 1195 @Override 1196 public long getEndPosition() { 1197 return end; 1198 } 1199 1200 @Override 1201 public String getCode() { 1202 return fatal 1203 ? "jdk.eval.error.illegal.modifiers" 1204 : "jdk.eval.warn.illegal.modifiers"; 1205 } 1206 1207 @Override 1208 public String getMessage(Locale locale) { 1209 return message; 1210 } 1211 } 1212 1213 private DiagList modifierDiagnostics(ModifiersTree modtree, 1214 final TreeDissector dis, boolean isAbstractProhibited) { 1215 1216 List<Modifier> list = new ArrayList<>(); 1217 boolean fatal = false; 1218 for (Modifier mod : modtree.getFlags()) { 1219 switch (mod) { 1220 case SYNCHRONIZED: 1221 case NATIVE: 1222 list.add(mod); 1223 fatal = true; 1224 break; 1225 case ABSTRACT: 1226 if (isAbstractProhibited) { 1227 list.add(mod); 1228 fatal = true; 1229 } 1230 break; 1231 case PUBLIC: 1232 case PROTECTED: 1233 case PRIVATE: 1234 // quietly ignore, user cannot see effects one way or the other 1235 break; 1236 case STATIC: 1237 case FINAL: 1238 list.add(mod); 1239 break; 1240 } 1241 } 1242 if (list.isEmpty()) { 1243 return new DiagList(); 1244 } else { 1245 StringBuilder sb = new StringBuilder(); 1246 for (Modifier mod : list) { 1247 sb.append("'"); 1248 sb.append(mod.toString()); 1249 sb.append("' "); 1250 } 1251 String key = (list.size() > 1) 1252 ? fatal 1253 ? "jshell.diag.modifier.plural.fatal" 1254 : "jshell.diag.modifier.plural.ignore" 1255 : fatal 1256 ? "jshell.diag.modifier.single.fatal" 1257 : "jshell.diag.modifier.single.ignore"; 1258 String message = state.messageFormat(key, sb.toString().trim()); 1259 return new DiagList(new ModifierDiagnostic(fatal, message, 1260 dis.getStartPosition(modtree), dis.getEndPosition(modtree))); 1261 } 1262 } 1263 1264 String computeDeclareName(TypeSymbol ts) { 1265 return Util.JSHELL_ANONYMOUS + "$" + Long.toUnsignedString(anonCount++); 1266 } 1267 } 1268