package com.oracle.truffle.api.test.polyglot;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;
import org.junit.After;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
public class LegacyLanguageSPITest {
@After
public void cleanup() {
ProxyLanguage.setDelegate(new ProxyLanguage());
}
@Test
public void testCancelExecutionWhileSleeping() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(1);
try {
Engine engine = Engine.create();
Context context = Context.newBuilder(LanguageSPITestLanguage.ID).engine(engine).build();
CountDownLatch beforeSleep = new CountDownLatch(1);
CountDownLatch interrupt = new CountDownLatch(1);
AtomicInteger gotInterrupt = new AtomicInteger(0);
Function<TruffleLanguage.Env, Object> f = new Function<TruffleLanguage.Env, Object>() {
public Object apply(TruffleLanguage.Env t) {
try {
beforeSleep.countDown();
Thread.sleep(5000);
} catch (InterruptedException e) {
gotInterrupt.incrementAndGet();
interrupt.countDown();
throw new LegacyInterrupted();
}
return null;
}
};
Future<Value> future = service.submit(() -> eval(context, f));
beforeSleep.await(10000, TimeUnit.MILLISECONDS);
context.close(true);
interrupt.await(10000, TimeUnit.MILLISECONDS);
assertEquals(1, gotInterrupt.get());
try {
future.get();
fail();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (!(cause instanceof PolyglotException)) {
throw new AssertionError(cause);
}
PolyglotException polyglotException = (PolyglotException) cause;
assertTrue(polyglotException.isCancelled());
}
engine.close();
} finally {
service.shutdown();
}
}
@Test
public void testExceptionGetSourceLocation() {
try (Context context = Context.create(LanguageSPITestLanguage.ID)) {
final String text = "0123456789";
LanguageSPITestLanguage.runinside = (env) -> {
Source src = Source.newBuilder(LanguageSPITestLanguage.ID, text, "test.txt").build();
throw new LegacyParseException(src, 1, 2);
};
try {
context.eval(LanguageSPITestLanguage.ID, text);
Assert.fail("PolyglotException expected.");
} catch (PolyglotException pe) {
Assert.assertTrue(pe.isSyntaxError());
Assert.assertEquals("12", pe.getSourceLocation().getCharacters().toString());
} finally {
LanguageSPITestLanguage.runinside = null;
}
}
}
@Test
public void testLanguageErrorDuringInitialization() {
ProxyLanguage.setDelegate(new ProxyLanguage() {
@Override
protected void initializeContext(ProxyLanguage.LanguageContext c) throws Exception {
throw new LegacyTestError();
}
});
Context context = Context.create();
try {
context.eval(ProxyLanguage.ID, "");
fail();
} catch (PolyglotException e) {
assertTrue(e.isGuestException());
assertEquals("testLanguageErrorDuringInitialization", e.getSourceLocation().getSource().getName());
}
context.close();
}
private static Value eval(Context context, Function<TruffleLanguage.Env, Object> f) {
LanguageSPITestLanguage.runinside = f;
try {
return context.eval(LanguageSPITestLanguage.ID, "");
} finally {
LanguageSPITestLanguage.runinside = null;
}
}
@SuppressWarnings({"deprecation", "serial"})
static class LegacyInterrupted extends RuntimeException implements com.oracle.truffle.api.TruffleException {
@Override
public boolean isCancelled() {
return true;
}
@Override
public Node getLocation() {
return null;
}
}
@SuppressWarnings({"deprecation", "serial"})
private static final class LegacyParseException extends RuntimeException implements com.oracle.truffle.api.TruffleException {
private final Source source;
private final int start;
private final int length;
LegacyParseException(final Source source, final int start, final int length) {
Objects.requireNonNull(source, "Source must be non null");
this.source = source;
this.start = start;
this.length = length;
}
@Override
public boolean isSyntaxError() {
return true;
}
@Override
public Node getLocation() {
return null;
}
@Override
public SourceSection getSourceLocation() {
return source.createSection(start, length);
}
}
static final Source TEST_SOURCE = Source.newBuilder("", "", "testLanguageErrorDuringInitialization").build();
@SuppressWarnings({"serial", "deprecation"})
static class LegacyTestError extends RuntimeException implements com.oracle.truffle.api.TruffleException {
public SourceSection getSourceLocation() {
return TEST_SOURCE.createSection(0, 0);
}
public Node getLocation() {
return null;
}
}
}