changeset 16702:2a5ec181dad4

SL: Added instrumentation testing
author David Piorkowski <david.piorkowski@oracle.com>
date Tue, 05 Aug 2014 16:34:08 -0700
parents bd6b44b04143
children fc9d2d081995
files graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/WrapperTest.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLCheckVariableEqualityInstrument.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestNodeProber.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestSuite.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLPrintAssigmentValueInstrument.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLSimpleInstrumentTestSuite.java graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_assnCount.output graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_assnCount.sl graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_varCompare.output graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_varCompare.sl graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java
diffstat 13 files changed, 1185 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/WrapperTest.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,270 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.test.instrument;
+
+import java.util.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
+
+/**
+ * This test does the following:
+ * <ol>
+ * <li>Creates a simple add AST</li>
+ * <li>Verifies its structure</li>
+ * <li>Instruments the add node</li>
+ * <li>Attaches a simple probe to the instrumented node</li>
+ * <li>Verifies the structure of the instrumented AST</li>
+ * <li>Verifies the execution of the instrumented AST</li>
+ * </ol>
+ * To do these tests, several required classes have been implemented in their most basic form, only
+ * implementing the methods necessary for the tests to pass, with stubs elsewhere.
+ */
+public class WrapperTest {
+
+    @Test
+    public void test() {
+        // Build a tree
+        TruffleRuntime runtime = Truffle.getRuntime();
+        TestChildNode leftChild = new TestChildNode();
+        TestChildNode rightChild = new TestChildNode();
+        TestSourceSection sourceSection = new TestSourceSection();
+        TestAddNode addNode = new TestAddNode(leftChild, rightChild, sourceSection);
+        TestRootNode rootNode = new TestRootNode(addNode);
+
+        // Have to create a call target before checking parent/child relationships
+        CallTarget target = runtime.createCallTarget(rootNode);
+
+        // Check tree structure
+        Assert.assertEquals(addNode, leftChild.getParent());
+        Assert.assertEquals(addNode, rightChild.getParent());
+        Iterator<Node> iterator = addNode.getChildren().iterator();
+        Assert.assertEquals(leftChild, iterator.next());
+        Assert.assertEquals(rightChild, iterator.next());
+        Assert.assertFalse(iterator.hasNext());
+        Assert.assertEquals(rootNode, addNode.getParent());
+        iterator = rootNode.getChildren().iterator();
+        Assert.assertEquals(addNode, iterator.next());
+        Assert.assertFalse(iterator.hasNext());
+        Object result = target.call();
+        Assert.assertEquals(42, result);
+
+        // Add a wrapper
+        TestExecutionContext context = new TestExecutionContext();
+        TestWrapper wrapper = new TestWrapper(addNode, context);
+        rootNode = new TestRootNode(wrapper);
+        target = runtime.createCallTarget(rootNode);
+
+        // Check the new tree structure
+        Assert.assertEquals(addNode, leftChild.getParent());
+        Assert.assertEquals(addNode, rightChild.getParent());
+        iterator = addNode.getChildren().iterator();
+        Assert.assertEquals(leftChild, iterator.next());
+        Assert.assertEquals(rightChild, iterator.next());
+        Assert.assertFalse(iterator.hasNext());
+        Assert.assertEquals(wrapper, addNode.getParent());
+        iterator = wrapper.getChildren().iterator();
+        Assert.assertEquals(addNode, iterator.next());
+        Assert.assertFalse(iterator.hasNext());
+        Assert.assertEquals(rootNode, wrapper.getParent());
+        iterator = rootNode.getChildren().iterator();
+        Assert.assertEquals(wrapper, iterator.next());
+        Assert.assertFalse(iterator.hasNext());
+        result = target.call();
+        Assert.assertEquals(42, result);
+
+        // Add an instrument
+        wrapper.getProbe().addInstrument(new TestInstrument());
+
+        // Check instrument and result
+        result = target.call();
+        Assert.assertEquals(Counter.numInstrumentEnter, 1);
+        Assert.assertEquals(Counter.numInstrumentLeave, 1);
+        Assert.assertEquals(42, result);
+
+    }
+
+    private class TestRootNode extends RootNode {
+        @Child private RootNode child;
+
+        public TestRootNode(RootNode child) {
+            super(null);
+            this.child = child;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return child.execute(frame);
+        }
+    }
+
+    private class TestAddNode extends RootNode {
+
+        @Child private TestChildNode left;
+        @Child private TestChildNode right;
+
+        public TestAddNode(TestChildNode left, TestChildNode right, TestSourceSection sourceSection) {
+            super(sourceSection);
+            this.left = left;
+            this.right = right;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return left.execute() + right.execute();
+        }
+    }
+
+    private class TestChildNode extends Node {
+
+        public TestChildNode() {
+            super(null);
+        }
+
+        public int execute() {
+            return 21;
+        }
+    }
+
+    private class TestWrapper extends RootNode implements Wrapper {
+        @Child private RootNode child;
+        private Probe probe;
+
+        public TestWrapper(RootNode child, ExecutionContext context) {
+            this.child = insert(child);
+            this.probe = context.getProbe(child.getSourceSection());
+        }
+
+        public boolean isTaggedAs(SyntaxTag tag) {
+            return false;
+        }
+
+        public Iterable<SyntaxTag> getSyntaxTags() {
+            return null;
+        }
+
+        public Node getChild() {
+            return child;
+        }
+
+        public Probe getProbe() {
+            return probe;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            probe.enter(child, frame);
+            Object result;
+
+            try {
+                result = child.execute(frame);
+                probe.leave(child, frame, result);
+            } catch (Exception e) {
+                probe.leaveExceptional(child, frame, e);
+                throw (e);
+            }
+            return result;
+        }
+    }
+
+    private class TestSourceSection implements SourceSection {
+
+        public Source getSource() {
+            return null;
+        }
+
+        public int getStartLine() {
+            return 0;
+        }
+
+        public LineLocation getLineLocation() {
+            return null;
+        }
+
+        public int getStartColumn() {
+            return 0;
+        }
+
+        public int getCharIndex() {
+            return 0;
+        }
+
+        public int getCharLength() {
+            return 0;
+        }
+
+        public int getCharEndIndex() {
+            return 0;
+        }
+
+        public String getIdentifier() {
+            return null;
+        }
+
+        public String getCode() {
+            return null;
+        }
+
+        public String getShortDescription() {
+            return null;
+        }
+
+    }
+
+    private class TestExecutionContext extends ExecutionContext {
+
+        @Override
+        public String getLanguageShortName() {
+            return "test";
+        }
+
+        @Override
+        protected void setSourceCallback(SourceCallback sourceCallback) {
+
+        }
+
+    }
+
+    private class TestInstrument extends Instrument {
+        @Override
+        public void enter(Node astNode, VirtualFrame frame) {
+            Counter.numInstrumentEnter++;
+        }
+
+        @Override
+        public void leave(Node astNode, VirtualFrame frame, Object result) {
+            Counter.numInstrumentLeave++;
+        }
+    }
+
+    public static class Counter {
+
+        public static int numInstrumentEnter = 0;
+        public static int numInstrumentLeave = 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLCheckVariableEqualityInstrument.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,80 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.sl.test.instrument;
+
+import java.io.*;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.sl.nodes.controlflow.*;
+import com.oracle.truffle.sl.runtime.*;
+
+/**
+ * This sample instrument provides an example of a naive way to check if two numbers in SL are
+ * equivalent using their variable names. This instrument is designed to be attached to an
+ * {@link SLReturnNode}, but provides no guards against this.
+ */
+public class SLCheckVariableEqualityInstrument extends Instrument {
+
+    private final String varName1;
+    private final String varName2;
+    private final PrintStream output;
+
+    /**
+     * Constructor
+     *
+     * @param varName1 The name of the first variable to compare
+     * @param varName2 The name of the second variable to compare
+     * @param output The {@link PrintStream} from the context used to print results. See
+     *            {@link SLContext#getOutput()} for more info.
+     */
+    public SLCheckVariableEqualityInstrument(String varName1, String varName2, PrintStream output) {
+        this.varName1 = varName1;
+        this.varName2 = varName2;
+        this.output = output;
+    }
+
+    /**
+     * In the instrumentation test, this instrument is attached to a return statement. Since returns
+     * are handled via exceptions in Simple, we need to override the leaveExceptional method. This
+     * method does very limited error checking and simply prints "true" if the passed-in variables
+     * match or "false" if they do not.
+     */
+    @Override
+    public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+        FrameSlot f1 = frame.getFrameDescriptor().findFrameSlot(varName1);
+        FrameSlot f2 = frame.getFrameDescriptor().findFrameSlot(varName2);
+
+        if (f1 == null || f2 == null)
+            output.println("false");
+        else {
+            try {
+                output.println(frame.getLong(f1) == frame.getLong(f2));
+            } catch (FrameSlotTypeException e1) {
+                e1.printStackTrace();
+            }
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestNodeProber.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,119 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.sl.test.instrument;
+
+import static com.oracle.truffle.api.instrument.StandardSyntaxTag.*;
+
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.sl.nodes.*;
+import com.oracle.truffle.sl.nodes.controlflow.*;
+import com.oracle.truffle.sl.nodes.instrument.*;
+import com.oracle.truffle.sl.nodes.local.*;
+import com.oracle.truffle.sl.runtime.*;
+
+/**
+ * This sample AST Node Prober for simple is used to instrument the nodes that we are interested in
+ * testing. This prober wraps return nodes and assignment nodes. For the purposes of this example,
+ * this is appropriate, but ideally there would be only one node prober responsible for
+ * instrumenting all the nodes of interest instead of a selective one like this one.
+ *
+ */
+public final class SLInstrumentTestNodeProber implements SLNodeProber {
+    private final SLContext slContext;
+
+    public SLInstrumentTestNodeProber(SLContext slContext) {
+        this.slContext = slContext;
+    }
+
+    /**
+     * Not implemented, only returns the astNode that was passed in.
+     */
+    public Node probeAs(Node astNode, SyntaxTag tag, Object... args) {
+        // TODO dp: Currently does nothing in the general case
+        return astNode;
+    }
+
+    /**
+     * If the passed in node is a {@link SLStatementWrapper}, then this simply tags it as a
+     * statement. If the passed in node is a {@link SLReturnNode}, then it is instrumented and
+     * tagged as a statement for testing. Only SLReturnNodes are wrapped.
+     */
+    public SLStatementNode probeAsStatement(SLStatementNode node) {
+        assert node != null;
+
+        SLStatementWrapper wrapper = null;
+        if (node instanceof SLStatementWrapper) {
+            wrapper = (SLStatementWrapper) node;
+            tagStatementNode(wrapper);
+            return wrapper;
+        } else if (node instanceof SLReturnNode) {
+            wrapper = new SLStatementWrapper(slContext, node);
+            tagStatementNode(wrapper);
+            return wrapper;
+        }
+        return node;
+    }
+
+    /**
+     * Not implemented. Returns the passed in node.
+     */
+    public SLExpressionNode probeAsCall(SLExpressionNode node, String callName) {
+        return node;
+    }
+
+    /**
+     * If the passed in node is a {@link SLExpressionWrapper}, then this simply tags it as an
+     * assignment. If the passed in node is a {@link SLWriteLocalVariableNode}, then it is
+     * instrumented and tagged as a assignment for testing. Only SLWriteLocalVariableNode are
+     * wrapped.
+     */
+    public SLExpressionNode probeAsLocalAssignment(SLExpressionNode node, String localName) {
+        assert node != null;
+
+        SLExpressionWrapper wrapper = null;
+        if (node instanceof SLExpressionWrapper) {
+            wrapper = (SLExpressionWrapper) node;
+            tagAssignmentNode(wrapper);
+            return wrapper;
+        } else if (node instanceof SLWriteLocalVariableNode) {
+            wrapper = new SLExpressionWrapper(slContext, node);
+            tagAssignmentNode(wrapper);
+            return wrapper;
+        }
+        return node;
+    }
+
+    private static void tagAssignmentNode(SLExpressionWrapper wrapper) {
+        if (!wrapper.isTaggedAs(ASSIGNMENT)) {
+            wrapper.tagAs(ASSIGNMENT);
+        }
+    }
+
+    private static void tagStatementNode(SLStatementWrapper wrapper) {
+        if (!wrapper.isTaggedAs(STATEMENT)) {
+            wrapper.tagAs(STATEMENT);
+        }
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,302 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.sl.test.instrument;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+
+import org.junit.*;
+import org.junit.internal.*;
+import org.junit.runner.*;
+import org.junit.runner.manipulation.*;
+import org.junit.runner.notification.*;
+import org.junit.runners.*;
+import org.junit.runners.model.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.sl.nodes.instrument.*;
+import com.oracle.truffle.sl.parser.*;
+import com.oracle.truffle.sl.runtime.*;
+import com.oracle.truffle.sl.test.*;
+import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase;
+
+/**
+ * This class builds and executes the tests for instrumenting SL. Although much of this class is
+ * written with future automation in mind, at the moment the tests that are created are hard-coded
+ * according to the file name of the test. To be automated, an automatic way of generating both the
+ * node visitor and the node prober is necessary.
+ *
+ * Testing is done via JUnit via comparing execution outputs with expected outputs.
+ */
+public final class SLInstrumentTestRunner extends ParentRunner<InstrumentTestCase> {
+
+    private static final String SOURCE_SUFFIX = ".sl";
+    private static final String INPUT_SUFFIX = ".input";
+    private static final String OUTPUT_SUFFIX = ".output";
+    private static final String VISITOR_ASSIGNMENT_COUNT_SUFFIX = "_assnCount";
+    private static final String VISITOR_VARIABLE_COMPARE_SUFFIX = "_varCompare";
+
+    private static final String LF = System.getProperty("line.separator");
+    private static SLContext slContext;
+
+    static class InstrumentTestCase {
+        protected final Description name;
+        protected final Path path;
+        protected final String baseName;
+        protected final String sourceName;
+        protected final String testInput;
+        protected final String expectedOutput;
+        protected String actualOutput;
+
+        protected InstrumentTestCase(Class<?> testClass, String baseName, String sourceName, Path path, String testInput, String expectedOutput) {
+            this.name = Description.createTestDescription(testClass, baseName);
+            this.baseName = baseName;
+            this.sourceName = sourceName;
+            this.path = path;
+            this.testInput = testInput;
+            this.expectedOutput = expectedOutput;
+        }
+    }
+
+    private final List<InstrumentTestCase> testCases;
+
+    public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError {
+        super(testClass);
+        try {
+            testCases = createTests(testClass);
+        } catch (IOException e) {
+            throw new InitializationError(e);
+        }
+    }
+
+    @Override
+    protected List<InstrumentTestCase> getChildren() {
+        return testCases;
+    }
+
+    @Override
+    protected Description describeChild(InstrumentTestCase child) {
+        return child.name;
+    }
+
+    /**
+     * Tests are created based on the files that exist in the directory specified in the passed in
+     * annotation. Each test must have a source file and an expected output file. Optionally, each
+     * test can also include an input file. Source files have an ".sl" extension. Expected output
+     * have a ".output" extension. Input files have an ".input" extension. All these files must
+     * share the same base name to be correctly grouped. For example: "test1_assnCount.sl",
+     * "test1_assnCount.output" and "test1_assnCount.input" would all be used to create a single
+     * test called "test1_assnCount".
+     *
+     * This method iterates over the files in the directory and creates a new InstrumentTestCase for
+     * each group of related files. Each file is also expected to end with an identified at the end
+     * of the base name to indicate what visitor needs to be attached. Currently, visitors are hard
+     * coded to work on specific lines, so the code here is not currently generalizable.
+     *
+     * @param c The annotation containing the directory with tests
+     * @return A list of {@link InstrumentTestCase}s to run.
+     * @throws IOException If the directory is invalid.
+     * @throws InitializationError If no directory is provided.
+     *
+     * @see #runChild(InstrumentTestCase, RunNotifier)
+     */
+    protected static List<InstrumentTestCase> createTests(final Class<?> c) throws IOException, InitializationError {
+        SLInstrumentTestSuite suite = c.getAnnotation(SLInstrumentTestSuite.class);
+        if (suite == null) {
+            throw new InitializationError(String.format("@%s annotation required on class '%s' to run with '%s'.", SLTestSuite.class.getSimpleName(), c.getName(), SLTestRunner.class.getSimpleName()));
+        }
+
+        String[] paths = suite.value();
+
+        Path root = null;
+        for (String path : paths) {
+            root = FileSystems.getDefault().getPath(path);
+            if (Files.exists(root)) {
+                break;
+            }
+        }
+        if (root == null && paths.length > 0) {
+            throw new FileNotFoundException(paths[0]);
+        }
+
+        final Path rootPath = root;
+
+        final List<InstrumentTestCase> testCases = new ArrayList<>();
+
+        // Scaffolding in place for future automation
+        Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path sourceFile, BasicFileAttributes attrs) throws IOException {
+                String sourceName = sourceFile.getFileName().toString();
+                if (sourceName.endsWith(SOURCE_SUFFIX)) {
+                    String baseName = sourceName.substring(0, sourceName.length() - SOURCE_SUFFIX.length());
+
+                    Path inputFile = sourceFile.resolveSibling(baseName + INPUT_SUFFIX);
+                    String testInput = "";
+                    if (Files.exists(inputFile)) {
+                        testInput = readAllLines(inputFile);
+                    }
+
+                    Path outputFile = sourceFile.resolveSibling(baseName + OUTPUT_SUFFIX);
+                    String expectedOutput = "";
+                    if (Files.exists(outputFile)) {
+                        expectedOutput = readAllLines(outputFile);
+                    }
+
+                    testCases.add(new InstrumentTestCase(c, baseName, sourceName, sourceFile, testInput, expectedOutput));
+
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+
+        return testCases;
+    }
+
+    private static String readAllLines(Path file) throws IOException {
+        // fix line feeds for non unix os
+        StringBuilder outFile = new StringBuilder();
+        for (String line : Files.readAllLines(file, Charset.defaultCharset())) {
+            outFile.append(line).append(LF);
+        }
+        return outFile.toString();
+    }
+
+    /**
+     * Executes the passed in test case. Instrumentation is added according to the name of the file
+     * as explained in {@link #createTests(Class)}. Note that this code is not generalizable.
+     */
+    @Override
+    protected void runChild(InstrumentTestCase testCase, RunNotifier notifier) {
+        // TODO Current tests are hard-coded, automate this eventually
+        notifier.fireTestStarted(testCase.name);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        PrintStream printer = new PrintStream(out);
+        try {
+            // We use the name of the file to determine what visitor to attach to it.
+            if (testCase.baseName.endsWith(VISITOR_ASSIGNMENT_COUNT_SUFFIX) || testCase.baseName.endsWith(VISITOR_VARIABLE_COMPARE_SUFFIX)) {
+                NodeVisitor nodeVisitor = null;
+                slContext = new SLContext(new BufferedReader(new StringReader(testCase.testInput)), printer);
+                final Source source = Source.fromText(readAllLines(testCase.path), testCase.sourceName);
+                SLASTProber prober = new SLASTProber();
+
+                // Note that the visitor looks for an attachment point via line number
+                if (testCase.baseName.endsWith(VISITOR_ASSIGNMENT_COUNT_SUFFIX)) {
+                    nodeVisitor = new NodeVisitor() {
+
+                        public boolean visit(Node node) {
+                            if (node instanceof SLExpressionWrapper) {
+                                SLExpressionWrapper wrapper = (SLExpressionWrapper) node;
+                                int lineNum = wrapper.getSourceSection().getLineLocation().getLineNumber();
+
+                                if (lineNum == 4) {
+                                    wrapper.getProbe().addInstrument(new SLPrintAssigmentValueInstrument(slContext.getOutput()));
+                                }
+                            }
+                            return true;
+                        }
+                    };
+
+                    // Note that the visitor looks for an attachment point via line number
+                } else if (testCase.baseName.endsWith(VISITOR_VARIABLE_COMPARE_SUFFIX)) {
+                    nodeVisitor = new NodeVisitor() {
+
+                        public boolean visit(Node node) {
+                            if (node instanceof SLStatementWrapper) {
+                                SLStatementWrapper wrapper = (SLStatementWrapper) node;
+                                int lineNum = wrapper.getSourceSection().getLineLocation().getLineNumber();
+
+                                if (lineNum == 6) {
+                                    wrapper.getProbe().addInstrument(new SLCheckVariableEqualityInstrument("i", "count", slContext.getOutput()));
+                                }
+                            }
+                            return true;
+                        }
+                    };
+                }
+
+                prober.addNodeProber(new SLInstrumentTestNodeProber(slContext));
+                Parser.parseSL(slContext, source, prober);
+                List<SLFunction> functionList = slContext.getFunctionRegistry().getFunctions();
+
+                // Since only functions can be global in SL, this guarantees that we instrument
+                // everything of interest. Parsing must occur before accepting the visitors since
+                // parsing is what creates our instrumentation points.
+                for (SLFunction function : functionList) {
+                    RootCallTarget rootCallTarget = function.getCallTarget();
+                    rootCallTarget.getRootNode().accept(nodeVisitor);
+                }
+
+                SLFunction main = slContext.getFunctionRegistry().lookup("main");
+                main.getCallTarget().call();
+            } else {
+                notifier.fireTestFailure(new Failure(testCase.name, new UnsupportedOperationException("No instrumentation found.")));
+            }
+
+            String actualOutput = new String(out.toByteArray());
+            Assert.assertEquals(testCase.expectedOutput, actualOutput);
+        } catch (Throwable ex) {
+            notifier.fireTestFailure(new Failure(testCase.name, ex));
+        } finally {
+            notifier.fireTestFinished(testCase.name);
+        }
+
+    }
+
+    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException {
+        JUnitCore core = new JUnitCore();
+        core.addListener(new TextListener(System.out));
+        SLTestRunner suite = new SLTestRunner(testClass);
+        if (args.length > 0) {
+            suite.filter(new NameFilter(args[0]));
+        }
+        Result r = core.run(suite);
+        if (!r.wasSuccessful()) {
+            System.exit(1);
+        }
+    }
+
+    private static final class NameFilter extends Filter {
+        private final String pattern;
+
+        private NameFilter(String pattern) {
+            this.pattern = pattern.toLowerCase();
+        }
+
+        @Override
+        public boolean shouldRun(Description description) {
+            return description.getMethodName().toLowerCase().contains(pattern);
+        }
+
+        @Override
+        public String describe() {
+            return "Filter contains " + pattern;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestSuite.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,37 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.sl.test.instrument;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SLInstrumentTestSuite {
+
+    /**
+     * Defines the base path of the test suite. Multiple base paths can be specified. However only
+     * the first base that exists is used to lookup the test cases.
+     */
+    String[] value();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLPrintAssigmentValueInstrument.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.sl.test.instrument;
+
+import java.io.*;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.sl.nodes.local.*;
+
+/**
+ * This sample instrument provides prints the value of an assignment (after the assignment is
+ * complete) to the {@link PrintStream} specified in the constructor. This instrument can only be
+ * attached to a wrapped {@link SLWriteLocalVariableNode}, but provides no guards to protect it from
+ * being attached elsewhere.
+ */
+public final class SLPrintAssigmentValueInstrument extends Instrument {
+
+    private PrintStream output;
+
+    public SLPrintAssigmentValueInstrument(PrintStream output) {
+        this.output = output;
+    }
+
+    @Override
+    public void leave(Node astNode, VirtualFrame frame, Object result) {
+        output.println(result);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLSimpleInstrumentTestSuite.java	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,43 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.sl.test.instrument;
+
+import org.junit.*;
+import org.junit.runner.*;
+
+@RunWith(SLInstrumentTestRunner.class)
+@SLInstrumentTestSuite({"graal/com.oracle.truffle.sl.test/tests_instrumentation", "tests_instrumentation"})
+public class SLSimpleInstrumentTestSuite {
+
+    public static void main(String[] args) throws Exception {
+        SLInstrumentTestRunner.runInMain(SLSimpleInstrumentTestSuite.class, args);
+    }
+
+    /*
+     * Our "mx unittest" command looks for methods that are annotated with @Test. By just defining
+     * an empty method, this class gets included and the test suite is properly executed.
+     */
+    @Test
+    public void unittest() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_assnCount.output	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,101 @@
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+100
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_assnCount.sl	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,12 @@
+function loop(count) {  
+  i = 0;  
+  while (i < count) {  
+    i = i + 1;  
+  }  
+  return i;  
+}
+
+function main() {
+  count = loop(100);
+  println(count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_varCompare.output	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,2 @@
+true
+100
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/tests_instrumentation/Instrumentation_varCompare.sl	Tue Aug 05 16:34:08 2014 -0700
@@ -0,0 +1,12 @@
+function loop(count) {  
+  i = 0;  
+  while (i < count) {  
+    i = i + 1;  
+  }  
+  return i;  
+}
+
+function main() {
+  count = loop(100);
+  println(count);
+}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Tue Aug 05 22:00:12 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Tue Aug 05 16:34:08 2014 -0700
@@ -155,40 +155,127 @@
         }
     }
 
+    /**
+     * Returns a {@link SLBreakNode} for the given token. This node will be instrumented, tagged as
+     * a statement and wrapped in an {@link SLStatementWrapper} if an {@link SLASTProber} was
+     * initialized in this class. ({@link #prober} != null)
+     *
+     * @param breakToken The token containing the break node's info
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLBreakNode if there is no prober</li>
+     *         <li>An {@link SLStatementWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLStatementNode createBreak(Token breakToken) {
-        return new SLBreakNode(srcFromToken(breakToken));
+        final SLBreakNode breakNode = new SLBreakNode(srcFromToken(breakToken));
+        if (prober != null) {
+            return prober.probeAsStatement(breakNode);
+        }
+        return breakNode;
     }
 
+    /**
+     * Returns a {@link SLContinueNode} for the given token. This node will be instrumented, tagged
+     * as a statement and wrapped in an {@link SLStatementWrapper} if an {@link SLASTProber} was
+     * initialized in this class. ({@link #prober} != null)
+     *
+     * @param continueToken The token containing the continue node's info
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLContinueNode if there is no prober</li>
+     *         <li>An {@link SLStatementWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLStatementNode createContinue(Token continueToken) {
-        return new SLContinueNode(srcFromToken(continueToken));
+        final SLContinueNode continueNode = new SLContinueNode(srcFromToken(continueToken));
+        if (prober != null) {
+            return prober.probeAsStatement(continueNode);
+        }
+        return continueNode;
     }
 
+    /**
+     * Returns a {@link SLWhileNode} for the given token. This node will be instrumented, tagged as
+     * a statement and wrapped in an {@link SLStatementWrapper} if an {@link SLASTProber} was
+     * initialized in this class. ({@link #prober} != null)
+     *
+     * @param whileToken The token containing the while node's info
+     * @param conditionNode The conditional node for this while loop
+     * @param bodyNode The body of the while loop
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLWhileNode if there is no prober</li>
+     *         <li>An {@link SLStatementWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLStatementNode createWhile(Token whileToken, SLExpressionNode conditionNode, SLStatementNode bodyNode) {
         final int start = whileToken.charPos;
         final int end = bodyNode.getSourceSection().getCharEndIndex();
-        return new SLWhileNode(source.createSection(whileToken.val, start, end - start), conditionNode, bodyNode);
+        final SLWhileNode whileNode = new SLWhileNode(source.createSection(whileToken.val, start, end - start), conditionNode, bodyNode);
+        if (prober != null) {
+            return prober.probeAsStatement(whileNode);
+        }
+        return whileNode;
     }
 
+    /**
+     * Returns a {@link SLIfNode} for the given token. This node will be instrumented, tagged as a
+     * statement and wrapped in an {@link SLStatementWrapper} if an {@link SLASTProber} was
+     * initialized in this class. ({@link #prober} != null)
+     *
+     * @param ifToken The token containing the if node's info
+     * @param conditionNode The condition node of this if statement
+     * @param thenPartNode The then part of the if
+     * @param elsePartNode The else part of the if
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLIfNode if there is no prober</li>
+     *         <li>An {@link SLStatementWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLStatementNode createIf(Token ifToken, SLExpressionNode conditionNode, SLStatementNode thenPartNode, SLStatementNode elsePartNode) {
         final int start = ifToken.charPos;
         final int end = elsePartNode == null ? thenPartNode.getSourceSection().getCharEndIndex() : elsePartNode.getSourceSection().getCharEndIndex();
-
-        // if (prober != null) {
-        // SLStatementNode wrappedThenNode = prober.probeAsStatement(thenPartNode);
-        // // SLStatementNode wrappedElseNode = prober.probeAsStatement(elsePartNode);
-        // return new SLIfNode(source.createSection(t.val, start, end - start), conditionNode,
-        // wrappedThenNode, elsePartNode);
-        // }
-
-        return new SLIfNode(source.createSection(ifToken.val, start, end - start), conditionNode, thenPartNode, elsePartNode);
+        final SLIfNode ifNode = new SLIfNode(source.createSection(ifToken.val, start, end - start), conditionNode, thenPartNode, elsePartNode);
+        if (prober != null) {
+            return prober.probeAsStatement(ifNode);
+        }
+        return ifNode;
     }
 
+    /**
+     * Returns a {@link SLReturnNode} for the given token. This node will be instrumented, tagged as
+     * a statement and wrapped in an {@link SLStatementWrapper} if an {@link SLASTProber} was
+     * initialized in this class. ({@link #prober} != null)
+     *
+     * @param t The token containing the return node's info
+     * @param valueNode The value of the return
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLReturnNode if there is no prober</li>
+     *         <li>An {@link SLStatementWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLStatementNode createReturn(Token t, SLExpressionNode valueNode) {
         final int start = t.charPos;
         final int length = valueNode == null ? t.val.length() : valueNode.getSourceSection().getCharEndIndex() - start;
-        return new SLReturnNode(source.createSection(t.val, start, length), valueNode);
+        final SLReturnNode returnNode = new SLReturnNode(source.createSection(t.val, start, length), valueNode);
+        if (prober != null) {
+            return prober.probeAsStatement(returnNode);
+        }
+        return returnNode;
     }
 
+    /**
+     * Returns the corresponding subclass of {@link SLExpressionNode} for binary expressions.
+     * </br>These nodes are currently not instrumented.
+     *
+     * @param opToken The operator of the binary expression
+     * @param leftNode The left node of the expression
+     * @param rightNode The right node of the expression
+     * @return A subclass of SLExpressionNode for the operation given by opToken.
+     */
     public SLExpressionNode createBinary(Token opToken, SLExpressionNode leftNode, SLExpressionNode rightNode) {
         int start = leftNode.getSourceSection().getCharIndex();
         int length = rightNode.getSourceSection().getCharEndIndex() - start;
@@ -223,6 +310,20 @@
         }
     }
 
+    /**
+     * Returns a {@link SLInvokeNode} for the given token. This node will be instrumented, tagged as
+     * a call and wrapped in an {@link SLExpressionWrapper} if an {@link SLASTProber} was
+     * initialized in this class. ({@link #prober} != null)
+     *
+     * @param nameToken The name of the function being called
+     * @param parameterNodes The parameters of the function call
+     * @param finalToken A token used to determine the end of the sourceSelection for this call
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLInvokeNode if there is no prober</li>
+     *         <li>An {@link SLExpressionWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLExpressionNode createCall(Token nameToken, List<SLExpressionNode> parameterNodes, Token finalToken) {
         final int startPos = nameToken.charPos;
         final int endPos = finalToken.charPos + finalToken.val.length();
@@ -235,18 +336,44 @@
         return SLInvokeNode.create(src, functionNode, parameterNodes.toArray(new SLExpressionNode[parameterNodes.size()]));
     }
 
+    /**
+     * Returns a {@link SLWriteLocalVariableNode} for the given token. This node will be
+     * instrumented, tagged as an assignment and wrapped in an {@link SLExpressionWrapper} if an
+     * {@link SLASTProber} was initialized in this class. ({@link #prober} != null)
+     *
+     * @param nameToken The name of the variable being assigned
+     * @param valueNode The value to be assigned
+     * @return either:
+     *         <ul>
+     *         <li>An un-instrumented SLWriteLocalVariableNode if there is no prober</li>
+     *         <li>An {@link SLExpressionWrapper} instrumenting this node.</li>
+     *         </ul>
+     */
     public SLExpressionNode createAssignment(Token nameToken, SLExpressionNode valueNode) {
         FrameSlot frameSlot = frameDescriptor.findOrAddFrameSlot(nameToken.val);
         lexicalScope.locals.put(nameToken.val, frameSlot);
         final int start = nameToken.charPos;
         final int length = valueNode.getSourceSection().getCharEndIndex() - start;
         if (prober != null) {
-            final SLExpressionNode wrappedNode = prober.probeAsLocalAssignment(valueNode, nameToken.val);
-            return SLWriteLocalVariableNodeFactory.create(source.createSection("=", start, length), wrappedNode, frameSlot);
+            SLWriteLocalVariableNode writeNode = SLWriteLocalVariableNodeFactory.create(source.createSection("=", start, length), valueNode, frameSlot);
+            final SLExpressionNode wrappedNode = prober.probeAsLocalAssignment(writeNode, nameToken.val);
+            return wrappedNode;
         }
         return SLWriteLocalVariableNodeFactory.create(source.createSection("=", start, length), valueNode, frameSlot);
     }
 
+    /**
+     * Returns a {@link SLReadLocalVariableNode} if this read is a local variable or a
+     * {@link SLFunctionLiteralNode} if this read is global. In Simple, the only global names are
+     * functions. </br> There is currently no instrumentation for this node.
+     *
+     * @param nameToken The name of the variable/function being read
+     * @return either:
+     *         <ul>
+     *         <li>A SLReadLocalVariableNode representing the local variable being read.</li>
+     *         <li>A SLFunctionLiteralNode representing the function definition</li>
+     *         </ul>
+     */
     public SLExpressionNode createRead(Token nameToken) {
         final FrameSlot frameSlot = lexicalScope.locals.get(nameToken.val);
         final SourceSection src = srcFromToken(nameToken);
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue Aug 05 22:00:12 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue Aug 05 16:34:08 2014 -0700
@@ -138,6 +138,16 @@
         getFunctionRegistry().register(name, rootNode);
     }
 
+    /**
+     * This function will parse the given source code, parse the code using the {@link Parser}, and
+     * then execute the function named main. To use this method with instrumentation,
+     * setASTNodeProber must have been already called. There is currently no guard to check if this
+     * is the case. <br/>
+     * Due to the experimental nature of the instrumentation framework, the parse that happens in
+     * this method will remove any previously added instrumentation.
+     *
+     * @param source The {@link Source} to execute.
+     */
     public void executeMain(Source source) {
 
         if (sourceCallback != null) {
@@ -157,8 +167,12 @@
         main.getCallTarget().call();
     }
 
+    /**
+     * Sets the {@link SLASTProber} for the executeMain method.
+     *
+     * @param astProber The prober to use for adding instrumentation for this context.
+     */
     public void setASTNodeProber(SLASTProber astProber) {
-        // TODO Auto-generated method stub
         this.astProber = astProber;
     }
 }