com.oracle.truffle.api.instrumentation.test.InstrumentationUpdateTest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of truffle-instrument-test Show documentation
Show all versions of truffle-instrument-test Show documentation
Instrumentation tests including InstrumentationTestLanguage.
The newest version!
/*
* Copyright (c) 2017, 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.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import org.graalvm.polyglot.Context;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.instrumentation.EventContext;
import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
import com.oracle.truffle.api.instrumentation.Instrumentable;
import com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode;
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.TruffleInstrument;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
public class InstrumentationUpdateTest {
private static Function run;
private Context context;
private final List executionEvents = new ArrayList<>();
private final List loadEvents = new ArrayList<>();
private InstrumentationUpdateLanguage language;
private TruffleInstrument.Env instrumentEnv;
private EventBinding> eventBinding;
@Before
public void setup() {
context = Context.create("InstrumentationUpdateLanguage");
run = (lang) -> {
this.language = lang;
return RootNode.createConstantNode(42);
};
context.eval("InstrumentationUpdateLanguage", "");
instrumentEnv = context.getEngine().getInstruments().get("InstrumentationUpdateInstrument").lookup(TruffleInstrument.Env.class);
}
@After
public void teardown() {
context.close();
}
/*
* Test that if indexed based filters were applied we are notified if the an instrumentable node
* was not contained within a root node's source section. (that was not the case at some point).
*/
@Test
public void testNotWithinRootSourceSection() {
setEventFilter(SourceSectionFilter.newBuilder().indexIn(6, 5).build());
MyRoot evalRoot = eval((lang) -> {
MyRoot root = new MyRoot(language, language.request.getSource().createSection(0, 5));
root.child = new InstrumentationUpdateNode(language.request.getSource().createSection(6, 5));
return root;
}, "root1, root2");
assertLoaded(evalRoot.getChild());
assertExecuted(evalRoot.getChild());
}
/*
* Test that we can change an instrumentable node after the first execute by notifying the
* framework.
*/
@Test
public void testInsertInstrumentableNode() {
setEventFilter(SourceSectionFilter.newBuilder().indexIn(6, 5).build());
MyRoot evalRoot = eval((lang) -> {
MyRoot root = new MyRoot(language, language.request.getSource().createSection(0, 5));
root.child = new InstrumentationUpdateNode(language.request.getSource().createSection(0, 5));
return root;
}, "root1, root2");
evalRoot.setChild(new InstrumentationUpdateNode(evalRoot.getSourceSection().getSource().createSection(6, 5)));
assertLoaded();
assertExecuted();
evalRoot.getCallTarget().call();
assertLoaded();
assertExecuted();
// trigger the notification, now we get events
evalRoot.notifyChildInsert();
assertLoaded(evalRoot.getChild());
assertExecuted();
evalRoot.getCallTarget().call();
assertLoaded(evalRoot.getChild());
assertExecuted(evalRoot.getChild());
}
/*
* Test that we can change instrumentable nodes after the first execute by notifying the
* framework.
*/
@Test
public void testInsertInstrumentableNodes() {
setEventFilter(SourceSectionFilter.newBuilder().indexIn(0, 6).build());
MyRoot evalRoot = eval((lang) -> {
MyRoot root = new MyRoot(language, language.request.getSource().createSection(0, 5));
return root;
}, "abcdef");
evalRoot.getCallTarget().call();
assertLoaded();
assertExecuted();
evalRoot.setChild(new InstrumentationUpdateNode(evalRoot.getSourceSection().getSource().createSection(0, 1)));
evalRoot.notifyChildInsert();
assertLoaded(evalRoot.getChild());
assertExecuted();
evalRoot.getCallTarget().call();
assertLoaded(evalRoot.getChild());
assertExecuted(evalRoot.getChild());
executionEvents.clear();
loadEvents.clear();
evalRoot.setChild(new InstrumentationUpdateNode(evalRoot.getSourceSection().getSource().createSection(1, 2)));
evalRoot.getChild().setChild(new InstrumentationUpdateNode(evalRoot.getSourceSection().getSource().createSection(2, 3)));
evalRoot.notifyChildInsert();
assertLoaded(evalRoot.getChild(), evalRoot.getChild().getChild());
assertExecuted();
evalRoot.getCallTarget().call();
assertLoaded(evalRoot.getChild(), evalRoot.getChild().getChild());
assertExecuted(evalRoot.getChild(), evalRoot.getChild().getChild());
}
private void assertLoaded(Node... children) {
Iterator loadIterator = loadEvents.iterator();
for (Node loadedChild : children) {
Assert.assertTrue(loadIterator.hasNext());
Assert.assertSame(loadedChild, loadIterator.next().getNode());
}
Assert.assertFalse(loadIterator.hasNext());
}
private void assertExecuted(Node... children) {
Iterator executeIterator = executionEvents.iterator();
for (Node loadedChild : children) {
Assert.assertTrue(executeIterator.hasNext());
Assert.assertSame(loadedChild, executeIterator.next().getInstrumentedNode());
}
Assert.assertFalse(executeIterator.hasNext());
}
private void setEventFilter(SourceSectionFilter filter) {
EventBinding> old = this.eventBinding;
eventBinding = instrumentEnv.getInstrumenter().attachListener(filter, new ExecutionEventListener() {
public void onReturnValue(EventContext ctx, VirtualFrame frame, Object result) {
}
public void onReturnExceptional(EventContext ctx, VirtualFrame frame, Throwable exception) {
}
public void onEnter(EventContext ctx, VirtualFrame frame) {
executionEvents.add(ctx);
}
});
instrumentEnv.getInstrumenter().attachLoadSourceSectionListener(filter, new LoadSourceSectionListener() {
public void onLoad(LoadSourceSectionEvent event) {
loadEvents.add(event);
}
}, true);
// dispose afterwards to avoid disposal of instrumentation wrappers
if (old != null) {
old.dispose();
}
}
@SuppressWarnings("unchecked")
private T eval(Function target, String source) {
executionEvents.clear();
RootNode[] roots = new RootNode[1];
run = (lang) -> {
T root = target.apply(lang);
roots[0] = root;
return root;
};
org.graalvm.polyglot.Source src = org.graalvm.polyglot.Source.create("InstrumentationUpdateLanguage", source);
context.eval(src);
return (T) roots[0];
}
@Instrumentable(factory = InstrumentationUpdateNodeWrapper.class)
public static class InstrumentationUpdateNode extends Node {
private final SourceSection sourceSection;
@Child InstrumentationUpdateNode child;
public InstrumentationUpdateNode(SourceSection sourceSection) {
this.sourceSection = sourceSection;
}
public void setChild(InstrumentationUpdateNode child) {
this.child = insert(child);
}
public InstrumentationUpdateNode getChild() {
if (child instanceof WrapperNode) {
return (InstrumentationUpdateNode) ((WrapperNode) child).getDelegateNode();
}
return child;
}
void notifyChildInsert() {
notifyInserted(child);
}
public void execute() {
if (child != null) {
child.execute();
}
}
@Override
public SourceSection getSourceSection() {
return sourceSection;
}
}
private static class MyRoot extends RootNode {
final SourceSection sourceSection;
@Child InstrumentationUpdateNode child;
MyRoot(TruffleLanguage> language, SourceSection sourceSection) {
super(language);
this.sourceSection = sourceSection;
}
public void setChild(InstrumentationUpdateNode child) {
this.child = insert(child);
}
public InstrumentationUpdateNode getChild() {
if (child instanceof WrapperNode) {
return (InstrumentationUpdateNode) ((WrapperNode) child).getDelegateNode();
}
return child;
}
@Override
public SourceSection getSourceSection() {
return sourceSection;
}
void notifyChildInsert() {
notifyInserted(child);
}
@Override
public Object execute(VirtualFrame frame) {
if (child != null) {
child.execute();
}
return "";
}
}
@TruffleLanguage.Registration(id = "InstrumentationUpdateLanguage", name = "", version = "", mimeType = "InstrumentationUpdateLanguage")
public static class InstrumentationUpdateLanguage extends TruffleLanguage © 2015 - 2025 Weber Informatics LLC | Privacy Policy