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