diff --git a/mx.truffle/suite.py b/mx.truffle/suite.py index 118d5e1b4f9f..b824086706e8 100644 --- a/mx.truffle/suite.py +++ b/mx.truffle/suite.py @@ -51,7 +51,7 @@ "sourceDirs" : ["src"], "dependencies" : [ "com.oracle.truffle.api.interop.java", - "com.oracle.truffle.api.debug", + "com.oracle.truffle.api.instrumentation", ], "javaCompliance" : "1.7", "workingSets" : "API,Truffle", @@ -63,6 +63,7 @@ "dependencies" : [ "com.oracle.truffle.api.profiles", "com.oracle.truffle.api.interop", + "com.oracle.truffle.api.debug", "com.oracle.truffle.api.vm", "mx:JUNIT", ], @@ -146,7 +147,10 @@ "com.oracle.truffle.api.debug" : { "subDir" : "truffle", "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.truffle.api.instrumentation"], + "dependencies" : [ + "com.oracle.truffle.api.instrumentation", + "com.oracle.truffle.api.vm" + ], "checkstyle" : "com.oracle.truffle.api", "annotationProcessors" : ["INTERNAL_PROCESSOR"], "javaCompliance" : "1.7", @@ -268,9 +272,12 @@ "com.oracle.truffle.tools.debug.shell" : { "subDir" : "truffle", "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.truffle.tools", - "com.oracle.truffle.api.vm", - "JLINE"], + "dependencies" : [ + "com.oracle.truffle.tools", + "com.oracle.truffle.api.debug", + "com.oracle.truffle.api.vm", + "JLINE" + ], "checkstyle" : "com.oracle.truffle.api", "javaCompliance" : "1.7", "workingSets" : "Truffle,Tools", @@ -331,6 +338,7 @@ "com.oracle.truffle.api.interop.java", "com.oracle.truffle.api.dsl", "com.oracle.truffle.api.profiles", + "com.oracle.truffle.api.debug", "com.oracle.truffle.api.vm", "com.oracle.truffle.object.basic", ], diff --git a/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Debugger.java b/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Debugger.java index e0a9b2b49a73..25d49d534d92 100644 --- a/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Debugger.java +++ b/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Debugger.java @@ -38,6 +38,7 @@ import com.oracle.truffle.api.QuitException; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.debug.impl.DebuggerInstrument; import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.frame.FrameInstanceVisitor; @@ -50,12 +51,11 @@ import com.oracle.truffle.api.instrumentation.EventListener; import com.oracle.truffle.api.instrumentation.Instrumenter; import com.oracle.truffle.api.instrumentation.SourceSectionFilter; -import com.oracle.truffle.api.instrumentation.TruffleInstrument; -import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration; 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.PolyglotEngine; /** * Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}. @@ -63,11 +63,7 @@ * {@link ExecutionEvent#getDebugger()} events, once {@link com.oracle.truffle.api.debug debugging * is turned on}. */ -@Registration(id = Debugger.ID, autostart = true) -public final class Debugger extends TruffleInstrument { - - public static final String ID = "debugger"; - +public final class Debugger { public static final String BLOCK_TAG = "debug-BLOCK"; public static final String CALL_TAG = "debug-CALL"; @@ -83,17 +79,53 @@ public final class Debugger extends TruffleInstrument { private static final SourceSectionFilter CALL_FILTER = SourceSectionFilter.newBuilder().tagIs(CALL_TAG).build(); private static final SourceSectionFilter HALT_FILTER = SourceSectionFilter.newBuilder().tagIs(HALT_TAG).build(); + /** Finds debugger associated with given engine. There is at most one + * debugger associated with any {@link PolyglotEngine}. One can access it + * by calling this static method. Once the debugger is initialized, events + * like {@link SuspendedEvent} or {@link ExecutionEvent} are delivered to + * {@link com.oracle.truffle.api.vm.PolyglotEngine.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) registered event handlers} + * whenever an important event (related to debugging) occurs in the + * engine. + * + * + * @param engine the engine to find debugger for + * @return an instance of associated debugger, never null + */ + public static Debugger find(PolyglotEngine engine) { + return find(engine, true); + } + + private static final DebuggerInstrument.Factory FACTORY = new DebuggerInstrument.Factory() { + @Override + public Debugger create(PolyglotEngine engine, Instrumenter instrumenter) { + return new Debugger(engine, instrumenter); + } + }; + + private static Debugger find(PolyglotEngine engine, boolean create) { + PolyglotEngine.Instrument instrument = engine.getInstruments().get(DebuggerInstrument.ID); + if (instrument == null) { + throw new IllegalStateException(); + } + final DebuggerInstrument debugInstrument = instrument.lookup(DebuggerInstrument.class); + return debugInstrument.getDebugger(engine, create ? FACTORY : null); + } + private static void trace(String format, Object... args) { if (TRACE) { OUT.println(TRACE_PREFIX + String.format(format, args)); } } - private Instrumenter instrumenter; - - private Object vm = null; // TODO + private final PolyglotEngine engine; + private final Instrumenter instrumenter; private Source lastSource; + Debugger(PolyglotEngine engine, Instrumenter instrumenter) { + this.engine = engine; + this.instrumenter = instrumenter; + } + interface BreakpointCallback { /** @@ -141,32 +173,6 @@ public void addWarning(String warning) { */ private DebugExecutionContext debugContext; - @Override - protected void onCreate(Env env, Instrumenter originalInstrumenter) { - - if (TRACE) { - trace("BEGIN onCreate()"); - } - - this.instrumenter = originalInstrumenter; - Source.setFileCaching(true); - - // Initialize execution context stack - debugContext = new DebugExecutionContext(null, null, 0); - debugContext.setStrategy(0, new Continue()); - debugContext.contextTrace("START EXEC DEFAULT"); - - this.lineBreaks = new LineBreakpointFactory(this, instrumenter, breakpointCallback, warningLog); - this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog); - - newestDebugger = this; // TODO (mlvdv) hack - - if (TRACE) { - trace("END onCreate()"); - } - - } - /** * Sets a breakpoint to halt at a source line. * @@ -885,7 +891,7 @@ public FrameInstance visitFrame(FrameInstance frameInstance) { try { // Pass control to the debug client with current execution suspended - ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, haltedNode, haltedFrame, contextStack, recentWarnings)); + ACCESSOR.dispatchEvent(engine, new SuspendedEvent(Debugger.this, haltedNode, haltedFrame, contextStack, recentWarnings)); // Debug client finished normally, execution resumes // Presume that the client has set a new strategy (or default to Continue) running = true; @@ -944,9 +950,6 @@ public Void visitFrame(FrameInstance frameInstance) { } void executionStarted(Object theVM, int depth, Source source) { - if (this.vm == null) { - this.vm = theVM; - } Source execSource = source; if (execSource == null) { execSource = lastSource; @@ -957,7 +960,7 @@ void executionStarted(Object theVM, int depth, Source source) { debugContext = new DebugExecutionContext(execSource, debugContext, depth); prepareContinue(depth); debugContext.contextTrace("START EXEC "); - ACCESSOR.dispatchEvent(vm, new ExecutionEvent(this)); + ACCESSOR.dispatchEvent(engine, new ExecutionEvent(this)); } void executionEnded() { @@ -980,9 +983,9 @@ void executionEnded() { */ Object evalInContext(SuspendedEvent ev, String code, FrameInstance frameInstance) throws IOException { if (frameInstance == null) { - return ACCESSOR.evalInContext(vm, ev, code, debugContext.haltedNode, debugContext.haltedFrame); + return ACCESSOR.evalInContext(engine, ev, code, debugContext.haltedNode, debugContext.haltedFrame); } else { - return ACCESSOR.evalInContext(vm, ev, code, frameInstance.getCallNode(), frameInstance.getFrame(FrameAccess.MATERIALIZE, true).materialize()); + return ACCESSOR.evalInContext(engine, ev, code, frameInstance.getCallNode(), frameInstance.getFrame(FrameAccess.MATERIALIZE, true).materialize()); } } @@ -990,14 +993,14 @@ static final class AccessorDebug extends Accessor { @Override protected Closeable executionStart(Object vm, int currentDepth, final Object d, Source s) { - if (d == null) { + final Debugger debugger = find((PolyglotEngine)vm, false); + if (debugger == null) { return new Closeable() { @Override public void close() throws IOException { } }; } - final Debugger debugger = (Debugger) d; debugger.executionStarted(vm, currentDepth, s); return new Closeable() { @Override @@ -1007,11 +1010,6 @@ public void close() throws IOException { }; } - @Override - protected Object getDebugger(Object vm) { - return newestDebugger; - } - @SuppressWarnings("rawtypes") @Override protected Class findLanguage(Node node) { @@ -1037,6 +1035,4 @@ protected CallTarget parse(Class languageClass, Sourc // registers into Accessor.DEBUG static final AccessorDebug ACCESSOR = new AccessorDebug(); - - static Debugger newestDebugger = null; } diff --git a/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/impl/DebuggerInstrument.java b/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/impl/DebuggerInstrument.java new file mode 100644 index 000000000000..0e17ade9c4f5 --- /dev/null +++ b/truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/impl/DebuggerInstrument.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, 2016, 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.impl; + +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.instrumentation.Instrumenter; +import com.oracle.truffle.api.instrumentation.TruffleInstrument; +import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; + +@Registration(id = DebuggerInstrument.ID, autostart = true) +public final class DebuggerInstrument extends TruffleInstrument { + public static final String ID = "debugger"; + + private Debugger debugger; + private Instrumenter instrumenter; + + @Override + protected void onCreate(Env env, Instrumenter originalInstrumenter) { + this.instrumenter = originalInstrumenter; + Source.setFileCaching(true); + } + + public Debugger getDebugger(PolyglotEngine engine, Factory factory) { + if (debugger == null && factory != null) { + debugger = factory.create(engine, instrumenter); + } + debugger.getClass(); + return debugger; + } + + public static interface Factory { + Debugger create(PolyglotEngine engine, Instrumenter instrumenter); + } +} diff --git a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/InstrumentationTest.java b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/InstrumentationTest.java index bba25009f540..511d1529ee9d 100644 --- a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/InstrumentationTest.java +++ b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/InstrumentationTest.java @@ -202,15 +202,14 @@ private static class MyLanguageException extends RuntimeException { } @TruffleLanguage.Registration(name = "", version = "", mimeType = "testLanguageInstrumentation") - public static class TestLanguageInstrumentationLanguage extends TruffleLanguage implements InstrumentationLanguage { + public static class TestLanguageInstrumentationLanguage extends TruffleLanguage { public static final TestLanguageInstrumentationLanguage INSTANCE = new TestLanguageInstrumentationLanguage(); static int installInstrumentationsCounter = 0; static int createContextCounter = 0; - @Override - public void installInstrumentations(Void env, Instrumenter instrumenter) { + private static void installInstrumentations(Instrumenter instrumenter) { installInstrumentationsCounter++; instrumenter.attachListener(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(), new EventListener() { public void onReturnValue(EventContext context, VirtualFrame frame, Object result) { @@ -244,6 +243,9 @@ public void onEnter(EventContext context, VirtualFrame frame) { @Override protected Void createContext(com.oracle.truffle.api.TruffleLanguage.Env env) { createContextCounter++; + Instrumenter instrumenter = env.lookup(Instrumenter.class); + Assert.assertNotNull("Instrumenter found", instrumenter); + installInstrumentations(instrumenter); return null; } diff --git a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerController.java b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerController.java new file mode 100644 index 000000000000..e18565633a7b --- /dev/null +++ b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerController.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 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.instrumentation.examples; + +import com.oracle.truffle.api.instrumentation.EventContext; +import com.oracle.truffle.api.vm.PolyglotEngine; + +/** + * Publicly exposed API of a debugger. Provides operations that its clients can use to control the + * execution. Put this class into an API package of your instrument. The clients shall use + * {@link PolyglotEngine.Instrument#lookup(java.lang.Class)} with + * DebuggerController.class parameter to obtain the interface. + */ +public interface DebuggerController { + public static final String ID = "example-debugger"; + + public void installBreakpoint(int i, Callback callback); + + public void stepInto(Callback callback); + + public void stepOut(Callback callback); + + public void stepOver(Callback callback); + + public interface Callback { + + void halted(DebuggerController debugger, EventContext haltedAt); + + } + +} diff --git a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExample.java b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExample.java index b05a2a2e65ba..7e533684afda 100644 --- a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExample.java +++ b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExample.java @@ -24,9 +24,6 @@ */ package com.oracle.truffle.api.instrumentation.examples; -import java.util.ArrayList; -import java.util.List; - import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; @@ -43,41 +40,26 @@ import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration; /** - * This is an example how debugging can be implemented using the instrumentation framework. Debugger - * front-ends can be used by installing a {@link DebuggerFrontEnd} implementation with - * {@link #installFrontEnd(Class)}. + * This is an example how debugging can be implemented using the instrumentation framework. This + * class itself shall be hidden in an implementation package. The actual API that + * {@link DebuggerExampleTest clients} can use to talk to the debugger is exposed in a separate + * {@link DebuggerController} interface. */ -@Registration(id = DebuggerExample.ID) -public final class DebuggerExample extends TruffleInstrument { - - public static final String ID = "example-debugger"; - +@Registration(id = DebuggerController.ID) +public final class DebuggerExample extends TruffleInstrument implements DebuggerController { private Instrumenter instrumenter; private EventBinding stepping; - private final List frontEnds = new ArrayList<>(); private Callback currentStatementCallback; @Override protected void onCreate(Env env, Instrumenter originalInstrumenter) { assert this.instrumenter == null; this.instrumenter = originalInstrumenter; - for (Class frontEnd : installedFrontEnds) { - try { - DebuggerFrontEnd frontEndInstance = frontEnd.newInstance(); - frontEndInstance.onAttach(this); - frontEnds.add(frontEndInstance); - } catch (Exception e) { - throw new RuntimeException(e); - } - } } @Override protected void onDispose(Env env) { - for (DebuggerFrontEnd debugger : frontEnds) { - debugger.onDispose(); - } } public boolean isStepping() { @@ -194,7 +176,7 @@ private abstract class StepCallBack implements Callback { this.delegate = delegate; } - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { if (shouldHalt()) { currentStatementCallback = null; delegate.halted(debugger, haltedAt); @@ -251,26 +233,4 @@ protected boolean shouldHalt() { } - public interface Callback { - - void halted(DebuggerExample debugger, EventContext haltedAt); - - } - - // in a production debugger this should be implemented using a proper service provider interface - - private static final List> installedFrontEnds = new ArrayList<>(); - - public static void installFrontEnd(Class frontEndClass) { - installedFrontEnds.add(frontEndClass); - } - - public interface DebuggerFrontEnd { - - void onAttach(DebuggerExample example); - - void onDispose(); - - } - } diff --git a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExampleTest.java b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExampleTest.java index 0df136c8571b..53afa81ef2ba 100644 --- a/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExampleTest.java +++ b/truffle/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/examples/DebuggerExampleTest.java @@ -29,38 +29,25 @@ import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import com.oracle.truffle.api.instrumentation.AbstractInstrumentationTest; import com.oracle.truffle.api.instrumentation.EventContext; import com.oracle.truffle.api.instrumentation.InstrumentationTestLanguage; -import com.oracle.truffle.api.instrumentation.examples.DebuggerExample.Callback; -import com.oracle.truffle.api.instrumentation.examples.DebuggerExample.DebuggerFrontEnd; +import com.oracle.truffle.api.instrumentation.examples.DebuggerController.Callback; import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine.Instrument; +import static org.junit.Assert.assertNotNull; public final class DebuggerExampleTest extends AbstractInstrumentationTest { - - private static DebuggerExample debugger; - - public static class TestFrontEnd implements DebuggerFrontEnd { - public void onAttach(DebuggerExample example) { - DebuggerExampleTest.debugger = example; - } - - public void onDispose() { - DebuggerExampleTest.debugger = null; - } - } - - @BeforeClass - public static void installFrontEnd() { - DebuggerExample.installFrontEnd(TestFrontEnd.class); - } + private DebuggerController debugger; @Before public void setupDebugger() throws IOException { - engine.getInstruments().get(DebuggerExample.ID).setEnabled(true); + Instrument instrument = engine.getInstruments().get(DebuggerController.ID); + instrument.setEnabled(true); + debugger = instrument.lookup(DebuggerController.class); + assertNotNull("Debugger interface found", debugger); assertEvalOut("", ""); // ensure debugger gets loaded } @@ -68,7 +55,8 @@ public void setupDebugger() throws IOException { public void testBreakpoint() throws IOException { final AtomicBoolean breakpointHit = new AtomicBoolean(); debugger.installBreakpoint(1, new Callback() { - public void halted(DebuggerExample d, EventContext haltedAt) { + @Override + public void halted(DebuggerController d, EventContext haltedAt) { Assert.assertTrue(containsTag(haltedAt.getInstrumentedSourceSection().getTags(), InstrumentationTestLanguage.STATEMENT)); Assert.assertEquals(1, haltedAt.getInstrumentedSourceSection().getStartLine()); breakpointHit.set(true); @@ -91,22 +79,22 @@ public void testStepInto() throws IOException { final AtomicBoolean allStepped = new AtomicBoolean(); debugger.installBreakpoint(7, new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 7); debugger.stepInto(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 4); debugger.stepInto(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 5); debugger.stepInto(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 2); debugger.stepInto(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 6); debugger.stepInto(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { throw new AssertionError(); } }); @@ -144,17 +132,17 @@ public void testStepOver() throws IOException { final AtomicBoolean allStepped = new AtomicBoolean(); debugger.installBreakpoint(4, new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 4); debugger.stepOver(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 5); debugger.stepOver(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 6); allStepped.set(true); debugger.stepOver(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + public void halted(DebuggerController debugger, EventContext haltedAt) { throw new AssertionError(); } }); @@ -181,13 +169,16 @@ public void testStepOut() throws IOException { final AtomicBoolean allStepped = new AtomicBoolean(); debugger.installBreakpoint(2, new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + @Override + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 2); debugger.stepOut(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + @Override + public void halted(DebuggerController debugger, EventContext haltedAt) { assertLineAt(haltedAt, 6); debugger.stepOver(new Callback() { - public void halted(DebuggerExample debugger, EventContext haltedAt) { + @Override + public void halted(DebuggerController debugger, EventContext haltedAt) { throw new AssertionError(); } }); diff --git a/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationHandler.java b/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationHandler.java index bf316a0c8f9b..1e4b67cd4dba 100644 --- a/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationHandler.java +++ b/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationHandler.java @@ -49,6 +49,7 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; +import java.util.Set; /** * Central coordinator class for the Truffle instrumentation framework. Allocated once per engine. @@ -124,8 +125,8 @@ void disposeInstrumentation(Object key, boolean cleanupRequired) { } } - void attachLanguage(Object context, InstrumentationLanguage language) { - addInstrumenter(context, new LanguageInstrumenter<>(language, context)); + Instrumenter forLanguage(TruffleLanguage.Env context, TruffleLanguage language) { + return new LanguageInstrumenter<>(language, context); } void detachLanguage(Object context) { @@ -413,6 +414,11 @@ static Instrumentable getInstrumentable(Node node) { return null; } + private T lookup(Object key, Class type) { + AbstractInstrumenter value = instrumentations.get(key); + return value == null ? null : value.lookup(this, type); + } + private abstract class AbstractNodeVisitor implements NodeVisitor { abstract boolean shouldVisit(RootNode root); @@ -611,19 +617,29 @@ void dispose() { } } + @Override + T lookup(InstrumentationHandler handler, Class type) { + if (type.isAssignableFrom(getInstrumentationClass())) { + if (instrumentation == null) { + handler.initialize(); + } + return type.cast(instrumentation); + } + return null; + } + } /** * Instrumenter implementation for use in {@link TruffleLanguage}. */ final class LanguageInstrumenter extends AbstractInstrumenter { + @SuppressWarnings("unused") private final TruffleLanguage.Env env; + private final TruffleLanguage language; - private final T context; - private final InstrumentationLanguage language; - - LanguageInstrumenter(InstrumentationLanguage language, T context) { + LanguageInstrumenter(TruffleLanguage language, TruffleLanguage.Env env) { this.language = language; - this.context = context; + this.env = env; } @Override @@ -637,13 +653,18 @@ boolean isInstrumentable(Node node) { @Override void initialize() { - language.installInstrumentations(context, this); + // language.installInstrumentations(env, this); } @Override void dispose() { // nothing todo } + + @Override + S lookup(InstrumentationHandler handler, Class type) { + return null; + } } /** @@ -657,6 +678,8 @@ abstract class AbstractInstrumenter extends Instrumenter { abstract void dispose(); + abstract T lookup(InstrumentationHandler handler, Class type); + void disposeBinding(EventBinding binding) { InstrumentationHandler.this.disposeBinding(binding); } @@ -712,12 +735,16 @@ protected boolean isInstrumentable(RootNode rootNode) { } @Override - @SuppressWarnings("unchecked") - protected void attachToInstrumentation(Object vm, TruffleLanguage impl, com.oracle.truffle.api.TruffleLanguage.Env env) { - if (impl instanceof InstrumentationLanguage) { - InstrumentationHandler instrumentationHandler = (InstrumentationHandler) ACCESSOR.getInstrumentationHandler(vm); - instrumentationHandler.attachLanguage(findContext(env), (InstrumentationLanguage) impl); - } + protected void collectEnvServices(Set collectTo, Object vm, TruffleLanguage impl, TruffleLanguage.Env env) { + InstrumentationHandler instrumentationHandler = (InstrumentationHandler) ACCESSOR.getInstrumentationHandler(vm); + Instrumenter instrumenter = instrumentationHandler.forLanguage(env, impl); + collectTo.add(instrumenter); + } + + @Override + protected T getInstrumentationHandlerService(Object vm, Object key, Class type) { + InstrumentationHandler instrumentationHandler = (InstrumentationHandler) vm; + return instrumentationHandler.lookup(key, type); } @Override diff --git a/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationLanguage.java b/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationLanguage.java deleted file mode 100644 index 77b94db044fb..000000000000 --- a/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/InstrumentationLanguage.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016, 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.instrumentation; - -import com.oracle.truffle.api.TruffleLanguage; - -/** - *

- * {@link TruffleLanguage} implementations can decide to implement this additional interface to - * register {@link EventBinding bindings} specifically for this language. This can be useful to - * implement core language features using the instrumentation API. Instrumentations created by an - * {@link InstrumentationLanguage} have elevated rights in the system. Exceptions thrown by - * {@link EventBinding bindings} that were created using the {@link Instrumenter} passed in - * {@link #installInstrumentations(Object, Instrumenter)} are directly passed on to the guest - * language AST. - *

- * - * Bindings created by the guest language are also automatically disposed together with the - * language. - */ -public interface InstrumentationLanguage { - - /** - * Invoked for each allocated context on guest language startup. Bindings attached to the - * instrumenter apply only for the given context and guest language. - * - * @param context the context of the language that - * @param instrumenter - * - * @see Instrumenter - */ - void installInstrumentations(C context, Instrumenter instrumenter); - -} diff --git a/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/package-info.java b/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/package-info.java index 600a9267cd13..0232f32216f2 100644 --- a/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/package-info.java +++ b/truffle/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/package-info.java @@ -35,9 +35,10 @@ * {@link com.oracle.truffle.api.instrumentation.TruffleInstrument} interface. Please refer to * {@link com.oracle.truffle.api.instrumentation.TruffleInstrument} for further details. * - * Guest languages that want to use the capabilities of the instrumentation framework can implement - * {@link com.oracle.truffle.api.instrumentation.InstrumentationLanguage} for their - * {@link com.oracle.truffle.api.TruffleLanguage language} subclass. + * Guest languages that want to use the capabilities of the instrumentation framework can access + * {@link com.oracle.truffle.api.instrumentation.Instrumenter} for their + * {@link com.oracle.truffle.api.TruffleLanguage language} by calling + * {@link com.oracle.truffle.api.TruffleLanguage.Env#lookup lookup(Instrumenter.class)}. * {@link com.oracle.truffle.api.instrumentation.SourceSectionFilter Filters} created using guest * languages may be used to implement guest language features that require meta-programming * capabilities. @@ -45,7 +46,6 @@ * Instrumentations can get enabled/disabled using PolyglotEngine. * * @see com.oracle.truffle.api.instrumentation.TruffleInstrument - * @see com.oracle.truffle.api.instrumentation.InstrumentationLanguage * @see com.oracle.truffle.api.instrumentation.Instrumentable */ package com.oracle.truffle.api.instrumentation; diff --git a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EventConsumer.java b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EventConsumer.java index da1f2fb8a89e..c3f79d267a73 100644 --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EventConsumer.java +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EventConsumer.java @@ -25,14 +25,12 @@ package com.oracle.truffle.api.vm; import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.debug.ExecutionEvent; -import com.oracle.truffle.api.debug.SuspendedEvent; /** * {@link PolyglotEngine} generates various events and delivers them to * {@link PolyglotEngine.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) registered} * handlers. Each handler is registered for a particular type of event. Examples of events include - * {@link ExecutionEvent} or {@link SuspendedEvent} useful when debugging {@link TruffleLanguage + * {@link com.oracle.truffle.api.debug.ExecutionEvent} or {@link com.oracle.truffle.api.debug.SuspendedEvent} useful when debugging {@link TruffleLanguage * Truffle language}s. * * @param type of event to observe and handle diff --git a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java index 2b5a5d3b40d0..f9622bf6c1f6 100644 --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java @@ -50,9 +50,6 @@ import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.TruffleLanguage.Registration; import com.oracle.truffle.api.TruffleOptions; -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.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.Accessor; @@ -114,7 +111,6 @@ public class PolyglotEngine { private final Map globals; private final Object instrumentationHandler; private final Map instruments; - private Object debugger = null; private boolean disposed; static { @@ -140,7 +136,6 @@ public class PolyglotEngine { this.globals = null; this.executor = null; this.instrumentationHandler = null; - this.debugger = null; this.instruments = null; } @@ -167,19 +162,6 @@ public class PolyglotEngine { } this.langs = map; this.instruments = createAndAutostartDescriptors(InstrumentCache.load(getClass().getClassLoader())); - try { - Class.forName("com.oracle.truffle.api.debug.Debugger", true, Debugger.class.getClassLoader()); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - private Object getDebugger() { - if (debugger == null) { - debugger = SPI.getDebugger(this); - } - return debugger; } private Map createAndAutostartDescriptors(List instrumentCaches) { @@ -482,7 +464,7 @@ Language createLanguage(Map.Entry en) { @SuppressWarnings("try") private Object evalImpl(TruffleLanguage[] fillLang, Source s, Language l) throws IOException { - try (Closeable d = SPI.executionStart(this, -1, getDebugger(), s)) { + try (Closeable d = SPI.executionStart(this, -1, null, s)) { TruffleLanguage langImpl = l.getImpl(true); fillLang[0] = langImpl; return SPI.eval(langImpl, s, l.cache); @@ -493,7 +475,7 @@ private Object evalImpl(TruffleLanguage[] fillLang, Source s, Language l) thr final Object invokeForeign(final Node foreignNode, VirtualFrame frame, final TruffleObject receiver) throws IOException { Object res; if (executor == null) { - try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, getDebugger(), null)) { + try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, null, null)) { final Object[] args = ForeignAccess.getArguments(frame).toArray(); res = ForeignAccess.execute(foreignNode, frame, receiver, args); } @@ -514,7 +496,7 @@ private Object invokeForeignOnExecutor(final Node foreignNode, VirtualFrame fram @SuppressWarnings("try") @Override protected Object compute() throws IOException { - try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, getDebugger(), null)) { + try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, null, null)) { final Object[] args = ForeignAccess.getArguments(materialized).toArray(); RootNode node = SymbolInvokerImpl.createTemporaryRoot(TruffleLanguage.class, foreignNode, receiver, args.length); final CallTarget target = Truffle.getRuntime().createCallTarget(node); @@ -601,21 +583,21 @@ private void checkThread() { @SuppressWarnings("unchecked") void dispatch(Object ev) { Class type = ev.getClass(); - if (type == SuspendedEvent.class) { - dispatchSuspendedEvent((SuspendedEvent) ev); + if (type.getSimpleName().equals("SuspendedEvent")) { + dispatchSuspendedEvent(ev); } - if (type == ExecutionEvent.class) { - dispatchExecutionEvent((ExecutionEvent) ev); + if (type.getSimpleName().equals("ExecutionEvent")) { + dispatchExecutionEvent(ev); } dispatch(type, ev); } @SuppressWarnings("unused") - void dispatchSuspendedEvent(SuspendedEvent event) { + void dispatchSuspendedEvent(Object event) { } @SuppressWarnings("unused") - void dispatchExecutionEvent(ExecutionEvent event) { + void dispatchExecutionEvent(Object event) { } @SuppressWarnings("unchecked") @@ -757,7 +739,7 @@ public Value execute(final Object... args) throws IOException { @SuppressWarnings("try") @Override protected Object compute() throws IOException { - try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) { + try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, null, null)) { List arr = new ArrayList<>(); arr.addAll(Arrays.asList(args)); for (;;) { @@ -818,6 +800,10 @@ public boolean isEnabled() { return enabled; } + public T lookup(Class type) { + return SPI.getInstrumentationHandlerService(instrumentationHandler, this, type); + } + public void setEnabled(final boolean enabled) { checkThread(); if (this.enabled != enabled) { @@ -852,7 +838,6 @@ void setEnabledImpl(final boolean enabled, boolean cleanup) { public String toString() { return "Instrument [id=" + getId() + ", name=" + getName() + ", version=" + getVersion() + ", enabled=" + enabled + "]"; } - } /** @@ -928,7 +913,7 @@ public Value eval(Source source) throws IOException { @SuppressWarnings("try") public Value getGlobalObject() { checkThread(); - try (Closeable d = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) { + try (Closeable d = SPI.executionStart(PolyglotEngine.this, -1, null, null)) { Object res = SPI.languageGlobal(getEnv(true)); return res == null ? null : new Value(new TruffleLanguage[]{info.getImpl(true)}, res); } catch (IOException ex) { @@ -946,7 +931,6 @@ TruffleLanguage.Env getEnv(boolean create) { if (env == null && create) { TruffleLanguage impl = info.getImpl(true); env = SPI.attachEnv(PolyglotEngine.this, impl, out, err, in); - SPI.attachToInstrumentation(PolyglotEngine.this, impl, env); } return env; } @@ -1055,11 +1039,6 @@ protected Object createInstrumentationHandler(Object vm, OutputStream out, Outpu return super.createInstrumentationHandler(vm, out, err, in); } - @Override - protected Object getDebugger(Object vm) { - return super.getDebugger(vm); - } - @Override protected Object getInstrumentationHandler(Object obj) { final PolyglotEngine vm = (PolyglotEngine) obj; @@ -1067,8 +1046,8 @@ protected Object getInstrumentationHandler(Object obj) { } @Override - protected void attachToInstrumentation(Object vm, TruffleLanguage impl, Env env) { - super.attachToInstrumentation(vm, impl, env); + protected T getInstrumentationHandlerService(Object vm, Object key, Class type) { + return super.getInstrumentationHandlerService(vm, key, type); } @Override diff --git a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index cfae7e692599..b381ebe97edb 100644 --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -45,6 +45,8 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; +import java.util.LinkedHashSet; +import java.util.Set; /** * An entry point for everyone who wants to implement a Truffle based language. By providing an @@ -356,6 +358,7 @@ public static final class Env { private final InputStream in; private final OutputStream err; private final OutputStream out; + private final Object[] services; Env(Object vm, TruffleLanguage lang, OutputStream out, OutputStream err, InputStream in) { this.vm = vm; @@ -363,6 +366,9 @@ public static final class Env { this.err = err; this.out = out; this.lang = lang; + LinkedHashSet collectedServices = new LinkedHashSet<>(); + API.collectEnvServices(collectedServices, vm, lang, this); + this.services = collectedServices.toArray(); this.langCtx = new LangCtx<>(lang, this); } @@ -433,6 +439,28 @@ public OutputStream err() { public Instrumenter instrumenter() { return null; } + + /** + * Looks additional service up. An environment for a particular {@link TruffleLanguage + * language} and a {@link com.oracle.truffle.api.vm.PolyglotEngine} may also be associated + * with additional services. One can request implementations of such services by calling + * this method with the type identifying the requested service and its API. + * + * Services that can be obtained via this method include + * {@link com.oracle.truffle.api.instrumentation.Instrumenter} and others. + * + * @param type of requested service + * @param type class of requested service + * @return instance of T or null if there is no such service available + */ + public T lookup(Class type) { + for (Object obj : services) { + if (type.isInstance(obj)) { + return type.cast(obj); + } + } + return null; + } } private static final AccessAPI API = new AccessAPI(); @@ -445,6 +473,11 @@ protected Env attachEnv(Object vm, TruffleLanguage language, OutputStream std return env; } + @Override + protected void collectEnvServices(Set collectTo, Object vm, TruffleLanguage impl, Env context) { + super.collectEnvServices(collectTo, vm, impl, context); + } + @Override protected Object importSymbol(Object vm, TruffleLanguage queryingLang, String globalName) { return super.importSymbol(vm, queryingLang, globalName); diff --git a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 840c026c1fa3..3ef0738bda8d 100644 --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -45,6 +45,7 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; +import java.util.Set; /** * Communication between PolyglotEngine, TruffleLanguage API/SPI, and other services. @@ -109,6 +110,11 @@ protected Object evalInContext(Source source, Node node, MaterializedFrame mFram new Node(null) { }.getRootNode(); + try { + Class.forName("com.oracle.truffle.api.debug.Debugger", true, Accessor.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException(ex); + } } protected Accessor() { @@ -261,15 +267,15 @@ protected Object getInstrumentationHandler(Object known) { return SPI.getInstrumentationHandler(vm); } + protected T getInstrumentationHandlerService(Object handler, Object key, Class type) { + return INSTRUMENTHANDLER.getInstrumentationHandlerService(handler, key, type); + } + // new instrumentation protected Object createInstrumentationHandler(Object vm, OutputStream out, OutputStream err, InputStream in) { return INSTRUMENTHANDLER.createInstrumentationHandler(vm, out, err, in); } - protected Object getDebugger(Object vm) { - return DEBUG == null ? null : DEBUG.getDebugger(vm); - } - private static Reference previousVM = new WeakReference<>(null); private static Assumption oneVM = Truffle.getRuntime().createAssumption(); @@ -337,8 +343,8 @@ protected void initializeCallTarget(RootCallTarget target) { INSTRUMENTHANDLER.initializeCallTarget(target); } - protected void attachToInstrumentation(Object vm, TruffleLanguage impl, Env context) { - INSTRUMENTHANDLER.attachToInstrumentation(vm, impl, context); + protected void collectEnvServices(Set collectTo, Object vm, TruffleLanguage impl, Env context) { + INSTRUMENTHANDLER.collectEnvServices(collectTo, vm, impl, context); } protected void detachFromInstrumentation(Object vm, Env context) { diff --git a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java index afd3b625d39a..9b09a2bb28c3 100644 --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java @@ -120,6 +120,7 @@ public int compare(Language lang1, Language lang2) { public REPLServer(String defaultMIMEType, Visualizer visualizer) { this.visualizer = visualizer == null ? new DefaultVisualizer() : visualizer; this.engine = PolyglotEngine.newBuilder().onEvent(onHalted).onEvent(onExec).build(); + this.db = Debugger.find(this.engine); engineLanguages.addAll(engine.getLanguages().values()); if (engineLanguages.size() == 0) { throw new RuntimeException("No language implementations installed");