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