package com.oracle.truffle.polyglot;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.polyglot.PolyglotLanguage.ContextProfile;
abstract class HostToGuestRootNode extends RootNode {
protected static final int ARGUMENT_OFFSET = 2;
@CompilationFinal private boolean seenEnter;
@CompilationFinal private boolean seenNonEnter;
@CompilationFinal private volatile ContextProfile profile;
private final PolyglotEngineImpl engine;
private final BranchProfile error = BranchProfile.create();
HostToGuestRootNode() {
this(null);
}
HostToGuestRootNode(PolyglotLanguageContext languageContext) {
super(languageContext != null ? languageContext.getLanguageInstance().spi : null);
this.engine = (PolyglotEngineImpl) EngineAccessor.NODES.getPolyglotEngine(this);
assert this.engine != null : "all host to guest root nodes need to be initialized when entered";
assert needsEnter() || !needsExceptionWrapping() : "HostToGuestRootNode which does not require enter cannot have exception wrapping.";
}
protected abstract Class<?> getReceiverType();
protected boolean needsEnter() {
return true;
}
protected boolean needsExceptionWrapping() {
return true;
}
@Override
public final Object execute(VirtualFrame frame) {
Object[] args = frame.getArguments();
PolyglotLanguageContext languageContext = profileContext(args[0]);
PolyglotContextImpl context;
PolyglotContextImpl prev;
boolean needsEnter;
try {
assert languageContext != null;
context = languageContext.context;
needsEnter = needsEnter() && languageContext != null && engine.needsEnter(context);
if (needsEnter) {
if (!seenEnter) {
CompilerDirectives.transferToInterpreterAndInvalidate();
seenEnter = true;
}
prev = engine.enter(context);
} else {
if (!seenNonEnter) {
CompilerDirectives.transferToInterpreterAndInvalidate();
seenNonEnter = true;
}
prev = null;
}
} catch (Throwable e) {
throw handleException(languageContext, e, false, RuntimeException.class);
}
try {
Object[] arguments = frame.getArguments();
Object receiver = getReceiverType().cast(arguments[1]);
Object result;
result = executeImpl(languageContext, receiver, arguments);
assert !(result instanceof TruffleObject);
return result;
} catch (Throwable e) {
throw handleException(languageContext, e, needsEnter(), RuntimeException.class);
} finally {
if (needsEnter) {
try {
engine.leave(prev, context);
} catch (Throwable e) {
throw handleException(languageContext, e, false, RuntimeException.class);
}
}
}
}
@SuppressWarnings({"unchecked", "unused"})
private <E extends Throwable> E handleException(PolyglotLanguageContext languageContext, Throwable e, boolean entered, Class<E> exceptionType) throws E {
if (needsExceptionWrapping()) {
error.enter();
throw PolyglotImpl.guestToHostException(languageContext, e, entered);
}
throw (E) e;
}
private PolyglotLanguageContext profileContext(Object languageContext) {
ContextProfile localProfile = this.profile;
if (localProfile == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
profile = localProfile = ((PolyglotLanguageContext) languageContext).language.profile;
}
return localProfile.profile(languageContext);
}
protected abstract Object executeImpl(PolyglotLanguageContext languageContext, Object receiver, Object[] args);
protected static CallTarget createTarget(HostToGuestRootNode node) {
return Truffle.getRuntime().createCallTarget(node);
}
static <T> T installHostCodeCache(PolyglotLanguageContext languageContext, Object key, T value, Class<T> expectedType) {
T result = expectedType.cast(languageContext.getLanguageInstance().hostInteropCodeCache.putIfAbsent(key, value));
if (result != null) {
return result;
} else {
return value;
}
}
static <T> T lookupHostCodeCache(PolyglotLanguageContext languageContext, Object key, Class<T> expectedType) {
return expectedType.cast(languageContext.getLanguageInstance().hostInteropCodeCache.get(key));
}
}