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