package com.oracle.truffle.api.test.polyglot;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import org.graalvm.options.OptionCategory;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
import org.graalvm.options.OptionStability;
import org.graalvm.options.OptionValues;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.PolyglotAccess;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.FileSystem;
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.CompilerDirectives;
import com.oracle.truffle.api.InstrumentInfo;
import com.oracle.truffle.api.Option;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleContext;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.ContextsListener;
import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.RootNode;
import java.util.Optional;
import org.graalvm.collections.Pair;
public class ContextPreInitializationTest {
static final String FIRST = "ContextPreInitializationFirst";
static final String SECOND = "ContextPreInitializationSecond";
static final String INTERNAL = "ContextPreInitializationInternal";
static final String SHARED = "ContextPreInitializationShared";
private static final AtomicInteger NEXT_ORDER_INDEX = new AtomicInteger();
private static final String SYS_OPTION1_KEY = "polyglot." + FIRST + ".Option1";
private static final String SYS_OPTION2_KEY = "polyglot." + FIRST + ".Option2";
private static final List<CountingContext> emittedContexts = new ArrayList<>();
private static final Set<String> patchableLanguages = new HashSet<>();
@Before
public void setUp() throws Exception {
Class.forName("org.graalvm.polyglot.Engine$ImplHolder", true, ContextPreInitializationTest.class.getClassLoader());
}
@After
public void tearDown() throws Exception {
ContextPreInitializationTestFirstLanguage.callDependentLanguage = false;
ContextPreInitializationTestSecondLanguage.callDependentLanguageInCreate = false;
ContextPreInitializationTestSecondLanguage.callDependentLanguageInPatch = false;
ContextPreInitializationTestSecondLanguage.lookupService = false;
ContextPreInitializationFirstInstrument.actions = null;
BaseLanguage.actions.clear();
BaseLanguage.parseStdOutOutput.clear();
BaseLanguage.parseStdErrOutput.clear();
resetSystemPropertiesOptions();
resetLanguageHomes();
patchableLanguages.clear();
emittedContexts.clear();
}
@Test
public void testOutputNoLanguagePreInitialization() throws Exception {
setPatchable();
final String stdOutContent = "output";
final String stdErrContent = "error";
BaseLanguage.parseStdOutOutput.put(FIRST, stdOutContent);
BaseLanguage.parseStdErrOutput.put(FIRST, stdErrContent);
doContextPreinitialize();
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(0, contexts.size());
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ByteArrayOutputStream err = new ByteArrayOutputStream();
try (Context ctx = Context.newBuilder().out(out).err(err).build()) {
final Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
assertEquals(stdOutContent, new String(out.toByteArray(), "UTF-8"));
assertEquals(stdErrContent, new String(err.toByteArray(), "UTF-8"));
}
}
@Test
public void testOutputSingleLanguagePreInitialization() throws Exception {
setPatchable(FIRST);
final String firstStdOutContent = "first-output";
final String firstStdErrContent = "first-error";
final String secondStdOutContent = "second-output";
final String secondStdErrContent = "second-error";
BaseLanguage.parseStdOutOutput.put(FIRST, firstStdOutContent);
BaseLanguage.parseStdErrOutput.put(FIRST, firstStdErrContent);
BaseLanguage.parseStdOutOutput.put(SECOND, secondStdOutContent);
BaseLanguage.parseStdErrOutput.put(SECOND, secondStdErrContent);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ByteArrayOutputStream err = new ByteArrayOutputStream();
try (Context ctx = Context.newBuilder().out(out).err(err).build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(firstStdOutContent, new String(out.toByteArray(), "UTF-8"));
assertEquals(firstStdErrContent, new String(err.toByteArray(), "UTF-8"));
out.reset();
err.reset();
res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(secondStdOutContent, new String(out.toByteArray(), "UTF-8"));
assertEquals(secondStdErrContent, new String(err.toByteArray(), "UTF-8"));
}
}
@Test
public void testArgumentsSingleLanguagePreInitialization() throws Exception {
setPatchable();
doContextPreinitialize();
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(0, contexts.size());
try (Context ctx = Context.newBuilder().arguments(FIRST, new String[]{"a", "b"}).build()) {
final Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext context = findContext(FIRST, contexts);
assertNotNull(context);
assertEquals(Arrays.asList("a", "b"), context.arguments);
}
}
@Test
public void testArgumentsSingleLanguagePreInitialization2() throws Exception {
setPatchable(FIRST);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
try (Context ctx = Context.newBuilder().arguments(FIRST, new String[]{"a", "b"}).arguments(SECOND, new String[]{"c", "d"}).build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext context = findContext(FIRST, contexts);
assertNotNull(context);
assertEquals(Arrays.asList("a", "b"), context.arguments);
res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
context = findContext(SECOND, contexts);
assertNotNull(context);
assertEquals(Arrays.asList("c", "d"), context.arguments);
}
}
@Test
public void testNoLanguagePreInitialization() throws Exception {
setPatchable();
doContextPreinitialize();
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(0, contexts.size());
final Context ctx = Context.create();
final Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext context = findContext(FIRST, contexts);
assertNotNull(context);
assertEquals(1, context.createContextCount);
assertEquals(1, context.initializeContextCount);
assertEquals(0, context.patchContextCount);
assertEquals(0, context.disposeContextCount);
assertEquals(1, context.initializeThreadCount);
assertEquals(0, context.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, context.createContextCount);
assertEquals(1, context.initializeContextCount);
assertEquals(0, context.patchContextCount);
assertEquals(1, context.disposeContextCount);
assertEquals(1, context.initializeThreadCount);
assertEquals(1, context.disposeThreadCount);
}
@Test
public void testSingleLanguagePreInitialization() throws Exception {
setPatchable(FIRST);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
}
@Test
public void testMoreLanguagesPreInitialization() throws Exception {
setPatchable(FIRST, SECOND);
doContextPreinitialize(FIRST, SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
}
@Test
public void testMoreLanguagesPreInitializationFailedPatch() throws Exception {
setPatchable(FIRST);
doContextPreinitialize(FIRST, SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(3, contexts.size());
Collection<? extends CountingContext> firstLangCtxs = findContexts(FIRST, contexts);
firstLangCtxs.remove(firstLangCtx);
assertFalse(firstLangCtxs.isEmpty());
final CountingContext firstLangCtx2 = firstLangCtxs.iterator().next();
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(1, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(0, firstLangCtx2.disposeContextCount);
assertEquals(1, firstLangCtx2.initializeThreadCount);
assertEquals(0, firstLangCtx2.disposeThreadCount);
ctx.close();
assertEquals(3, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(1, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
}
@Test
public void testSystemPropertiesOptionsSuccessfulPatch() throws Exception {
System.setProperty(SYS_OPTION1_KEY, "true");
setPatchable(FIRST);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertTrue(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertFalse(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
firstLangCtx.optionValues.clear();
System.clearProperty(SYS_OPTION1_KEY);
System.setProperty(SYS_OPTION2_KEY, "true");
Context ctx = Context.create();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
assertFalse(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertTrue(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
ctx.close();
ctx = Context.create();
res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
assertFalse(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertTrue(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
ctx.close();
}
@Test
public void testSystemPropertiesOptionsFailedPatch() throws Exception {
System.setProperty(SYS_OPTION1_KEY, "true");
setPatchable();
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertTrue(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertFalse(firstLangCtx.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
firstLangCtx.optionValues.clear();
System.clearProperty(SYS_OPTION1_KEY);
System.setProperty(SYS_OPTION2_KEY, "true");
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
contexts.remove(firstLangCtx);
final CountingContext firstLangCtx2 = findContext(FIRST, contexts);
assertNotNull(firstLangCtx2);
assertFalse(firstLangCtx2.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertTrue(firstLangCtx2.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
ctx.close();
}
@Test
public void testContextOptionsNoLanguagePreInitialization() throws Exception {
setPatchable();
doContextPreinitialize();
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(0, contexts.size());
final Context ctx = Context.newBuilder().option(FIRST + ".Option1", "true").build();
final Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext context = findContext(FIRST, contexts);
assertNotNull(context);
assertTrue(context.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertFalse(context.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
ctx.close();
}
@Test
public void testContextOptionsSingleLanguagePreInitialization() throws Exception {
setPatchable(FIRST);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext context = findContext(FIRST, contexts);
assertNotNull(context);
final Context ctx = Context.newBuilder().option(FIRST + ".Option1", "true").build();
final Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertTrue(context.optionValues.get(ContextPreInitializationTestFirstLanguage.Option1));
assertFalse(context.optionValues.get(ContextPreInitializationTestFirstLanguage.Option2));
ctx.close();
}
@SuppressWarnings("try")
@Test
public void testContextOptionsCompatibleAfterSuccessfulPatch() throws Exception {
setPatchable(SHARED);
doContextPreinitialize(SHARED);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
final CountingContext firstLangCtx = findContext(SHARED, contexts);
final BaseLanguage firstLang = firstLangCtx.language;
assertNotNull(firstLangCtx);
assertFalse(firstLangCtx.optionValues.get(ContextPreInitializationTestSharedLanguage.Option1));
assertFalse(firstLangCtx.optionValues.get(ContextPreInitializationTestSharedLanguage.Option2));
try (Context ctx = Context.newBuilder().option(SHARED + ".Option1", "true").build()) {
Value res = ctx.eval(Source.create(SHARED, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext langCtx = contexts.get(0);
assertTrue(langCtx.optionValues.get(ContextPreInitializationTestSharedLanguage.Option1));
assertFalse(langCtx.optionValues.get(ContextPreInitializationTestSharedLanguage.Option2));
assertSame(firstLang, langCtx.language);
assertSame(firstLangCtx, langCtx);
ctx.enter();
try (TruffleContext truffleContext = langCtx.environment().newContextBuilder().build()) {
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
langCtx = contexts.get(1);
assertTrue(langCtx.optionValues.get(ContextPreInitializationTestSharedLanguage.Option1));
assertFalse(langCtx.optionValues.get(ContextPreInitializationTestSharedLanguage.Option2));
assertSame("Patched pre-initialized language should be shared with the second context since the options are compatible.", firstLang, langCtx.language);
} finally {
ctx.leave();
}
}
}
@Test
public void testDependentLanguagePreInitializationSuccessfulPatch() throws Exception {
setPatchable(SECOND, FIRST, INTERNAL);
ContextPreInitializationTestSecondLanguage.callDependentLanguageInCreate = true;
ContextPreInitializationTestFirstLanguage.callDependentLanguage = true;
doContextPreinitialize(SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(3, contexts.size());
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
final CountingContext internalLangCtx = findContext(INTERNAL, contexts);
assertNotNull(internalLangCtx);
assertEquals(1, internalLangCtx.createContextCount);
assertEquals(1, internalLangCtx.initializeContextCount);
assertEquals(0, internalLangCtx.patchContextCount);
assertEquals(0, internalLangCtx.disposeContextCount);
assertEquals(0, internalLangCtx.initializeThreadCount);
assertEquals(0, internalLangCtx.disposeThreadCount);
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(3, contexts.size());
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, internalLangCtx.createContextCount);
assertEquals(1, internalLangCtx.initializeContextCount);
assertEquals(1, internalLangCtx.patchContextCount);
assertEquals(0, internalLangCtx.disposeContextCount);
assertEquals(1, internalLangCtx.initializeThreadCount);
assertEquals(0, internalLangCtx.disposeThreadCount);
assertTrue(internalLangCtx.patchContextOrder < firstLangCtx.patchContextOrder);
assertTrue(firstLangCtx.patchContextOrder < secondLangCtx.patchContextOrder);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(3, contexts.size());
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, internalLangCtx.createContextCount);
assertEquals(1, internalLangCtx.initializeContextCount);
assertEquals(1, internalLangCtx.patchContextCount);
assertEquals(1, internalLangCtx.disposeContextCount);
assertEquals(1, internalLangCtx.initializeThreadCount);
assertEquals(1, internalLangCtx.disposeThreadCount);
}
@Test
public void testDependentLanguagePreInitializationFailedPatch() throws Exception {
setPatchable(SECOND);
ContextPreInitializationTestSecondLanguage.callDependentLanguageInCreate = true;
ContextPreInitializationTestFirstLanguage.callDependentLanguage = true;
doContextPreinitialize(SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(3, contexts.size());
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
final CountingContext internalLangCtx = findContext(INTERNAL, contexts);
assertNotNull(internalLangCtx);
assertEquals(1, internalLangCtx.createContextCount);
assertEquals(1, internalLangCtx.initializeContextCount);
assertEquals(0, internalLangCtx.patchContextCount);
assertEquals(0, internalLangCtx.disposeContextCount);
assertEquals(0, internalLangCtx.initializeThreadCount);
assertEquals(0, internalLangCtx.disposeThreadCount);
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(6, contexts.size());
Collection<? extends CountingContext> ctxs = findContexts(SECOND, contexts);
ctxs.remove(secondLangCtx);
assertEquals(1, ctxs.size());
final CountingContext secondLangCtx2 = ctxs.iterator().next();
ctxs = findContexts(FIRST, contexts);
ctxs.remove(firstLangCtx);
assertEquals(1, ctxs.size());
final CountingContext firstLangCtx2 = ctxs.iterator().next();
ctxs = findContexts(INTERNAL, contexts);
ctxs.remove(internalLangCtx);
assertEquals(1, ctxs.size());
final CountingContext internalLangCtx2 = ctxs.iterator().next();
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, internalLangCtx.createContextCount);
assertEquals(1, internalLangCtx.initializeContextCount);
assertEquals(1, internalLangCtx.patchContextCount);
assertEquals(1, internalLangCtx.disposeContextCount);
assertEquals(1, internalLangCtx.initializeThreadCount);
assertEquals(1, internalLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx2.createContextCount);
assertEquals(1, secondLangCtx2.initializeContextCount);
assertEquals(0, secondLangCtx2.patchContextCount);
assertEquals(0, secondLangCtx2.disposeContextCount);
assertEquals(1, secondLangCtx2.initializeThreadCount);
assertEquals(0, secondLangCtx2.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(1, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(0, firstLangCtx2.disposeContextCount);
assertEquals(1, firstLangCtx2.initializeThreadCount);
assertEquals(0, firstLangCtx2.disposeThreadCount);
assertEquals(1, internalLangCtx2.createContextCount);
assertEquals(1, internalLangCtx2.initializeContextCount);
assertEquals(0, internalLangCtx2.patchContextCount);
assertEquals(0, internalLangCtx2.disposeContextCount);
assertEquals(1, internalLangCtx2.initializeThreadCount);
assertEquals(0, internalLangCtx2.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(6, contexts.size());
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, internalLangCtx.createContextCount);
assertEquals(1, internalLangCtx.initializeContextCount);
assertEquals(1, internalLangCtx.patchContextCount);
assertEquals(1, internalLangCtx.disposeContextCount);
assertEquals(1, internalLangCtx.initializeThreadCount);
assertEquals(1, internalLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx2.createContextCount);
assertEquals(1, secondLangCtx2.initializeContextCount);
assertEquals(0, secondLangCtx2.patchContextCount);
assertEquals(1, secondLangCtx2.disposeContextCount);
assertEquals(1, secondLangCtx2.initializeThreadCount);
assertEquals(1, secondLangCtx2.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(1, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(1, firstLangCtx2.disposeContextCount);
assertEquals(1, firstLangCtx2.initializeThreadCount);
assertEquals(1, firstLangCtx2.disposeThreadCount);
assertEquals(1, internalLangCtx2.createContextCount);
assertEquals(1, internalLangCtx2.initializeContextCount);
assertEquals(0, internalLangCtx2.patchContextCount);
assertEquals(1, internalLangCtx2.disposeContextCount);
assertEquals(1, internalLangCtx2.initializeThreadCount);
assertEquals(1, internalLangCtx2.disposeThreadCount);
}
@Test
public void testSingleLanguageExceptionFromContextPatch() throws Exception {
setPatchable(FIRST);
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
throw new RuntimeException("patchContext() exception");
});
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
try {
Context.create();
Assert.fail("Should not reach here.");
} catch (PolyglotException pe) {
}
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
}
@Test
public void testMoreLanguagesExceptionFromContextPatch() throws Exception {
setPatchable(FIRST, SECOND);
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
throw new RuntimeException("patchContext() exception");
});
doContextPreinitialize(FIRST, SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
try {
Context.create();
Assert.fail("Should not reach here.");
} catch (PolyglotException pe) {
}
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
}
@Test
public void testLanguageHome() throws Exception {
setPatchable(FIRST);
String expectedPath = Paths.get(String.format("/compile-graalvm/languages/%s", FIRST)).toString();
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), expectedPath);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
Assert.assertEquals(expectedPath, firstLangCtx.languageHome);
expectedPath = Paths.get(String.format("/run-graalvm/languages/%s", FIRST)).toString();
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), expectedPath);
try (Context ctx = Context.newBuilder().build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
Assert.assertEquals(expectedPath, firstLangCtx.languageHome);
}
}
@Test
public void testTemporaryEngine() throws Exception {
setPatchable(FIRST);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
Engine.create().close();
final Context ctx = Context.create();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
}
@Test
public void testLogging() throws Exception {
setPatchable(FIRST);
final PrintStream origErr = System.err;
final ByteArrayOutputStream preInitErr = new ByteArrayOutputStream();
String origLogFile = System.getProperty("polyglot.log.file");
try (PrintStream printStream = new PrintStream(preInitErr)) {
System.setErr(printStream);
System.setProperty("polyglot.log.engine.level", "FINE");
System.clearProperty("polyglot.log.file");
doContextPreinitialize(FIRST);
} finally {
System.setErr(origErr);
System.clearProperty("polyglot.log.engine.level");
if (origLogFile != null) {
System.setProperty("polyglot.log.file", origLogFile);
}
}
final String preInitLog = preInitErr.toString("UTF-8");
assertTrue(preInitLog.contains("Pre-initialized context for language: ContextPreInitializationFirst"));
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
final TestHandler testHandler = new TestHandler("engine.com.oracle.truffle.polyglot.PolyglotLanguageContext");
final Context ctx = Context.newBuilder().option("log.engine.level", "FINE").logHandler(testHandler).build();
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
assertEquals(1, testHandler.logs.size());
assertEquals(FIRST, testHandler.logs.get(0).getParameters()[0]);
assertEquals("Successfully patched context of language: {0}", testHandler.logs.get(0).getMessage());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
}
@Test
public void testFileSystemSwitch() throws Exception {
setPatchable(FIRST);
Path tmpDir = Files.createTempDirectory("testFileSystemSwitch");
Path buildHome = tmpDir.resolve("build");
Path execHome = tmpDir.resolve("exec");
Files.createDirectories(buildHome);
Files.createDirectories(execHome);
Path buildFile = write(buildHome.resolve("test"), "build");
Path execFile = write(execHome.resolve("test"), "exec");
Path noLangHomeFile = write(tmpDir.resolve("test"), "abs");
try {
List<TruffleFile> files = new ArrayList<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
TruffleFile f = env.getPublicTruffleFile(buildFile.toString());
files.add(f);
f = env.getPublicTruffleFile(noLangHomeFile.toString());
files.add(f);
f = env.getPublicTruffleFile("relative_file");
files.add(f);
});
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
try {
assertTrue(files.get(0).isAbsolute());
assertEquals(execFile.toString(), files.get(0).getPath());
assertEquals("exec", read(files.get(0)).trim());
assertTrue(files.get(1).isAbsolute());
assertEquals(noLangHomeFile.toString(), files.get(1).getPath());
assertEquals("abs", read(files.get(1)).trim());
assertFalse(files.get(2).isAbsolute());
assertEquals("relative_file", files.get(2).getPath());
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
});
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), buildHome.toString());
doContextPreinitialize(FIRST);
assertFalse(files.isEmpty());
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), execHome.toString());
try (Context ctx = Context.newBuilder().allowIO(true).build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
}
} finally {
delete(tmpDir);
}
}
@Test
public void testServiceLookupSuccessfulPatch() throws Exception {
setPatchable(FIRST, SECOND);
ContextPreInitializationTestSecondLanguage.lookupService = true;
doContextPreinitialize(SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(0, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
final Context ctx = Context.newBuilder().allowPolyglotAccess(PolyglotAccess.ALL).option(FIRST + ".ServiceKind", Service.Kind.IMAGE_EXECUTION_TIME.name()).build();
Value res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(0, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(0, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
}
@Test
public void testServiceLookupFailedPatch() throws Exception {
setPatchable(SECOND);
ContextPreInitializationTestSecondLanguage.lookupService = true;
doContextPreinitialize(SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(0, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
final Context ctx = Context.newBuilder().allowPolyglotAccess(PolyglotAccess.ALL).option(FIRST + ".ServiceKind", Service.Kind.IMAGE_EXECUTION_TIME.name()).build();
Value res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(4, contexts.size());
Collection<? extends CountingContext> ctxs = findContexts(SECOND, contexts);
ctxs.remove(secondLangCtx);
assertEquals(1, ctxs.size());
final CountingContext secondLangCtx2 = ctxs.iterator().next();
ctxs = findContexts(FIRST, contexts);
ctxs.remove(firstLangCtx);
assertEquals(1, ctxs.size());
final CountingContext firstLangCtx2 = ctxs.iterator().next();
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(0, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx2.createContextCount);
assertEquals(1, secondLangCtx2.initializeContextCount);
assertEquals(0, secondLangCtx2.patchContextCount);
assertEquals(0, secondLangCtx2.disposeContextCount);
assertEquals(1, secondLangCtx2.initializeThreadCount);
assertEquals(0, secondLangCtx2.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(0, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(0, firstLangCtx2.disposeContextCount);
assertEquals(0, firstLangCtx2.initializeThreadCount);
assertEquals(0, firstLangCtx2.disposeThreadCount);
ctx.close();
contexts = new ArrayList<>(emittedContexts);
assertEquals(4, contexts.size());
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(0, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx2.createContextCount);
assertEquals(1, secondLangCtx2.initializeContextCount);
assertEquals(0, secondLangCtx2.patchContextCount);
assertEquals(1, secondLangCtx2.disposeContextCount);
assertEquals(1, secondLangCtx2.initializeThreadCount);
assertEquals(1, secondLangCtx2.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(0, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(1, firstLangCtx2.disposeContextCount);
assertEquals(0, firstLangCtx2.initializeThreadCount);
assertEquals(1, firstLangCtx2.disposeThreadCount);
}
@Test
public void testInstrumentsEvents() throws Exception {
ContextPreInitializationFirstInstrument.actions = Collections.singletonMap("onLanguageContextInitialized", (e) -> {
if (FIRST.equals(e.language.getId())) {
final CountDownLatch signal = new CountDownLatch(1);
new Thread(new Runnable() {
@Override
public void run() {
InstrumentInfo instrument = e.env.getInstruments().get(ContextPreInitializationSecondInstrument.ID);
e.env.lookup(instrument, Service.class);
signal.countDown();
}
}).start();
try {
signal.await();
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
});
ContextPreInitializationTestSecondLanguage.callDependentLanguageInPatch = true;
setPatchable(FIRST, SECOND);
doContextPreinitialize(SECOND);
try (Context ctx = Context.newBuilder().option(ContextPreInitializationFirstInstrument.ID, "true").build()) {
Value res = ctx.eval(Source.create(SECOND, "test"));
assertEquals("test", res.asString());
}
}
@Test
public void testSingeInstrumentInstanceAfterContextPatch() throws Exception {
AtomicInteger instrumentCreateCount = new AtomicInteger();
ContextPreInitializationFirstInstrument.actions = Collections.singletonMap("onCreate", (e) -> {
instrumentCreateCount.incrementAndGet();
});
setPatchable(FIRST);
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, instrumentCreateCount.get());
try (Context ctx = Context.newBuilder().option(ContextPreInitializationFirstInstrument.ID, "true").build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
assertEquals(1, emittedContexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, instrumentCreateCount.get());
}
}
@Test
public void testInstrumentCreatedAfterFailedContextPatch() throws Exception {
AtomicInteger instrumentCreateCount = new AtomicInteger();
ContextPreInitializationFirstInstrument.actions = Collections.singletonMap("onCreate", (e) -> {
instrumentCreateCount.incrementAndGet();
});
setPatchable();
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, instrumentCreateCount.get());
try (Context ctx = Context.newBuilder().option(ContextPreInitializationFirstInstrument.ID, "true").build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
contexts.remove(firstLangCtx);
CountingContext newFirstLangCtx = findContext(FIRST, contexts);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, newFirstLangCtx.createContextCount);
assertEquals(1, newFirstLangCtx.initializeContextCount);
assertEquals(0, newFirstLangCtx.patchContextCount);
assertEquals(0, newFirstLangCtx.disposeContextCount);
assertEquals(1, newFirstLangCtx.initializeThreadCount);
assertEquals(0, newFirstLangCtx.disposeThreadCount);
assertEquals(1, instrumentCreateCount.get());
}
}
@Test
public void testSetCurrentWorkingDirectory() throws Exception {
setPatchable(FIRST);
Path newCwd = Files.createTempDirectory("testSetCWD");
Path absoluteFolder = Files.createTempDirectory("testSetCWDAbs");
try {
List<TruffleFile> filesFromPreInitialization = new ArrayList<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
TruffleFile f = env.getPublicTruffleFile("relative");
filesFromPreInitialization.add(f);
f = env.getPublicTruffleFile(absoluteFolder.toString());
filesFromPreInitialization.add(f);
f = env.getInternalTruffleFile("relative");
filesFromPreInitialization.add(f);
f = env.getInternalTruffleFile(absoluteFolder.toString());
filesFromPreInitialization.add(f);
});
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
Assert.assertEquals(newCwd.resolve("relative"), Paths.get(filesFromPreInitialization.get(0).getAbsoluteFile().getPath()));
Assert.assertEquals(absoluteFolder, Paths.get(filesFromPreInitialization.get(1).getAbsoluteFile().getPath()));
Assert.assertEquals(newCwd.resolve("relative"), Paths.get(filesFromPreInitialization.get(2).getAbsoluteFile().getPath()));
Assert.assertEquals(absoluteFolder, Paths.get(filesFromPreInitialization.get(3).getAbsoluteFile().getPath()));
});
doContextPreinitialize(FIRST);
assertFalse(filesFromPreInitialization.isEmpty());
try (Context ctx = Context.newBuilder().allowIO(true).currentWorkingDirectory(newCwd).build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
}
} finally {
delete(newCwd);
delete(absoluteFolder);
}
}
@Test
@SuppressWarnings("try")
public void testSourceInLanguageHome() throws Exception {
setPatchable(FIRST);
Path testFolder = Files.createTempDirectory("testSources").toRealPath();
try {
Path buildtimeHome = Files.createDirectories(testFolder.resolve("build").resolve(FIRST));
Path buildtimeResource = Files.write(buildtimeHome.resolve("testSourceInLanguageHome.test"), Collections.singleton("test"));
Path runtimeHome = Files.createDirectories(testFolder.resolve("exec").resolve(FIRST));
Path runtimeResource = Files.copy(buildtimeResource, runtimeHome.resolve(buildtimeResource.getFileName()));
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), buildtimeHome.toString());
AtomicReference<com.oracle.truffle.api.source.Source> buildtimeCachedSource = new AtomicReference<>();
AtomicReference<com.oracle.truffle.api.source.Source> buildtimeUnCachedSource = new AtomicReference<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
buildtimeCachedSource.set(createSource(env, buildtimeResource, true));
buildtimeUnCachedSource.set(createSource(env, buildtimeResource, false));
assertFalse(buildtimeCachedSource.get() == buildtimeUnCachedSource.get());
});
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertEquals(1, firstLangCtx.initializeContextCount);
assertNotNull(buildtimeCachedSource.get());
assertNotNull(buildtimeUnCachedSource.get());
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), runtimeHome.toString());
AtomicReference<com.oracle.truffle.api.source.Source> runtimeCachedSource = new AtomicReference<>();
AtomicReference<com.oracle.truffle.api.source.Source> runtimeUnCachedSource = new AtomicReference<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
runtimeCachedSource.set(createSource(env, runtimeResource, true));
runtimeUnCachedSource.set(createSource(env, runtimeResource, false));
assertFalse(runtimeCachedSource.get() == runtimeUnCachedSource.get());
});
try (Context ctx = Context.create()) {
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(runtimeResource, Paths.get(buildtimeCachedSource.get().getPath()));
assertEquals(runtimeResource, Paths.get(buildtimeUnCachedSource.get().getPath()));
assertEquals(runtimeResource, Paths.get(runtimeUnCachedSource.get().getPath()));
assertSame(buildtimeCachedSource.get(), runtimeCachedSource.get());
assertFalse(buildtimeCachedSource.get() == buildtimeUnCachedSource.get());
assertFalse(buildtimeCachedSource.get() == runtimeUnCachedSource.get());
assertFalse(buildtimeUnCachedSource.get() == runtimeUnCachedSource.get());
}
} finally {
delete(testFolder);
}
}
@Test
@SuppressWarnings("try")
public void testSourceOutsideLanguageHome() throws Exception {
setPatchable(FIRST);
Path testFolder = Files.createTempDirectory("testSources").toRealPath();
try {
Path buildtimeHome = Files.createDirectories(testFolder.resolve("build").resolve(FIRST));
Path runtimeHome = Files.createDirectories(testFolder.resolve("exec").resolve(FIRST));
Path resource = Files.write(testFolder.resolve("testSourceOutsideLanguageHome.test"), Collections.singleton("test"));
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), buildtimeHome.toString());
AtomicReference<com.oracle.truffle.api.source.Source> buildtimeCachedSource = new AtomicReference<>();
AtomicReference<com.oracle.truffle.api.source.Source> buildtimeUnCachedSource = new AtomicReference<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
buildtimeCachedSource.set(createSource(env, resource, true));
buildtimeUnCachedSource.set(createSource(env, resource, false));
assertFalse(buildtimeCachedSource.get() == buildtimeUnCachedSource.get());
});
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertEquals(1, firstLangCtx.initializeContextCount);
assertNotNull(buildtimeCachedSource.get());
assertNotNull(buildtimeUnCachedSource.get());
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), runtimeHome.toString());
AtomicReference<com.oracle.truffle.api.source.Source> runtimeCachedSource = new AtomicReference<>();
AtomicReference<com.oracle.truffle.api.source.Source> runtimeUnCachedSource = new AtomicReference<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
runtimeCachedSource.set(createSource(env, resource, true));
runtimeUnCachedSource.set(createSource(env, resource, false));
});
try (Context ctx = Context.newBuilder().allowAllAccess(true).build()) {
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(resource, Paths.get(buildtimeCachedSource.get().getPath()));
assertEquals(resource, Paths.get(buildtimeUnCachedSource.get().getPath()));
assertEquals(resource, Paths.get(runtimeUnCachedSource.get().getPath()));
assertSame(buildtimeCachedSource.get(), runtimeCachedSource.get());
assertFalse(buildtimeCachedSource.get() == buildtimeUnCachedSource.get());
assertFalse(buildtimeCachedSource.get() == runtimeUnCachedSource.get());
assertFalse(buildtimeUnCachedSource.get() == runtimeUnCachedSource.get());
}
} finally {
delete(testFolder);
}
}
@Test
@SuppressWarnings("try")
public void testSourceNotPatchedContext() throws Exception {
setPatchable(FIRST);
Path testFolder = Files.createTempDirectory("testSources").toRealPath();
try {
Path buildtimeHome = Files.createDirectories(testFolder.resolve("build").resolve(FIRST));
Path buildtimeResource = Files.write(buildtimeHome.resolve("testSourceNotPatchedContext.test"), Collections.singleton("test"));
Path runtimeHome = Files.createDirectories(testFolder.resolve("exec").resolve(FIRST));
Path runtimeResource = Files.copy(buildtimeResource, runtimeHome.resolve(buildtimeResource.getFileName()));
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), buildtimeHome.toString());
AtomicReference<com.oracle.truffle.api.source.Source> buildtimeSource = new AtomicReference<>();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
buildtimeSource.set(createSource(env, buildtimeResource, true));
});
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
CountingContext firstLangCtx = findContext(FIRST, contexts);
assertEquals(1, firstLangCtx.initializeContextCount);
assertNotNull(buildtimeSource.get());
System.setProperty(String.format("org.graalvm.language.%s.home", FIRST), runtimeHome.toString());
Source.newBuilder(FIRST, runtimeResource.toFile()).build();
} finally {
delete(testFolder);
}
}
@Test
public void testAccessPriviledgePatching() throws ReflectiveOperationException {
setPatchable(FIRST, SECOND);
doContextPreinitialize(FIRST, SECOND);
try (Context context = Context.create(SECOND)) {
context.enter();
Env env = ContextPreInitializationTestSecondLanguage.getCurrentContext();
assertFalse(env.getPublicLanguages().containsKey(FIRST));
assertTrue(env.getInternalLanguages().containsKey(FIRST));
}
}
@Test
@SuppressWarnings("try")
public void testFailToLookupInstrumentDuringContextPreInitialization() throws Exception {
setPatchable(FIRST);
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
InstrumentInfo instrumentInfo = env.getInstruments().get(ContextPreInitializationSecondInstrument.ID);
assertNotNull("Cannot find instrument " + ContextPreInitializationSecondInstrument.ID, instrumentInfo);
try {
env.lookup(instrumentInfo, Service.class);
Assert.fail("Expected IllegalStateException");
} catch (IllegalStateException ise) {
}
});
doContextPreinitialize(FIRST);
}
@Test
public void testIsSameFileAllowedIO() throws Exception {
IsSameFileResult res = testIsSameFileImpl(true, null);
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageExecInternalFile));
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageExecInternalFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageExecInternalFile));
}
@Test
public void testIsSameFileDeniedIO() throws Exception {
IsSameFileResult res = testIsSameFileImpl(false, null);
assertFalse(res.imageBuildInternalFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageExecInternalFile));
assertFalse(res.imageBuildInternalFile.isSameFile(res.imageExecPublicFile));
assertFalse(res.imageBuildPublicFile.isSameFile(res.imageBuildInternalFile));
assertFalse(res.imageBuildPublicFile.isSameFile(res.imageExecInternalFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageBuildInternalFile));
assertFalse(res.imageExecInternalFile.isSameFile(res.imageBuildPublicFile));
assertFalse(res.imageExecInternalFile.isSameFile(res.imageExecPublicFile));
assertFalse(res.imageExecPublicFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageBuildPublicFile));
assertFalse(res.imageExecPublicFile.isSameFile(res.imageExecInternalFile));
}
@Test
public void testIsSameFileCustomFileSystem() throws Exception {
IsSameFileResult res = testIsSameFileImpl(true, FileSystem.newDefaultFileSystem());
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageExecInternalFile));
assertTrue(res.imageBuildInternalFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageExecInternalFile));
assertTrue(res.imageBuildPublicFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageExecInternalFile.isSameFile(res.imageExecPublicFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageBuildInternalFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageBuildPublicFile));
assertTrue(res.imageExecPublicFile.isSameFile(res.imageExecInternalFile));
}
@Test
public void testEnvValidInFinalizeContext() throws Exception {
BaseLanguage.registerAction(BaseLanguage.class, ActionKind.ON_FINALIZE_CONTEXT, (env) -> {
env.parseInternal(com.oracle.truffle.api.source.Source.newBuilder(FIRST, "", "onfinalize").build());
});
setPatchable(FIRST);
doContextPreinitialize(FIRST, SECOND);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(2, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertNotNull(firstLangCtx);
final CountingContext secondLangCtx = findContext(SECOND, contexts);
assertNotNull(secondLangCtx);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
assertEquals(0, firstLangCtx.disposeContextCount);
assertEquals(0, firstLangCtx.initializeThreadCount);
assertEquals(0, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(0, secondLangCtx.patchContextCount);
assertEquals(0, secondLangCtx.disposeContextCount);
assertEquals(0, secondLangCtx.initializeThreadCount);
assertEquals(0, secondLangCtx.disposeThreadCount);
try (Context ctx = Context.create()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(3, contexts.size());
Collection<? extends CountingContext> firstLangCtxs = findContexts(FIRST, contexts);
firstLangCtxs.remove(firstLangCtx);
assertFalse(firstLangCtxs.isEmpty());
final CountingContext firstLangCtx2 = firstLangCtxs.iterator().next();
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
assertEquals(1, firstLangCtx.disposeContextCount);
assertEquals(1, firstLangCtx.initializeThreadCount);
assertEquals(1, firstLangCtx.disposeThreadCount);
assertEquals(1, secondLangCtx.createContextCount);
assertEquals(1, secondLangCtx.initializeContextCount);
assertEquals(1, secondLangCtx.patchContextCount);
assertEquals(1, secondLangCtx.disposeContextCount);
assertEquals(1, secondLangCtx.initializeThreadCount);
assertEquals(1, secondLangCtx.disposeThreadCount);
assertEquals(1, firstLangCtx2.createContextCount);
assertEquals(1, firstLangCtx2.initializeContextCount);
assertEquals(0, firstLangCtx2.patchContextCount);
assertEquals(0, firstLangCtx2.disposeContextCount);
assertEquals(1, firstLangCtx2.initializeThreadCount);
assertEquals(0, firstLangCtx2.disposeThreadCount);
}
}
private static IsSameFileResult testIsSameFileImpl(boolean allowIO, FileSystem fs) throws ReflectiveOperationException {
String path = Paths.get(".").toAbsolutePath().toString();
setPatchable(FIRST);
IsSameFileResult result = new IsSameFileResult();
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, (env) -> {
result.imageBuildInternalFile = env.getInternalTruffleFile(path);
result.imageBuildPublicFile = env.getPublicTruffleFile(path);
});
doContextPreinitialize(FIRST);
List<CountingContext> contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
final CountingContext firstLangCtx = findContext(FIRST, contexts);
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(0, firstLangCtx.patchContextCount);
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
result.imageExecInternalFile = env.getInternalTruffleFile(path);
result.imageExecPublicFile = env.getPublicTruffleFile(path);
});
Context.Builder builder = Context.newBuilder().allowIO(allowIO);
if (fs != null) {
builder.fileSystem(fs);
}
try (Context ctx = builder.build()) {
Value res = ctx.eval(Source.create(FIRST, "test"));
assertEquals("test", res.asString());
contexts = new ArrayList<>(emittedContexts);
assertEquals(1, contexts.size());
assertEquals(1, firstLangCtx.createContextCount);
assertEquals(1, firstLangCtx.initializeContextCount);
assertEquals(1, firstLangCtx.patchContextCount);
return result;
}
}
private static final class IsSameFileResult {
TruffleFile imageBuildPublicFile;
TruffleFile imageBuildInternalFile;
TruffleFile imageExecPublicFile;
TruffleFile imageExecInternalFile;
}
private static com.oracle.truffle.api.source.Source createSource(TruffleLanguage.Env env, Path resource, boolean cached) {
try {
TruffleFile file = env.getInternalTruffleFile(resource.toString());
assertNotNull(file);
return com.oracle.truffle.api.source.Source.newBuilder(FIRST, file).cached(cached).build();
} catch (IOException ioe) {
throw new AssertionError(ioe);
}
}
private static void delete(Path file) throws IOException {
if (Files.isDirectory(file)) {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(file)) {
for (Path child : stream) {
delete(child);
}
}
}
Files.delete(file);
}
private static Path write(Path path, CharSequence... lines) throws IOException {
Files.write(path, Arrays.asList(lines), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
return path;
}
private static String read(TruffleFile file) throws IOException {
return new String(file.readAllBytes());
}
private static void resetSystemPropertiesOptions() {
System.clearProperty("polyglot.image-build-time.PreinitializeContexts");
System.clearProperty(SYS_OPTION1_KEY);
System.clearProperty(SYS_OPTION2_KEY);
}
private static void resetLanguageHomes() throws ReflectiveOperationException {
Class<?> languageCache = Class.forName("com.oracle.truffle.polyglot.LanguageCache");
Method reset = languageCache.getDeclaredMethod("resetNativeImageCacheLanguageHomes");
reset.setAccessible(true);
reset.invoke(null);
}
private static void doContextPreinitialize(String... languages) throws ReflectiveOperationException {
final Class<?> holderClz = Class.forName("org.graalvm.polyglot.Engine$ImplHolder", true, ContextPreInitializationTest.class.getClassLoader());
final StringBuilder languagesOptionValue = new StringBuilder();
for (String language : languages) {
languagesOptionValue.append(language).append(',');
}
if (languagesOptionValue.length() > 0) {
languagesOptionValue.replace(languagesOptionValue.length() - 1, languagesOptionValue.length(), "");
System.setProperty("polyglot.image-build-time.PreinitializeContexts", languagesOptionValue.toString());
}
final Method preInitMethod = holderClz.getDeclaredMethod("preInitializeEngine");
preInitMethod.setAccessible(true);
try {
preInitMethod.invoke(null);
} finally {
System.clearProperty("polyglot.image-build-time.PreinitializeContexts");
}
}
private static Collection<? extends CountingContext> findContexts(
final String languageId,
Collection<? extends CountingContext> contexts) {
final Set<CountingContext> result = new HashSet<>();
for (CountingContext context : contexts) {
if (context.getLanguageId().equals(languageId)) {
result.add(context);
}
}
return result;
}
private static CountingContext findContext(
final String languageId,
Collection<? extends CountingContext> contexts) {
final Collection<? extends CountingContext> found = findContexts(languageId, contexts);
return found.isEmpty() ? null : found.iterator().next();
}
static void setPatchable(String... languageIds) {
patchableLanguages.clear();
Collections.addAll(patchableLanguages, languageIds);
}
private static int nextId() {
int id = NEXT_ORDER_INDEX.getAndIncrement();
return id;
}
static class CountingContext {
private final String id;
private TruffleLanguage.Env env;
int createContextCount = 0;
int createContextOrder = -1;
int initializeContextCount = 0;
int initializeContextOrder = -1;
int patchContextCount = 0;
int patchContextOrder = -1;
int disposeContextCount = 0;
int disposeContextOrder = -1;
int initializeThreadCount = 0;
int initializeThreadOrder = -1;
int disposeThreadCount = 0;
int disposeThreadOrder = -1;
final Map<OptionKey<Boolean>, Boolean> optionValues;
final List<String> arguments;
String languageHome;
boolean preInitialized;
final BaseLanguage language;
private final Map<Class<?>, Object> lookupItems = new HashMap<>();
CountingContext(final String id, final TruffleLanguage.Env env, final BaseLanguage language) {
this.id = id;
this.env = env;
this.language = language;
this.optionValues = new HashMap<>();
this.arguments = new ArrayList<>();
}
String getLanguageId() {
return id;
}
void environment(TruffleLanguage.Env newEnv) {
this.env = newEnv;
}
TruffleLanguage.Env environment() {
return env;
}
<T> void register(Class<T> type, T item) {
Objects.requireNonNull(type, "Type must be non null.");
Objects.requireNonNull(item, "Item must be non null.");
if (lookupItems.putIfAbsent(type, item) != null) {
throw new IllegalArgumentException("Registration for " + type + " already exists");
}
}
<T> T lookup(Class<T> type) {
return type.cast(lookupItems.get(type));
}
}
enum ActionKind {
ON_INITIALIZE_CONTEXT,
ON_PATCH_CONTEXT,
ON_FINALIZE_CONTEXT
}
static class BaseLanguage extends TruffleLanguage<CountingContext> {
static Map<String, String> parseStdOutOutput = new HashMap<>();
static Map<String, String> parseStdErrOutput = new HashMap<>();
static Map<Pair<Class<? extends BaseLanguage>, ActionKind>, Consumer<TruffleLanguage.Env>> actions = new HashMap<>();
static void registerAction(Class<? extends BaseLanguage> languageClass, ActionKind kind, Consumer<TruffleLanguage.Env> action) {
Pair<Class<? extends BaseLanguage>, ActionKind> key = Pair.create(languageClass, kind);
actions.put(key, action);
}
@Override
protected CountingContext createContext(TruffleLanguage.Env env) {
final String languageId = new RootNode(this) {
@Override
public Object execute(VirtualFrame frame) {
return null;
}
}.getLanguageInfo().getId();
final CountingContext ctx = new CountingContext(languageId, env, this);
ctx.createContextCount++;
ctx.createContextOrder = nextId();
ctx.languageHome = getLanguageHome();
Collections.addAll(ctx.arguments, env.getApplicationArguments());
ctx.preInitialized = env.isPreInitialization();
emittedContexts.add(ctx);
return ctx;
}
@Override
protected void initializeContext(CountingContext context) throws Exception {
context.initializeContextCount++;
context.initializeContextOrder = nextId();
findAction(ActionKind.ON_INITIALIZE_CONTEXT).ifPresent((c) -> c.accept(context.environment()));
}
@Override
protected boolean patchContext(CountingContext context, TruffleLanguage.Env newEnv) {
assertNotNull(getCurrentContext(getClass()));
assertTrue(context.preInitialized);
assertFalse(context.env.isPreInitialization());
context.patchContextCount++;
context.patchContextOrder = nextId();
context.languageHome = getLanguageHome();
boolean patchable = patchableLanguages.contains(context.getLanguageId());
if (patchable) {
context.environment(newEnv);
context.arguments.clear();
Collections.addAll(context.arguments, newEnv.getApplicationArguments());
}
findAction(ActionKind.ON_PATCH_CONTEXT).ifPresent((c) -> c.accept(context.environment()));
return patchable;
}
@Override
protected void finalizeContext(CountingContext context) {
findAction(ActionKind.ON_FINALIZE_CONTEXT).ifPresent((c) -> c.accept(context.environment()));
}
@Override
protected void disposeContext(CountingContext context) {
context.disposeContextCount++;
context.disposeContextOrder = nextId();
}
@Override
protected void initializeThread(CountingContext context, Thread thread) {
context.initializeThreadCount++;
context.initializeThreadOrder = nextId();
}
@Override
protected void disposeThread(CountingContext context, Thread thread) {
context.disposeThreadCount++;
context.disposeThreadOrder = nextId();
}
@Override
protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Exception {
final CharSequence result = request.getSource().getCharacters();
Class<? extends TruffleLanguage<CountingContext>> languageClass = getClass();
return Truffle.getRuntime().createCallTarget(new RootNode(this) {
@Override
public Object execute(VirtualFrame frame) {
executeImpl(lookupContextReference(languageClass).get(), getLanguageInfo());
return result;
}
});
}
protected void useLanguage(CountingContext context, String id) {
com.oracle.truffle.api.source.Source source = com.oracle.truffle.api.source.Source.newBuilder(id, "", "").internal(true).build();
context.environment().parseInternal(source);
}
@CompilerDirectives.TruffleBoundary
private static void executeImpl(CountingContext ctx, LanguageInfo info) {
String msg = parseStdOutOutput.get(info.getId());
if (msg != null) {
write(ctx.environment().out(), msg);
}
msg = parseStdErrOutput.get(info.getId());
if (msg != null) {
write(ctx.environment().err(), msg);
}
assertEquals(0, ctx.disposeContextCount);
}
private static void write(final OutputStream out, final String content) {
try {
out.write(content.getBytes("UTF-8"));
out.flush();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
Optional<Consumer<Env>> findAction(ActionKind kind) {
Class<?> clz = getClass();
while (clz != null) {
Pair<Class<?>, ActionKind> key = Pair.create(clz, kind);
Consumer<Env> action = actions.get(key);
if (action != null) {
return Optional.of(action);
}
if (clz == BaseLanguage.class) {
break;
}
clz = clz.getSuperclass();
}
return Optional.empty();
}
}
public interface Service {
enum Kind {
IMAGE_BUILD_TIME,
IMAGE_EXECUTION_TIME
}
Kind getKind();
}
@TruffleLanguage.Registration(id = FIRST, name = FIRST, version = "1.0", dependentLanguages = INTERNAL, contextPolicy = TruffleLanguage.ContextPolicy.SHARED, services = Service.class)
public static final class ContextPreInitializationTestFirstLanguage extends BaseLanguage {
@Option(category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Option 1")
public static final OptionKey<Boolean> Option1 = new OptionKey<>(false);
@Option(category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Option 2")
public static final OptionKey<Boolean> Option2 = new OptionKey<>(false);
@Option(category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Service Kind")
public static final OptionKey<String> ServiceKind = new OptionKey<>(Service.Kind.IMAGE_BUILD_TIME.name());
private static boolean callDependentLanguage;
@Override
protected CountingContext createContext(Env env) {
final CountingContext ctx = super.createContext(env);
ctx.optionValues.put(Option1, env.getOptions().get(Option1));
ctx.optionValues.put(Option2, env.getOptions().get(Option2));
Service service = new ServiceImpl(Service.Kind.valueOf(env.getOptions().get(ServiceKind)));
env.registerService(service);
ctx.register(Service.class, service);
return ctx;
}
@Override
protected void initializeContext(CountingContext context) throws Exception {
super.initializeContext(context);
if (callDependentLanguage) {
useLanguage(context, INTERNAL);
}
}
@Override
protected boolean patchContext(CountingContext context, Env newEnv) {
context.optionValues.put(Option1, newEnv.getOptions().get(Option1));
context.optionValues.put(Option2, newEnv.getOptions().get(Option2));
((ServiceImpl) context.lookup(Service.class)).setKind(Service.Kind.valueOf(newEnv.getOptions().get(ServiceKind)));
final boolean result = super.patchContext(context, newEnv);
return result;
}
@Override
protected OptionDescriptors getOptionDescriptors() {
return new ContextPreInitializationTestFirstLanguageOptionDescriptors();
}
private static final class ServiceImpl implements Service {
private Kind kind;
ServiceImpl(Kind kind) {
this.kind = kind;
}
void setKind(Kind newKind) {
this.kind = newKind;
}
@Override
public Kind getKind() {
return kind;
}
}
}
@TruffleLanguage.Registration(id = SECOND, name = SECOND, version = "1.0", dependentLanguages = FIRST)
public static final class ContextPreInitializationTestSecondLanguage extends BaseLanguage {
private static boolean callDependentLanguageInCreate;
private static boolean callDependentLanguageInPatch;
private static boolean lookupService;
@Override
protected void initializeContext(CountingContext context) throws Exception {
super.initializeContext(context);
if (lookupService) {
expectService(context.environment(),
context.environment().isPreInitialization() ? Service.Kind.IMAGE_BUILD_TIME : Service.Kind.IMAGE_EXECUTION_TIME);
}
if (callDependentLanguageInCreate) {
useLanguage(context, FIRST);
}
}
public static Env getCurrentContext() {
return getCurrentContext(ContextPreInitializationTestSecondLanguage.class).env;
}
@Override
protected boolean patchContext(CountingContext context, Env newEnv) {
if (lookupService) {
expectService(newEnv, Service.Kind.IMAGE_EXECUTION_TIME);
}
boolean res = super.patchContext(context, newEnv);
if (res && callDependentLanguageInPatch) {
useLanguage(context, FIRST);
}
return res;
}
private static void expectService(Env env, Service.Kind expectedServiceKind) {
LanguageInfo firstLanguage = env.getPublicLanguages().get(FIRST);
Service service = env.lookup(firstLanguage, Service.class);
assertNotNull("Service not found", service);
assertEquals(expectedServiceKind, service.getKind());
}
}
@TruffleLanguage.Registration(id = INTERNAL, name = INTERNAL, version = "1.0", internal = true)
public static final class ContextPreInitializationTestInternalLanguage extends BaseLanguage {
}
private static final class TestHandler extends Handler {
private final Set<String> importantLoggers;
final List<LogRecord> logs = new ArrayList<>();
TestHandler(String... importantLoggers) {
this.importantLoggers = new HashSet<>();
Collections.addAll(this.importantLoggers, importantLoggers);
}
@Override
public void publish(LogRecord record) {
if (importantLoggers.isEmpty() || importantLoggers.contains(record.getLoggerName())) {
logs.add(record);
}
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
@TruffleLanguage.Registration(id = SHARED, name = SHARED, version = "1.0", contextPolicy = TruffleLanguage.ContextPolicy.SHARED)
public static final class ContextPreInitializationTestSharedLanguage extends BaseLanguage {
@Option(category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Option 1")
public static final OptionKey<Boolean> Option1 = new OptionKey<>(false);
@Option(category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Option 2")
public static final OptionKey<Boolean> Option2 = new OptionKey<>(false);
@Override
protected CountingContext createContext(Env env) {
final CountingContext ctx = super.createContext(env);
ctx.optionValues.put(Option1, env.getOptions().get(Option1));
ctx.optionValues.put(Option2, env.getOptions().get(Option2));
return ctx;
}
@Override
protected void initializeContext(CountingContext context) throws Exception {
super.initializeContext(context);
}
@Override
protected boolean patchContext(CountingContext context, Env newEnv) {
context.optionValues.put(Option1, newEnv.getOptions().get(Option1));
context.optionValues.put(Option2, newEnv.getOptions().get(Option2));
final boolean result = super.patchContext(context, newEnv);
return result;
}
@Override
protected OptionDescriptors getOptionDescriptors() {
return new ContextPreInitializationTestSharedLanguageOptionDescriptors();
}
@Override
protected boolean areOptionsCompatible(OptionValues firstOptions, OptionValues newOptions) {
return firstOptions.equals(newOptions);
}
}
public abstract static class BaseInstrument extends TruffleInstrument implements ContextsListener {
private Env environment;
private EventBinding<BaseInstrument> contextsListenerBinding;
@Override
protected void onCreate(Env env) {
if (getActions() != null) {
environment = env;
contextsListenerBinding = env.getInstrumenter().attachContextsListener(this, true);
performAction(null, null);
}
}
@Override
protected void onDispose(Env env) {
if (contextsListenerBinding != null) {
contextsListenerBinding.dispose();
contextsListenerBinding = null;
environment = null;
}
}
@Override
public void onContextCreated(TruffleContext context) {
performAction(context, null);
}
@Override
public void onLanguageContextCreated(TruffleContext context, LanguageInfo language) {
performAction(context, language);
}
@Override
public void onLanguageContextInitialized(TruffleContext context, LanguageInfo language) {
performAction(context, language);
}
@Override
public void onLanguageContextFinalized(TruffleContext context, LanguageInfo language) {
performAction(context, language);
}
@Override
public void onLanguageContextDisposed(TruffleContext context, LanguageInfo language) {
performAction(context, language);
}
@Override
public void onContextClosed(TruffleContext context) {
performAction(context, null);
}
private void performAction(TruffleContext context, LanguageInfo language) {
StackTraceElement element = Thread.currentThread().getStackTrace()[2];
Consumer<Event> action = getActions().get(element.getMethodName());
if (action != null) {
action.accept(new Event(environment, context, language));
}
}
protected abstract Map<String, Consumer<Event>> getActions();
static final class Event {
final TruffleInstrument.Env env;
final TruffleContext ctx;
final LanguageInfo language;
Event(TruffleInstrument.Env env, TruffleContext ctx, LanguageInfo info) {
this.env = env;
this.ctx = ctx;
this.language = info;
}
}
}
@TruffleInstrument.Registration(id = ContextPreInitializationFirstInstrument.ID, name = ContextPreInitializationFirstInstrument.ID)
public static final class ContextPreInitializationFirstInstrument extends BaseInstrument {
static final String ID = "ContextPreInitializationFirstInstrument";
static volatile Map<String, Consumer<Event>> actions;
@Option(name = "", category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Activates instrument")
static final OptionKey<Boolean> Enabled = new OptionKey<>(false);
@Override
protected OptionDescriptors getOptionDescriptors() {
return new ContextPreInitializationFirstInstrumentOptionDescriptors();
}
@Override
protected Map<String, Consumer<Event>> getActions() {
return actions;
}
}
@TruffleInstrument.Registration(id = ContextPreInitializationSecondInstrument.ID, name = ContextPreInitializationSecondInstrument.ID, services = Service.class)
public static final class ContextPreInitializationSecondInstrument extends BaseInstrument {
static final String ID = "ContextPreInitializationSecondInstrument";
@Override
protected Map<String, Consumer<Event>> getActions() {
return Collections.emptyMap();
}
@Override
protected void onCreate(Env env) {
super.onCreate(env);
env.registerService(new Service() {
@Override
public Service.Kind getKind() {
return Service.Kind.IMAGE_EXECUTION_TIME;
}
});
}
}
}