# HG changeset patch # User Michael Van De Vanter # Date 1387340565 28800 # Node ID 69d2e4baa2154bd5fb1dccdb89817aa11a58b43d # Parent dfb7800809232826917faae0e79a1142136eed7d Truffle: new infrastructure related to instrumentation, and in particular debugging: support for managing Source objects; framework for generalized "instrumentation proxy nodes" (to be inserted into ASTs with no runtime cost when inactive), and "probes" (which can be attached to proxy nodes to receive event notification); a rudimentary interface and abstract implementation for a "debug manager" (mostly a placeholder at this point); and the beginning of a language-agnostic ExecutionContext interface. diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/DebugManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/DebugManager.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013, 2013, 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; + +import com.oracle.truffle.api.source.*; + +/** + * Language-agnostic access to AST-based debugging support. + *

+ * WARNING: this interface is under development and will change substantially. + */ +public interface DebugManager { + + /** + * Sets a breakpoint at a line-based location. + */ + LineBreakpoint setBreakpoint(SourceLineLocation lineLocation); + + /** + * Sets a breakpoint at a line-based location with a boolean expression in the guest language to + * serve as a break condition. + */ + LineBreakpoint setConditionalBreakpoint(SourceLineLocation lineLocation, String condition); + + /** + * Gets a list of current breakpoints. + */ + LineBreakpoint[] getBreakpoints(); + + /** + * Removes a breakpoint at a line-based location. + */ + void removeBreakpoint(SourceLineLocation lineLocation); + + /** + * Description of a line-based breakpoint. + */ + interface LineBreakpoint { + + SourceLineLocation getSourceLineLocation(); + + String getDebugStatus(); + } + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 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; + +/** + * Information about the runtime context of a Truffle program. + */ +public interface ExecutionContext { + + /** + * Gets the name of the language, possibly with version number. in short enough form that it + * might be used for an interactive prompt. + */ + String getLanguageShortName(); + + /** + * Gets access to debugging services, {@code null} if not enabled in this context. + */ + DebugManager getDebugManager(); + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java Tue Dec 17 20:22:45 2013 -0800 @@ -24,8 +24,10 @@ */ package com.oracle.truffle.api; +import java.io.*; + /** - * Represents the source code of a guest language program. + * Represents a unit (typically a file) of guest language source code. */ public interface Source { @@ -38,9 +40,52 @@ String getName(); /** - * Returns the guest language source code represented by this source object. - * - * @return the source code as a String object + * The normalized, canonical name of the file. + */ + String getPath(); + + /** + * Access to the source contents. + */ + Reader getReader(); + + /** + * Access to the source contents. + */ + InputStream getInputStream(); + + /** + * Return the complete text of the code. */ String getCode(); + + /** + * Given a 1-based line number, return the text in the line, not including a possible + * terminating newline. + */ + String getCode(int lineNumber); + + /** + * The number of text lines in the source, including empty lines; characters at the end of the + * source without a terminating newline count as a line. + */ + int getLineCount(); + + /** + * Given a 0-based character offset, return the 1-based number of the line that includes the + * position. + */ + int getLineNumber(int offset); + + /** + * Given a 1-based line number, return the 0-based offset of the first character in the line. + */ + int getLineStartOffset(int lineNumber); + + /** + * The number of characters (not counting a possible terminating newline) in a (1-based) + * numbered line. + */ + int getLineLength(int lineNumber); + } diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java Tue Dec 17 20:22:45 2013 -0800 @@ -92,4 +92,44 @@ */ String getCode(); + /** + * Singleton instance with no content. + */ + SourceSection NULL = new SourceSection() { + + public Source getSource() { + return null; + } + + public int getStartLine() { + return 0; + } + + public int getStartColumn() { + return 0; + } + + public int getCharIndex() { + return 0; + } + + @Override + public int getCharLength() { + return 0; + } + + public int getCharEndIndex() { + return 0; + } + + public String getIdentifier() { + return null; + } + + public String getCode() { + return null; + } + + }; + } diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/EmptyProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/EmptyProbe.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013, 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.nodes.instrument; + +import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.DefaultProbeNode; + +/** + * An "probe" that does nothing, used for testing. It relies on the + * {@link InstrumentationProbeNode.DefaultProbeNode} implementation to override every event with an + * empty method. + */ +public class EmptyProbe extends DefaultProbeNode { + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationNode.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013, 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.nodes.instrument; + +/** + * Marker interface for all Truffle instrumentation nodes: nodes that are do not + * appear in Truffle ASTs as part of a language's execution semantics. + *

+ * In documentation related to instrumentation nodes, these are distinguished by referring to all + * other nodes (i.e. ones that do implement language semantics) as AST nodes. + */ +public interface InstrumentationNode { + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeEvents.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeEvents.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2013, 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.nodes.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Marker interface for all Truffle instrumentation nodes: nodes that are do not + * appear in Truffle ASTs as part of a language's execution semantics. + *

+ * In documentation related to instrumentation nodes, these are distinguished by referring to all + * other nodes (i.e. ones that do implement language semantics) as AST nodes. + */ +public interface InstrumentationProbeEvents { + + /** + * Notifies a probe that receiver that an AST node's execute method has just been entered. + * Callers should assure that a matching call to {@link #leave(Node, VirtualFrame, Object)} + * always follows. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame being passed to the execute method + */ + void enter(Node astNode, VirtualFrame frame); + + /** + * Notifies a probe that an AST Node's void-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + */ + void leave(Node astNode, VirtualFrame frame); + + /** + * Notifies a probe that an AST Node's boolean-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, boolean result); + + /** + * Notifies a probe that an AST Node's byte-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, byte result); + + /** + * Notifies a probe that an AST Node's short-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, short result); + + /** + * Notifies a probe that an AST Node's integer-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, int result); + + /** + * Notifies a probe that an AST Node's long-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, long result); + + /** + * Notifies a probe that an AST Node's float-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, float result); + + /** + * Notifies a probe that an AST Node's double-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, double result); + + /** + * Notifies a probe that an AST Node's char-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, char result); + + /** + * Notifies a probe that an AST Node's object-valued execute method is about to exit. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param result The result of the call to the execute method. + */ + void leave(Node astNode, VirtualFrame frame, Object result); + + /** + * Notifies a probe that an AST Node's execute method is about to leave under exceptional + * conditions, returning no value. + *

+ * Callers should assure (via {@code try/finally}) that a matching call to this method always + * follows a call to {@link #enter(Node, VirtualFrame)}. + * + * @param astNode The AST node on which the execute method is being called + * @param frame The frame that was passed to the execute method + * @param e the exception associated with the unusual return + */ + void leaveExceptional(Node astNode, VirtualFrame frame, Exception e); + + /** + * Notifies a probe that an AST node is about to be replaced with another. + * + * @param oldAstNode the AST node currently in the tree + * @param newAstNode the AST replacement node + * @param reason explanation for the replacement + */ + void replace(Node oldAstNode, Node newAstNode, String reason); + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2013, 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.nodes.instrument; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * A probe: a Truffle instrumentation node that holds code to perform some action + * when notified (via a {@linkplain InstrumentationProxyNode proxy node} in the AST) of a + * {@linkplain InstrumentationProbeEvents probe event} taking place at the AST node. + *

+ * Probes are only active when attached to a {@linkplain ProbeChain "probe chain"} that is referred + * to by one or more {@linkplain InstrumentationProxyNode proxy nodes} in an AST. + */ +public abstract class InstrumentationProbeNode extends Node implements InstrumentationNode, InstrumentationProbeEvents { + + /** + * Next in chain. + */ + @Child protected InstrumentationProbeNode next; + + protected InstrumentationProbeNode() { + } + + protected int countProbes() { + return next == null ? 0 : next.countProbes() + 1; + } + + /** + * Add a probe to the end of this probe chain. + */ + protected void internalAppendProbe(InstrumentationProbeNode newProbeNode) { + if (next == null) { + this.next = adoptChild(newProbeNode); + } else { + next.internalAppendProbe(newProbeNode); + } + } + + protected void internalRemoveProbe(InstrumentationProbeNode oldProbeNode) { + if (next == null) { + throw new RuntimeException("Couldn't find probe to remove: " + oldProbeNode); + } else if (next == oldProbeNode) { + if (oldProbeNode.next == null) { + this.next = null; + } else { + this.next = adoptChild(oldProbeNode.next); + } + } else { + next.internalRemoveProbe(oldProbeNode); + } + } + + /** + * Passes up the chain notification that a probe has changed its execution state in a way that + * invalidates fast path code. Assumes that there is an instance of {@link ProbeChain} at the + * head of the chain. + */ + @CompilerDirectives.SlowPath + protected void notifyProbeChanged(InstrumentationProbeNode probeNode) { + final InstrumentationProbeNode parent = (InstrumentationProbeNode) getParent(); + parent.notifyProbeChanged(probeNode); + } + + // TODO (mlvdv) making the internal*() methods public is a workaround for a bug/limitation in + // the Truffle compiler; they are intended to be private. + + public void internalEnter(Node astNode, VirtualFrame frame) { + enter(astNode, frame); + if (next != null) { + next.internalEnter(astNode, frame); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame) { + leave(astNode, frame); + if (next != null) { + next.internalLeave(astNode, frame); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, boolean result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, byte result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, short result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, int result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, long result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, char result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, float result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, double result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeave(Node astNode, VirtualFrame frame, Object result) { + leave(astNode, frame, result); + if (next != null) { + next.internalLeave(astNode, frame, result); + } + } + + public void internalLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + leaveExceptional(astNode, frame, null); + if (next != null) { + next.internalLeaveExceptional(astNode, frame, e); + } + } + + public void internalReplace(Node oldAstNode, Node newAstNode, String reason) { + replace(oldAstNode, newAstNode, reason); + if (next != null) { + next.internalReplace(oldAstNode, newAstNode, reason); + } + } + + /** + * A probe implementation that implements all of {@link InstrumentationProbeEvents} with empty + * methods; concrete subclasses can override only the methods for which something is to be done. + */ + public static class DefaultProbeNode extends InstrumentationProbeNode { + + public void enter(Node astNode, VirtualFrame frame) { + } + + public void leave(Node astNode, VirtualFrame frame) { + } + + public void leave(Node astNode, VirtualFrame frame, boolean result) { + } + + public void leave(Node astNode, VirtualFrame frame, byte result) { + } + + public void leave(Node astNode, VirtualFrame frame, short result) { + } + + public void leave(Node astNode, VirtualFrame frame, int result) { + } + + public void leave(Node astNode, VirtualFrame frame, long result) { + } + + public void leave(Node astNode, VirtualFrame frame, char result) { + } + + public void leave(Node astNode, VirtualFrame frame, float result) { + } + + public void leave(Node astNode, VirtualFrame frame, double result) { + } + + public void leave(Node astNode, VirtualFrame frame, Object result) { + } + + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + } + + public void replace(Node oldAstNode, Node newAstNode, String reason) { + } + + } + + /** + * Holder of a chain of {@linkplain InstrumentationProbeNode probes}: manages the + * {@link Assumption} that the chain has not changed since checked checked. + */ + public static final class ProbeChain extends DefaultProbeNode { + + @CompilerDirectives.CompilationFinal private Assumption probeUnchanged; + + /** + * Source information about the node to which this probe chain is attached; it isn't + * otherwise available. A probe chain is shared by every copy made during runtime, so there + * is no parent pointer. + */ + @SuppressWarnings("unused") private final SourceSection sourceSection; + + private final String description; // for debugging + + /** + * Creates a new, empty chain of {@linkplain InstrumentationProbeNode probes}, to which + * probes can be added/removed, and all of which will be notified of + * {@linkplain InstrumentationProbeEvents events} when the chain is notified. + */ + public ProbeChain(SourceSection sourceSection, String description) { + this.probeUnchanged = Truffle.getRuntime().createAssumption(); + this.sourceSection = sourceSection; + this.description = description; + this.next = null; + } + + public int probeCount() { + return countProbes(); + } + + public String getDescription() { + return description; + } + + @Override + protected int countProbes() { + // The head of the chain does not itself hold a probe + return next == null ? 0 : next.countProbes(); + } + + @Override + protected void notifyProbeChanged(InstrumentationProbeNode probeNode) { + probeUnchanged.invalidate(); + probeUnchanged = Truffle.getRuntime().createAssumption(); + } + + @CompilerDirectives.SlowPath + public void appendProbe(InstrumentationProbeNode newProbeNode) { + probeUnchanged.invalidate(); + super.internalAppendProbe(newProbeNode); + probeUnchanged = Truffle.getRuntime().createAssumption(); + } + + @CompilerDirectives.SlowPath + public void removeProbe(InstrumentationProbeNode oldProbeNode) { + probeUnchanged.invalidate(); + super.internalRemoveProbe(oldProbeNode); + probeUnchanged = Truffle.getRuntime().createAssumption(); + } + + public void notifyEnter(Node astNode, VirtualFrame frame) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalEnter(astNode, frame); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, boolean result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, byte result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, short result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, int result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, long result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, char result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, float result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, double result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeave(Node astNode, VirtualFrame frame, Object result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeaveExceptional(astNode, frame, e); + } + } + + public void notifyReplace(Node oldAstNode, Node newAstNode, String reason) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalReplace(oldAstNode, newAstNode, reason); + } + } + + } + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProxyNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProxyNode.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, 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.nodes.instrument; + +import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; + +/** + * Interface implemented by language-specific Truffle proxy nodes: nodes that do + * not participate in the language's execution semantics, but which are inserted into an AST so that + * tools (e.g. tracers, analyzers, debuggers) can be notified of AST interpretation events and + * possibly intervene. + *

+ * Language-specific proxy nodes call notification methods on an attached {@linkplain ProbeChain + * probe chain} which passes along {@linkplain InstrumentationProbeEvents events} to any + * {@linkplain InstrumentationProbeNode probes} that might have been attached. + */ +public interface InstrumentationProxyNode extends InstrumentationNode { + + /** + * Gets the chain of probes to which events at this node are delegated. Note that a chain of + * probes may be used by more than one proxy. + */ + ProbeChain getProbeChain(); + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013, 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.source; + +import com.oracle.truffle.api.*; + +/** + * A specification for a location in guest language source, expressed as a line number in a specific + * instance of {@link Source}, suitable for hash table keys with equality defined in terms of + * content. + */ +public class SourceLineLocation implements Comparable { + + private final Source source; + private final int line; + + public SourceLineLocation(Source source, int line) { + assert source != null; + assert source != SourceSection.NULL; + this.source = source; + this.line = line; + } + + public Source getSource() { + return source; + } + + public int getLine() { + return line; + } + + @Override + public String toString() { + return "SourceLine [" + source.getName() + ", " + line + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + line; + result = prime * result + source.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SourceLineLocation)) { + return false; + } + SourceLineLocation other = (SourceLineLocation) obj; + if (line != other.line) { + return false; + } + return source.equals(other.source); + } + + @Override + public int compareTo(Object o) { + final SourceLineLocation other = (SourceLineLocation) o; + final int nameOrder = source.getName().compareTo(other.source.getName()); + if (nameOrder != 0) { + return nameOrder; + } + return Integer.compare(line, other.line); + } + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2013, 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.source; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.api.*; + +/** + * A representation of source code information, suitable for hash table keys with equality defined + * in terms of content. There are three kinds of sources supported at present. + *

+ */ +public final class SourceManager { + + // Only files and fake files are indexed. + private final Map sourceMap = new HashMap<>(); + + public SourceManager() { + + } + + /** + * Gets the canonical representation of a source file, whose contents will be read lazily and + * then cached. + * + * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read. + */ + public Source get(String fileName, boolean reset) { + SourceImpl source = sourceMap.get(fileName); + if (source == null) { + String path = findPath(fileName); + if (path == null) { + throw new RuntimeException("Can't find file " + fileName); + } + source = sourceMap.get(path); + if (source == null) { + source = new FileSourceImpl(fileName, path); + sourceMap.put(path, source); + } + } else { + if (reset) { + source.reset(); + } + } + return source; + } + + /** + * Gets the canonical representation of a source file, whose contents will be read lazily and + * then cached. + */ + public Source get(String fileName) { + return get(fileName, false); + } + + /** + * Creates a source from literal text. + */ + @SuppressWarnings("static-method") + public Source get(String name, String code) { + assert code != null; + return new LiteralSourceImpl(name, code); + } + + /** + * Creates a source whose contents will be read immediately and cached. + */ + @SuppressWarnings("static-method") + public Source get(String name, InputStream stream) throws IOException { + InputStreamReader reader = new InputStreamReader(stream); + return new LiteralSourceImpl(name, readCode(reader)); + } + + /** + * Creates a source from literal text, but which acts as a file and can be retrieved by name + * (unlike other literal sources); intended for testing. + */ + public Source getFakeFile(String name, String code) { + final SourceImpl source = new LiteralSourceImpl(name, code); + sourceMap.put(name, source); + return source; + } + + // If it names a real file, get the (canonical) normalized absolute path. + private static String findPath(String name) { + final File file = new File(name); + if (file.exists()) { + try { + return file.getCanonicalPath(); + } catch (IOException e) { + } + } + return null; + } + + private static String readCode(Reader reader) throws IOException { + final StringBuilder builder = new StringBuilder(); + final char[] buffer = new char[1024]; + + while (true) { + final int n = reader.read(buffer); + if (n == -1) { + break; + } + builder.append(buffer, 0, n); + } + + return builder.toString(); + } + + private abstract static class SourceImpl implements Source { + + protected TextMap textMap = null; + + protected abstract void reset(); + + public final InputStream getInputStream() { + return new ByteArrayInputStream(getCode().getBytes()); + } + + /** + * Gets the text (not including a possible terminating newline) in a (1-based) numbered + * line. + */ + public final String getCode(int lineNumber) { + checkTextMap(); + final int offset = textMap.lineStartOffset(lineNumber); + final int length = textMap.lineLength(lineNumber); + return getCode().substring(offset, offset + length); + } + + /** + * The number of text lines in the source. + */ + public final int getLineCount() { + return checkTextMap().lineCount(); + } + + /** + * The 1-based number of the line that includes a 0-based character offset. + */ + public final int getLineNumber(int offset) { + return checkTextMap().offsetToLine(offset); + } + + /** + * The 0-based character offset at the start of a (1-based) numbered line. + */ + public final int getLineStartOffset(int lineNumber) { + return checkTextMap().lineStartOffset(lineNumber); + } + + /** + * The number of characters (not counting a possible terminating newline) in a (1-based) + * numbered line. + */ + public final int getLineLength(int lineNumber) { + return checkTextMap().lineLength(lineNumber); + } + + private TextMap checkTextMap() { + if (textMap == null) { + final String code = getCode(); + if (code == null) { + throw new RuntimeException("can't read file " + getName()); + } + textMap = new TextMap(code); + } + return textMap; + } + } + + public static class LiteralSourceImpl extends SourceImpl { + + private final String name; // Name used originally to describe the source + private final String code; + + public LiteralSourceImpl(String name, String code) { + this.name = name; + this.code = code; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getPath() { + return name; + } + + @Override + public Reader getReader() { + return new StringReader(code); + } + + @Override + protected void reset() { + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + name.hashCode(); + result = prime * result + (code == null ? 0 : code.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof LiteralSourceImpl)) { + return false; + } + LiteralSourceImpl other = (LiteralSourceImpl) obj; + return name.equals(other.name) && code.equals(other.code); + } + + } + + private static class FileSourceImpl extends SourceImpl { + + private final String name; // Name used originally to describe the source + private String code = null; + private final String path; // Normalized path description of an actual file + private boolean readAttempted; + + public FileSourceImpl(String name, String path) { + this.name = name; + this.path = path; + this.readAttempted = false; + + } + + @Override + public String getName() { + return name; + } + + @Override + public String getCode() { + if (code == null && !readAttempted) { + readAttempted = true; + try { + code = readCode(getReader()); + } catch (IOException e) { + } + } + return code; + } + + @Override + public String getPath() { + return path; + } + + @Override + public Reader getReader() { + if (code != null) { + return new StringReader(code); + } + try { + return new BufferedReader(new FileReader(path)); + } catch (FileNotFoundException e) { + throw new RuntimeException("Can't find file " + path); + } + } + + @Override + protected void reset() { + this.code = null; + this.readAttempted = false; + } + + } + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/TextMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/TextMap.java Tue Dec 17 20:22:45 2013 -0800 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2013, 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.source; + +import java.util.*; + +/** + * A utility for converting between coordinate systems in a string of text interspersed with newline + * characters. The coordinate systems are: + * + *

+ * This utility is based on positions occupied by characters, not text stream positions as in a text + * editor. The distinction shows up in editors where you can put the cursor just past the last + * character in a buffer; this is necessary, among other reasons, so that you can put the edit + * cursor in a new (empty) buffer. For the purposes of this utility, however, there are no character + * positions in an empty text string and there are no lines in an empty text string. + *

+ * A newline character designates the end of a line and occupies a column position. + *

+ * If the text ends with a character other than a newline, then the characters following the final + * newline character count as a line, even though not newline-terminated. + *

+ * Limitations: + *

+ */ +public final class TextMap { + + // 0-based offsets of newline characters in the text, with sentinel + private final int[] nlOffsets; + + // The number of characters in the text, including newlines (which count as 1). + private final int textLength; + + // Is the final text character a newline? + final boolean finalNL; + + /** + * Constructs map permitting translation between 0-based character offsets and 1-based + * lines/columns. + */ + public TextMap(String text) { + this.textLength = text.length(); + final ArrayList lines = new ArrayList<>(); + lines.add(0); + int offset = 0; + + while (offset < text.length()) { + final int nlIndex = text.indexOf('\n', offset); + if (nlIndex >= 0) { + offset = nlIndex + 1; + lines.add(offset); + } else { + break; + } + } + lines.add(Integer.MAX_VALUE); + + nlOffsets = new int[lines.size()]; + for (int line = 0; line < lines.size(); line++) { + nlOffsets[line] = lines.get(line); + } + + finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]); + } + + /** + * Converts 0-based character offset to 1-based number of the line containing the character. + * + * @throws IllegalArgumentException if the offset is outside the string. + */ + public int offsetToLine(int offset) throws IllegalArgumentException { + if (offset < 0 || offset >= textLength) { + throw new IllegalArgumentException("offset out of bounds"); + } + int line = 1; + while (offset >= nlOffsets[line]) { + line++; + } + return line; + } + + /** + * Converts 0-based character offset to 1-based number of the column occupied by the character. + *

+ * Tabs are not expanded; they occupy 1 column. + * + * @throws IllegalArgumentException if the offset is outside the string. + */ + public int offsetToCol(int offset) throws IllegalArgumentException { + return 1 + offset - nlOffsets[offsetToLine(offset) - 1]; + } + + /** + * The number of lines in the text; if characters appear after the final newline, then they also + * count as a line, even though not newline-terminated. + */ + public int lineCount() { + if (textLength == 0) { + return 0; + } + return finalNL ? nlOffsets.length - 2 : nlOffsets.length - 1; + } + + /** + * Converts 1-based line number to the 0-based offset of the line's first character; this would + * be the offset of a newline if the line is empty. + * + * @throws IllegalArgumentException if there is no such line in the text. + */ + public int lineStartOffset(int line) throws IllegalArgumentException { + if (textLength == 0 || lineOutOfRange(line)) { + throw new IllegalArgumentException("line out of bounds"); + } + return nlOffsets[line - 1]; + } + + /** + * Gets the number of characters in a line, identified by 1-based line number; does not + * include the final newline, if any. + * + * @throws IllegalArgumentException if there is no such line in the text. + */ + public int lineLength(int line) throws IllegalArgumentException { + if (textLength == 0 || lineOutOfRange(line)) { + throw new IllegalArgumentException("line out of bounds"); + } + if (line == nlOffsets.length - 1 && !finalNL) { + return textLength - nlOffsets[line - 1]; + } + return (nlOffsets[line] - nlOffsets[line - 1]) - 1; + + } + + /** + * Is the line number out of range. + */ + private boolean lineOutOfRange(int line) { + return line <= 0 || line >= nlOffsets.length || (line == nlOffsets.length - 1 && finalNL); + } + +} diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/AbstractTest.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/AbstractTest.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/AbstractTest.java Tue Dec 17 20:22:45 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, 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 @@ -50,17 +50,14 @@ } protected static void executeSL(String[] input, String[] expectedOutput, boolean useConsole) { - InputStream in = new ByteArrayInputStream(concat(input).getBytes()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream printer = new PrintStream(useConsole ? new SplitOutputStream(out, System.err) : out); PrintStream origErr = System.err; System.setErr(printer); - SimpleLanguage.run(in, printer, REPEATS, false); + SimpleLanguage.run("(test)", concat(input), printer, REPEATS, false); System.setErr(origErr); Assert.assertEquals(repeat(concat(expectedOutput), REPEATS), new String(out.toByteArray())); } - } diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/FibonacciTest.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/FibonacciTest.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/FibonacciTest.java Tue Dec 17 20:22:45 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, 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 @@ -26,6 +26,7 @@ import org.junit.*; +import com.oracle.truffle.api.*; import com.oracle.truffle.sl.*; import com.oracle.truffle.sl.runtime.*; @@ -75,7 +76,9 @@ for (String line : INPUT) { s.append(line).append("\n"); } - SLScript script = SLScript.create(new SLContext(System.out), s.toString()); + final SLContext context = new SLContext(System.out); + final Source source = context.getSourceManager().get("(fib test)", s.toString()); + SLScript script = SLScript.create(context, source); Integer reference = test(TEST_VALUE); for (int i = 0; i < ITERATIONS; i++) { if (!reference.equals(script.run(TEST_VALUE))) { diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLScript.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLScript.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLScript.java Tue Dec 17 20:22:45 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, 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 @@ -22,8 +22,6 @@ */ package com.oracle.truffle.sl; -import java.io.*; - import javax.script.*; import com.oracle.truffle.api.*; @@ -57,24 +55,11 @@ return main.toString(); } - public static SLScript create(SLContext context, String input) throws ScriptException { - return create(context, new ByteArrayInputStream(input.getBytes())); - - } - - public static SLScript create(SLContext context, InputStream input) throws ScriptException { + public static SLScript create(SLContext context, Source source) throws ScriptException { SLNodeFactory factory = new SLNodeFactory(context); - Parser parser = new Parser(new Scanner(input), factory); + Parser parser = new Parser(new Scanner(source.getInputStream()), factory); factory.setParser(parser); - factory.setSource(new Source() { - public String getName() { - return "Unknown"; - } - - public String getCode() { - return null; - } - }); + factory.setSource(source); String error = parser.ParseErrors(); if (!error.isEmpty()) { throw new ScriptException(String.format("Error(s) parsing script: %s", error)); diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SimpleLanguage.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SimpleLanguage.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SimpleLanguage.java Tue Dec 17 20:22:45 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, 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 @@ -35,21 +35,41 @@ private static final Object[] NO_ARGUMENTS = new Object[0]; - public static void main(String[] args) throws IOException { - run(new FileInputStream(args[0]), System.out, 10, true); + public static void main(String[] args) { + run(args[0], System.out, 10, true); } - public static void run(InputStream input, PrintStream printOutput, int repeats, boolean log) { + public static void run(String name, String input, PrintStream printOutput, int repeats, boolean log) { if (log) { // CheckStyle: stop system..print check System.out.printf("== running on %s\n", Truffle.getRuntime().getName()); // CheckStyle: resume system..print check } - SLContext context = new SLContext(printOutput); + final SLContext context = new SLContext(printOutput); + final Source source = context.getSourceManager().get(name, input); + + run(context, source, printOutput, repeats, log); + } + + public static void run(String fileName, PrintStream printOutput, int repeats, boolean log) { + if (log) { + // CheckStyle: stop system..print check + System.out.printf("== running on %s\n", Truffle.getRuntime().getName()); + // CheckStyle: resume system..print check + } + + final SLContext context = new SLContext(printOutput); + final Source source = context.getSourceManager().get(fileName); + + run(context, source, printOutput, repeats, log); + } + + public static void run(SLContext context, Source source, PrintStream printOutput, int repeats, boolean log) { + SLScript script; try { - script = SLScript.create(context, input); + script = SLScript.create(context, source); } catch (ScriptException e) { // TODO temporary hack throw new RuntimeException(e); diff -r dfb780080923 -r 69d2e4baa215 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Thu Dec 12 14:56:52 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Tue Dec 17 20:22:45 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, 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 @@ -24,17 +24,20 @@ import java.io.*; +import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.builtins.*; public final class SLContext { private final PrintStream printOutput; private final SLFunctionRegistry functionRegistry; + private final SourceManager sourceManager; public SLContext(PrintStream print) { this.printOutput = print; this.functionRegistry = new SLFunctionRegistry(); DefaultBuiltins.install(this); + this.sourceManager = new SourceManager(); } public PrintStream getPrintOutput() { @@ -45,4 +48,8 @@ return functionRegistry; } + public SourceManager getSourceManager() { + return sourceManager; + } + }