# HG changeset patch # User Michael Van De Vanter # Date 1447810192 28800 # Node ID c17794973b646cec540cb8935ba5e14c0b5c81fb # Parent c350ef0119c6337c5f7f3ec330dd553bbe653f7c Truffle/REPL debugger: move closer to multi-lang diff -r c350ef0119c6 -r c17794973b64 truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Tue Nov 17 17:28:29 2015 -0800 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Tue Nov 17 17:29:52 2015 -0800 @@ -74,10 +74,14 @@ public static final String FRAME_INFO = "frame-info"; public static final String FRAME_NUMBER = "frame-number"; public static final String INFO = "info"; + public static final String INFO_CURRENT_LANGUAGE = "info-current-language"; public static final String INFO_KEY = "info-key"; + public static final String INFO_SUPPORTED_LANGUAGES = "info-supported-languages"; public static final String INFO_VALUE = "info-value"; public static final String KILL = "kill"; - public static final String LANGUAGE = "language"; + public static final String LANG_NAME = "language-name"; + public static final String LANG_VER = "language-version"; + public static final String LANG_MIME = "language-MIME type"; public static final String LINE_NUMBER = "line-number"; public static final String LIST = "list"; public static final String LOAD_SOURCE = "load-source"; @@ -88,6 +92,7 @@ public static final String REPEAT = "repeat"; public static final String SET = "set"; public static final String SET_BREAK_CONDITION = "set-breakpoint-condition"; + public static final String SET_LANGUAGE = "set-language"; public static final String SOURCE_LINE_TEXT = "source-line-text"; public static final String SOURCE_LOCATION = "source-location"; public static final String SOURCE_NAME = "source-name"; @@ -110,6 +115,7 @@ public static final String UP = "up"; public static final String VALUE = "value"; public static final String WARNINGS = "warnings"; + public static final String WELCOME_MESSAGE = "welcome-message"; private final Map map; /** diff -r c350ef0119c6 -r c17794973b64 truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java Tue Nov 17 17:28:29 2015 -0800 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java Tue Nov 17 17:29:52 2015 -0800 @@ -24,11 +24,11 @@ */ package com.oracle.truffle.tools.debug.shell.client; +import java.util.List; + import com.oracle.truffle.api.source.Source; import com.oracle.truffle.tools.debug.shell.server.REPLServer; -import java.util.List; - /** * Client context for interaction with a program halted by the {@link REPLServer}. */ @@ -97,4 +97,9 @@ */ void displayFailReply(String message); + /** + * Recompute the client's command line prompt. + */ + void updatePrompt(); + } diff -r c350ef0119c6 -r c17794973b64 truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java Tue Nov 17 17:28:29 2015 -0800 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java Tue Nov 17 17:29:52 2015 -0800 @@ -561,6 +561,28 @@ } }; + public static final REPLRemoteCommand SET_LANG_CMD = new REPLRemoteCommand("language", "lang", "Set current language") { + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + if (args.length == 1) { + context.displayFailReply("no language specified"); + return null; + } + final REPLMessage request = new REPLMessage(); + request.put(REPLMessage.OP, REPLMessage.SET_LANGUAGE); + request.put(REPLMessage.LANG_NAME, args[1]); + return request; + } + + @Override + void processReply(REPLClientContext context, REPLMessage[] replies) { + context.updatePrompt(); + super.processReply(context, replies); + } + + }; + public static final REPLRemoteCommand STEP_INTO_CMD = new REPLRemoteCommand("step", "s", "(StepInto) next statement, going into functions.") { @Override diff -r c350ef0119c6 -r c17794973b64 truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Tue Nov 17 17:28:29 2015 -0800 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Tue Nov 17 17:29:52 2015 -0800 @@ -82,6 +82,8 @@ */ public class SimpleREPLClient implements REPLClient { + // TODO (mlvdv) Temporarily in hybrid mode; will work either single language or multi (sort of) + private static final String REPLY_PREFIX = "==> "; private static final String FAIL_PREFIX = "**> "; private static final String WARNING_PREFIX = "!!> "; @@ -95,8 +97,6 @@ private static final String STACK_FRAME_FORMAT = " %3d: at %s in %s line =\"%s\"\n"; private static final String STACK_FRAME_SELECTED_FORMAT = "==> %3d: at %s in %s line =\"%s\"\n"; - private final String languageName; - // Top level commands private final Map commandMap = new HashMap<>(); private final Collection commandNames = new TreeSet<>(); @@ -145,8 +145,6 @@ public SimpleREPLClient(REPLServer replServer) { this.replServer = replServer; - // TODO (mlvdv) language-dependent - this.languageName = replServer.getLanguageName(); this.writer = System.out; try { this.reader = new ConsoleReader(); @@ -177,6 +175,7 @@ addCommand(REPLRemoteCommand.LOAD_CMD); addCommand(quitCommand); addCommand(setCommand); + addCommand(REPLRemoteCommand.SET_LANG_CMD); addCommand(REPLRemoteCommand.STEP_INTO_CMD); addCommand(REPLRemoteCommand.STEP_OUT_CMD); addCommand(REPLRemoteCommand.STEP_OVER_CMD); @@ -206,11 +205,22 @@ this.clientContext = new ClientContextImpl(null, null); try { + showWelcome(); clientContext.startContextSession(); } catch (QuitException ex) { - clientContext.displayReply("Goodbye from " + languageName + "/REPL"); + clientContext.displayReply("Goodbye"); } + } + private void showWelcome() { + final REPLMessage request = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + request.put(REPLMessage.TOPIC, REPLMessage.WELCOME_MESSAGE); + final REPLMessage[] replies = clientContext.sendToServer(request); + if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) { + clientContext.displayReply("Welcome"); + } else { + clientContext.displayReply(replies[0].get(REPLMessage.INFO_VALUE)); + } } public void addCommand(REPLCommand replCommand) { @@ -286,7 +296,17 @@ updatePrompt(); } - private void updatePrompt() { + @Override + public void updatePrompt() { + + String languageName = "???"; + final REPLMessage request = new REPLMessage(); + request.put(REPLMessage.OP, REPLMessage.INFO); + request.put(REPLMessage.TOPIC, REPLMessage.INFO_CURRENT_LANGUAGE); + final REPLMessage[] replies = replServer.receive(request); + if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.SUCCEEDED)) { + languageName = replies[0].get(REPLMessage.LANG_NAME); + } if (level == 0) { // 0-level context; no executions halted. if (selectedSource == null) { @@ -299,7 +319,9 @@ final StringBuilder sb = new StringBuilder(); sb.append("(<" + Integer.toString(level) + "> "); sb.append(selectedSource.getShortName()); - sb.append(") "); + sb.append(")"); + sb.append("(" + languageName + ")"); + sb.append(" "); currentPrompt = sb.toString(); } else { // Prompt reveals where currently halted. @@ -309,7 +331,9 @@ if (haltedLineNumber > 0) { sb.append(":" + Integer.toString(haltedLineNumber)); } - sb.append(") "); + sb.append(")"); + sb.append("(" + languageName + ")"); + sb.append(" "); currentPrompt = sb.toString(); } @@ -923,9 +947,9 @@ } }; - private final REPLCommand infoLanguageCommand = new REPLRemoteCommand("language", "lang", "language and implementation details") { + private final REPLCommand infoLanguageCommand = new REPLRemoteCommand("languages", "lang", "languages supported") { - final String[] help = {"info language: list details about the language implementation"}; + final String[] help = {"info language: list details about supported languages"}; @Override public String[] getHelp() { @@ -936,7 +960,7 @@ public REPLMessage createRequest(REPLClientContext context, String[] args) { final REPLMessage request = new REPLMessage(); request.put(REPLMessage.OP, REPLMessage.INFO); - request.put(REPLMessage.TOPIC, REPLMessage.LANGUAGE); + request.put(REPLMessage.TOPIC, REPLMessage.INFO_SUPPORTED_LANGUAGES); return request; } @@ -945,12 +969,12 @@ if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) { clientContext.displayFailReply(replies[0].get(REPLMessage.DISPLAY_MSG)); } else { - clientContext.displayReply("Language info:"); + clientContext.displayReply("Languages supported:"); for (REPLMessage message : replies) { final StringBuilder sb = new StringBuilder(); - sb.append(message.get(REPLMessage.INFO_KEY)); - sb.append(": "); - sb.append(message.get(REPLMessage.INFO_VALUE)); + sb.append(message.get(REPLMessage.LANG_NAME)); + sb.append(" ver. "); + sb.append(message.get(REPLMessage.LANG_VER)); clientContext.displayInfo(sb.toString()); } } diff -r c350ef0119c6 -r c17794973b64 truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Tue Nov 17 17:28:29 2015 -0800 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Tue Nov 17 17:29:52 2015 -0800 @@ -109,23 +109,6 @@ return replies; } - protected static final REPLMessage[] createLanguageInfoReply(REPLServer replServer) { - final Language language = replServer.getLanguage(); - final ArrayList langMessages = new ArrayList<>(); - langMessages.add(createLanguageInfoMessage("Language Name", language.getName())); - langMessages.add(createLanguageInfoMessage("Language version", language.getVersion())); - return langMessages.toArray(new REPLMessage[0]); - } - - protected static final REPLMessage createLanguageInfoMessage(String key, String value) { - final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - infoMessage.put(REPLMessage.TOPIC, REPLMessage.LANGUAGE); - infoMessage.put(REPLMessage.INFO_KEY, key); - infoMessage.put(REPLMessage.INFO_VALUE, value); - infoMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - return infoMessage; - } - protected static final REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint, REPLServer replServer) { final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.BREAKPOINT_INFO); infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); @@ -508,8 +491,32 @@ switch (topic) { - case REPLMessage.LANGUAGE: - return createLanguageInfoReply(replServer); + case REPLMessage.INFO_SUPPORTED_LANGUAGES: + final ArrayList langMessages = new ArrayList<>(); + + for (Language language : replServer.getLanguages()) { + final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + infoMessage.put(REPLMessage.TOPIC, REPLMessage.INFO_SUPPORTED_LANGUAGES); + infoMessage.put(REPLMessage.LANG_NAME, language.getName()); + infoMessage.put(REPLMessage.LANG_VER, language.getVersion()); + infoMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + langMessages.add(infoMessage); + } + return langMessages.toArray(new REPLMessage[0]); + + case REPLMessage.INFO_CURRENT_LANGUAGE: + final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + reply.put(REPLMessage.TOPIC, REPLMessage.INFO_CURRENT_LANGUAGE); + final String languageName = replServer.getCurrentContext().getLanguageName(); + reply.put(REPLMessage.LANG_NAME, languageName); + return finishReplySucceeded(reply, languageName); + + case REPLMessage.WELCOME_MESSAGE: + final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + infoMessage.put(REPLMessage.TOPIC, REPLMessage.WELCOME_MESSAGE); + infoMessage.put(REPLMessage.INFO_VALUE, replServer.getWelcome()); + infoMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + return finishReplySucceeded(infoMessage, "welcome"); default: final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); @@ -558,6 +565,28 @@ } }; + public static final REPLHandler SET_LANGUAGE_HANDLER = new REPLHandler(REPLMessage.SET_LANGUAGE) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.SET_LANGUAGE); + String languageName = request.get(REPLMessage.LANG_NAME); + if (languageName == null) { + return finishReplyFailed(reply, "missing language name"); + } + reply.put(REPLMessage.LANG_NAME, languageName); + try { + final String newLanguageName = replServer.getCurrentContext().setLanguage(languageName); + if (newLanguageName != null) { + return finishReplySucceeded(reply, "Language set to " + newLanguageName); + } + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + return finishReplyFailed(reply, "Language \"" + languageName + "\" not supported"); + } + }; + public static final REPLHandler SET_BREAK_CONDITION_HANDLER = new REPLHandler(REPLMessage.SET_BREAK_CONDITION) { @Override diff -r c350ef0119c6 -r c17794973b64 truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java Tue Nov 17 17:28:29 2015 -0800 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java Tue Nov 17 17:29:52 2015 -0800 @@ -28,9 +28,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.WeakHashMap; import com.oracle.truffle.api.debug.Breakpoint; @@ -70,9 +73,19 @@ private String statusPrefix; private final Map handlerMap = new HashMap<>(); + /** Languages sorted by name. */ + private final TreeSet engineLanguages = new TreeSet<>(new Comparator() { + + public int compare(Language lang1, Language lang2) { + return lang1.getName().compareTo(lang2.getName()); + } + }); + + /** MAP: language name => Language. */ + private final Map nameToLanguage = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + // TODO (mlvdv) Language-specific - private final String mimeType; - private final PolyglotEngine.Language language; + private PolyglotEngine.Language defaultLanguage; private final Visualizer visualizer; private int nextBreakpointUID = 0; @@ -89,58 +102,68 @@ */ private Map breakpoints = new WeakHashMap<>(); - /** - * Create a single-language server. - */ - public REPLServer(String mimeType, Visualizer visualizer) { - this.mimeType = mimeType; + public REPLServer(String defaultMIMEType, Visualizer visualizer) { this.visualizer = visualizer == null ? new DefaultVisualizer() : visualizer; - EventConsumer onHalted = new EventConsumer(SuspendedEvent.class) { - @Override - protected void on(SuspendedEvent ev) { - REPLServer.this.haltedAt(ev); + this.engine = PolyglotEngine.newBuilder().onEvent(onHalted).onEvent(onExec).build(); + engineLanguages.addAll(engine.getLanguages().values()); + if (engineLanguages.size() == 0) { + throw new RuntimeException("No language implementations installed"); + } + for (Language language : engineLanguages) { + nameToLanguage.put(language.getName(), language); + } + + if (defaultMIMEType == null) { + defaultLanguage = engineLanguages.iterator().next(); + } else { + this.defaultLanguage = engine.getLanguages().get(defaultMIMEType); + if (defaultLanguage == null) { + throw new RuntimeException("Implementation not found for \"" + defaultMIMEType + "\""); } - }; - EventConsumer onExec = new EventConsumer(ExecutionEvent.class) { - @Override - protected void on(ExecutionEvent event) { - if (db == null) { - db = event.getDebugger(); - if (!breakpoints.isEmpty()) { - ArrayList pendingBreakpoints = new ArrayList<>(breakpoints.keySet()); - try { - for (Breakpoint pending : pendingBreakpoints) { + } + statusPrefix = languageName(defaultLanguage); + } + + private final EventConsumer onHalted = new EventConsumer(SuspendedEvent.class) { + @Override + protected void on(SuspendedEvent ev) { + REPLServer.this.haltedAt(ev); + } + }; - Integer uid = breakpoints.get(pending); - pending.dispose(); - Breakpoint replacement = null; - if (pending instanceof PendingLineBreakpoint) { - final PendingLineBreakpoint lineBreak = (PendingLineBreakpoint) pending; - replacement = db.setLineBreakpoint(lineBreak.getIgnoreCount(), lineBreak.getLineLocation(), lineBreak.isOneShot()); + private final EventConsumer onExec = new EventConsumer(ExecutionEvent.class) { + @Override + protected void on(ExecutionEvent event) { + if (db == null) { + db = event.getDebugger(); + if (!breakpoints.isEmpty()) { + ArrayList pendingBreakpoints = new ArrayList<>(breakpoints.keySet()); + try { + for (Breakpoint pending : pendingBreakpoints) { - } else if (pending instanceof PendingTagBreakpoint) { - final PendingTagBreakpoint tagBreak = (PendingTagBreakpoint) pending; - replacement = db.setTagBreakpoint(tagBreak.getIgnoreCount(), tagBreak.getTag(), tagBreak.isOneShot()); - } - breakpoints.put(replacement, uid); + Integer uid = breakpoints.get(pending); + pending.dispose(); + Breakpoint replacement = null; + if (pending instanceof PendingLineBreakpoint) { + final PendingLineBreakpoint lineBreak = (PendingLineBreakpoint) pending; + replacement = db.setLineBreakpoint(lineBreak.getIgnoreCount(), lineBreak.getLineLocation(), lineBreak.isOneShot()); + + } else if (pending instanceof PendingTagBreakpoint) { + final PendingTagBreakpoint tagBreak = (PendingTagBreakpoint) pending; + replacement = db.setTagBreakpoint(tagBreak.getIgnoreCount(), tagBreak.getTag(), tagBreak.isOneShot()); } - } catch (IOException e) { - throw new IllegalStateException("pending breakpoints should all be valid"); + breakpoints.put(replacement, uid); } + } catch (IOException e) { + throw new IllegalStateException("pending breakpoints should all be valid"); } } - if (!currentServerContext.isEval) { - event.prepareStepInto(); - } } - }; - engine = PolyglotEngine.newBuilder().onEvent(onHalted).onEvent(onExec).build(); - this.language = engine.getLanguages().get(mimeType); - if (language == null) { - throw new RuntimeException("Implementation not found for \"" + mimeType + "\""); + if (!currentServerContext.isEval) { + event.prepareStepInto(); + } } - statusPrefix = languageName(language); - } + }; public void add(REPLHandler handler) { handlerMap.put(handler.getOp(), handler); @@ -171,6 +194,7 @@ add(REPLHandler.LOAD_HANDLER); add(REPLHandler.QUIT_HANDLER); add(REPLHandler.SET_BREAK_CONDITION_HANDLER); + add(REPLHandler.SET_LANGUAGE_HANDLER); add(REPLHandler.STEP_INTO_HANDLER); add(REPLHandler.STEP_OUT_HANDLER); add(REPLHandler.STEP_OVER_HANDLER); @@ -178,17 +202,38 @@ add(REPLHandler.TRUFFLE_NODE_HANDLER); add(REPLHandler.UNSET_BREAK_CONDITION_HANDLER); this.replClient = new SimpleREPLClient(this); - this.currentServerContext = new Context(null, null); + this.currentServerContext = new Context(null, null, defaultLanguage); replClient.start(); } + @SuppressWarnings("static-method") + public String getWelcome() { + return "GraalVM MultiLanguage Debugger 0.9\n" + "Copyright (c) 2013-5, Oracle and/or its affiliates"; + } + void haltedAt(SuspendedEvent event) { - // Create and push a new debug context where execution is halted - currentServerContext = new Context(currentServerContext, event); - // Message the client that execution is halted and is in a new debugging context final REPLMessage message = new REPLMessage(); message.put(REPLMessage.OP, REPLMessage.STOPPED); + + // Identify language execution where halted; default to previous context + Language haltedLanguage = currentServerContext.currentLanguage; + final String mimeType = findMime(event.getNode()); + if (mimeType == null) { + message.put(REPLMessage.WARNINGS, "unable to detect language at halt"); + } else { + final Language language = engine.getLanguages().get(mimeType); + if (language == null) { + message.put(REPLMessage.WARNINGS, "no language installed for MIME type \"" + mimeType + "\""); + } else { + haltedLanguage = language; + } + } + + // Create and push a new debug context where execution is halted + currentServerContext = new Context(currentServerContext, event, haltedLanguage); + + message.put(REPLMessage.LANG_NAME, haltedLanguage.getName()); final SourceSection src = event.getNode().getSourceSection(); final Source source = src.getSource(); message.put(REPLMessage.SOURCE_NAME, source.getName()); @@ -199,6 +244,7 @@ message.put(REPLMessage.FILE_PATH, path); } message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine())); + message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel())); List warnings = event.getRecentWarnings(); @@ -221,6 +267,19 @@ } } + @SuppressWarnings("static-method") + private String findMime(Node node) { + String result = null; + final SourceSection section = node.getEncapsulatingSourceSection(); + if (section != null) { + final Source source = section.getSource(); + if (source != null) { + result = source.getMimeType(); + } + } + return result; + } + /** * Execution context of a halted program, possibly nested. */ @@ -229,12 +288,15 @@ private final Context predecessor; private final int level; private final SuspendedEvent event; + private Language currentLanguage; private boolean isEval = false; // When true, run without StepInto - Context(Context predecessor, SuspendedEvent event) { + Context(Context predecessor, SuspendedEvent event, Language language) { + assert language != null; this.level = predecessor == null ? 0 : predecessor.getLevel() + 1; this.predecessor = predecessor; this.event = event; + this.currentLanguage = language; } /** @@ -265,6 +327,7 @@ if (event == null) { try { isEval = true; + final String mimeType = defaultMIME(currentLanguage); final Value value = engine.eval(Source.fromText(code, "eval(\"" + code + "\")").withMimeType(mimeType)); return value.get(); } finally { @@ -318,6 +381,22 @@ return Collections.unmodifiableList(frames); } + public String getLanguageName() { + return currentLanguage.getName(); + } + + /** + * Case-insensitive; returns actual language name set. + */ + String setLanguage(String name) { + final Language language = nameToLanguage.get(name); + if (language == null) { + return null; + } + this.currentLanguage = language; + return language.getName(); + } + void prepareStepOut() { event.prepareStepOut(); } @@ -333,6 +412,7 @@ void prepareContinue() { event.prepareContinue(); } + } /** @@ -361,18 +441,27 @@ // TODO (mlvdv) language-specific Language getLanguage() { - return language; + return defaultLanguage; + } + + TreeSet getLanguages() { + return engineLanguages; } // TODO (mlvdv) language-specific public String getLanguageName() { - return languageName(this.language); + return languageName(this.defaultLanguage); } private static String languageName(Language lang) { return lang.getName() + "(" + lang.getVersion() + ")"; } + @SuppressWarnings("static-method") + private String defaultMIME(Language language) { + return language.getMimeTypes().iterator().next(); + } + void eval(Source source) throws IOException { engine.eval(source); }