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