Skip to content

Commit

Permalink
Merge pull request #123 from chumer/improved_tagging
Browse files Browse the repository at this point in the history
Revise tagging mechanism. Add the notion of @ProvidedTags.
  • Loading branch information
chumer committed Mar 29, 2016
2 parents 47a4555 + 09d7384 commit 162e68a
Show file tree
Hide file tree
Showing 33 changed files with 1,183 additions and 463 deletions.
3 changes: 1 addition & 2 deletions mx.truffle/suite.py
Expand Up @@ -142,8 +142,7 @@
"subDir" : "truffle",
"sourceDirs" : ["src"],
"dependencies" : [
"com.oracle.truffle.api.vm",
"com.oracle.truffle.api.debug",
"com.oracle.truffle.tools",
"com.oracle.truffle.api.dsl.test",
"mx:JUNIT"
],
Expand Down
Expand Up @@ -57,6 +57,7 @@
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
import com.oracle.truffle.api.instrumentation.Instrumenter;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter.IndexRange;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
Expand Down Expand Up @@ -155,7 +156,7 @@ Breakpoint create(int ignoreCount, LineLocation lineLocation, boolean oneShot) t
BreakpointImpl breakpoint = breakpoints.get(lineLocation);
if (breakpoint == null) {
final SourceSectionFilter query = SourceSectionFilter.newBuilder().sourceIs(lineLocation.getSource()).lineStartsIn(IndexRange.byLength(lineLocation.getLineNumber(), 1)).tagIs(
Debugger.HALT_TAG).build();
StandardTags.StatementTag.class).build();
breakpoint = new BreakpointImpl(lineLocation, query, ignoreCount, oneShot);
if (TRACE) {
trace("NEW " + breakpoint.getShortDescription());
Expand Down Expand Up @@ -187,7 +188,7 @@ Breakpoint create(int ignoreCount, LineLocation lineLocation, boolean oneShot) t
* @throws IOException if a breakpoint already exists at the location and the ignore count is
* the same
*/
Breakpoint create(int ignoreCount, String tag, boolean oneShot) throws IOException {
Breakpoint create(int ignoreCount, Class<?> tag, boolean oneShot) throws IOException {
BreakpointImpl breakpoint = breakpoints.get(tag);
if (breakpoint == null) {
final SourceSectionFilter query = SourceSectionFilter.newBuilder().tagIs(tag).build();
Expand Down
Expand Up @@ -31,6 +31,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
Expand All @@ -49,12 +50,12 @@
import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
import com.oracle.truffle.api.instrumentation.Instrumenter;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
import com.oracle.truffle.api.instrumentation.StandardTags.CallTag;
import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.LineLocation;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.vm.PolyglotEngine;
import java.util.concurrent.Callable;

/**
* Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}.
Expand All @@ -67,30 +68,23 @@
public final class Debugger {

/**
* A {@link SourceSection#withTags(java.lang.String...) tag} used to mark program locations
* where ordinary stepping should halt. The debugger will halt just <em>before</em> a code
* location is executed that is marked with this tag.
*
* @since 0.9
* @deprecated use class literal {@link StatementTag} instead for tagging
*/
public static final String HALT_TAG = "debug-HALT";
@Deprecated public static final String HALT_TAG = "debug-HALT";

/**
* A {@link SourceSection#withTags(java.lang.String...) tag} used to mark program locations
* where <em>returning</em> or <em>stepping out</em> from a method/procedure call should halt.
* The debugger will halt at the code location that has just executed the call that returned.
*
* @see #HALT_TAG
* @since 0.9
* @deprecated use class literal {@link CallTag} instead for tagging
*/
public static final String CALL_TAG = "debug-CALL";
@Deprecated public static final String CALL_TAG = "debug-CALL";

private static final boolean TRACE = Boolean.getBoolean("truffle.debug.trace");
private static final String TRACE_PREFIX = "Debug: ";
private static final PrintStream OUT = System.out;

private static final SourceSectionFilter CALL_FILTER = SourceSectionFilter.newBuilder().tagIs(CALL_TAG).build();
private static final SourceSectionFilter HALT_FILTER = SourceSectionFilter.newBuilder().tagIs(HALT_TAG).build();
private static final SourceSectionFilter CALL_FILTER = SourceSectionFilter.newBuilder().tagIs(CallTag.class).build();
private static final SourceSectionFilter HALT_FILTER = SourceSectionFilter.newBuilder().tagIs(StatementTag.class).build();

/**
* Finds debugger associated with given engine. There is at most one debugger associated with
Expand Down
Expand Up @@ -78,9 +78,14 @@ protected String run(String source) throws IOException {

protected void assertEvalOut(String source, String output) throws IOException {
String actual = run(lines(source));
String error = getErr();
if (!error.isEmpty()) {
throw new AssertionError("Unexpected error printed: %s" + error);
}
if (!actual.equals(output)) {
Assert.assertEquals(output, actual);
}

}

protected final String getOut() {
Expand Down
Expand Up @@ -25,6 +25,7 @@
package com.oracle.truffle.api.instrumentation;

import java.io.IOException;
import java.util.Set;

import org.junit.Assert;
import org.junit.Test;
Expand Down Expand Up @@ -176,6 +177,7 @@ private static class MyLanguageException extends RuntimeException {
}

@TruffleLanguage.Registration(name = "", version = "", mimeType = "testLanguageInstrumentation")
@ProvidedTags({InstrumentationTestLanguage.ExpressionNode.class, StandardTags.StatementTag.class})
public static class TestLanguageInstrumentationLanguage extends TruffleLanguage<Void> {

public static final TestLanguageInstrumentationLanguage INSTANCE = new TestLanguageInstrumentationLanguage();
Expand Down Expand Up @@ -724,7 +726,216 @@ public void onReturnValue(EventContext context, VirtualFrame frame, Object resul
public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
returnExceptionalCount++;
}
});
}
}

/*
* Use tags that are not declarded as required.
*/
@Test
public void testUsedTagNotRequired1() throws IOException {
TestInstrumentNonInstrumentable1.onStatement = 0;

engine.getInstruments().get("testUsedTagNotRequired1").setEnabled(true);
run("ROOT()");

Assert.assertEquals(0, TestInstrumentNonInstrumentable1.onStatement);
}

@Registration(id = "testUsedTagNotRequired1")
public static class TestUsedTagNotRequired1 extends TruffleInstrument {

private static class Foobar {

}

@Override
protected void onCreate(final Env env) {
try {
env.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(Foobar.class).build(), new ExecutionEventListener() {
public void onEnter(EventContext context, VirtualFrame frame) {
}

public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
}

public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
}
});
Assert.fail();
} catch (IllegalArgumentException e) {
Assert.assertEquals(
"The attached filter SourceSectionFilter[tag is one of [foobar0]] references the " +
"following tags [foobar0] which are not declared as required by the instrument. To fix " +
"this annotate the instrument class com.oracle.truffle.api.instrumentation." +
"InstrumentationTest$TestUsedTagNotRequired1 with @RequiredTags({foobar0}).",
e.getMessage());
}
}
}

/*
* Test behavior of queryTags when used with instruments
*/
@Test
public void testQueryTags1() throws IOException {
Instrument instrument = engine.getInstruments().get("testIsNodeTaggedWith1");
instrument.setEnabled(true);
Instrumenter instrumenter = instrument.lookup(Instrumenter.class);

TestIsNodeTaggedWith1.expressionNode = null;
TestIsNodeTaggedWith1.statementNode = null;

Assert.assertTrue(instrumenter.queryTags(new Node() {
}).isEmpty());

run("STATEMENT(EXPRESSION)");

assertTags(instrumenter.queryTags(TestIsNodeTaggedWith1.expressionNode), InstrumentationTestLanguage.EXPRESSION);
assertTags(instrumenter.queryTags(TestIsNodeTaggedWith1.statementNode), InstrumentationTestLanguage.STATEMENT);

try {
instrumenter.queryTags(null);
Assert.fail();
} catch (NullPointerException e) {
}
}

private static void assertTags(Set<Class<?>> tags, Class<?>... expectedTags) {
Assert.assertEquals(expectedTags.length, tags.size());
for (Class<?> clazz : expectedTags) {
Assert.assertTrue("Tag: " + clazz, tags.contains(clazz));
}
}

/*
* Test behavior of queryTags when used with languages
*/
@Test
public void testQueryTags2() throws IOException {
Instrument instrument = engine.getInstruments().get("testIsNodeTaggedWith1");
instrument.setEnabled(true);
TestIsNodeTaggedWith1.expressionNode = null;
TestIsNodeTaggedWith1.statementNode = null;
TestIsNodeTaggedWith1Language.instrumenter = null;

Source otherLanguageSource = Source.fromText("STATEMENT(EXPRESSION)", null).withMimeType("testIsNodeTaggedWith1");
run(otherLanguageSource);

Instrumenter instrumenter = TestIsNodeTaggedWith1Language.instrumenter;

Node languageExpression = TestIsNodeTaggedWith1.expressionNode;
Node languageStatement = TestIsNodeTaggedWith1.statementNode;

assertTags(instrumenter.queryTags(languageExpression), InstrumentationTestLanguage.EXPRESSION);
assertTags(instrumenter.queryTags(languageStatement), InstrumentationTestLanguage.STATEMENT);

TestIsNodeTaggedWith1.expressionNode = null;
TestIsNodeTaggedWith1.statementNode = null;

run("EXPRESSION");

// fail if called with nodes from a different language
Node otherLanguageExpression = TestIsNodeTaggedWith1.expressionNode;
try {
instrumenter.queryTags(otherLanguageExpression);
Assert.fail();
} catch (IllegalArgumentException e) {
}

}

@TruffleLanguage.Registration(name = "", version = "", mimeType = "testIsNodeTaggedWith1")
@ProvidedTags({InstrumentationTestLanguage.ExpressionNode.class, StandardTags.StatementTag.class})
public static class TestIsNodeTaggedWith1Language extends TruffleLanguage<Void> {

public static final TestIsNodeTaggedWith1Language INSTANCE = new TestIsNodeTaggedWith1Language();

static Instrumenter instrumenter;

@Override
protected Void createContext(com.oracle.truffle.api.TruffleLanguage.Env env) {
instrumenter = env.lookup(Instrumenter.class);
return null;
}

@Override
protected CallTarget parse(final Source code, Node context, String... argumentNames) throws IOException {
return Truffle.getRuntime().createCallTarget(new RootNode(TestIsNodeTaggedWith1Language.class, null, null) {

@Child private BaseNode base = InstrumentationTestLanguage.parse(code);

@Override
public Object execute(VirtualFrame frame) {
return base.execute(frame);
}
});
}

@Override
protected Object findExportedSymbol(Void context, String globalName, boolean onlyExplicit) {
return null;
}

@Override
protected Object getLanguageGlobal(Void context) {
return null;
}

@Override
protected boolean isObjectOfLanguage(Object object) {
return false;
}

@SuppressWarnings("deprecation")
@Override
protected Visualizer getVisualizer() {
return null;
}

@Override
protected boolean isInstrumentable(Node node) {
return false;
}

@Override
protected WrapperNode createWrapperNode(Node node) {
return null;
}

@Override
protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
return null;
}

}

@Registration(id = "testIsNodeTaggedWith1")
public static class TestIsNodeTaggedWith1 extends TruffleInstrument {

static Node expressionNode;
static Node statementNode;

@Override
protected void onCreate(final Env env) {
env.registerService(env.getInstrumenter());
env.getInstrumenter().attachFactory(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(), new ExecutionEventNodeFactory() {

public ExecutionEventNode create(EventContext context) {
expressionNode = context.getInstrumentedNode();
return new ExecutionEventNode() {
};
}
});

env.getInstrumenter().attachFactory(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.STATEMENT).build(), new ExecutionEventNodeFactory() {

public ExecutionEventNode create(EventContext context) {
statementNode = context.getInstrumentedNode();
return new ExecutionEventNode() {
};
}
});
}
}
Expand Down

0 comments on commit 162e68a

Please sign in to comment.