Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise tagging mechanism. Add the notion of @ProvidedTags. #123

Merged
merged 9 commits into from Mar 29, 2016
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