/*
* Copyright (c) 2016, 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.instrumentation.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import com.oracle.truffle.api.instrumentation.Instrumenter;
import com.oracle.truffle.api.instrumentation.LoadSourceSectionEvent;
import com.oracle.truffle.api.instrumentation.LoadSourceSectionListener;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.vm.PolyglotRuntime;
public class SourceSectionListenerTest extends AbstractInstrumentationTest {
@Test
public void testLoadSourceSection1() throws IOException {
testLoadSourceSectionImpl(1);
}
@Test
public void testLoadSourceSection2() throws IOException {
testLoadSourceSectionImpl(2);
}
@Test
public void testLoadSourceSection3() throws IOException {
testLoadSourceSectionImpl(5);
}
private void testLoadSourceSectionImpl(int runTimes) throws IOException {
PolyglotRuntime.Instrument instrument = engine.getRuntime().getInstruments().get("testLoadSourceSection1");
SourceSection[] sourceSections1 = sections("STATEMENT(EXPRESSION, EXPRESSION)", "STATEMENT(EXPRESSION, EXPRESSION)", "EXPRESSION");
final SourceSectionFilter statementFilter = SourceSectionFilter.newBuilder().tagIs(StandardTags.StatementTag.class).build();
final SourceSectionFilter exprFilter = SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build();
Source source1 = sourceSections1[0].getSource();
for (int i = 0; i < runTimes; i++) {
run(source1);
}
instrument.setEnabled(true);
TestLoadSourceSection1 impl = instrument.lookup(TestLoadSourceSection1.class);
assertSections(impl.query(SourceSectionFilter.ANY), sourceSections1);
assertSections(impl.query(statementFilter), sourceSections1[0]);
assertSections(impl.query(exprFilter), sourceSections1[1], sourceSections1[2]);
SourceSection[] sourceSections2 = sections("STATEMENT(EXPRESSION)", "STATEMENT(EXPRESSION)", "EXPRESSION");
Source source2 = sourceSections2[0].getSource();
for (int i = 0; i < runTimes; i++) {
run(source2);
}
assertEvents(impl.allEvents, merge(sourceSections1, sourceSections2));
assertEvents(impl.onlyNewEvents, sourceSections2);
assertEvents(impl.onlyStatements, sourceSections1[0], sourceSections2[0]);
assertEvents(impl.onlyExpressions, sourceSections1[1], sourceSections1[2], sourceSections2[1]);
assertSections(impl.query(SourceSectionFilter.ANY), merge(sourceSections1, sourceSections2));
assertSections(impl.query(statementFilter), sourceSections1[0], sourceSections2[0]);
assertSections(impl.query(exprFilter), sourceSections1[1], sourceSections1[2], sourceSections2[1]);
instrument.setEnabled(false);
SourceSection[] sourceSections3 = sections("STATEMENT(EXPRESSION, EXPRESSION, EXPRESSION)", "STATEMENT(EXPRESSION, EXPRESSION, EXPRESSION)", "EXPRESSION");
Source source3 = sourceSections3[0].getSource();
for (int i = 0; i < runTimes; i++) {
run(source3);
}
assertEvents(impl.allEvents, merge(sourceSections1, sourceSections2));
assertEvents(impl.onlyNewEvents, sourceSections2);
assertEvents(impl.onlyStatements, sourceSections1[0], sourceSections2[0]);
assertEvents(impl.onlyExpressions, sourceSections1[1], sourceSections1[2], sourceSections2[1]);
assertSections(impl.query(SourceSectionFilter.ANY), merge(sourceSections1, sourceSections2, sourceSections3));
assertSections(impl.query(statementFilter), sourceSections1[0], sourceSections2[0], sourceSections3[0]);
assertSections(impl.query(exprFilter), sourceSections1[1], sourceSections1[2], sourceSections2[1], sourceSections3[1], sourceSections3[2], sourceSections3[3]);
instrument.setEnabled(true);
// new instrument needs update
impl = instrument.lookup(TestLoadSourceSection1.class);
assertEvents(impl.onlyNewEvents);
assertEvents(impl.allEvents, merge(sourceSections1, sourceSections2, sourceSections3));
assertEvents(impl.onlyStatements, sourceSections1[0], sourceSections2[0], sourceSections3[0]);
assertEvents(impl.onlyExpressions, sourceSections1[1], sourceSections1[2], sourceSections2[1], sourceSections3[1], sourceSections3[2], sourceSections3[3]);
}
private static SourceSection[] sections(String code, String... match) {
Source source = Source.newBuilder(code).name("sourceSectionTest").mimeType(InstrumentationTestLanguage.MIME_TYPE).build();
List<SourceSection> sections = new ArrayList<>();
for (String matchExpression : match) {
int index = -1;
while ((index = code.indexOf(matchExpression, index + 1)) != -1) {
sections.add(source.createSection(index, matchExpression.length()));
}
}
return sections.toArray(new SourceSection[0]);
}
private static SourceSection[] merge(SourceSection[]... arrays) {
int totalLength = 0;
for (SourceSection[] array : arrays) {
totalLength += array.length;
}
SourceSection[] newArray = new SourceSection[totalLength];
int index = 0;
for (SourceSection[] array : arrays) {
System.arraycopy(array, 0, newArray, index, array.length);
index += array.length;
}
return newArray;
}
private static void assertEvents(List<LoadSourceSectionEvent> actualEvents, SourceSection... expectedSourceSections) {
Assert.assertEquals(expectedSourceSections.length, actualEvents.size());
for (int i = 0; i < expectedSourceSections.length; i++) {
LoadSourceSectionEvent actualEvent = actualEvents.get(i);
SourceSection expectedSourceSection = expectedSourceSections[i];
Assert.assertEquals("index " + i, expectedSourceSection, actualEvent.getSourceSection());
Assert.assertSame("index " + i, actualEvent.getNode().getSourceSection(), actualEvent.getSourceSection());
}
}
private static void assertSections(List<SourceSection> actualSections, SourceSection... expectedSections) {
Assert.assertEquals(expectedSections.length, actualSections.size());
for (int i = 0; i < expectedSections.length; i++) {
Assert.assertEquals("index " + i, expectedSections[i], actualSections.get(i));
}
}
@Registration(id = "testLoadSourceSection1")
public static class TestLoadSourceSection1 extends TruffleInstrument {
List<LoadSourceSectionEvent> onlyNewEvents = new ArrayList<>();
List<LoadSourceSectionEvent> allEvents = new ArrayList<>();
List<LoadSourceSectionEvent> onlyStatements = new ArrayList<>();
List<LoadSourceSectionEvent> onlyExpressions = new ArrayList<>();
Instrumenter instrumenter = null;
@Override
protected void onCreate(Env env) {
instrumenter = env.getInstrumenter();
instrumenter.attachLoadSourceSectionListener(SourceSectionFilter.ANY, new SourceSectionListener(allEvents), true);
instrumenter.attachLoadSourceSectionListener(SourceSectionFilter.ANY,
new SourceSectionListener(onlyNewEvents), false);
instrumenter.attachLoadSourceSectionListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.StatementTag.class).build(),
new SourceSectionListener(onlyStatements), true);
instrumenter.attachLoadSourceSectionListener(SourceSectionFilter.newBuilder().tagIs(InstrumentationTestLanguage.EXPRESSION).build(),
new SourceSectionListener(onlyExpressions), true);
env.registerService(this);
}
private class SourceSectionListener implements LoadSourceSectionListener {
private final List<LoadSourceSectionEvent> events;
SourceSectionListener(List<LoadSourceSectionEvent> events) {
this.events = events;
}
public void onLoad(LoadSourceSectionEvent event) {
events.add(event);
}
}
private List<SourceSection> query(SourceSectionFilter filter) {
return instrumenter.querySourceSections(filter);
}
}
@Test
public void testLoadSourceSectionException() throws IOException {
engine.getRuntime().getInstruments().get("testLoadSourceSectionException").setEnabled(true);
run("STATEMENT");
Assert.assertTrue(getErr().contains("TestLoadSourceSectionExceptionClass"));
}
private static class TestLoadSourceSectionExceptionClass extends RuntimeException {
private static final long serialVersionUID = 1L;
}
@Registration(id = "testLoadSourceSectionException")
public static class TestLoadSourceSectionException extends TruffleInstrument {
@Override
protected void onCreate(Env env) {
env.getInstrumenter().attachLoadSourceSectionListener(SourceSectionFilter.ANY, new LoadSourceSectionListener() {
public void onLoad(LoadSourceSectionEvent event) {
throw new TestLoadSourceSectionExceptionClass();
}
}, true);
}
}
}