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