Eval.java revision 3357:3e3553ee39d9
1/*
2 * Copyright (c) 2014, 2016, 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 */
25package jdk.jshell;
26
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.List;
31import java.util.Locale;
32import java.util.regex.Matcher;
33import java.util.regex.Pattern;
34import java.util.stream.Collectors;
35import javax.lang.model.element.Modifier;
36import com.sun.source.tree.ArrayTypeTree;
37import com.sun.source.tree.AssignmentTree;
38import com.sun.source.tree.ClassTree;
39import com.sun.source.tree.ExpressionTree;
40import com.sun.source.tree.IdentifierTree;
41import com.sun.source.tree.MethodTree;
42import com.sun.source.tree.ModifiersTree;
43import com.sun.source.tree.Tree;
44import com.sun.source.tree.VariableTree;
45import com.sun.tools.javac.tree.JCTree;
46import com.sun.tools.javac.tree.Pretty;
47import java.io.IOException;
48import java.io.StringWriter;
49import java.io.Writer;
50import java.util.LinkedHashSet;
51import java.util.Set;
52import jdk.jshell.ClassTracker.ClassInfo;
53import jdk.jshell.Key.ErroneousKey;
54import jdk.jshell.Key.MethodKey;
55import jdk.jshell.Key.TypeDeclKey;
56import jdk.jshell.Snippet.SubKind;
57import jdk.jshell.TaskFactory.AnalyzeTask;
58import jdk.jshell.TaskFactory.BaseTask;
59import jdk.jshell.TaskFactory.CompileTask;
60import jdk.jshell.TaskFactory.ParseTask;
61import jdk.jshell.TreeDissector.ExpressionInfo;
62import jdk.jshell.Wrap.Range;
63import jdk.jshell.Snippet.Status;
64import static java.util.stream.Collectors.toList;
65import static java.util.stream.Collectors.toSet;
66import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
67import static jdk.jshell.Util.*;
68import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
69import static jdk.internal.jshell.remote.RemoteCodes.PREFIX_PATTERN;
70import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
71import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
72import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
73import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
74
75/**
76 * The Evaluation Engine. Source internal analysis, wrapping control,
77 * compilation, declaration. redefinition, replacement, and execution.
78 *
79 * @author Robert Field
80 */
81class Eval {
82
83    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}_\\$]+|\\*))");
84
85    private int varNumber = 0;
86
87    private final JShell state;
88
89    Eval(JShell state) {
90        this.state = state;
91    }
92
93    List<SnippetEvent> eval(String userSource) throws IllegalStateException {
94        String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
95        if (compileSource.length() == 0) {
96            return Collections.emptyList();
97        }
98        // String folding messes up position information.
99        ParseTask pt = state.taskFactory.new ParseTask(compileSource);
100        if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
101            return compileFailResult(pt, userSource);
102        }
103
104        List<? extends Tree> units = pt.units();
105        if (units.isEmpty()) {
106            return compileFailResult(pt, userSource);
107        }
108        // Erase illegal modifiers
109        compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
110        Tree unitTree = units.get(0);
111        state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
112        switch (unitTree.getKind()) {
113            case IMPORT:
114                return processImport(userSource, compileSource);
115            case VARIABLE:
116                return processVariables(userSource, units, compileSource, pt);
117            case EXPRESSION_STATEMENT:
118                return processExpression(userSource, compileSource);
119            case CLASS:
120                return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
121            case ENUM:
122                return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
123            case ANNOTATION_TYPE:
124                return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
125            case INTERFACE:
126                return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
127            case METHOD:
128                return processMethod(userSource, unitTree, compileSource, pt);
129            default:
130                return processStatement(userSource, compileSource);
131        }
132    }
133
134    private List<SnippetEvent> processImport(String userSource, String compileSource) {
135        Wrap guts = Wrap.simpleWrap(compileSource);
136        Matcher mat = IMPORT_PATTERN.matcher(compileSource);
137        String fullname;
138        String name;
139        boolean isStatic;
140        if (mat.find()) {
141            isStatic = mat.group("static") != null;
142            name = mat.group("name");
143            fullname = mat.group("fullname");
144        } else {
145            // bad import -- fake it
146            isStatic = compileSource.contains("static");
147            name = fullname = compileSource;
148        }
149        String fullkey = (isStatic ? "static-" : "") + fullname;
150        boolean isStar = name.equals("*");
151        String keyName = isStar
152                ? fullname
153                : name;
154        SubKind snippetKind = isStar
155                ? (isStatic ? STATIC_IMPORT_ON_DEMAND_SUBKIND : TYPE_IMPORT_ON_DEMAND_SUBKIND)
156                : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
157        Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
158                userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
159        return declare(snip);
160    }
161
162    private static class EvalPretty extends Pretty {
163
164        private final Writer out;
165
166        public EvalPretty(Writer writer, boolean bln) {
167            super(writer, bln);
168            this.out = writer;
169        }
170
171        /**
172         * Print string, DO NOT replacing all non-ascii character with unicode
173         * escapes.
174         */
175        @Override
176        public void print(Object o) throws IOException {
177            out.write(o.toString());
178        }
179
180        static String prettyExpr(JCTree tree, boolean bln) {
181            StringWriter out = new StringWriter();
182            try {
183                new EvalPretty(out, bln).printExpr(tree);
184            } catch (IOException e) {
185                throw new AssertionError(e);
186            }
187            return out.toString();
188        }
189    }
190
191    private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
192        List<SnippetEvent> allEvents = new ArrayList<>();
193        TreeDissector dis = TreeDissector.createByFirstClass(pt);
194        for (Tree unitTree : units) {
195            VariableTree vt = (VariableTree) unitTree;
196            String name = vt.getName().toString();
197            String typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
198            Tree baseType = vt.getType();
199            TreeDependencyScanner tds = new TreeDependencyScanner();
200            tds.scan(baseType); // Not dependent on initializer
201            StringBuilder sbBrackets = new StringBuilder();
202            while (baseType instanceof ArrayTypeTree) {
203                //TODO handle annotations too
204                baseType = ((ArrayTypeTree) baseType).getType();
205                sbBrackets.append("[]");
206            }
207            Range rtype = dis.treeToRange(baseType);
208            Range runit = dis.treeToRange(vt);
209            runit = new Range(runit.begin, runit.end - 1);
210            ExpressionTree it = vt.getInitializer();
211            Range rinit = null;
212            int nameMax = runit.end - 1;
213            SubKind subkind;
214            if (it != null) {
215                subkind = SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND;
216                rinit = dis.treeToRange(it);
217                nameMax = rinit.begin - 1;
218            } else {
219                subkind = SubKind.VAR_DECLARATION_SUBKIND;
220            }
221            int nameStart = compileSource.lastIndexOf(name, nameMax);
222            if (nameStart < 0) {
223                throw new AssertionError("Name '" + name + "' not found");
224            }
225            int nameEnd = nameStart + name.length();
226            Range rname = new Range(nameStart, nameEnd);
227            Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit);
228            Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
229                    name, subkind, typeName,
230                    tds.declareReferences());
231            DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
232            List<SnippetEvent> res1 = declare(snip, modDiag);
233            allEvents.addAll(res1);
234        }
235
236        return allEvents;
237    }
238
239    private List<SnippetEvent> processExpression(String userSource, String compileSource) {
240        String name = null;
241        ExpressionInfo ei = typeOfExpression(compileSource);
242        ExpressionTree assignVar;
243        Wrap guts;
244        Snippet snip;
245        if (ei != null && ei.isNonVoid) {
246            String typeName = ei.typeName;
247            SubKind subkind;
248            if (ei.tree instanceof IdentifierTree) {
249                IdentifierTree id = (IdentifierTree) ei.tree;
250                name = id.getName().toString();
251                subkind = SubKind.VAR_VALUE_SUBKIND;
252
253            } else if (ei.tree instanceof AssignmentTree
254                    && (assignVar = ((AssignmentTree) ei.tree).getVariable()) instanceof IdentifierTree) {
255                name = assignVar.toString();
256                subkind = SubKind.ASSIGNMENT_SUBKIND;
257            } else {
258                subkind = SubKind.OTHER_EXPRESSION_SUBKIND;
259            }
260            if (shouldGenTempVar(subkind)) {
261                if (state.tempVariableNameGenerator != null) {
262                    name = state.tempVariableNameGenerator.get();
263                }
264                while (name == null || state.keyMap.doesVariableNameExist(name)) {
265                    name = "$" + ++varNumber;
266                }
267                guts = Wrap.tempVarWrap(compileSource, typeName, name);
268                Collection<String> declareReferences = null; //TODO
269                snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
270                        name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences);
271            } else {
272                guts = Wrap.methodReturnWrap(compileSource);
273                snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
274                        name, subkind);
275            }
276        } else {
277            guts = Wrap.methodWrap(compileSource);
278            if (ei == null) {
279                // We got no type info, check for not a statement by trying
280                AnalyzeTask at = trialCompile(guts);
281                if (at.getDiagnostics().hasNotStatement()) {
282                    guts = Wrap.methodReturnWrap(compileSource);
283                    at = trialCompile(guts);
284                }
285                if (at.hasErrors()) {
286                    return compileFailResult(at, userSource);
287                }
288            }
289            snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
290        }
291        return declare(snip);
292    }
293
294    private List<SnippetEvent> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
295        TreeDependencyScanner tds = new TreeDependencyScanner();
296        tds.scan(unitTree);
297
298        TreeDissector dis = TreeDissector.createByFirstClass(pt);
299
300        ClassTree klassTree = (ClassTree) unitTree;
301        String name = klassTree.getSimpleName().toString();
302        DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
303        TypeDeclKey key = state.keyMap.keyForClass(name);
304        // Corralling mutates.  Must be last use of pt, unitTree, klassTree
305        Wrap corralled = new Corraller(key.index(), pt.getContext()).corralType(klassTree);
306
307        Wrap guts = Wrap.classMemberWrap(compileSource);
308        Snippet snip = new TypeDeclSnippet(key, userSource, guts,
309                name, snippetKind,
310                corralled, tds.declareReferences(), tds.bodyReferences());
311        return declare(snip, modDiag);
312    }
313
314    private List<SnippetEvent> processStatement(String userSource, String compileSource) {
315        Wrap guts = Wrap.methodWrap(compileSource);
316        // Check for unreachable by trying
317        AnalyzeTask at = trialCompile(guts);
318        if (at.hasErrors()) {
319            if (at.getDiagnostics().hasUnreachableError()) {
320                guts = Wrap.methodUnreachableSemiWrap(compileSource);
321                at = trialCompile(guts);
322                if (at.hasErrors()) {
323                    if (at.getDiagnostics().hasUnreachableError()) {
324                        // Without ending semicolon
325                        guts = Wrap.methodUnreachableWrap(compileSource);
326                        at = trialCompile(guts);
327                    }
328                    if (at.hasErrors()) {
329                        return compileFailResult(at, userSource);
330                    }
331                }
332            } else {
333                return compileFailResult(at, userSource);
334            }
335        }
336        Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
337        return declare(snip);
338    }
339
340    private AnalyzeTask trialCompile(Wrap guts) {
341        OuterWrap outer = state.outerMap.wrapInTrialClass(guts);
342        return state.taskFactory.new AnalyzeTask(outer);
343    }
344
345    private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
346        TreeDependencyScanner tds = new TreeDependencyScanner();
347        tds.scan(unitTree);
348        TreeDissector dis = TreeDissector.createByFirstClass(pt);
349
350        MethodTree mt = (MethodTree) unitTree;
351        String name = mt.getName().toString();
352        String parameterTypes
353                = mt.getParameters()
354                .stream()
355                .map(param -> dis.treeToRange(param.getType()).part(compileSource))
356                .collect(Collectors.joining(","));
357        Tree returnType = mt.getReturnType();
358        DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
359        MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
360        // Corralling mutates.  Must be last use of pt, unitTree, mt
361        Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt);
362
363        if (modDiag.hasErrors()) {
364            return compileFailResult(modDiag, userSource);
365        }
366        Wrap guts = Wrap.classMemberWrap(compileSource);
367        Range typeRange = dis.treeToRange(returnType);
368        String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
369
370        Snippet snip = new MethodSnippet(key, userSource, guts,
371                name, signature,
372                corralled, tds.declareReferences(), tds.bodyReferences());
373        return declare(snip, modDiag);
374    }
375
376    /**
377     * The snippet has failed, return with the rejected event
378     *
379     * @param xt the task from which to extract the failure diagnostics
380     * @param userSource the incoming bad user source
381     * @return a rejected snippet event
382     */
383    private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) {
384        return compileFailResult(xt.getDiagnostics(), userSource);
385    }
386
387    /**
388     * The snippet has failed, return with the rejected event
389     *
390     * @param diags the failure diagnostics
391     * @param userSource the incoming bad user source
392     * @return a rejected snippet event
393     */
394    private List<SnippetEvent> compileFailResult(DiagList diags, String userSource) {
395        ErroneousKey key = state.keyMap.keyForErroneous();
396        Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND);
397        snip.setFailed(diags);
398        state.maps.installSnippet(snip);
399        return Collections.singletonList(new SnippetEvent(
400                snip, Status.NONEXISTENT, Status.REJECTED,
401                false, null, null, null)
402        );
403    }
404
405    private ExpressionInfo typeOfExpression(String expression) {
406        Wrap guts = Wrap.methodReturnWrap(expression);
407        TaskFactory.AnalyzeTask at = trialCompile(guts);
408        if (!at.hasErrors() && at.firstCuTree() != null) {
409            return TreeDissector.createByFirstClass(at)
410                    .typeOfReturnStatement(at, state);
411        }
412        return null;
413    }
414
415    /**
416     * Should a temp var wrap the expression. TODO make this user configurable.
417     *
418     * @param snippetKind
419     * @return
420     */
421    private boolean shouldGenTempVar(SubKind snippetKind) {
422        return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND;
423    }
424
425    List<SnippetEvent> drop(Snippet si) {
426        Unit c = new Unit(state, si);
427
428        Set<Unit> ins = c.dependents().collect(toSet());
429        Set<Unit> outs = compileAndLoad(ins);
430
431        return events(c, outs, null, null);
432    }
433
434    private List<SnippetEvent> declare(Snippet si) {
435        return declare(si, new DiagList());
436    }
437
438    private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
439        Unit c = new Unit(state, si, null, generatedDiagnostics);
440        Set<Unit> ins = new LinkedHashSet<>();
441        ins.add(c);
442        Set<Unit> outs = compileAndLoad(ins);
443
444        if (!si.status().isDefined
445                && si.diagnostics().isEmpty()
446                && si.unresolved().isEmpty()) {
447            // did not succeed, but no record of it, extract from others
448            si.setDiagnostics(outs.stream()
449                    .flatMap(u -> u.snippet().diagnostics().stream())
450                    .collect(Collectors.toCollection(DiagList::new)));
451        }
452
453        // If appropriate, execute the snippet
454        String value = null;
455        Exception exception = null;
456        if (si.status().isDefined) {
457            if (si.isExecutable()) {
458                try {
459                value = state.executionControl().commandInvoke(si.classFullName());
460                    value = si.subKind().hasValue()
461                            ? expunge(value)
462                            : "";
463                } catch (EvalException ex) {
464                    exception = translateExecutionException(ex);
465                } catch (UnresolvedReferenceException ex) {
466                    exception = ex;
467                }
468            } else if (si.subKind() == SubKind.VAR_DECLARATION_SUBKIND) {
469                switch (((VarSnippet) si).typeName()) {
470                    case "byte":
471                    case "short":
472                    case "int":
473                    case "long":
474                        value = "0";
475                        break;
476                    case "float":
477                    case "double":
478                        value = "0.0";
479                        break;
480                    case "boolean":
481                        value = "false";
482                        break;
483                    case "char":
484                        value = "''";
485                        break;
486                    default:
487                        value = "null";
488                        break;
489                }
490            }
491        }
492        return events(c, outs, value, exception);
493    }
494
495    private boolean interestingEvent(SnippetEvent e) {
496        return e.isSignatureChange()
497                    || e.causeSnippet() == null
498                    || e.status() != e.previousStatus()
499                    || e.exception() != null;
500    }
501
502    private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, Exception exception) {
503        List<SnippetEvent> events = new ArrayList<>();
504        events.add(c.event(value, exception));
505        events.addAll(outs.stream()
506                .filter(u -> u != c)
507                .map(u -> u.event(null, null))
508                .filter(this::interestingEvent)
509                .collect(Collectors.toList()));
510        events.addAll(outs.stream()
511                .flatMap(u -> u.secondaryEvents().stream())
512                .filter(this::interestingEvent)
513                .collect(Collectors.toList()));
514        //System.err.printf("Events: %s\n", events);
515        return events;
516    }
517
518    private Set<OuterWrap> outerWrapSet(Collection<Unit> units) {
519        return units.stream()
520                .map(u -> u.snippet().outerWrap())
521                .collect(toSet());
522    }
523
524    private Set<Unit> compileAndLoad(Set<Unit> ins) {
525        if (ins.isEmpty()) {
526            return ins;
527        }
528        Set<Unit> replaced = new LinkedHashSet<>();
529        // Loop until dependencies and errors are stable
530        while (true) {
531            state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
532
533            ins.stream().forEach(u -> u.initialize());
534            ins.stream().forEach(u -> u.setWrap(ins, ins));
535            AnalyzeTask at = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
536            ins.stream().forEach(u -> u.setDiagnostics(at));
537
538            // corral any Snippets that need it
539            AnalyzeTask cat;
540            if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
541                // if any were corralled, re-analyze everything
542                cat = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
543                ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
544            } else {
545                cat = at;
546            }
547            ins.stream().forEach(u -> u.setStatus(cat));
548            // compile and load the legit snippets
549            boolean success;
550            while (true) {
551                List<Unit> legit = ins.stream()
552                        .filter(u -> u.isDefined())
553                        .collect(toList());
554                state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
555                        ins, legit);
556                if (legit.isEmpty()) {
557                    // no class files can be generated
558                    success = true;
559                } else {
560                    // re-wrap with legit imports
561                    legit.stream().forEach(u -> u.setWrap(ins, legit));
562
563                    // generate class files for those capable
564                    CompileTask ct = state.taskFactory.new CompileTask(outerWrapSet(legit));
565                    if (!ct.compile()) {
566                        // oy! compile failed because of recursive new unresolved
567                        if (legit.stream()
568                                .filter(u -> u.smashingErrorDiagnostics(ct))
569                                .count() > 0) {
570                            // try again, with the erroreous removed
571                            continue;
572                        } else {
573                            state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
574                                    legit);
575                        }
576                    }
577
578                    // load all new classes
579                    load(legit.stream()
580                            .flatMap(u -> u.classesToLoad(ct.classInfoList(u.snippet().outerWrap())))
581                            .collect(toSet()));
582                    // attempt to redefine the remaining classes
583                    List<Unit> toReplace = legit.stream()
584                            .filter(u -> !u.doRedefines())
585                            .collect(toList());
586
587                    // prevent alternating redefine/replace cyclic dependency
588                    // loop by replacing all that have been replaced
589                    if (!toReplace.isEmpty()) {
590                        replaced.addAll(toReplace);
591                        replaced.stream().forEach(u -> u.markForReplacement());
592                    }
593
594                    success = toReplace.isEmpty();
595                }
596                break;
597            }
598
599            // add any new dependencies to the working set
600            List<Unit> newDependencies = ins.stream()
601                    .flatMap(u -> u.effectedDependents())
602                    .collect(toList());
603            state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s  success: %s\n",
604                    ins, newDependencies, success);
605            if (!ins.addAll(newDependencies) && success) {
606                // all classes that could not be directly loaded (because they
607                // are new) have been redefined, and no new dependnencies were
608                // identified
609                ins.stream().forEach(u -> u.finish());
610                return ins;
611            }
612        }
613    }
614
615    private void load(Set<ClassInfo> cil) {
616        if (!cil.isEmpty()) {
617            state.executionControl().commandLoad(cil);
618        }
619    }
620
621    private EvalException translateExecutionException(EvalException ex) {
622        StackTraceElement[] raw = ex.getStackTrace();
623        int last = raw.length;
624        do {
625            if (last == 0) {
626                last = raw.length - 1;
627                break;
628            }
629        } while (!isWrap(raw[--last]));
630        StackTraceElement[] elems = new StackTraceElement[last + 1];
631        for (int i = 0; i <= last; ++i) {
632            StackTraceElement r = raw[i];
633            OuterSnippetsClassWrap outer = state.outerMap.getOuter(r.getClassName());
634            if (outer != null) {
635                String klass = expunge(r.getClassName());
636                String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName();
637                int wln = r.getLineNumber() - 1;
638                int line = outer.wrapLineToSnippetLine(wln) + 1;
639                Snippet sn = outer.wrapLineToSnippet(wln);
640                String file = "#" + sn.id();
641                elems[i] = new StackTraceElement(klass, method, file, line);
642            } else if (r.getFileName().equals("<none>")) {
643                elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber());
644            } else {
645                elems[i] = r;
646            }
647        }
648        String msg = ex.getMessage();
649        if (msg.equals("<none>")) {
650            msg = null;
651        }
652        return new EvalException(msg, ex.getExceptionClassName(), elems);
653    }
654
655    private boolean isWrap(StackTraceElement ste) {
656        return PREFIX_PATTERN.matcher(ste.getClassName()).find();
657    }
658
659    private DiagList modifierDiagnostics(ModifiersTree modtree,
660            final TreeDissector dis, boolean isAbstractProhibited) {
661
662        class ModifierDiagnostic extends Diag {
663
664            final boolean fatal;
665            final String message;
666
667            ModifierDiagnostic(List<Modifier> list, boolean fatal) {
668                this.fatal = fatal;
669                StringBuilder sb = new StringBuilder();
670                for (Modifier mod : list) {
671                    sb.append("'");
672                    sb.append(mod.toString());
673                    sb.append("' ");
674                }
675                String key = (list.size() > 1)
676                        ? fatal
677                            ? "jshell.diag.modifier.plural.fatal"
678                            : "jshell.diag.modifier.plural.ignore"
679                        : fatal
680                            ? "jshell.diag.modifier.single.fatal"
681                            : "jshell.diag.modifier.single.ignore";
682                this.message = state.messageFormat(key, sb.toString());
683            }
684
685            @Override
686            public boolean isError() {
687                return fatal;
688            }
689
690            @Override
691            public long getPosition() {
692                return dis.getStartPosition(modtree);
693            }
694
695            @Override
696            public long getStartPosition() {
697                return dis.getStartPosition(modtree);
698            }
699
700            @Override
701            public long getEndPosition() {
702                return dis.getEndPosition(modtree);
703            }
704
705            @Override
706            public String getCode() {
707                return fatal
708                        ? "jdk.eval.error.illegal.modifiers"
709                        : "jdk.eval.warn.illegal.modifiers";
710            }
711
712            @Override
713            public String getMessage(Locale locale) {
714                return message;
715            }
716        }
717
718        List<Modifier> list = new ArrayList<>();
719        boolean fatal = false;
720        for (Modifier mod : modtree.getFlags()) {
721            switch (mod) {
722                case SYNCHRONIZED:
723                case NATIVE:
724                    list.add(mod);
725                    fatal = true;
726                    break;
727                case ABSTRACT:
728                    if (isAbstractProhibited) {
729                        list.add(mod);
730                        fatal = true;
731                    }
732                    break;
733                case PUBLIC:
734                case PROTECTED:
735                case PRIVATE:
736                case STATIC:
737                case FINAL:
738                    list.add(mod);
739                    break;
740            }
741        }
742        return list.isEmpty()
743                ? new DiagList()
744                : new DiagList(new ModifierDiagnostic(list, fatal));
745    }
746
747}
748