package com.oracle.truffle.js.test.instrumentation;
import java.util.function.Predicate;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.junit.After;
import org.junit.Test;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
import com.oracle.truffle.api.instrumentation.LoadSourceListener;
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.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
public class GR22048 {
static final ThreadLocal<Object> filterTL = new ThreadLocal<>();
static final ThreadLocal<Context> contextTL = new ThreadLocal<>();
@After
public void cleanup() {
filterTL.set(null);
contextTL.set(null);
}
@Test
public void testReentrant1() {
try (Context context = TestUtil.newContextBuilder().build()) {
contextTL.set(context);
context.getBindings(JavaScriptLanguage.ID).putMember("instrument", new InstrumentInstaller(context.getEngine()));
Source src = Source.newBuilder(JavaScriptLanguage.ID, "" +
"function rootNameIs(name){ return true; }\n" +
"instrument(rootNameIs);\n", "test.js").buildLiteral();
context.eval(src);
}
}
@Test
public void testReentrant2() {
try (Context context = TestUtil.newContextBuilder().build()) {
contextTL.set(context);
context.getBindings(JavaScriptLanguage.ID).putMember("instrument", new InstrumentInstaller(context.getEngine()));
Source src = Source.newBuilder(JavaScriptLanguage.ID, "" +
"(function fib(n) {\n" +
" function rootNameIs(name){ return true; }\n" +
" if (n < 2) return 1;\n" +
" return ((n == 5) ? instrument(rootNameIs) : 0) + fib(n - 1) + fib(n - 2);\n" +
"})\n", "test.js").buildLiteral();
Value f = context.eval(src);
f.execute(10);
}
}
@TruffleInstrument.Registration(id = GR22048Instrument.ID, services = {GR22048Instrument.class})
public static class GR22048Instrument extends TruffleInstrument {
public static final String ID = "GR22048Instrument";
private final Context context;
public GR22048Instrument() {
this.context = contextTL.get();
}
@Override
protected void onCreate(Env env) {
env.registerService(this);
env.getInstrumenter().attachLoadSourceListener(SourceFilter.ANY, (LoadSourceListener) event -> {
}, false);
SourceSectionFilter sourceSectionFilter = SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).rootNameIs(new Predicate<String>() {
private final ThreadLocal<Boolean> querying = new ThreadLocal<>();
@Override
public boolean test(String name) {
Object res = Boolean.FALSE;
Object filter = filterTL.get();
if (filter != null && name != null) {
Boolean prev = querying.get();
try {
if (Boolean.TRUE.equals(prev)) {
return false;
}
querying.set(Boolean.TRUE);
context.enter();
try {
res = InteropLibrary.getFactory().getUncached().execute(filter, name);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
throw new AssertionError(e);
} finally {
context.leave();
}
} finally {
querying.set(prev);
}
}
return res == Boolean.TRUE;
}
}).build();
env.getInstrumenter().attachExecutionEventFactory(sourceSectionFilter, (ExecutionEventNodeFactory) ec -> null);
}
}
@ExportLibrary(InteropLibrary.class)
public static class InstrumentInstaller implements TruffleObject {
private final Engine engine;
public InstrumentInstaller(Engine engine) {
this.engine = engine;
}
@SuppressWarnings("static-method")
@ExportMessage
final boolean isExecutable() {
return true;
}
@TruffleBoundary
@ExportMessage
final Object execute(Object[] arguments) {
filterTL.set(arguments[0]);
engine.getInstruments().get(GR22048Instrument.ID).lookup(GR22048Instrument.class);
return 0;
}
}
}