Mercurial > hg > truffle
changeset 22478:92e99d8e236c
Merge with a6c162686d8129526bf7f21e1f83a13df11c4560
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Sun, 06 Dec 2015 20:36:50 -0800 |
parents | 50baaa7da8e8 (current diff) a6c162686d81 (diff) |
children | e1d15415bb2e |
files | mx.truffle/mx_truffle.py truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package.html truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/FrameDebugDescription.java truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java |
diffstat | 32 files changed, 1689 insertions(+), 1178 deletions(-) [+] |
line wrap: on
line diff
--- a/mx.truffle/mx_truffle.py Fri Dec 04 14:32:24 2015 +0100 +++ b/mx.truffle/mx_truffle.py Sun Dec 06 20:36:50 2015 -0800 @@ -53,7 +53,7 @@ def sldebug(args): """run a simple command line debugger for the Simple Language""" vmArgs, slArgs = mx.extract_VM_args(args, useDoubleDash=True) - mx.run_java(vmArgs + ['-cp', mx.classpath("com.oracle.truffle.sl.tools"), "com.oracle.truffle.sl.tools.debug.SLREPLServer"] + slArgs) + mx.run_java(vmArgs + ['-cp', mx.classpath("com.oracle.truffle.sl.tools"), "com.oracle.truffle.sl.tools.debug.SLREPL"] + slArgs) def _truffle_gate_runner(args, tasks): with Task('Truffle UnitTests', tasks) as t:
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Sun Dec 06 20:36:50 2015 -0800 @@ -37,7 +37,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.Executor; +import java.util.logging.Level; import java.util.logging.Logger; import com.oracle.truffle.api.CallTarget; @@ -61,8 +63,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; -import java.util.WeakHashMap; -import java.util.logging.Level; /** * Gate way into the world of {@link TruffleLanguage Truffle languages}. {@link #buildNew()
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Sun Dec 06 20:36:50 2015 -0800 @@ -32,22 +32,24 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.impl.FindContextNode; import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.KillException; +import com.oracle.truffle.api.instrument.QuitException; import com.oracle.truffle.api.instrument.SyntaxTag; import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; -import java.util.Objects; /** * An entry point for everyone who wants to implement a Truffle based language. By providing an @@ -391,7 +393,7 @@ * . The names of arguments are parameters for the resulting {#link CallTarget} that allow * the <code>source</code> to reference the actual parameters passed to * {@link CallTarget#call(java.lang.Object...)}. - * + * * @param source the source to evaluate * @param argumentNames the names of {@link CallTarget#call(java.lang.Object...)} arguments * that can be referenced from the source @@ -470,21 +472,21 @@ } try { return target.call(); + } catch (KillException | QuitException ex) { + throw ex; } catch (Throwable ex) { throw new IOException(ex); } } @Override - protected Object evalInContext(Object vm, SuspendedEvent ev, String code, FrameInstance frame) throws IOException { - Node n = frame == null ? ev.getNode() : frame.getCallNode(); - RootNode rootNode = n.getRootNode(); + protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException { + RootNode rootNode = node.getRootNode(); Class<? extends TruffleLanguage> languageType = findLanguage(rootNode); - Env env = findLanguage(vm, languageType); - TruffleLanguage<?> lang = findLanguage(env); - Source source = Source.fromText(code, "eval in context"); - CallTarget target = lang.parse(source, n); - return target.call(); + final Env env = findLanguage(vm, languageType); + final TruffleLanguage<?> lang = findLanguage(env); + final Source source = Source.fromText(code, "eval in context"); + return lang.evalInContext(source, node, frame); } @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Sun Dec 06 20:36:50 2015 -0800 @@ -33,13 +33,10 @@ import com.oracle.truffle.api.source.Source; /** - * Breakpoint in a {@link com.oracle.truffle.api.vm.PolyglotEngine} with - * {@link com.oracle.truffle.api.debug debugging turned on}. You can ask - * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean)} or - * {@link Debugger#setTagBreakpoint(int, com.oracle.truffle.api.instrument.SyntaxTag, boolean)} to - * create an instance of {@link Breakpoint}. + * Breakpoint in an executing {@link com.oracle.truffle.api.vm.PolyglotEngine}. + * + * @see Debugger */ -@SuppressWarnings("javadoc") public abstract class Breakpoint { /** @@ -111,7 +108,7 @@ private State state; - Breakpoint(State state, int ignoreCount, boolean isOneShot) { + protected Breakpoint(State state, int ignoreCount, boolean isOneShot) { this.state = state; this.isOneShot = isOneShot; this.ignoreCount = ignoreCount; @@ -200,7 +197,7 @@ assert state == s; } - final void setState(State state) { + protected final void setState(State state) { this.state = state; }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Sun Dec 06 20:36:50 2015 -0800 @@ -29,6 +29,7 @@ import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -50,6 +51,7 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; /** * Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}. @@ -152,7 +154,7 @@ * @throws IOException if the breakpoint can not be set. */ @TruffleBoundary - public Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { + public LineBreakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { return lineBreaks.create(ignoreCount, lineLocation, oneShot); } @@ -165,7 +167,7 @@ * @throws IOException if the breakpoint already set */ @TruffleBoundary - public Breakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException { + public TagBreakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException { return tagBreaks.create(ignoreCount, tag, oneShot); } @@ -678,6 +680,9 @@ */ private MaterializedFrame haltedFrame; + /** Subset of the Truffle stack corresponding to the current execution. */ + private List<FrameDebugDescription> contextStack; + private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) { this(executionSource, previousContext, -1); } @@ -748,7 +753,56 @@ // Clean up, just in cased the one-shot breakpoints got confused lineBreaks.disposeOneShots(); - final int contextStackDepth = currentStackDepth() - contextStackBase; + // Includes the "caller" frame (not iterated) + final int contextStackDepth = (currentStackDepth() - contextStackBase) + 1; + + final List<String> recentWarnings = new ArrayList<>(warnings); + warnings.clear(); + + final List<FrameDebugDescription> frames = new ArrayList<>(); + // Map the Truffle stack for this execution, ignore nested executions + // Ignore frames for which no CallNode is available. + // The top/current/0 frame is not produced by the iterator. + frames.add(new FrameDebugDescription(0, haltedNode, haltedFrame)); + Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<FrameInstance>() { + int stackIndex = 1; + int frameIndex = 1; + + @Override + public FrameInstance visitFrame(FrameInstance frameInstance) { + if (stackIndex < contextStackDepth) { + final Node callNode = frameInstance.getCallNode(); + if (callNode != null) { + final SourceSection sourceSection = callNode.getEncapsulatingSourceSection(); + if (sourceSection != null && sourceSection.getIdentifier() != SourceSection.UNKNOWN) { + frames.add(new FrameDebugDescription(frameIndex, frameInstance)); + frameIndex++; + } else if (TRACE) { + if (callNode != null) { + contextTrace("HIDDEN frame added: " + callNode); + } else { + contextTrace("HIDDEN frame added"); + } + frames.add(new FrameDebugDescription(frameIndex, frameInstance)); + frameIndex++; + } + } else if (TRACE) { + if (callNode != null) { + contextTrace("HIDDEN frame added: " + callNode); + } else { + contextTrace("HIDDEN frame added"); + } + frames.add(new FrameDebugDescription(frameIndex, frameInstance)); + frameIndex++; + } + stackIndex++; + return null; + } + return frameInstance; + } + }); + contextStack = Collections.unmodifiableList(frames); + if (TRACE) { final String reason = haltReason == null ? "" : haltReason + ""; final String where = before ? "BEFORE" : "AFTER"; @@ -757,12 +811,9 @@ // printStack(OUT); } - final List<String> recentWarnings = new ArrayList<>(warnings); - warnings.clear(); - try { // Pass control to the debug client with current execution suspended - ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, astNode, mFrame, recentWarnings, contextStackDepth)); + ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, contextStack, recentWarnings)); // Debug client finished normally, execution resumes // Presume that the client has set a new strategy (or default to Continue) running = true; @@ -840,8 +891,8 @@ debugContext = debugContext.predecessor; } - Object evalInContext(SuspendedEvent ev, String code, FrameInstance frame) throws IOException { - return ACCESSOR.evalInContext(vm, ev, code, frame); + Object evalInContext(SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException { + return ACCESSOR.evalInContext(vm, ev, code, node, frame); } @SuppressWarnings("rawtypes") @@ -874,8 +925,8 @@ } @Override - protected Object evalInContext(Object vm, SuspendedEvent ev, String code, FrameInstance frame) throws IOException { - return super.evalInContext(vm, ev, code, frame); + protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException { + return super.evalInContext(vm, ev, code, node, frame); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/FrameDebugDescription.java Sun Dec 06 20:36:50 2015 -0800 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.debug; + +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.nodes.Node; + +/** + * Summary of information about a frame for use by debugging clients. + */ +public final class FrameDebugDescription { + private final int index; + private final Node node; + private final MaterializedFrame frame; + + FrameDebugDescription(int index, Node node, MaterializedFrame frame) { + this.index = index; + this.node = node; + this.frame = frame; + } + + FrameDebugDescription(int index, FrameInstance frameInstance) { + this.index = index; + this.node = frameInstance.getCallNode(); + this.frame = frameInstance.getFrame(FrameAccess.MATERIALIZE, true).materialize(); + } + + /** + * Position in the current stack: {@code 0} at the top. + */ + public int index() { + return index; + } + + /** + * AST location of the call that created the frame. + */ + public Node node() { + return node; + } + + public MaterializedFrame frame() { + return frame; + } +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Sun Dec 06 20:36:50 2015 -0800 @@ -31,16 +31,25 @@ * * @see Debugger */ -abstract class LineBreakpoint extends Breakpoint { +public abstract class LineBreakpoint extends Breakpoint { + + private final LineLocation lineLocation; - LineBreakpoint(State state, int ignoreCount, boolean isOneShot) { + protected LineBreakpoint(State state, LineLocation lineLocation, int ignoreCount, boolean isOneShot) { super(state, ignoreCount, isOneShot); + this.lineLocation = lineLocation; } /** * Gets the {@linkplain LineLocation source line location} that specifies where this breakpoint * will trigger. */ - public abstract LineLocation getLineLocation(); + public final LineLocation getLineLocation() { + return lineLocation; + } + @Override + public String getLocationDescription() { + return "Line: " + lineLocation.getShortDescription(); + } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Sun Dec 06 20:36:50 2015 -0800 @@ -271,8 +271,6 @@ private static final String SHOULD_NOT_HAPPEN = "LineBreakpointImpl: should not happen"; - private final LineLocation lineLocation; - // Cached assumption that the global status of line breakpoint activity has not changed. private Assumption breakpointsActiveAssumption; @@ -289,9 +287,7 @@ private List<ProbeInstrument> instruments = new ArrayList<>(); public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) { - super(ENABLED_UNRESOLVED, ignoreCount, oneShot); - this.lineLocation = lineLocation; - + super(ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot); this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); this.isEnabled = true; this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); @@ -468,16 +464,6 @@ warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); } - @Override - public String getLocationDescription() { - return "Line: " + lineLocation.getShortDescription(); - } - - @Override - public LineLocation getLineLocation() { - return lineLocation; - } - private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java Sun Dec 06 20:36:50 2015 -0800 @@ -24,17 +24,15 @@ */ package com.oracle.truffle.api.debug; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Collections; +import java.util.List; + import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.frame.FrameInstanceVisitor; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.nodes.Node; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; /** * This event is delivered to all @@ -48,36 +46,28 @@ */ @SuppressWarnings("javadoc") public final class SuspendedEvent { - private final List<String> recentWarnings; - private final MaterializedFrame mFrame; - private final Node astNode; - private final List<FrameInstance> frames; - private final Debugger debugger; + + private static boolean TRACE = false; + private static final String TRACE_PREFIX = "EVENT: "; + private static final PrintStream OUT = System.out; - SuspendedEvent(Debugger prepares, Node astNode, MaterializedFrame mFrame, List<String> recentWarnings, final int stackDepth) { - this.debugger = prepares; - this.astNode = astNode; - this.mFrame = mFrame; - this.recentWarnings = recentWarnings; + private static void trace(String format, Object... args) { + if (TRACE) { + OUT.println(TRACE_PREFIX + String.format(format, args)); + } + } - this.frames = new ArrayList<>(); - // Map the Truffle stack for this execution, ignore nested executions - // The top (current) frame is not produced by the iterator. - frames.add(Truffle.getRuntime().getCurrentFrame()); - Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<FrameInstance>() { - int frameCount = 1; + private final Debugger debugger; + private final List<FrameDebugDescription> stack; + private final List<String> warnings; - @Override - public FrameInstance visitFrame(FrameInstance frameInstance) { - if (frameCount < stackDepth) { - frames.add(frameInstance); - frameCount++; - return null; - } - return frameInstance; - } - }); - + SuspendedEvent(Debugger debugger, List<FrameDebugDescription> stack, List<String> warnings) { + this.debugger = debugger; + this.stack = stack; + this.warnings = warnings; + if (TRACE) { + trace("Execution suspended at Node=" + stack.get(0).node()); + } } /** @@ -93,15 +83,15 @@ } public Node getNode() { - return astNode; + return stack.get(0).node(); } public MaterializedFrame getFrame() { - return mFrame; + return stack.get(0).frame(); } public List<String> getRecentWarnings() { - return Collections.unmodifiableList(recentWarnings); + return Collections.unmodifiableList(warnings); } /** @@ -111,8 +101,8 @@ * @return list of stack frames */ @CompilerDirectives.TruffleBoundary - public List<FrameInstance> getStack() { - return Collections.unmodifiableList(frames); + public List<FrameDebugDescription> getStack() { + return stack; } /** @@ -198,13 +188,20 @@ * Evaluates given code snippet in the context of currently suspended execution. * * @param code the snippet to evaluate - * @param frame <code>null</code> in case the evaluation should happen in top most frame, + * @param frameNumber <code>null</code> in case the evaluation should happen in top most frame, * non-null value to specify a frame from those {@link #getStack() currently on * stack} to perform the evaluation in context of * @return the computed value * @throws IOException in case an evaluation goes wrong */ - public Object eval(String code, FrameInstance frame) throws IOException { - return debugger.evalInContext(this, code, frame); + public Object eval(String code, Integer frameNumber) throws IOException { + int n = 0; + if (frameNumber != null) { + if (frameNumber < 0 || frameNumber >= stack.size()) { + throw new IOException("invalid frame number"); + } + n = frameNumber; + } + return debugger.evalInContext(this, code, stack.get(n).node(), stack.get(n).frame()); } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Sun Dec 06 20:36:50 2015 -0800 @@ -31,15 +31,24 @@ * * @see Debugger */ -abstract class TagBreakpoint extends Breakpoint { +public abstract class TagBreakpoint extends Breakpoint { + + private final SyntaxTag tag; - TagBreakpoint(State state, int ignoreCount, boolean isOneShot) { + protected TagBreakpoint(State state, SyntaxTag tag, int ignoreCount, boolean isOneShot) { super(state, ignoreCount, isOneShot); + this.tag = tag; } /** * Gets the tag that specifies where this breakpoint will trigger. */ - public abstract SyntaxTag getTag(); + public final SyntaxTag getTag() { + return tag; + } + @Override + public String getLocationDescription() { + return "Tag " + tag.name(); + } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Sun Dec 06 20:36:50 2015 -0800 @@ -240,8 +240,6 @@ private static final String SHOULD_NOT_HAPPEN = "TagBreakpointImpl: should not happen"; - private final SyntaxTag tag; - // Cached assumption that the global status of tag breakpoint activity has not changed. private Assumption breakpointsActiveAssumption; @@ -258,8 +256,7 @@ private List<ProbeInstrument> instruments = new ArrayList<>(); private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) { - super(ENABLED, ignoreCount, oneShot); - this.tag = tag; + super(ENABLED, tag, ignoreCount, oneShot); this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); this.isEnabled = true; this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); @@ -356,7 +353,7 @@ @TruffleBoundary private String getShortDescription() { - return BREAKPOINT_NAME + "@" + tag.name(); + return BREAKPOINT_NAME + "@" + getTag().name(); } private void changeState(State after) { @@ -430,16 +427,6 @@ warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); } - @Override - public String getLocationDescription() { - return "Tag " + tag.name(); - } - - @Override - public SyntaxTag getTag() { - return tag; - } - private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override @@ -447,7 +434,5 @@ TagBreakpointImpl.this.nodeEnter(node, vFrame); } } - } - }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Sun Dec 06 20:36:50 2015 -0800 @@ -39,7 +39,6 @@ import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.Instrumenter; @@ -158,8 +157,8 @@ return API.eval(l, s, cache); } - protected Object evalInContext(Object vm, SuspendedEvent ev, String code, FrameInstance frame) throws IOException { - return API.evalInContext(vm, ev, code, frame); + protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException { + return API.evalInContext(vm, ev, code, node, frame); } protected Object importSymbol(Object vm, TruffleLanguage<?> queryingLang, String globalName) { @@ -320,10 +319,6 @@ return API.findContext(env); } - protected TruffleLanguage<?> findLanguage(Env env) { - return API.findLanguage(env); - } - /** Applies all registered {@linkplain ASTProber probers} to the AST. */ protected void probeAST(RootNode rootNode) { INSTRUMENT.probeAST(rootNode); @@ -339,6 +334,10 @@ return parse(truffleLanguage, code, context, argumentNames); } + protected TruffleLanguage<?> findLanguage(Env env) { + return API.findLanguage(env); + } + protected CallTarget parse(TruffleLanguage<?> truffleLanguage, Source code, Node context, String... argumentNames) throws IOException { return API.parse(truffleLanguage, code, context, argumentNames); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java Sun Dec 06 20:36:50 2015 -0800 @@ -76,6 +76,9 @@ } public String displayValue(Object value, int trim) { + if (value == null) { + return "<empty>"; + } return trim(value.toString(), trim); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java Sun Dec 06 20:36:50 2015 -0800 @@ -45,7 +45,7 @@ /** * Gets the 1-based number of a line in the source. - * + * * @return value from 1 to infinity */ public int getLineNumber() { @@ -90,7 +90,15 @@ @Override public int compareTo(LineLocation o) { - final int sourceResult = this.getSource().getPath().compareTo(o.getSource().getPath()); + int sourceResult = 0; + final Source thisSource = this.getSource(); + final String thisPath = thisSource.getPath(); + if (thisPath == null) { + sourceResult = thisSource.getCode().compareTo(o.getSource().getCode()); + } else { + final String thatPath = o.getSource().getPath(); + sourceResult = thisPath.compareTo(thatPath); + } if (sourceResult != 0) { return sourceResult; }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Sun Dec 06 20:36:50 2015 -0800 @@ -728,7 +728,7 @@ @Override public String getPath() { - return description; + return null; } @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java Sun Dec 06 20:36:50 2015 -0800 @@ -37,6 +37,12 @@ * @see #createUnavailable */ public final class SourceSection { + + /** + * The identifier stored when source information is unavailable. + */ + public static String UNKNOWN = "<unknown>"; + private final Source source; private final String identifier; private final int startLine; @@ -275,6 +281,6 @@ * @return source section which is mostly <em>empty</em> */ public static SourceSection createUnavailable(String kind, String name) { - return new SourceSection(kind, null, name == null ? "<unknown>" : name, -1, -1, -1, -1); + return new SourceSection(kind, null, name == null ? UNKNOWN : name, -1, -1, -1, -1); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPL.java Sun Dec 06 20:36:50 2015 -0800 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.sl.tools.debug; + +import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + +// TODO (mlvdv) delete when the REPL is fully multi-language +/** + * Start a single-language REPL for the Simple Language implementation. + */ +public final class SLREPL { + + public static void main(String[] args) { + + // Cheating for the prototype: start from SL, rather than from the client. + final REPLServer server = new REPLServer("application/x-sl", new SLDefaultVisualizer()); + server.start(); + } + + private SLREPL() { + } +}
--- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java Fri Dec 04 14:32:24 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.oracle.truffle.sl.tools.debug; - -import com.oracle.truffle.api.instrument.KillException; -import com.oracle.truffle.api.instrument.QuitException; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; -import com.oracle.truffle.tools.debug.shell.server.REPLHandler; -import com.oracle.truffle.tools.debug.shell.server.REPLServerContext; -import java.io.File; -import java.util.ArrayList; - -/** - * Instantiation of the "server handler" part of the "REPL*" debugger for the simple language. - * <p> - * These handlers implement debugging commands that require language-specific support. - * - * @see SimpleREPLClient - */ -public abstract class SLREPLHandler extends REPLHandler { - - protected SLREPLHandler(String op) { - super(op); - } - - public static final SLREPLHandler INFO_HANDLER = new SLREPLHandler(REPLMessage.INFO) { - - @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - final String topic = request.get(REPLMessage.TOPIC); - - if (topic == null || topic.isEmpty()) { - final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - return finishReplyFailed(message, "No info topic specified"); - } - - switch (topic) { - - case REPLMessage.LANGUAGE: - return createLanguageInfoReply(); - - default: - final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - return finishReplyFailed(message, "No info about topic \"" + topic + "\""); - } - } - }; - - private static REPLMessage[] createLanguageInfoReply() { - final ArrayList<REPLMessage> langMessages = new ArrayList<>(); - - final REPLMessage msg1 = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); - msg1.put(REPLMessage.TOPIC, REPLMessage.LANGUAGE); - msg1.put(REPLMessage.INFO_KEY, "Language"); - msg1.put(REPLMessage.INFO_VALUE, "Simple"); - msg1.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - langMessages.add(msg1); - - return langMessages.toArray(new REPLMessage[0]); - } - - public static final SLREPLHandler LOAD_RUN_SOURCE_HANDLER = new SLREPLHandler(REPLMessage.LOAD_RUN) { - - @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - return loadHandler(request, serverContext, false); - } - }; - - public static final SLREPLHandler LOAD_STEP_SOURCE_HANDLER = new SLREPLHandler(REPLMessage.LOAD_STEP) { - - @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - return loadHandler(request, serverContext, true); - } - }; - - // TODO (mlvdv) re-implement stepInto when vm support replaced - /** - * Runs a source, optionally stepping into a specified tag. - */ - private static REPLMessage[] loadHandler(REPLMessage request, REPLServerContext serverContext, @SuppressWarnings("unused") boolean stepInto) { - final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.LOAD_RUN); - final String fileName = request.get(REPLMessage.SOURCE_NAME); - try { - final File file = new File(fileName); - if (!file.canRead()) { - return finishReplyFailed(reply, "can't find file \"" + fileName + "\""); - } - final PolyglotEngine vm = serverContext.engine(); - vm.eval(Source.fromFileName(file.getPath())); - PolyglotEngine.Value main = vm.findGlobalSymbol("main"); - if (main != null) { - main.execute(); - } - final String path = file.getCanonicalPath(); - reply.put(REPLMessage.FILE_PATH, path); - return finishReplySucceeded(reply, fileName + " exited"); - } catch (QuitException ex) { - throw ex; - } catch (KillException ex) { - return finishReplySucceeded(reply, fileName + " killed"); - } catch (Exception ex) { - return finishReplyFailed(reply, "error loading file \"" + fileName + "\": " + ex.getMessage()); - } - } -}
--- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java Fri Dec 04 14:32:24 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,281 +0,0 @@ -/* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.oracle.truffle.sl.tools.debug; - -import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.debug.ExecutionEvent; -import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.instrument.QuitException; -import com.oracle.truffle.api.instrument.Visualizer; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.vm.EventConsumer; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.api.vm.PolyglotEngine.Language; -import com.oracle.truffle.sl.SLLanguage; -import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.REPLServer; -import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; -import com.oracle.truffle.tools.debug.shell.server.REPLHandler; -import com.oracle.truffle.tools.debug.shell.server.REPLServerContext; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Instantiation of the "server" side of the "REPL*" debugger for the Simple language. - * <p> - * The SL parser is not equipped to parse program fragments, so any debugging functions that depend - * on this are not supported, for example "eval" and breakpoint conditions. - * - * @see SimpleREPLClient - */ -public final class SLREPLServer extends REPLServer { - - // TODO (mlvdv) remove when there's a better way to express this dependency - @SuppressWarnings("unused") private static final Class<SLLanguage> DYNAMIC_DEPENDENCY = com.oracle.truffle.sl.SLLanguage.class; - - public static void main(String[] args) { - // Cheating for the prototype: start from SL, rather than from the client. - final SLREPLServer server = new SLREPLServer(); - final SimpleREPLClient client = new SimpleREPLClient(getShortName(server.language), server); - - // Cheating for the prototype: allow server access to client for recursive debugging - server.setClient(client); - - try { - client.start(); - } catch (QuitException ex) { - } - } - - private static String getShortName(Language language) { - return language.getName() + "(" + language.getVersion() + ")"; - } - - private final Language language; - private final PolyglotEngine vm; - private Debugger db; - private final String statusPrefix; - private final Map<String, REPLHandler> handlerMap = new HashMap<>(); - private SLServerContext currentServerContext; - private SimpleREPLClient replClient = null; - - private void add(REPLHandler fileHandler) { - handlerMap.put(fileHandler.getOp(), fileHandler); - } - - public SLREPLServer() { - add(REPLHandler.BACKTRACE_HANDLER); - add(REPLHandler.BREAK_AT_LINE_HANDLER); - add(REPLHandler.BREAK_AT_LINE_ONCE_HANDLER); - add(REPLHandler.BREAK_AT_THROW_HANDLER); - add(REPLHandler.BREAK_AT_THROW_ONCE_HANDLER); - add(REPLHandler.BREAKPOINT_INFO_HANDLER); - add(REPLHandler.CLEAR_BREAK_HANDLER); - add(REPLHandler.CONTINUE_HANDLER); - add(REPLHandler.DELETE_HANDLER); - add(REPLHandler.DISABLE_BREAK_HANDLER); - add(REPLHandler.ENABLE_BREAK_HANDLER); - add(REPLHandler.FILE_HANDLER); - add(REPLHandler.FRAME_HANDLER); - add(SLREPLHandler.INFO_HANDLER); - add(REPLHandler.KILL_HANDLER); - add(SLREPLHandler.LOAD_RUN_SOURCE_HANDLER); - add(SLREPLHandler.LOAD_STEP_SOURCE_HANDLER); - add(REPLHandler.QUIT_HANDLER); - add(REPLHandler.STEP_INTO_HANDLER); - add(REPLHandler.STEP_OUT_HANDLER); - add(REPLHandler.STEP_OVER_HANDLER); - add(REPLHandler.TRUFFLE_HANDLER); - add(REPLHandler.TRUFFLE_NODE_HANDLER); - - EventConsumer<SuspendedEvent> onHalted = new EventConsumer<SuspendedEvent>(SuspendedEvent.class) { - @Override - protected void on(SuspendedEvent ev) { - SLREPLServer.this.haltedAt(ev); - } - }; - EventConsumer<ExecutionEvent> onExec = new EventConsumer<ExecutionEvent>(ExecutionEvent.class) { - @Override - protected void on(ExecutionEvent event) { - event.prepareStepInto(); - db = event.getDebugger(); - } - }; - - PolyglotEngine newVM = PolyglotEngine.newBuilder().onEvent(onHalted).onEvent(onExec).build(); - this.language = newVM.getLanguages().get("application/x-sl"); - assert language != null; - - this.vm = newVM; - this.statusPrefix = getShortName(language) + " REPL:"; - } - - private void setClient(SimpleREPLClient replClient) { - this.replClient = replClient; - } - - @Override - public REPLMessage start() { - - this.currentServerContext = new SLServerContext(null, null); - - // SL doesn't load modules (like other languages), so we just return a success - final REPLMessage reply = new REPLMessage(); - reply.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - reply.put(REPLMessage.DISPLAY_MSG, getShortName(language) + " started"); - return reply; - } - - @Override - public REPLMessage[] receive(REPLMessage request) { - if (currentServerContext == null) { - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.STATUS, REPLMessage.FAILED); - message.put(REPLMessage.DISPLAY_MSG, "server not started"); - final REPLMessage[] reply = new REPLMessage[]{message}; - return reply; - } - return currentServerContext.receive(request); - } - - /** - * Execution context of a halted SL program. - */ - public final class SLServerContext extends REPLServerContext { - - private final SLServerContext predecessor; - - public SLServerContext(SLServerContext predecessor, SuspendedEvent event) { - super(predecessor == null ? 0 : predecessor.getLevel() + 1, event); - this.predecessor = predecessor; - } - - @Override - public REPLMessage[] receive(REPLMessage request) { - final String command = request.get(REPLMessage.OP); - final REPLHandler handler = handlerMap.get(command); - - if (handler == null) { - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.OP, command); - message.put(REPLMessage.STATUS, REPLMessage.FAILED); - message.put(REPLMessage.DISPLAY_MSG, statusPrefix + " op \"" + command + "\" not supported"); - final REPLMessage[] reply = new REPLMessage[]{message}; - return reply; - } - return handler.receive(request, currentServerContext); - } - - @Override - public Language getLanguage() { - return language; - } - - @Override - public Visualizer getVisualizer() { - return new SLDefaultVisualizer(); - } - - @Override - public PolyglotEngine engine() { - return vm; - } - - @Override - protected Debugger db() { - return db; - } - - @Override - public void registerBreakpoint(Breakpoint breakpoint) { - SLREPLServer.this.registerBreakpoint(breakpoint); - } - - @Override - public Breakpoint findBreakpoint(int id) { - return SLREPLServer.this.findBreakpoint(id); - } - - @Override - public int getBreakpointID(Breakpoint breakpoint) { - return SLREPLServer.this.getBreakpointID(breakpoint); - } - - } - - void haltedAt(SuspendedEvent event) { - // Create and push a new debug context where execution is halted - currentServerContext = new SLServerContext(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); - final SourceSection src = event.getNode().getSourceSection(); - final Source source = src.getSource(); - message.put(REPLMessage.SOURCE_NAME, source.getName()); - message.put(REPLMessage.FILE_PATH, source.getPath()); - 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<String> warnings = event.getRecentWarnings(); - if (!warnings.isEmpty()) { - final StringBuilder sb = new StringBuilder(); - for (String warning : warnings) { - sb.append(warning + "\n"); - } - message.put(REPLMessage.WARNINGS, sb.toString()); - } - try { - // Cheat with synchrony: call client directly about entering a nested debugging - // context. - replClient.halted(message); - } finally { - // Returns when "continue" is called in the new debugging context - - // Pop the debug context, and return so that the old context will continue - currentServerContext = currentServerContext.predecessor; - } - } -}
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Sun Dec 06 20:36:50 2015 -0800 @@ -190,17 +190,6 @@ * with the new version. * </ul> */ - -/* - * - * <p> <b>Tools:</b><br> The use of some of Truffle's support for developer tools (based on the - * Truffle {@linkplain Instrumenter Instrumentation Framework}) are demonstrated in this file, for - * example: <ul> <li>a {@linkplain NodeExecCounter counter for node executions}, tabulated by node - * type; and</li> <li>a simple {@linkplain CoverageTracker code coverage engine}.</li> </ul> In each - * case, the tool is enabled if a corresponding local boolean variable in this file is set to {@code - * true}. Results are printed at the end of the execution using each tool's <em>default - * printer</em>. - */ @TruffleLanguage.Registration(name = "SL", version = "0.5", mimeType = "application/x-sl") public final class SLLanguage extends TruffleLanguage<SLContext> { public static final String builtinKind = "SL builtin"; @@ -228,11 +217,6 @@ return context; } - /* Small tools that can be installed for demonstration */ - // private static NodeExecCounter nodeExecCounter = null; - // private static NodeExecCounter statementExecCounter = null; - // private static CoverageTracker coverageTracker = null; - /** * The main entry point. Use the mx command "mx sl" to run it with the correct class path setup. */ @@ -240,8 +224,6 @@ PolyglotEngine vm = PolyglotEngine.newBuilder().build(); assert vm.getLanguages().containsKey("application/x-sl"); - setupToolDemos(); - int repeats = 1; if (args.length >= 2) { repeats = Integer.parseInt(args[1]); @@ -261,7 +243,6 @@ while (repeats-- > 0) { main.execute(); } - reportToolDemos(); } public static int parsingCount() { @@ -525,7 +506,7 @@ @Override protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { - throw new IllegalStateException("evalInContext not supported in this language"); + throw new IllegalStateException("evalInContext not supported in SL"); } public Node createFindContextNode0() { @@ -535,38 +516,4 @@ public SLContext findContext0(Node contextNode) { return findContext(contextNode); } - - // TODO (mlvdv) remove the static hack when we no longer have the static demo variables - private static void setupToolDemos() { - // if (nodeExecCounts) { - // nodeExecCounter = new NodeExecCounter(); - // nodeExecCounter.install(); - // } - // - // if (statementCounts) { - // statementExecCounter = new NodeExecCounter(StandardSyntaxTag.STATEMENT); - // statementExecCounter.install(); - // } - // - // if (coverage) { - // coverageTracker = new CoverageTracker(); - // coverageTracker.install(); - // } - } - - private static void reportToolDemos() { - // if (nodeExecCounter != null) { - // nodeExecCounter.print(System.out); - // nodeExecCounter.dispose(); - // } - // if (statementExecCounter != null) { - // statementExecCounter.print(System.out); - // statementExecCounter.dispose(); - // } - // if (coverageTracker != null) { - // coverageTracker.print(System.out); - // coverageTracker.dispose(); - // } - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLDefaultVisualizer.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLDefaultVisualizer.java Sun Dec 06 20:36:50 2015 -0800 @@ -94,7 +94,10 @@ @Override public String displayValue(Object value, int trim) { - if (value == null || value == SLNull.SINGLETON) { + if (value == null) { + return "<empty>"; + } + if (value == SLNull.SINGLETON) { return "null"; } return trim(value.toString(), trim);
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Sun Dec 06 20:36:50 2015 -0800 @@ -30,6 +30,8 @@ import java.util.Set; import java.util.TreeMap; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + /** * A message for communication between a Read-Eval-Print-Loop server associated with a language * implementation and a possibly remote client. @@ -53,6 +55,18 @@ public static final String BREAKPOINT_IGNORE_COUNT = "breakpoint-ignore-count"; public static final String BREAKPOINT_INFO = "breakpoint-info"; public static final String BREAKPOINT_STATE = "breakpoint-state"; + public static final String CALL = "call"; + public static final String ARG0 = "call-argument-0"; + public static final String ARG1 = "call-argument-1"; + public static final String ARG2 = "call-argument-2"; + public static final String ARG3 = "call-argument-3"; + public static final String ARG4 = "call-argument-4"; + public static final String ARG5 = "call-argument-5"; + public static final String ARG6 = "call-argument-6"; + public static final String ARG7 = "call-argument-7"; + public static final String ARG8 = "call-argument-8"; + public static final String ARG9 = "call-argument-9"; + public static final String CALL_NAME = "call-name"; public static final String CLEAR_BREAK = "clear-breakpoint"; public static final String CODE = "code"; public static final String CONTINUE = "continue"; @@ -70,14 +84,17 @@ 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_RUN = "load-run-source"; - public static final String LOAD_STEP = "load-step-into-source"; + public static final String LOAD_SOURCE = "load-source"; public static final String METHOD_NAME = "method-name"; public static final String OP = "op"; public static final String OPTION = "option"; @@ -85,6 +102,10 @@ 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 SLOT_ID = "slot-identifier"; + public static final String SLOT_INDEX = "slot-index"; + public static final String SLOT_VALUE = "slot-value"; 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"; @@ -99,6 +120,7 @@ public static final String SUBTREE = "subtree"; public static final String SUCCEEDED = "succeeded"; public static final String TOPIC = "topic"; + public static final String TRUE = "true"; public static final String TRUFFLE = "truffle"; public static final String TRUFFLE_AST = "truffle-ast"; public static final String TRUFFLE_NODE = "truffle-node"; @@ -107,6 +129,8 @@ 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"; + public static final String[] ARG_NAMES = new String[]{ARG0, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9}; private final Map<String, String> map; /**
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java Fri Dec 04 14:32:24 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.tools.debug.shell; - -import com.oracle.truffle.api.debug.Breakpoint; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * The server side of a simple message-based protocol for a possibly remote language - * Read-Eval-Print-Loop. - */ -public abstract class REPLServer { - - /** - * Starts up a server; status returned in a message. - */ - public abstract REPLMessage start(); - - /** - * Ask the server to handle a request. Return a non-empty array of messages to simulate remote - * operation where the protocol has possibly multiple messages being returned asynchronously in - * response to each request. - */ - public abstract REPLMessage[] receive(REPLMessage request); - - private int breakpointCounter; - private Map<Breakpoint, Integer> breakpoints = new WeakHashMap<>(); - - protected final synchronized void registerBreakpoint(Breakpoint breakpoint) { - breakpoints.put(breakpoint, breakpointCounter++); - } - - protected final synchronized Breakpoint findBreakpoint(int id) { - for (Map.Entry<Breakpoint, Integer> entrySet : breakpoints.entrySet()) { - if (id == entrySet.getValue()) { - return entrySet.getKey(); - } - } - return null; - } - - protected final synchronized int getBreakpointID(Breakpoint breakpoint) { - final Integer id = breakpoints.get(breakpoint); - return id == null ? -1 : id; - } - -}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLClientContext.java Sun Dec 06 20:36:50 2015 -0800 @@ -24,14 +24,15 @@ */ 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.REPLServer; -import java.util.List; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; /** * Client context for interaction with a program halted by the {@link REPLServer}. */ -public interface REPLClientContext { +interface REPLClientContext { /** * The source code halted in this context. @@ -96,4 +97,9 @@ */ void displayFailReply(String message); + /** + * Recompute the client's command line prompt. + */ + void updatePrompt(); + }
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/REPLRemoteCommand.java Sun Dec 06 20:36:50 2015 -0800 @@ -26,6 +26,7 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.tools.debug.shell.REPLMessage; + import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -200,6 +201,71 @@ } }; + public static final REPLRemoteCommand CALL_CMD = new REPLRemoteCommand("call", null, "call a method/function") { + + private final String[] help = {"call <name> <args>: calls a function by name"}; + + @Override + public String[] getHelp() { + return help; + } + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + if (args.length == 1) { + context.displayFailReply("name to call not speciified"); + return null; + } + final int maxArgs = REPLMessage.ARG_NAMES.length; + if (args.length > maxArgs + 2) { + context.displayFailReply("too many call arguments; no more than " + maxArgs + " supported"); + return null; + } + final REPLMessage request = new REPLMessage(); + request.put(REPLMessage.OP, REPLMessage.CALL); + request.put(REPLMessage.CALL_NAME, args[1]); + for (int argIn = 2, argOut = 0; argIn < args.length; argIn++, argOut++) { + request.put(REPLMessage.ARG_NAMES[argOut], args[argIn]); + } + return request; + } + + @Override + void processReply(REPLClientContext context, REPLMessage[] replies) { + REPLMessage firstReply = replies[0]; + if (firstReply.get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) { + final String result = firstReply.get(REPLMessage.DISPLAY_MSG); + context.displayFailReply(result != null ? result : firstReply.toString()); + } else { + context.displayReply(firstReply.get(REPLMessage.VALUE)); + } + } + }; + + public static final REPLRemoteCommand CALL_STEP_INTO_CMD = new REPLRemoteCommand("call-step-into", "calls", "Call a method/function and step into") { + + private final String[] help = {"call <name> <args>: calls function by name and step into"}; + + @Override + public String[] getHelp() { + return help; + } + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + final REPLMessage request = CALL_CMD.createRequest(context, args); + if (request != null) { + request.put(REPLMessage.STEP_INTO, REPLMessage.TRUE); + } + return request; + } + + @Override + void processReply(REPLClientContext context, REPLMessage[] replies) { + CALL_CMD.processReply(context, replies); + } + }; + public static final REPLRemoteCommand CLEAR_BREAK_CMD = new REPLRemoteCommand("clear", null, "Clear a breakpoint") { private final String[] help = {"clear <n>: clear breakpoint number <n>"}; @@ -436,6 +502,59 @@ } }; + public static final REPLRemoteCommand EVAL_CMD = new REPLRemoteCommand("eval", null, "Evaluate a string, in context of the current frame if any") { + + private int evalCounter = 0; + + private final String[] help = {"eval <string>: evaluate <string> in context of the current frame if any"}; + + @Override + public String[] getHelp() { + return help; + } + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + if (args.length > 1) { + final String code = args[1]; + if (!code.isEmpty()) { + // Create a fake entry in the file maps and cache, based on this unique name + final String fakeFileName = "<eval" + ++evalCounter + ">"; + Source.fromNamedText(fakeFileName, code); + final REPLMessage request = new REPLMessage(); + request.put(REPLMessage.OP, REPLMessage.EVAL); + request.put(REPLMessage.CODE, code); + request.put(REPLMessage.SOURCE_NAME, fakeFileName); + if (context.level() > 0) { + // Specify a requested execution context, if one exists; otherwise top level + request.put(REPLMessage.FRAME_NUMBER, Integer.toString(context.getSelectedFrameNumber())); + } + return request; + } + } + return null; + } + }; + + public static final REPLRemoteCommand EVAL_STEP_INTO_CMD = new REPLRemoteCommand("eval-step-into", "evals", "Evaluate and step into a string, in context of the current frame if any") { + + private final String[] help = {"eval-step-into <string>: evaluate <string> in context of the current frame if any, and step into"}; + + @Override + public String[] getHelp() { + return help; + } + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + final REPLMessage request = EVAL_CMD.createRequest(context, args); + if (request != null) { + request.put(REPLMessage.STEP_INTO, REPLMessage.TRUE); + } + return request; + } + }; + public static final REPLRemoteCommand FRAME_CMD = new REPLRemoteCommand("frame", null, "Display a stack frame") { private final String[] help = {"frame : display currently selected frame", "frame <n> : display frame <n>"}; @@ -479,9 +598,11 @@ context.selectFrameNumber(frameNumber); context.displayReply("Frame " + frameNumber + ":"); for (REPLMessage message : replies) { - for (String line : message.get(REPLMessage.DISPLAY_MSG).split("\n")) { - context.displayInfo(line); - } + final StringBuilder sb = new StringBuilder(); + sb.append("#" + message.get(REPLMessage.SLOT_INDEX) + ": "); + sb.append(message.get(REPLMessage.SLOT_ID) + " = "); + sb.append(message.get(REPLMessage.SLOT_VALUE)); + context.displayInfo(sb.toString()); } } } @@ -510,7 +631,14 @@ } }; - public static final REPLRemoteCommand LOAD_RUN_CMD = new REPLRemoteCommand("load-run", "loadr", "Load and run a source") { + public static final REPLRemoteCommand LOAD_CMD = new REPLRemoteCommand("load", null, "Load source") { + + private final String[] help = new String[]{"load : load currently selected file source", "load <file name> : load file <file name>"}; + + @Override + public String[] getHelp() { + return help; + } @Override public REPLMessage createRequest(REPLClientContext context, String[] args) { @@ -530,43 +658,66 @@ } } final REPLMessage request = new REPLMessage(); - request.put(REPLMessage.OP, REPLMessage.LOAD_RUN); + request.put(REPLMessage.OP, REPLMessage.LOAD_SOURCE); request.put(REPLMessage.SOURCE_NAME, runSource.getPath()); return request; } }; - public static final REPLRemoteCommand LOAD_STEP_CMD = new REPLRemoteCommand("load-step", "loads", "Load and step into a source") { + public static final REPLRemoteCommand LOAD_STEP_INTO_CMD = new REPLRemoteCommand("load-step-into", "loads", "Load source and step in") { + + private final String[] help = new String[]{"load : load currently selected file source and step in", "load <file name> : load file <file name> and step in"}; + + @Override + public String[] getHelp() { + return help; + } @Override public REPLMessage createRequest(REPLClientContext context, String[] args) { - Source runSource = null; - if (args.length == 1) { - runSource = context.getSelectedSource(); - if (runSource == null) { - context.displayFailReply("No file selected"); - return null; - } - } else { - try { - runSource = Source.fromFileName(args[1]); - } catch (IOException e) { - context.displayFailReply("Can't find file: " + args[1]); - return null; - } + final REPLMessage request = LOAD_CMD.createRequest(context, args); + if (request != null) { + request.put(REPLMessage.STEP_INTO, REPLMessage.TRUE); } - final REPLMessage request = new REPLMessage(); - request.put(REPLMessage.OP, REPLMessage.LOAD_STEP); - request.put(REPLMessage.SOURCE_NAME, runSource.getPath()); return request; } }; - public static final REPLRemoteCommand STEP_INTO_CMD = new REPLRemoteCommand("step", "s", "(StepInto) next statement, going into functions.") { + public static final REPLRemoteCommand SET_LANG_CMD = new REPLRemoteCommand("language", "lang", "Set current language") { + + private final String[] help = new String[]{"lang <language short name>: set default language, \"info lang\" displays choices"}; @Override public String[] getHelp() { - return new String[]{"step into: step to next statement (into calls)", "step <n>: step to nth next statement (into calls)"}; + return help; + } + + @Override + public REPLMessage createRequest(REPLClientContext context, String[] args) { + + final REPLMessage request = new REPLMessage(); + request.put(REPLMessage.OP, REPLMessage.SET_LANGUAGE); + if (args.length > 1) { + 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.") { + + private final String[] help = new String[]{"step into: step to next statement (into calls)", "step <n>: step to nth next statement (into calls)"}; + + @Override + public String[] getHelp() { + return help; } @Override @@ -625,9 +776,12 @@ public static final REPLRemoteCommand STEP_OVER_CMD = new REPLRemoteCommand("next", "n", "(StepOver) execute next line of code, not into functions.") { + private final String[] help = new String[]{"next: (StepOver) execute next line of code, not into functions.", + "next <n>: (StepOver) execute to nth next statement (not counting into functions)"}; + @Override public String[] getHelp() { - return new String[]{"next: (StepOver) execute next line of code, not into functions.", "next <n>: (StepOver) execute to nth next statement (not counting into functions)"}; + return help; } @Override
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Sun Dec 06 20:36:50 2015 -0800 @@ -24,10 +24,12 @@ */ package com.oracle.truffle.tools.debug.shell.client; +import com.oracle.truffle.api.instrument.QuitException; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.tools.debug.shell.REPLClient; import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.REPLServer; +import com.oracle.truffle.tools.debug.shell.server.REPLServer; + import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; @@ -39,6 +41,7 @@ import java.util.List; import java.util.Map; import java.util.TreeSet; + import jline.console.ConsoleReader; /** @@ -79,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 = "!!> "; @@ -89,10 +94,8 @@ static final String CODE_LINE_FORMAT = " %3d %s\n"; static final String CODE_LINE_BREAK_FORMAT = "--> %3d %s\n"; - 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; + private static final String STACK_FRAME_FORMAT = " %3d: at %s in %s %s\n"; + private static final String STACK_FRAME_SELECTED_FORMAT = "==> %3d: at %s in %s %s\n"; // Top level commands private final Map<String, REPLCommand> commandMap = new HashMap<>(); @@ -105,13 +108,6 @@ // Current local context ClientContextImpl clientContext; - // Cheating for the prototype; prototype startup now happens from the language server. - // So this isn't used. - public static void main(String[] args) { - final SimpleREPLClient repl = new SimpleREPLClient(null, null); - repl.start(); - } - private final ConsoleReader reader; private final PrintStream writer; @@ -147,8 +143,7 @@ */ private Source selectedSource = null; - public SimpleREPLClient(String languageName, REPLServer replServer) { - this.languageName = languageName; + public SimpleREPLClient(REPLServer replServer) { this.replServer = replServer; this.writer = System.out; try { @@ -162,6 +157,8 @@ addCommand(REPLRemoteCommand.BREAK_AT_LINE_ONCE_CMD); addCommand(REPLRemoteCommand.BREAK_AT_THROW_CMD); addCommand(REPLRemoteCommand.BREAK_AT_THROW_ONCE_CMD); + addCommand(REPLRemoteCommand.CALL_CMD); + addCommand(REPLRemoteCommand.CALL_STEP_INTO_CMD); addCommand(REPLRemoteCommand.CLEAR_BREAK_CMD); addCommand(REPLRemoteCommand.CONDITION_BREAK_CMD); addCommand(REPLRemoteCommand.CONTINUE_CMD); @@ -169,17 +166,19 @@ addCommand(REPLRemoteCommand.DISABLE_CMD); addCommand(REPLRemoteCommand.DOWN_CMD); addCommand(REPLRemoteCommand.ENABLE_CMD); - addCommand(evalCommand); + addCommand(REPLRemoteCommand.EVAL_CMD); + addCommand(REPLRemoteCommand.EVAL_STEP_INTO_CMD); addCommand(fileCommand); addCommand(REPLRemoteCommand.FRAME_CMD); addCommand(helpCommand); addCommand(infoCommand); addCommand(REPLRemoteCommand.KILL_CMD); addCommand(listCommand); - addCommand(REPLRemoteCommand.LOAD_RUN_CMD); - addCommand(REPLRemoteCommand.LOAD_STEP_CMD); + addCommand(REPLRemoteCommand.LOAD_CMD); + addCommand(REPLRemoteCommand.LOAD_STEP_INTO_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); @@ -207,21 +206,24 @@ public void start() { - REPLMessage startReply = replServer.start(); - - if (startReply.get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) { - clientContext.displayFailReply(startReply.get(REPLMessage.DISPLAY_MSG)); - throw new RuntimeException("Can't start REPL server"); + this.clientContext = new ClientContextImpl(null, null); + try { + showWelcome(); + clientContext.startContextSession(); + } catch (QuitException ex) { + clientContext.displayReply("Goodbye"); } + } - this.clientContext = new ClientContextImpl(null, null); - - try { - clientContext.startSession(); - } finally { - clientContext.displayReply("Goodbye from " + languageName + "/REPL"); + 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) { @@ -263,18 +265,26 @@ this.level = predecessor == null ? 0 : predecessor.level + 1; if (message != null) { + final String sourceName = message.get(REPLMessage.SOURCE_NAME); try { - this.haltedSource = Source.fromFileName(message.get(REPLMessage.SOURCE_NAME)); - selectedSource = this.haltedSource; + this.haltedSource = Source.fromFileName(sourceName); + } catch (IOException ex) { + final String code = message.get(REPLMessage.SOURCE_TEXT); + if (code != null) { + this.haltedSource = Source.fromText(code, sourceName); + } + } + if (this.haltedSource != null) { + selectedSource = haltedSource; try { haltedLineNumber = Integer.parseInt(message.get(REPLMessage.LINE_NUMBER)); } catch (NumberFormatException e) { haltedLineNumber = 0; } - } catch (IOException e1) { + } else { this.haltedSource = null; this.haltedLineNumber = 0; - this.unknownSourceName = message.get(REPLMessage.SOURCE_NAME); + this.unknownSourceName = sourceName; } } updatePrompt(); @@ -289,20 +299,33 @@ 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); + } + final String showLang = languageName == null ? "() " : "( " + languageName + " )"; if (level == 0) { // 0-level context; no executions halted. if (selectedSource == null) { - currentPrompt = languageName == null ? "() " : "( " + languageName + " ) "; + currentPrompt = showLang + " "; } else { - currentPrompt = "(" + selectedSource.getShortName() + ") "; + currentPrompt = "(" + selectedSource.getShortName() + ") " + showLang + " "; } } else if (selectedSource != null && selectedSource != haltedSource) { // User is focusing somewhere else than the current locn; show no line number. final StringBuilder sb = new StringBuilder(); sb.append("(<" + Integer.toString(level) + "> "); sb.append(selectedSource.getShortName()); - sb.append(") "); + sb.append(")"); + sb.append(showLang); + sb.append(" "); currentPrompt = sb.toString(); } else { // Prompt reveals where currently halted. @@ -312,7 +335,9 @@ if (haltedLineNumber > 0) { sb.append(":" + Integer.toString(haltedLineNumber)); } - sb.append(") "); + sb.append(")"); + sb.append(showLang); + sb.append(" "); currentPrompt = sb.toString(); } @@ -443,7 +468,9 @@ for (REPLFrame frame : frameList) { String sourceLineText = frame.sourceLineText(); if (sourceLineText == null) { - sourceLineText = "<??>"; + sourceLineText = ""; + } else { + sourceLineText = "line=\"" + sourceLineText + "\""; } if (frame.index() == selectedFrameNumber) { writer.format(STACK_FRAME_SELECTED_FORMAT, frame.index(), frame.locationDescription(), frame.name(), sourceLineText); @@ -476,7 +503,7 @@ writer.println(TRACE_PREFIX + message); } - public void startSession() { + private void startContextSession() { while (true) { try { @@ -515,15 +542,19 @@ } else if (command instanceof REPLRemoteCommand) { final REPLRemoteCommand remoteCommand = (REPLRemoteCommand) command; - final REPLMessage request = remoteCommand.createRequest(clientContext, args); if (request == null) { continue; } REPLMessage[] replies = sendToServer(request); + remoteCommand.processReply(clientContext, replies); - remoteCommand.processReply(clientContext, replies); + final String path = replies[0].get(REPLMessage.FILE_PATH); + if (path != null && !path.isEmpty()) { + selectSource(path); + } + } else { assert false; // Should not happen. } @@ -651,7 +682,7 @@ } try { - clientContext.startSession(); + clientContext.startContextSession(); } finally { // To continue execution, pop the context and return @@ -698,33 +729,6 @@ } }; - private final REPLCommand evalCommand = new REPLRemoteCommand("eval", null, "Evaluate a string, in context of the current frame if any") { - - private int evalCounter = 0; - - @Override - public REPLMessage createRequest(REPLClientContext context, String[] args) { - if (args.length > 1) { - final String code = args[1]; - if (!code.isEmpty()) { - // Create a fake entry in the file maps and cache, based on this unique name - final String fakeFileName = "<eval" + ++evalCounter + ">"; - Source.fromNamedText(fakeFileName, code); - final REPLMessage request = new REPLMessage(); - request.put(REPLMessage.OP, REPLMessage.EVAL); - request.put(REPLMessage.CODE, code); - request.put(REPLMessage.SOURCE_NAME, fakeFileName); - if (clientContext.level > 0) { - // Specify a requested execution context, if one exists; otherwise top level - request.put(REPLMessage.FRAME_NUMBER, Integer.toString(context.getSelectedFrameNumber())); - } - return request; - } - } - return null; - } - }; - private final REPLCommand fileCommand = new REPLRemoteCommand("file", null, "Set/display current file for viewing") { final String[] help = {"file: display current file path", "file <filename>: Set file to be current file for viewing"}; @@ -922,9 +926,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() { @@ -935,7 +939,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; } @@ -944,12 +948,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()); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package-info.java Sun Dec 06 20:36:50 2015 -0800 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @ApiInfo( + group="To Review" + ) + */ + +/** + * This package contains <strong>REPL*</strong>: an experimental framework for + * building a <em>language-agnostic</em> command-line oriented debugger that: + * <ul> + * <li>works with every Truffle-implemented language "out of the box", + * i.e. requiring minimal additional support by language implementors;</li> + * <li>works simultaneously, without special configuration, for all Truffle language implementations + * available to it; and</li> + * <li>demonstrates Truffle language-interopability by debugging seamlessly + * across Truffle <em>cross-language</em> calls.</li> + * </ul> + * <p> + * <h4>Goals for building <strong>REPL*</strong></h4> + * <ol> + * <li>Exercise and test <em>in action</em> the built-in + * {@linkplain com.oracle.truffle.api.debug.Debugger} debugging support services + * provided as part of the Truffle API, which in turn relies on Truffle + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter Instrumentation}.</li> + * <li>Emulates a client/server architecture to demonstrate that + * language-agnostic debugging can be implemented over wire protocols. Wire + * protocol communication between client and server is <em>partially</em> emulated + * by passing messages expressed as textual key-value pairs. The emulation is + * <em>partial</em> because both run interleaved on a single JVM thread, with some + * sharing of resources.</li> + * <li>Provide a working debugger that is always available during development + * of new Truffle language implementations.</li> + * <li>Provide a working debugger with extra support for Truffle language + * development, in particular the ability to inspect the current structure + * of the Truffle AST around a halted location.</li> + * </ol> + * <h4>Command Set</h4> + * The Command Line Interface (CLI) for <strong>REPL*</strong> is based as + * much as possible on the CLI for the + * <a href="http://www.gnu.org/software/gdb/documentation/">GDB Debugger.</a> + * <h4>REPL* Functionality</h4> + * Basic navigation: + * <ul> + * <li>StepIn (n times)</li> + * <li>StepOut (n times)</li> + * <li>StepOver (n times)</li> + * </ul> + * Execution: + * <ul> + * <li>Load a file source</li> + * <li>Call a defined symbol</li> + * </ul> + * Stack: + * <ul> + * <li>List frames in current execution stack</li> + * <li>Select a frame</li> + * <li>Display selected frame contents</li> + * <li>Move frame selection up/down</li> + * </ul> + * Evaluate: + * <ul> + * <li>Evaluate a Language string in halted context</li> + * <li>Evaluate a Language string in selected frame</li> + * </ul> + * Breakpoints: + * <ul> + * <li>Set/create on a specified line</li> + * <li>Set/create on any throw (before exception created)</li> + * <li>Enable / Disable</li> + * <li>One-shot (once only)</li> + * <li>Get <em>Hit</em> count</li> + * <li>Set <em>Ignore</em> count</li> + * <li>Unset/dispose</li> + * <li>Get all breakpoints</li> + * <li>Find a breakpoint by UID</li> + * <li>Set/clear the condition on a breakpoint</li> + * </ul> + * Others: + * <ul> + * <li>Display halted location in source</li> + * <li>Nested execution + * <li>Help</li> + * <li>Info displays</li> + * <li>Set/display options</li> + * <li>Display Truffle AST structure</li> + * </ul> + */ +package com.oracle.truffle.tools.debug.shell; +
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package.html Fri Dec 04 14:32:24 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -<!DOCTYPE html> -<!-- - -Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. -DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - -This code is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License version 2 only, as -published by the Free Software Foundation. Oracle designates this -particular file as subject to the "Classpath" exception as provided -by Oracle in the LICENSE file that accompanied this code. - -This code is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -version 2 for more details (a copy is included in the LICENSE file that -accompanied this code). - -You should have received a copy of the GNU General Public License version -2 along with this work; if not, write to the Free Software Foundation, -Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - -Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -or visit www.oracle.com if you need additional information or have any -questions. ---> -<html> -<body> - This package contains an experimental framework for building simple command-line oriented - debuggers that work with Truffle-implemented languages; it is used mainly for testing Truffle's - built-in , which actually provides the - debugging services. - <p> - Truffle debugging is made possible by the general purpose Instrumentation Framework built into - the Truffle platform. Some online documentation for the Instrumentation Framework is available - online: - <q> <a href="https://wiki.openjdk.java.net/display/Graal/Instrumentation+API">https://wiki. - openjdk.java.net/display/Graal/Instrumentation+API</a> </q> - <p> - Building one of these command line debuggers requires creating language-specific instances of: - <ol> - <li>DebugEngine, noting that this instance also - depends on related services provided by the language implementation,</li> - <li>{@link com.oracle.truffle.tools.debug.shell.REPLServer}, best accomplished by copying the - implementation for Truffle's demonstration language "Simple" (a.k.a. "SL").</li> - </ol> - - <strong>Disclaimer: </strong> although these command line debuggers are useful, they are not - intended, and will not be maintained as, fully functioning debuggers. They should be considered - valuable tools for the maintainers of the - DebugEngine, as well as for Truffle language - implementors for whom concurrent access to any kind debugging services can be quite helpful. - <p> - <strong>Note:</strong> Both the functionality and API for this package are under active - development. - <p> - </body> -</html>
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/FrameDebugDescription.java Fri Dec 04 14:32:24 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.tools.debug.shell.server; - -import com.oracle.truffle.api.frame.Frame; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.nodes.Node; - -public final class FrameDebugDescription { - private final int index; - private final Node node; - private final FrameInstance frameInstance; - - FrameDebugDescription(int index, Node node, FrameInstance frameInstance) { - this.index = index; - this.node = node; - this.frameInstance = frameInstance; - } - - /** - * Position in the current stack: {@code 0} at the top. - */ - public int index() { - return index; - } - - /** - * AST location. - */ - public Node node() { - return node; - } - - /** - * Access to the Truffle {@link Frame}. - */ - public FrameInstance frameInstance() { - return frameInstance; - } -}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Fri Dec 04 14:32:24 2015 +0100 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Sun Dec 06 20:36:50 2015 -0800 @@ -24,10 +24,14 @@ */ package com.oracle.truffle.tools.debug.shell.server; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import com.oracle.truffle.api.debug.Breakpoint; +import com.oracle.truffle.api.debug.FrameDebugDescription; import com.oracle.truffle.api.frame.Frame; -import com.oracle.truffle.api.frame.FrameDescriptor; -import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.instrument.ASTPrinter; import com.oracle.truffle.api.instrument.KillException; @@ -37,10 +41,9 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.vm.PolyglotEngine.Language; import com.oracle.truffle.tools.debug.shell.REPLMessage; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import com.oracle.truffle.tools.debug.shell.server.REPLServer.Context; /** * Server-side REPL implementation of an {@linkplain REPLMessage "op"}. @@ -61,30 +64,23 @@ /** * Gets the "op" implemented by this handler. */ - public final String getOp() { + final String getOp() { return op; } /** * Passes a request to this handler. */ - public abstract REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext); + abstract REPLMessage[] receive(REPLMessage request, REPLServer replServer); /** * Creates skeleton for a reply message that identifies the operation currently being handled. */ - protected final REPLMessage createReply() { + REPLMessage createReply() { return new REPLMessage(REPLMessage.OP, op); } /** - * Creates skeleton for a reply message that identifies a specified operation. - */ - protected static final REPLMessage createReply(String opString) { - return new REPLMessage(REPLMessage.OP, opString); - } - - /** * Completes a reply, reporting and explaining successful handling. */ protected static final REPLMessage[] finishReplySucceeded(REPLMessage reply, String explanation) { @@ -104,9 +100,18 @@ return replies; } - static final REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint, REPLServerContext serverContext) { + protected static final REPLMessage[] finishReplyFailed(REPLMessage reply, Exception ex) { + reply.put(REPLMessage.STATUS, REPLMessage.FAILED); + String message = ex.getMessage(); + reply.put(REPLMessage.DISPLAY_MSG, message == null ? ex.getClass().getSimpleName() : message); + final REPLMessage[] replies = new REPLMessage[]{reply}; + return replies; + } + + 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(serverContext.getBreakpointID(breakpoint))); + infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); + infoMessage.put(REPLMessage.BREAKPOINT_STATE, breakpoint.getState().getName()); infoMessage.put(REPLMessage.BREAKPOINT_HIT_COUNT, Integer.toString(breakpoint.getHitCount())); infoMessage.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, Integer.toString(breakpoint.getIgnoreCount())); infoMessage.put(REPLMessage.INFO_VALUE, breakpoint.getLocationDescription().toString()); @@ -117,12 +122,10 @@ return infoMessage; } - protected static REPLMessage createFrameInfoMessage(final REPLServerContext serverContext, FrameDebugDescription frame) { - final Visualizer visualizer = serverContext.getVisualizer(); + protected static final REPLMessage createFrameInfoMessage(final REPLServer replServer, int number, Node node) { + final Visualizer visualizer = replServer.getVisualizer(); final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.FRAME_INFO); - infoMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(frame.index())); - final Node node = frame.node(); - + infoMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(number)); infoMessage.put(REPLMessage.SOURCE_LOCATION, visualizer.displaySourceLocation(node)); infoMessage.put(REPLMessage.METHOD_NAME, visualizer.displayMethodName(node)); @@ -131,7 +134,7 @@ if (section == null) { section = node.getEncapsulatingSourceSection(); } - if (section != null) { + if (section != null && section.getSource() != null) { infoMessage.put(REPLMessage.FILE_PATH, section.getSource().getPath()); infoMessage.put(REPLMessage.LINE_NUMBER, Integer.toString(section.getStartLine())); infoMessage.put(REPLMessage.SOURCE_LINE_TEXT, section.getSource().getCode(section.getStartLine())); @@ -144,23 +147,45 @@ public static final REPLHandler BACKTRACE_HANDLER = new REPLHandler(REPLMessage.BACKTRACE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - final REPLMessage reply = createReply(); - final ArrayList<REPLMessage> frameMessages = new ArrayList<>(); - for (FrameDebugDescription frame : serverContext.getStack()) { - frameMessages.add(createFrameInfoMessage(serverContext, frame)); + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final Visualizer visualizer = replServer.getVisualizer(); + final ArrayList<REPLMessage> replies = new ArrayList<>(); + final List<FrameDebugDescription> stack = replServer.getCurrentContext().getStack(); + + for (int i = 0; i < stack.size(); i++) { + replies.add(btMessage(i, stack.get(i).node(), visualizer)); } - if (frameMessages.size() > 0) { - return frameMessages.toArray(new REPLMessage[0]); + if (replies.size() > 0) { + return replies.toArray(new REPLMessage[0]); } - return finishReplyFailed(reply, "No stack"); + return finishReplyFailed(new REPLMessage(REPLMessage.OP, REPLMessage.BACKTRACE), "No stack"); } }; + private static REPLMessage btMessage(int index, Node node, Visualizer visualizer) { + final REPLMessage btMessage = new REPLMessage(REPLMessage.OP, REPLMessage.BACKTRACE); + btMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(index)); + if (node != null) { + btMessage.put(REPLMessage.SOURCE_LOCATION, visualizer.displaySourceLocation(node)); + btMessage.put(REPLMessage.METHOD_NAME, visualizer.displayMethodName(node)); + SourceSection section = node.getSourceSection(); + if (section == null) { + section = node.getEncapsulatingSourceSection(); + } + if (section != null && section.getSource() != null) { + btMessage.put(REPLMessage.FILE_PATH, section.getSource().getPath()); + btMessage.put(REPLMessage.LINE_NUMBER, Integer.toString(section.getStartLine())); + btMessage.put(REPLMessage.SOURCE_LINE_TEXT, section.getSource().getCode(section.getStartLine())); + } + btMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + } + return btMessage; + } + public static final REPLHandler BREAK_AT_LINE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_LINE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final String path = request.get(REPLMessage.FILE_PATH); final String fileName = request.get(REPLMessage.SOURCE_NAME); @@ -169,7 +194,7 @@ try { source = Source.fromFileName(lookupFile, true); } catch (Exception ex) { - return finishReplyFailed(reply, ex.getMessage()); + return finishReplyFailed(reply, ex); } if (source == null) { return finishReplyFailed(reply, fileName + " not found"); @@ -184,14 +209,13 @@ } Breakpoint breakpoint; try { - breakpoint = serverContext.db().setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false); - serverContext.registerBreakpoint(breakpoint); - } catch (Exception ex) { - return finishReplyFailed(reply, ex.getMessage()); + breakpoint = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false); + } catch (IOException ex) { + return finishReplyFailed(reply, ex); } reply.put(REPLMessage.SOURCE_NAME, fileName); reply.put(REPLMessage.FILE_PATH, source.getPath()); - reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(serverContext.getBreakpointID(breakpoint))); + reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber)); reply.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, ignoreCount.toString()); return finishReplySucceeded(reply, "Breakpoint set"); @@ -201,7 +225,7 @@ public static final REPLHandler BREAK_AT_LINE_ONCE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_LINE_ONCE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final String path = request.get(REPLMessage.FILE_PATH); final String fileName = request.get(REPLMessage.SOURCE_NAME); @@ -210,7 +234,7 @@ try { source = Source.fromFileName(lookupFile, true); } catch (Exception ex) { - return finishReplyFailed(reply, ex.getMessage()); + return finishReplyFailed(reply, ex); } if (source == null) { return finishReplyFailed(reply, fileName + " not found"); @@ -219,14 +243,15 @@ if (lineNumber == null) { return finishReplyFailed(reply, "missing line number"); } + Breakpoint breakpoint; try { - Breakpoint b = serverContext.db().setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true); - serverContext.registerBreakpoint(b); - } catch (Exception ex) { - return finishReplyFailed(reply, ex.getMessage()); + breakpoint = replServer.setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true); + } catch (IOException ex) { + return finishReplyFailed(reply, ex); } reply.put(REPLMessage.SOURCE_NAME, fileName); reply.put(REPLMessage.FILE_PATH, source.getPath()); + reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(replServer.getBreakpointID(breakpoint))); reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber)); return finishReplySucceeded(reply, "One-shot line breakpoint set"); } @@ -235,14 +260,13 @@ public static final REPLHandler BREAK_AT_THROW_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_THROW) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); try { - Breakpoint b = serverContext.db().setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, false); - serverContext.registerBreakpoint(b); + replServer.setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, false); return finishReplySucceeded(reply, "Breakpoint at any throw set"); } catch (Exception ex) { - return finishReplyFailed(reply, ex.getMessage()); + return finishReplyFailed(reply, ex); } } }; @@ -250,13 +274,13 @@ public static final REPLHandler BREAK_AT_THROW_ONCE_HANDLER = new REPLHandler(REPLMessage.BREAK_AT_THROW_ONCE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); try { - serverContext.db().setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, true); + replServer.setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, true); return finishReplySucceeded(reply, "One-shot breakpoint at any throw set"); } catch (Exception ex) { - return finishReplyFailed(reply, ex.getMessage()); + return finishReplyFailed(reply, ex); } } }; @@ -264,11 +288,11 @@ public static final REPLHandler BREAKPOINT_INFO_HANDLER = new REPLHandler(REPLMessage.BREAKPOINT_INFO) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final ArrayList<REPLMessage> infoMessages = new ArrayList<>(); - for (Breakpoint breakpoint : serverContext.db().getBreakpoints()) { - infoMessages.add(createBreakpointInfoMessage(breakpoint, serverContext)); + for (Breakpoint breakpoint : replServer.getBreakpoints()) { + infoMessages.add(createBreakpointInfoMessage(breakpoint, replServer)); } if (infoMessages.size() > 0) { return infoMessages.toArray(new REPLMessage[0]); @@ -277,20 +301,52 @@ } }; + public static final REPLHandler CALL_HANDLER = new REPLHandler(REPLMessage.CALL) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.CALL); + final String callName = request.get(REPLMessage.CALL_NAME); + if (callName == null) { + return finishReplyFailed(reply, "no name specified"); + } + final ArrayList<String> argList = new ArrayList<>(); + for (int argCount = 0; argCount < REPLMessage.ARG_NAMES.length; argCount++) { + final String arg = request.get(REPLMessage.ARG_NAMES[argCount]); + if (arg == null) { + break; + } + argList.add(arg); + } + final boolean stepInto = REPLMessage.TRUE.equals(request.get(REPLMessage.STEP_INTO)); + try { + final Object result = replServer.getCurrentContext().call(callName, stepInto, argList); + reply.put(REPLMessage.VALUE, result == null ? "<void>" : result.toString()); + } catch (QuitException ex) { + throw ex; + } catch (KillException ex) { + return finishReplySucceeded(reply, callName + " killed"); + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + return finishReplySucceeded(reply, callName + " returned"); + } + }; + public static final REPLHandler CLEAR_BREAK_HANDLER = new REPLHandler(REPLMessage.CLEAR_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } - breakpoint.dispose(); + replServer.clearBreakpoint(breakpoint); reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); return finishReplySucceeded(reply, "Breakpoint " + breakpointNumber + " cleared"); } @@ -299,9 +355,9 @@ public static final REPLHandler CONTINUE_HANDLER = new REPLHandler(REPLMessage.CONTINUE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - serverContext.prepareContinue(); + replServer.getCurrentContext().prepareContinue(); return finishReplySucceeded(reply, "Continue mode entered"); } }; @@ -309,30 +365,29 @@ public static final REPLHandler DELETE_HANDLER = new REPLHandler(REPLMessage.DELETE_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - int deleteCount = 0; - for (Breakpoint breakpoint : serverContext.db().getBreakpoints()) { - breakpoint.dispose(); - deleteCount++; - } - if (deleteCount == 0) { + final Collection<Breakpoint> breakpoints = replServer.getBreakpoints(); + if (breakpoints.isEmpty()) { return finishReplyFailed(reply, "no breakpoints to delete"); } - return finishReplySucceeded(reply, Integer.toString(deleteCount) + " breakpoints deleted"); + for (Breakpoint breakpoint : breakpoints) { + breakpoint.dispose(); + } + return finishReplySucceeded(reply, Integer.toString(breakpoints.size()) + " breakpoints deleted"); } }; public static final REPLHandler DISABLE_BREAK_HANDLER = new REPLHandler(REPLMessage.DISABLE_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -345,13 +400,13 @@ public static final REPLHandler ENABLE_BREAK_HANDLER = new REPLHandler(REPLMessage.ENABLE_BREAK) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -360,29 +415,53 @@ return finishReplySucceeded(reply, "Breakpoint " + breakpointNumber + " enabled"); } }; + public static final REPLHandler EVAL_HANDLER = new REPLHandler(REPLMessage.EVAL) { + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = createReply(); + final String sourceName = request.get(REPLMessage.SOURCE_NAME); + reply.put(REPLMessage.SOURCE_NAME, sourceName); + final Context serverContext = replServer.getCurrentContext(); + reply.put(REPLMessage.DEBUG_LEVEL, Integer.toString(serverContext.getLevel())); + final String source = request.get(REPLMessage.CODE); + final Visualizer visualizer = replServer.getVisualizer(); + final Integer frameNumber = request.getIntValue(REPLMessage.FRAME_NUMBER); + final boolean stepInto = REPLMessage.TRUE.equals(request.get(REPLMessage.STEP_INTO)); + try { + Object returnValue = serverContext.eval(source, frameNumber, stepInto); + return finishReplySucceeded(reply, visualizer.displayValue(returnValue, 0)); + } catch (QuitException ex) { + throw ex; + } catch (KillException ex) { + return finishReplySucceeded(reply, "eval (" + sourceName + ") killed"); + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + } + }; public static final REPLHandler FILE_HANDLER = new REPLHandler(REPLMessage.FILE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); final String fileName = request.get(REPLMessage.SOURCE_NAME); if (fileName == null) { return finishReplyFailed(reply, "no file specified"); } + reply.put(REPLMessage.SOURCE_NAME, fileName); try { Source source = Source.fromFileName(fileName); if (source == null) { - reply.put(REPLMessage.SOURCE_NAME, fileName); - return finishReplyFailed(reply, " not found"); + return finishReplyFailed(reply, "file \"" + fileName + "\" not found"); } else { - reply.put(REPLMessage.SOURCE_NAME, fileName); reply.put(REPLMessage.FILE_PATH, source.getPath()); reply.put(REPLMessage.CODE, source.getCode()); return finishReplySucceeded(reply, "file found"); } + } catch (IOException ex) { + return finishReplyFailed(reply, "can't read file \"" + fileName + "\""); } catch (Exception ex) { - reply.put(REPLMessage.SOURCE_NAME, fileName); - return finishReplyFailed(reply, "file \"" + fileName + "\" not found"); + return finishReplyFailed(reply, ex); } } }; @@ -395,69 +474,161 @@ public static final REPLHandler FRAME_HANDLER = new REPLHandler(REPLMessage.FRAME) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - final REPLMessage reply = createReply(); + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final Integer frameNumber = request.getIntValue(REPLMessage.FRAME_NUMBER); if (frameNumber == null) { - return finishReplyFailed(reply, "no frame number specified"); + return finishReplyFailed(createReply(), "no frame number specified"); } - final List<FrameDebugDescription> stack = serverContext.getStack(); + final List<FrameDebugDescription> stack = replServer.getCurrentContext().getStack(); if (frameNumber < 0 || frameNumber >= stack.size()) { - return finishReplyFailed(reply, "frame number " + frameNumber + " out of range"); + return finishReplyFailed(createReply(), "frame number " + frameNumber + " out of range"); } + final Visualizer visualizer = replServer.getVisualizer(); + final FrameDebugDescription frameDescription = stack.get(frameNumber); - final REPLMessage frameMessage = createFrameInfoMessage(serverContext, frameDescription); - final Frame frame = frameDescription.frameInstance().getFrame(FrameInstance.FrameAccess.READ_ONLY, true); - final Visualizer visualizer = serverContext.getVisualizer(); - final FrameDescriptor frameDescriptor = frame.getFrameDescriptor(); - try { - final StringBuilder sb = new StringBuilder(); - for (FrameSlot slot : frameDescriptor.getSlots()) { - sb.append(Integer.toString(slot.getIndex()) + ": " + visualizer.displayIdentifier(slot) + " = "); - try { - final Object value = frame.getValue(slot); - sb.append(visualizer.displayValue(value, 0)); - } catch (Exception ex) { - sb.append("???"); + final Frame frame = frameDescription.frame(); + final Node node = frameDescription.node(); + List<? extends FrameSlot> slots = frame.getFrameDescriptor().getSlots(); + + if (slots.size() == 0) { + final REPLMessage emptyFrameMessage = createFrameInfoMessage(replServer, frameNumber, node); + return finishReplySucceeded(emptyFrameMessage, "empty frame"); + } + final ArrayList<REPLMessage> replies = new ArrayList<>(); + + for (FrameSlot slot : slots) { + final REPLMessage slotMessage = createFrameInfoMessage(replServer, frameNumber, node); + slotMessage.put(REPLMessage.SLOT_INDEX, Integer.toString(slot.getIndex())); + slotMessage.put(REPLMessage.SLOT_ID, visualizer.displayIdentifier(slot)); + slotMessage.put(REPLMessage.SLOT_VALUE, visualizer.displayValue(frame.getValue(slot), 0)); + slotMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + replies.add(slotMessage); + } + return replies.toArray(new REPLMessage[0]); + } + }; + + public static final REPLHandler INFO_HANDLER = new REPLHandler(REPLMessage.INFO) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final String topic = request.get(REPLMessage.TOPIC); + + if (topic == null || topic.isEmpty()) { + final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.INFO); + return finishReplyFailed(message, "No info topic specified"); + } + + switch (topic) { + + case REPLMessage.INFO_SUPPORTED_LANGUAGES: + final ArrayList<REPLMessage> 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); } - sb.append("\n"); - } - return finishReplySucceeded(frameMessage, sb.toString()); - } catch (Exception ex) { - return finishReplyFailed(frameMessage, ex.toString()); + 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); + return finishReplyFailed(message, "No info about topic \"" + topic + "\""); } } }; - public static final REPLHandler KILL_HANDLER = new REPLHandler(REPLMessage.KILL) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - if (serverContext.getLevel() == 0) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + if (replServer.getCurrentContext().getLevel() == 0) { return finishReplyFailed(createReply(), "nothing to kill"); } throw new KillException(); } }; + public static final REPLHandler LOAD_HANDLER = new REPLHandler(REPLMessage.LOAD_SOURCE) { + + @Override + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.LOAD_SOURCE); + final String fileName = request.get(REPLMessage.SOURCE_NAME); + final boolean stepInto = REPLMessage.TRUE.equals(request.get(REPLMessage.STEP_INTO)); + try { + final Source fileSource = Source.fromFileName(fileName); + replServer.getCurrentContext().eval(fileSource, stepInto); + reply.put(REPLMessage.FILE_PATH, fileName); + return finishReplySucceeded(reply, fileName + " loaded"); + } catch (QuitException ex) { + throw ex; + } catch (KillException ex) { + return finishReplySucceeded(reply, fileName + " killed"); + } catch (Exception ex) { + return finishReplyFailed(reply, ex); + } + } + }; + public static final REPLHandler QUIT_HANDLER = new REPLHandler(REPLMessage.QUIT) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { throw new QuitException(); } }; + 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) { + final String oldLanguageName = replServer.getCurrentContext().getLanguageName(); + reply.put(REPLMessage.LANG_NAME, reply.put(REPLMessage.LANG_NAME, oldLanguageName)); + return finishReplySucceeded(reply, "Language set to " + oldLanguageName); + } + 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 - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.SET_BREAK_CONDITION); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(message, "missing breakpoint number"); } message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(message, "no breakpoint number " + breakpointNumber); } @@ -470,7 +641,9 @@ } catch (IOException ex) { return finishReplyFailed(message, "invalid condition for " + breakpointNumber); } catch (UnsupportedOperationException ex) { - return finishReplyFailed(message, "conditions not unsupported by breakpoint " + breakpointNumber); + return finishReplyFailed(message, "conditions not supported by breakpoint " + breakpointNumber); + } catch (Exception ex) { + return finishReplyFailed(message, ex); } message.put(REPLMessage.BREAKPOINT_CONDITION, expr); return finishReplySucceeded(message, "Breakpoint " + breakpointNumber + " condition=\"" + expr + "\""); @@ -480,13 +653,13 @@ public static final REPLHandler STEP_INTO_HANDLER = new REPLHandler(REPLMessage.STEP_INTO) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer repeat = request.getIntValue(REPLMessage.REPEAT); if (repeat == null) { repeat = 1; } - serverContext.prepareStepInto(repeat); + replServer.getCurrentContext().prepareStepInto(repeat); return finishReplySucceeded(reply, "StepInto <" + repeat + "> enabled"); } }; @@ -494,8 +667,8 @@ public static final REPLHandler STEP_OUT_HANDLER = new REPLHandler(REPLMessage.STEP_OUT) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - serverContext.prepareStepOut(); + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { + replServer.getCurrentContext().prepareStepOut(); return finishReplySucceeded(createReply(), "StepOut enabled"); } }; @@ -503,13 +676,13 @@ public static final REPLHandler STEP_OVER_HANDLER = new REPLHandler(REPLMessage.STEP_OVER) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext debugServerContextFrame) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); Integer repeat = request.getIntValue(REPLMessage.REPEAT); if (repeat == null) { repeat = 1; } - debugServerContextFrame.prepareStepOver(repeat); + replServer.getCurrentContext().prepareStepOver(repeat); return finishReplySucceeded(reply, "StepOver <" + repeat + "> enabled"); } }; @@ -517,12 +690,12 @@ public static final REPLHandler TRUFFLE_HANDLER = new REPLHandler(REPLMessage.TRUFFLE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - final ASTPrinter astPrinter = serverContext.getVisualizer().getASTPrinter(); + final ASTPrinter astPrinter = replServer.getVisualizer().getASTPrinter(); final String topic = request.get(REPLMessage.TOPIC); reply.put(REPLMessage.TOPIC, topic); - Node node = serverContext.getNodeAtHalt(); + Node node = replServer.getCurrentContext().getNodeAtHalt(); if (node == null) { return finishReplyFailed(reply, "no current AST node"); } @@ -536,7 +709,7 @@ while (node.getParent() != null) { node = node.getParent(); } - final String astText = astPrinter.printTreeToString(node, depth, serverContext.getNodeAtHalt()); + final String astText = astPrinter.printTreeToString(node, depth, replServer.getCurrentContext().getNodeAtHalt()); return finishReplySucceeded(reply, astText); case REPLMessage.SUBTREE: case REPLMessage.SUB: @@ -547,7 +720,7 @@ } } catch (Exception ex) { - return finishReplyFailed(reply, ex.toString()); + return finishReplyFailed(reply, ex); } } }; @@ -555,33 +728,33 @@ public static final REPLHandler UNSET_BREAK_CONDITION_HANDLER = new REPLHandler(REPLMessage.UNSET_BREAK_CONDITION) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage message = new REPLMessage(REPLMessage.OP, REPLMessage.UNSET_BREAK_CONDITION); Integer breakpointNumber = request.getIntValue(REPLMessage.BREAKPOINT_ID); if (breakpointNumber == null) { return finishReplyFailed(message, "missing breakpoint number"); } message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); - final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = replServer.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(message, "no breakpoint number " + breakpointNumber); } try { breakpoint.setCondition(null); - } catch (IOException e) { - return finishReplyFailed(message, e.getMessage()); + } catch (Exception ex) { + return finishReplyFailed(message, ex); } - return finishReplyFailed(message, "Breakpoint " + breakpointNumber + " condition cleared"); + return finishReplySucceeded(message, "Breakpoint " + breakpointNumber + " condition cleared"); } }; public static final REPLHandler TRUFFLE_NODE_HANDLER = new REPLHandler(REPLMessage.TRUFFLE_NODE) { @Override - public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { + public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - final ASTPrinter astPrinter = serverContext.getVisualizer().getASTPrinter(); - final Node node = serverContext.getNodeAtHalt(); + final ASTPrinter astPrinter = replServer.getVisualizer().getASTPrinter(); + final Node node = replServer.getCurrentContext().getNodeAtHalt(); if (node == null) { return finishReplyFailed(reply, "no current AST node"); } @@ -599,7 +772,7 @@ } return finishReplySucceeded(reply, sb.toString()); } catch (Exception ex) { - return finishReplyFailed(reply, ex.toString()); + return finishReplyFailed(reply, ex); } } };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java Sun Dec 06 20:36:50 2015 -0800 @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.tools.debug.shell.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +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; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.FrameDebugDescription; +import com.oracle.truffle.api.debug.LineBreakpoint; +import com.oracle.truffle.api.debug.SuspendedEvent; +import com.oracle.truffle.api.debug.TagBreakpoint; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.StandardSyntaxTag; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.impl.DefaultVisualizer; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.vm.EventConsumer; +import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.api.vm.PolyglotEngine.Language; +import com.oracle.truffle.api.vm.PolyglotEngine.Value; +import com.oracle.truffle.tools.debug.shell.REPLMessage; +import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; + +/** + * The server side of a simple message-based protocol for a possibly remote language + * Read-Eval-Print-Loop. + */ +public final class REPLServer { + + // Language-agnostic + private final PolyglotEngine engine; + private Debugger db; + private Context currentServerContext; + private SimpleREPLClient replClient = null; + private String statusPrefix; + private final Map<String, REPLHandler> handlerMap = new HashMap<>(); + + /** Languages sorted by name. */ + private final TreeSet<Language> engineLanguages = new TreeSet<>(new Comparator<Language>() { + + public int compare(Language lang1, Language lang2) { + return lang1.getName().compareTo(lang2.getName()); + } + }); + + /** MAP: language name => Language. */ + private final Map<String, Language> nameToLanguage = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + // TODO (mlvdv) Language-specific + private PolyglotEngine.Language defaultLanguage; + private final Visualizer visualizer; + + private int nextBreakpointUID = 0; + + /** + * Map: breakpoints => breakpoint UID. + * <ul> + * <li>Contains only pending breakpoints when the Debugger not yet available.</li> + * <li>When the Debugger becomes available, each pending breakpoint is replaced with a Debugger + * breakpoint, keeping same UID.</li> + * <li>Contains no disposed breakpoints.</li> + * <li>UIDs for disposed breakpoints are never reused.</li> + * </ul> + */ + private Map<Breakpoint, Integer> breakpoints = new WeakHashMap<>(); + + public REPLServer(String defaultMIMEType, Visualizer visualizer) { + this.visualizer = visualizer == null ? new DefaultVisualizer() : visualizer; + 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 + "\""); + } + } + statusPrefix = languageName(defaultLanguage); + } + + private final EventConsumer<SuspendedEvent> onHalted = new EventConsumer<SuspendedEvent>(SuspendedEvent.class) { + @Override + protected void on(SuspendedEvent ev) { + REPLServer.this.haltedAt(ev); + } + }; + + private final EventConsumer<ExecutionEvent> onExec = new EventConsumer<ExecutionEvent>(ExecutionEvent.class) { + @Override + protected void on(ExecutionEvent event) { + if (db == null) { + db = event.getDebugger(); + if (!breakpoints.isEmpty()) { + ArrayList<? extends Breakpoint> pendingBreakpoints = new ArrayList<>(breakpoints.keySet()); + try { + for (Breakpoint pending : pendingBreakpoints) { + + 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()); + } + breakpoints.put(replacement, uid); + } + } catch (IOException e) { + throw new IllegalStateException("pending breakpoints should all be valid"); + } + } + } + if (currentServerContext.steppingInto) { + event.prepareStepInto(); + } + } + }; + + public void add(REPLHandler handler) { + handlerMap.put(handler.getOp(), handler); + } + + /** + * Starts up a server; status returned in a message. + */ + public void start() { + + add(REPLHandler.BACKTRACE_HANDLER); + add(REPLHandler.BREAK_AT_LINE_HANDLER); + add(REPLHandler.BREAK_AT_LINE_ONCE_HANDLER); + add(REPLHandler.BREAK_AT_THROW_HANDLER); + add(REPLHandler.BREAK_AT_THROW_ONCE_HANDLER); + add(REPLHandler.BREAKPOINT_INFO_HANDLER); + add(REPLHandler.CALL_HANDLER); + add(REPLHandler.CLEAR_BREAK_HANDLER); + add(REPLHandler.CONTINUE_HANDLER); + add(REPLHandler.DELETE_HANDLER); + add(REPLHandler.DISABLE_BREAK_HANDLER); + add(REPLHandler.ENABLE_BREAK_HANDLER); + add(REPLHandler.EVAL_HANDLER); + add(REPLHandler.FILE_HANDLER); + add(REPLHandler.FRAME_HANDLER); + add(REPLHandler.INFO_HANDLER); + add(REPLHandler.KILL_HANDLER); + 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); + add(REPLHandler.TRUFFLE_HANDLER); + add(REPLHandler.TRUFFLE_NODE_HANDLER); + add(REPLHandler.UNSET_BREAK_CONDITION_HANDLER); + this.replClient = new SimpleREPLClient(this); + 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) { + // 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()); + final String path = source.getPath(); + if (path == null) { + message.put(REPLMessage.SOURCE_TEXT, source.getCode()); + } else { + 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<String> warnings = event.getRecentWarnings(); + if (!warnings.isEmpty()) { + final StringBuilder sb = new StringBuilder(); + for (String warning : warnings) { + sb.append(warning + "\n"); + } + message.put(REPLMessage.WARNINGS, sb.toString()); + } + try { + // Cheat with synchrony: call client directly about entering a nested debugging + // context. + replClient.halted(message); + } finally { + // Returns when "continue" or "kill" is called in the new debugging context + + // Pop the debug context, and return so that the old context will continue + currentServerContext = currentServerContext.predecessor; + } + } + + @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. + */ + public final class Context { + + private final Context predecessor; + private final int level; + private final SuspendedEvent event; + private Language currentLanguage; + private boolean steppingInto = false; // Only true during a "stepInto" engine call + + 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; + } + + /** + * The nesting depth of this context in the current session. + */ + int getLevel() { + return level; + } + + /** + * The AST node where execution is halted in this context. + */ + Node getNodeAtHalt() { + return event.getNode(); + } + + Object call(String name, boolean stepInto, List<String> argList) throws IOException { + Value symbol = engine.findGlobalSymbol(name); + if (symbol == null) { + throw new IOException("symbol \"" + name + "\" not found"); + } + final List<Object> args = new ArrayList<>(); + for (String stringArg : argList) { + Integer intArg = null; + try { + intArg = Integer.valueOf(stringArg); + args.add(intArg); + } catch (NumberFormatException e) { + args.add(stringArg); + } + } + this.steppingInto = stepInto; + try { + return symbol.invoke(null, args.toArray(new Object[0])).get(); + } finally { + this.steppingInto = false; + } + } + + void eval(Source source, boolean stepInto) throws IOException { + this.steppingInto = stepInto; + try { + engine.eval(source); + } finally { + this.steppingInto = false; + } + } + + /** + * Evaluates a code snippet in the context of a selected frame in the currently suspended + * execution. + * + * @param code the snippet to evaluate + * @param frameNumber index of the stack frame in which to evaluate, {@code null} if the + * topmost frame should be used. + * @return result of the evaluation + * @throws IOException if something goes wrong + */ + Object eval(String code, Integer frameNumber, boolean stepInto) throws IOException { + if (event == null) { + this.steppingInto = stepInto; + final String mimeType = defaultMIME(currentLanguage); + try { + return engine.eval(Source.fromText(code, "eval(\"" + code + "\")").withMimeType(mimeType)).get(); + } finally { + this.steppingInto = false; + } + } else { + if (stepInto) { + event.prepareStepInto(1); + } + try { + final Object result = event.eval(code, frameNumber); + return (result instanceof Value) ? ((Value) result).get() : result; + } finally { + event.prepareContinue(); + } + } + } + + /** + * The frame where execution is halted in this context. + */ + MaterializedFrame getFrameAtHalt() { + return event.getFrame(); + } + + /** + * Dispatches a REPL request to the appropriate handler. + */ + REPLMessage[] receive(REPLMessage request) { + final String command = request.get(REPLMessage.OP); + final REPLHandler handler = handlerMap.get(command); + + if (handler == null) { + final REPLMessage message = new REPLMessage(); + message.put(REPLMessage.OP, command); + message.put(REPLMessage.STATUS, REPLMessage.FAILED); + message.put(REPLMessage.DISPLAY_MSG, statusPrefix + " op \"" + command + "\" not supported"); + final REPLMessage[] reply = new REPLMessage[]{message}; + return reply; + } + return handler.receive(request, REPLServer.this); + } + + /** + * Provides access to the execution stack. + * + * @return immutable list of stack elements + */ + List<FrameDebugDescription> getStack() { + return event.getStack(); + } + + public String getLanguageName() { + return currentLanguage.getName(); + } + + /** + * Case-insensitive; returns actual language name set. + * + * @throws IOException if fails + */ + String setLanguage(String name) throws IOException { + assert name != null; + final Language language = nameToLanguage.get(name); + if (language == null) { + throw new IOException("Language \" + name + \" not supported"); + } + if (language == currentLanguage) { + return currentLanguage.getName(); + } + if (event != null) { + throw new IOException("Only supported at top level"); + } + this.currentLanguage = language; + return language.getName(); + } + + void prepareStepOut() { + event.prepareStepOut(); + } + + void prepareStepInto(int repeat) { + event.prepareStepInto(repeat); + } + + void prepareStepOver(int repeat) { + event.prepareStepOver(repeat); + } + + void prepareContinue() { + event.prepareContinue(); + } + + } + + /** + * Ask the server to handle a request. Return a non-empty array of messages to simulate remote + * operation where the protocol has possibly multiple messages being returned asynchronously in + * response to each request. + */ + public REPLMessage[] receive(REPLMessage request) { + if (currentServerContext == null) { + final REPLMessage message = new REPLMessage(); + message.put(REPLMessage.STATUS, REPLMessage.FAILED); + message.put(REPLMessage.DISPLAY_MSG, "server not started"); + final REPLMessage[] reply = new REPLMessage[]{message}; + return reply; + } + return currentServerContext.receive(request); + } + + Context getCurrentContext() { + return currentServerContext; + } + + Visualizer getVisualizer() { + return visualizer; + } + + // TODO (mlvdv) language-specific + Language getLanguage() { + return defaultLanguage; + } + + TreeSet<Language> getLanguages() { + return engineLanguages; + } + + // TODO (mlvdv) language-specific + public String getLanguageName() { + 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(); + } + + Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { + Breakpoint breakpoint; + if (db == null) { + breakpoint = new PendingLineBreakpoint(ignoreCount, lineLocation, oneShot); + } else { + breakpoint = db.setLineBreakpoint(ignoreCount, lineLocation, oneShot); + } + registerNewBreakpoint(breakpoint); + return breakpoint; + } + + Breakpoint setTagBreakpoint(int ignoreCount, StandardSyntaxTag tag, boolean oneShot) throws IOException { + Breakpoint breakpoint; + if (db == null) { + breakpoint = new PendingTagBreakpoint(ignoreCount, tag, oneShot); + } else { + breakpoint = db.setTagBreakpoint(ignoreCount, tag, oneShot); + } + registerNewBreakpoint(breakpoint); + return breakpoint; + } + + private synchronized void registerNewBreakpoint(Breakpoint breakpoint) { + breakpoints.put(breakpoint, nextBreakpointUID++); + } + + synchronized Breakpoint findBreakpoint(int id) { + for (Map.Entry<Breakpoint, Integer> entrySet : breakpoints.entrySet()) { + if (id == entrySet.getValue()) { + return entrySet.getKey(); + } + } + return null; + } + + /** + * Gets a list of the currently existing breakpoints. + */ + Collection<Breakpoint> getBreakpoints() { + return new ArrayList<>(breakpoints.keySet()); + } + + synchronized int getBreakpointID(Breakpoint breakpoint) { + final Integer id = breakpoints.get(breakpoint); + return id == null ? -1 : id; + } + + void clearBreakpoint(Breakpoint breakpoint) { + breakpoint.dispose(); + breakpoints.remove(breakpoint); + } + + /** + * The intention to create a line breakpoint. + */ + private final class PendingLineBreakpoint extends LineBreakpoint { + + private Source conditionSource; + + PendingLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) { + super(Breakpoint.State.ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot); + } + + @Override + public void setEnabled(boolean enabled) { + switch (getState()) { + case ENABLED_UNRESOLVED: + if (!enabled) { + setState(State.DISABLED_UNRESOLVED); + } + break; + case DISABLED_UNRESOLVED: + if (enabled) { + setState(State.ENABLED_UNRESOLVED); + } + break; + case DISPOSED: + throw new IllegalStateException("Disposed breakpoints must stay disposed"); + default: + throw new IllegalStateException("Unexpected breakpoint state"); + } + } + + @Override + public boolean isEnabled() { + return getState() == State.ENABLED_UNRESOLVED; + } + + @Override + public void setCondition(String expr) throws IOException { + + this.conditionSource = expr == null ? null : Source.fromText(expr, "breakpoint condition from text: " + expr); + } + + @Override + public Source getCondition() { + return conditionSource; + } + + @Override + public void dispose() { + if (getState() == State.DISPOSED) { + throw new IllegalStateException("Breakpoint already disposed"); + } + setState(State.DISPOSED); + breakpoints.remove(this); + } + } + + /** + * The intention to create a line breakpoint. + */ + private final class PendingTagBreakpoint extends TagBreakpoint { + + private Source conditionSource; + + PendingTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) { + super(Breakpoint.State.ENABLED_UNRESOLVED, tag, ignoreCount, oneShot); + } + + @Override + public void setEnabled(boolean enabled) { + switch (getState()) { + case ENABLED_UNRESOLVED: + if (!enabled) { + setState(State.DISABLED_UNRESOLVED); + } + break; + case DISABLED_UNRESOLVED: + if (enabled) { + setState(State.ENABLED_UNRESOLVED); + } + break; + case DISPOSED: + throw new IllegalStateException("Disposed breakpoints must stay disposed"); + default: + throw new IllegalStateException("Unexpected breakpoint state"); + } + } + + @Override + public boolean isEnabled() { + return getState() == State.ENABLED_UNRESOLVED; + } + + @Override + public void setCondition(String expr) throws IOException { + this.conditionSource = Source.fromText(expr, "breakpoint condition from text: " + expr); + } + + @Override + public Source getCondition() { + return conditionSource; + } + + @Override + public void dispose() { + if (getState() == State.DISPOSED) { + throw new IllegalStateException("Breakpoint already disposed"); + } + setState(State.DISPOSED); + breakpoints.remove(this); + } + } +}
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java Fri Dec 04 14:32:24 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.tools.debug.shell.server; - -import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.instrument.Visualizer; -import com.oracle.truffle.api.instrument.impl.DefaultVisualizer; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.api.vm.PolyglotEngine.Language; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class REPLServerContext { - - private final int level; - private final SuspendedEvent event; - - protected REPLServerContext(int level, SuspendedEvent event) { - this.level = level; - this.event = event; - } - - /** - * The nesting depth of this context in the current session. - */ - public int getLevel() { - return level; - } - - /** - * The AST node where execution is halted in this context. - */ - public Node getNodeAtHalt() { - return event.getNode(); - } - - /** - * Evaluates given code snippet in the context of currently suspended execution. - * - * @param code the snippet to evaluate - * @param frame <code>null</code> in case the evaluation should happen in top most frame, - * non-null value - * @return result of the evaluation - * @throws IOException if something goes wrong - */ - public Object eval(String code, FrameInstance frame) throws IOException { - return event.eval(code, frame); - } - - /** - * The frame where execution is halted in this context. - */ - public MaterializedFrame getFrameAtHalt() { - return event.getFrame(); - } - - public abstract Language getLanguage(); - - public Visualizer getVisualizer() { - return new DefaultVisualizer(); - } - - public PolyglotEngine engine() { - throw new IllegalStateException(); - } - - protected abstract Debugger db(); - - /** - * Dispatches a REPL request to the appropriate handler. - */ - public abstract REPLMessage[] receive(REPLMessage request); - - public abstract void registerBreakpoint(Breakpoint breakpoint); - - public abstract Breakpoint findBreakpoint(int id); - - public abstract int getBreakpointID(Breakpoint breakpoint); - - /** - * Provides access to the execution stack. - * - * @return immutable list of stack elements - */ - public List<FrameDebugDescription> getStack() { - List<FrameDebugDescription> frames = new ArrayList<>(); - int frameCount = 1; - for (FrameInstance frameInstance : event.getStack()) { - if (frameCount == 1) { - frames.add(new FrameDebugDescription(frameCount, event.getNode(), frameInstance)); - } else { - frames.add(new FrameDebugDescription(frameCount, frameInstance.getCallNode(), frameInstance)); - } - frameCount++; - } - return Collections.unmodifiableList(frames); - } - - void prepareStepOut() { - event.prepareStepOut(); - } - - void prepareStepInto(int repeat) { - event.prepareStepInto(repeat); - } - - void prepareStepOver(int repeat) { - event.prepareStepOver(repeat); - } - - void prepareContinue() { - event.prepareContinue(); - } -}