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