package com.oracle.truffle.api.instrumentation.test;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.EventContext;
import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
import com.oracle.truffle.api.instrumentation.Instrumenter;
import com.oracle.truffle.api.instrumentation.SourceFilter;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.test.polyglot.ProxyInstrument;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
public class InstrumentationNoExceptionsTest extends AbstractInstrumentationTest {
@Before
@Override
public void setup() {
Context.Builder builder = Context.newBuilder().allowAllAccess(true).option("engine.InstrumentExceptionsAreThrown", "false").out(out).err(err);
setupEnv(builder.build(), new TestDecentInstrument());
engine = context.getEngine();
}
@Test
public void testInstrumentExceptionOnEnter() throws Exception {
AtomicInteger returnedValue = new AtomicInteger();
AtomicInteger returnedExceptional = new AtomicInteger();
instrumentEnv.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(), new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
throw new MyInstrumentException();
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
returnedValue.incrementAndGet();
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
returnedExceptional.incrementAndGet();
}
});
run("ROOT(EXPRESSION)");
String errOut = getErr();
Assert.assertTrue(errOut, errOut.contains("MyInstrumentException"));
Assert.assertEquals(1, returnedValue.get());
Assert.assertEquals(0, returnedExceptional.get());
TestDecentInstrument.assertHitOK(1);
}
@Test
public void testInstrumentExceptionOnReturn() throws Exception {
AtomicInteger entered = new AtomicInteger();
AtomicInteger returnedExceptional = new AtomicInteger();
instrumentEnv.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(), new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
entered.incrementAndGet();
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
throw new MyInstrumentException();
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
returnedExceptional.incrementAndGet();
}
});
run("ROOT(EXPRESSION)");
String errOut = getErr();
Assert.assertTrue(errOut, errOut.contains("MyInstrumentException"));
Assert.assertEquals(1, entered.get());
Assert.assertEquals(0, returnedExceptional.get());
TestDecentInstrument.assertHitOK(1);
}
@Test
public void testInstrumentExceptionOnReturnExceptional() throws Exception {
AtomicInteger entered = new AtomicInteger();
AtomicInteger returnedValue = new AtomicInteger();
instrumentEnv.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(), new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
entered.incrementAndGet();
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
returnedValue.incrementAndGet();
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
throw new MyInstrumentException();
}
});
try {
run("ROOT(EXPRESSION(THROW(test, test)))");
Assert.fail();
} catch (PolyglotException ex) {
Assert.assertEquals("test", ex.getMessage());
}
String errOut = getErr();
Assert.assertTrue(errOut, errOut.contains("MyInstrumentException"));
Assert.assertEquals(1, entered.get());
Assert.assertEquals(0, returnedValue.get());
TestDecentInstrument.assertHitErr(1);
}
@Test
public void testInstrumentExceptionOnUnwind() throws Exception {
testExceptionInInstrument("ROOT(STATEMENT(EXPRESSION))", (ins) -> ins.attachExecutionEventListener(
SourceSectionFilter.ANY,
new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
if (ctx.hasTag(StandardTags.ExpressionTag.class)) {
throw ctx.createUnwind(ctx);
}
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
}
@Override
public Object onUnwind(EventContext ctx, VirtualFrame frame, Object info) {
if (info == ctx) {
throw new MyInstrumentException();
} else {
return 1;
}
}
}), false);
}
@Test
public void testExceptionInFactoryCreate() throws Exception {
testExceptionInInstrument((ins) -> ins.attachExecutionEventFactory(
SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(),
new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext ctx) {
throw new MyInstrumentException();
}
}));
}
@Test
public void testInstrumentExceptionOnInputValue() throws Exception {
SourceSectionFilter filter = SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build();
testExceptionInInstrument("ROOT(EXPRESSION(EXPRESSION))", (ins) -> ins.attachExecutionEventFactory(filter, filter, new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext ctx) {
return new ExecutionEventNode() {
@Override
protected void onInputValue(VirtualFrame frame, EventContext inputContext, int inputIndex, Object inputValue) {
throw new MyInstrumentException();
}
};
}
}), false);
TestDecentInstrument.assertHitOK(2);
}
@Test
public void testInstrumentExceptionOnInputValueFilter() throws Exception {
SourceSectionFilter filter = SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build();
SourceSectionFilter buggySourceSectionFilter = SourceSectionFilter.newBuilder().sourceIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument("ROOT(EXPRESSION(EXPRESSION))", (ins) -> ins.attachExecutionEventFactory(filter, buggySourceSectionFilter, new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext ctx) {
return new ExecutionEventNode() {
};
}
}), false);
TestDecentInstrument.assertHitOK(2);
}
@Test
public void testExceptionInLoadSource() throws Exception {
testExceptionInInstrument((ins) -> ins.attachLoadSourceListener(SourceFilter.ANY, (s) -> {
throw new MyInstrumentException();
}, true));
}
@Test
public void testExceptionInExecuteSource() throws Exception {
testExceptionInInstrument((ins) -> ins.attachExecuteSourceListener(SourceFilter.ANY, (s) -> {
throw new MyInstrumentException();
}, true));
}
@Test
public void testExceptionInLoadSourceFilterPredicate() throws Exception {
SourceFilter buggySourceFilter = SourceFilter.newBuilder().sourceIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument((ins) -> ins.attachLoadSourceListener(buggySourceFilter, (s) -> {
}, true));
}
@Test
public void testExceptionInExecuteSourceFilterPredicate() throws Exception {
SourceFilter buggySourceFilter = SourceFilter.newBuilder().sourceIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument((ins) -> ins.attachExecuteSourceListener(buggySourceFilter, (s) -> {
}, true));
}
@Test
public void testExceptionInLoadSourceSectionFilterPredicate1() throws Exception {
SourceSectionFilter buggySourceSectionFilter = SourceSectionFilter.newBuilder().sourceIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument((ins) -> ins.attachLoadSourceSectionListener(buggySourceSectionFilter, (s) -> {
}, true));
}
@Test
public void testExceptionInLoadSourceSectionFilterPredicate2() throws Exception {
SourceSectionFilter buggySourceSectionFilter = SourceSectionFilter.newBuilder().rootNameIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument((ins) -> ins.attachLoadSourceSectionListener(buggySourceSectionFilter, (s) -> {
}, true));
}
@Test
public void testExceptionInExecutionEventSectionFilterPredicate1() throws Exception {
SourceSectionFilter buggySourceSectionFilter = SourceSectionFilter.newBuilder().sourceIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument((ins) -> ins.attachExecutionEventListener(buggySourceSectionFilter, new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
}
}));
}
@Test
public void testExceptionInExecutionEventSectionFilterPredicate2() throws Exception {
SourceSectionFilter buggySourceSectionFilter = SourceSectionFilter.newBuilder().rootNameIs((s) -> {
throw new MyInstrumentException();
}).build();
testExceptionInInstrument((ins) -> ins.attachExecutionEventListener(buggySourceSectionFilter, new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
}
}));
}
private void testExceptionInInstrument(Consumer<Instrumenter> attachInstrumentation) throws Exception {
testExceptionInInstrument("ROOT(EXPRESSION)", attachInstrumentation, true);
}
private void testExceptionInInstrument(String code, Consumer<Instrumenter> attachInstrumentation, boolean assertDecentInstrument) throws Exception {
attachInstrumentation.accept(instrumentEnv.getInstrumenter());
run(code);
String errOut = getErr();
Assert.assertTrue(errOut, errOut.contains("MyInstrumentException"));
if (assertDecentInstrument) {
TestDecentInstrument.assertHitOK(1);
}
}
public static class TestDecentInstrument extends ProxyInstrument {
static int entered;
static int returnedValue;
static int returnedExceptional;
TestDecentInstrument() {
entered = 0;
returnedValue = 0;
returnedExceptional = 0;
}
static void assertHitOK(int count) {
Assert.assertEquals(count, entered);
Assert.assertEquals(count, returnedValue);
Assert.assertEquals(0, returnedExceptional);
}
static void assertHitErr(int count) {
Assert.assertEquals(count, entered);
Assert.assertEquals(count - 1, returnedValue);
Assert.assertEquals(1, returnedExceptional);
}
@Override
protected void onCreate(Env env) {
super.onCreate(env);
env.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(), new ExecutionEventListener() {
@Override
public void onEnter(EventContext ctx, VirtualFrame frame) {
entered++;
}
@Override
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
returnedValue++;
}
@Override
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
returnedExceptional++;
}
});
}
}
@SuppressWarnings("serial")
private static class MyInstrumentException extends RuntimeException {
}
}