com.oracle.truffle.polyglot.PolyglotExceptionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of truffle-api Show documentation
Show all versions of truffle-api Show documentation
Truffle is a multi-language framework for executing dynamic languages
that achieves high performance when combined with Graal.
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.polyglot;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.function.Function;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.PolyglotException.StackFrame;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.SourceSection;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.impl.AbstractPolyglotImpl.APIAccess;
import org.graalvm.polyglot.proxy.Proxy;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleStackTrace;
import com.oracle.truffle.api.TruffleStackTraceElement;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.ExceptionType;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.polyglot.PolyglotEngineImpl.CancelExecution;
final class PolyglotExceptionImpl {
private static final String CAUSE_CAPTION = "Caused by host exception: ";
private static final boolean TRACE_STACK_TRACE_WALKING = false;
private Object api;
final PolyglotImpl polyglot;
final PolyglotEngineImpl engine;
final PolyglotContextImpl context;
final Throwable exception;
final boolean showInternalStackFrames;
private final List guestFrames;
private StackTraceElement[] javaStackTrace;
private List materializedFrames;
private final SourceSection sourceLocation;
private final boolean internal;
private final boolean cancelled;
private final boolean exit;
private final boolean incompleteSource;
private final boolean syntaxError;
private final boolean resourceExhausted;
private final boolean interrupted;
private final int exitStatus;
private final Value guestObject;
private final String message;
PolyglotExceptionImpl(PolyglotEngineImpl engine, PolyglotContextImpl.State polyglotContextState, boolean polyglotContextResourceExhausted, Throwable original) {
this(engine.impl, engine, polyglotContextState, polyglotContextResourceExhausted, null, original, false, false);
}
// Exception coming from an instrument
PolyglotExceptionImpl(PolyglotImpl polyglot, Throwable original) {
this(polyglot, null, null, false, null, original, true, false);
}
@SuppressWarnings("deprecation")
PolyglotExceptionImpl(PolyglotImpl polyglot, PolyglotEngineImpl engine, PolyglotContextImpl.State polyglotContextState, boolean polyglotContextResourceExhausted,
PolyglotLanguageContext languageContext,
Throwable original,
boolean allowInterop,
boolean entered) {
this.polyglot = polyglot;
this.engine = engine;
this.context = (languageContext != null) ? languageContext.context : null;
this.exception = original;
this.guestFrames = TruffleStackTrace.getStackTrace(original);
this.showInternalStackFrames = engine == null ? false : engine.engineOptionValues.get(PolyglotEngineOptions.ShowInternalStackFrames);
Error resourceLimitError = getResourceLimitError(engine, exception);
String exceptionMessage = null;
InteropLibrary interop;
if (allowInterop && (interop = InteropLibrary.getUncached()).isException(exception)) {
try {
ExceptionType exceptionType = interop.getExceptionType(exception);
this.internal = false;
boolean truffleException = exception instanceof com.oracle.truffle.api.TruffleException;
boolean cancelInducedTruffleException = (polyglotContextState != null && (polyglotContextState.isCancelling() || polyglotContextState == PolyglotContextImpl.State.CLOSED_CANCELLED) &&
truffleException);
this.cancelled = cancelInducedTruffleException || isLegacyTruffleExceptionCancelled(exception);
this.resourceExhausted = resourceLimitError != null || (cancelInducedTruffleException && polyglotContextResourceExhausted);
this.syntaxError = exceptionType == ExceptionType.PARSE_ERROR;
this.exit = exceptionType == ExceptionType.EXIT;
this.exitStatus = this.exit ? interop.getExceptionExitStatus(exception) : 0;
this.incompleteSource = this.syntaxError ? interop.isExceptionIncompleteSource(exception) : false;
this.interrupted = (exceptionType == ExceptionType.INTERRUPT) && !this.cancelled;
if (interop.hasExceptionMessage(exception)) {
exceptionMessage = interop.asString(interop.getExceptionMessage(exception));
}
if (interop.hasSourceLocation(exception)) {
this.sourceLocation = newSourceSection(interop.getSourceLocation(exception));
} else {
this.sourceLocation = null;
}
Object exceptionObject;
if (entered && languageContext != null && languageContext.isCreated() &&
!isHostException(engine, exception) && (exceptionObject = ((com.oracle.truffle.api.TruffleException) exception).getExceptionObject()) != null) {
/*
* Allow proxies in guest language objects. This is for legacy support. Ideally
* we should get rid of this if it is no longer relied upon.
*/
Object receiver = exceptionObject;
if (receiver instanceof Proxy) {
receiver = languageContext.toGuestValue(null, receiver);
}
this.guestObject = languageContext.asValue(receiver);
} else {
this.guestObject = null;
}
} catch (UnsupportedMessageException ume) {
throw CompilerDirectives.shouldNotReachHere(ume);
}
} else {
/*
* When polyglot context is invalid, we cannot obtain the exception type from
* InterruptExecution exception via interop. Please note that in this case the
* InterruptExecution was thrown before the context was made invalid.
*/
boolean interruptException = (exception instanceof PolyglotEngineImpl.InterruptExecution) || (exception != null && exception.getCause() instanceof InterruptedException) ||
(isHostException(engine, exception) && asHostException() instanceof InterruptedException);
boolean truffleException = exception instanceof com.oracle.truffle.api.TruffleException;
boolean cancelInducedTruffleOrInterruptException = (polyglotContextState != null &&
(polyglotContextState.isCancelling() || polyglotContextState == PolyglotContextImpl.State.CLOSED_CANCELLED) &&
(interruptException || truffleException));
/*
* In case the exception is not a cancel exception, but the context is in cancelling or
* cancelled state, set the cancelled flag, but only if the original exception is a
* truffle exception or interrupt exception.
*/
this.cancelled = cancelInducedTruffleOrInterruptException || (exception instanceof CancelExecution) || isLegacyTruffleExceptionCancelled(exception);
this.resourceExhausted = resourceLimitError != null || (cancelInducedTruffleOrInterruptException && polyglotContextResourceExhausted);
this.interrupted = interruptException && !this.cancelled;
this.internal = !interrupted && !cancelled && !resourceExhausted;
this.syntaxError = false;
this.incompleteSource = false;
if (allowInterop) {
this.exit = isLegacyTruffleExceptionExit(exception);
this.exitStatus = exit ? getLegacyTruffleExceptionExitStatus(exception) : 0;
this.guestObject = getLegacyTruffleExceptionGuestObject(languageContext, exception);
} else {
this.exit = false;
this.exitStatus = 0;
this.guestObject = null;
}
com.oracle.truffle.api.source.SourceSection location;
if (exception instanceof CancelExecution) {
location = ((CancelExecution) exception).getSourceLocation();
} else if (allowInterop) {
location = getLegacyTruffleExceptionSourceLocation(exception);
} else {
location = null;
}
this.sourceLocation = location != null ? newSourceSection(location) : null;
}
if (exceptionMessage == null) {
exceptionMessage = isHostException() ? asHostException().getMessage() : internal ? exception.toString() : exception.getMessage();
}
if (exceptionMessage != null) {
this.message = exceptionMessage;
} else if (resourceLimitError != null) {
String resourceExhaustedMessage = "Resource exhausted";
if (resourceLimitError instanceof StackOverflowError) {
resourceExhaustedMessage += ": Stack overflow";
}
if (resourceLimitError instanceof OutOfMemoryError) {
resourceExhaustedMessage += ": Out of memory";
}
this.message = resourceExhaustedMessage;
} else {
this.message = null;
}
// late materialization of host frames. only needed if polyglot exceptions cross the
// host boundary.
EngineAccessor.LANGUAGE.materializeHostFrames(original);
}
private static Error getResourceLimitError(PolyglotEngineImpl engine, Throwable e) {
if (e instanceof CancelExecution) {
return ((CancelExecution) e).isResourceLimit() ? (Error) e : null;
} else if (isHostException(engine, e)) {
Throwable toCheck = engine.host.toHostResourceError(e);
assert toCheck == null || toCheck instanceof StackOverflowError || toCheck instanceof OutOfMemoryError;
return (Error) toCheck;
} else if (e instanceof StackOverflowError || e instanceof OutOfMemoryError) {
return (Error) e;
}
return null;
}
@SuppressWarnings("deprecation")
private static boolean isLegacyTruffleExceptionCancelled(Throwable e) {
// Legacy TruffleException
if (e instanceof com.oracle.truffle.api.TruffleException) {
return ((com.oracle.truffle.api.TruffleException) e).isCancelled();
}
return false;
}
@SuppressWarnings("deprecation")
private static boolean isLegacyTruffleExceptionExit(Throwable e) {
// Legacy TruffleException
if (e instanceof com.oracle.truffle.api.TruffleException) {
return ((com.oracle.truffle.api.TruffleException) e).isExit();
}
return false;
}
@SuppressWarnings("deprecation")
private static int getLegacyTruffleExceptionExitStatus(Throwable e) {
// Legacy TruffleException
if (e instanceof com.oracle.truffle.api.TruffleException) {
return ((com.oracle.truffle.api.TruffleException) e).getExitStatus();
}
return 0;
}
@SuppressWarnings("deprecation")
private static com.oracle.truffle.api.source.SourceSection getLegacyTruffleExceptionSourceLocation(Throwable e) {
// Legacy TruffleException
if (e instanceof com.oracle.truffle.api.TruffleException) {
return ((com.oracle.truffle.api.TruffleException) e).getSourceLocation();
}
return null;
}
@SuppressWarnings("deprecation")
private static Value getLegacyTruffleExceptionGuestObject(PolyglotLanguageContext languageContext, Throwable e) {
// Legacy TruffleException
if (e instanceof com.oracle.truffle.api.TruffleException && languageContext != null) {
Object exceptionObject = ((com.oracle.truffle.api.TruffleException) e).getExceptionObject();
if (exceptionObject != null) {
if (exceptionObject instanceof Proxy) {
exceptionObject = languageContext.toGuestValue(null, exceptionObject);
}
return languageContext.asValue(exceptionObject);
}
}
return null;
}
private SourceSection newSourceSection(com.oracle.truffle.api.source.SourceSection section) {
com.oracle.truffle.api.source.Source truffleSource = section.getSource();
Source source = polyglot.getAPIAccess().newSource(truffleSource);
return polyglot.getAPIAccess().newSourceSection(source, section);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PolyglotExceptionImpl) {
return exception == ((PolyglotExceptionImpl) obj).exception;
}
return false;
}
@Override
public int hashCode() {
return exception.hashCode();
}
public org.graalvm.polyglot.SourceSection getSourceLocation() {
return sourceLocation;
}
public void onCreate(PolyglotException instance) {
this.api = instance;
}
public boolean isResourceExhausted() {
return resourceExhausted;
}
public boolean isInterrupted() {
return interrupted;
}
public boolean isHostException() {
return isHostException(engine, exception);
}
public Throwable asHostException() {
if (!isHostException()) {
throw PolyglotEngineException.unsupported(
String.format("Unsupported operation %s.%s. You can ensure that the operation is supported using %s.%s.",
PolyglotException.class.getSimpleName(), "asHostException()",
PolyglotException.class.getSimpleName(), "isHostException()"));
}
return engine.host.unboxHostException(exception);
}
public void printStackTrace(PrintWriter s) {
printStackTrace(new WrappedPrintWriter(s));
}
public void printStackTrace(PrintStream s) {
printStackTrace(new WrappedPrintStream(s));
}
private void printStackTrace(PrintStreamOrWriter s) {
synchronized (s.lock()) {
// For an internal error without guest frames print only the internal error.
if (isInternalError() && (guestFrames == null || guestFrames.isEmpty())) {
s.print(api.getClass().getName() + ": ");
s.printStackTrace(exception);
s.println("Internal GraalVM error, please report at https://github.com/oracle/graal/issues/.");
return;
}
// Print our stack trace
if (isInternalError() || getMessage() == null || getMessage().isEmpty()) {
s.println(api);
} else {
s.println(getMessage());
}
materialize();
int languageIdLength = 0; // java
for (StackFrame traceElement : getPolyglotStackTrace()) {
if (!traceElement.isHostFrame()) {
languageIdLength = Math.max(languageIdLength, polyglot.getAPIAccess().getDispatch(traceElement).getLanguage().getId().length());
}
}
for (StackFrame traceElement : getPolyglotStackTrace()) {
s.println("\tat " + polyglot.getAPIAccess().getDispatch(traceElement).toStringImpl(languageIdLength));
}
// Print cause, if any
if (isHostException()) {
s.println(CAUSE_CAPTION + asHostException());
}
if (isInternalError()) {
s.println("Original Internal Error: ");
s.printStackTrace(exception);
}
}
}
public String getMessage() {
return message;
}
public StackTraceElement[] getJavaStackTrace() {
if (javaStackTrace == null) {
materialize();
javaStackTrace = new StackTraceElement[materializedFrames.size()];
for (int i = 0; i < javaStackTrace.length; i++) {
javaStackTrace[i] = materializedFrames.get(i).toHostFrame();
}
}
return javaStackTrace;
}
private void materialize() {
if (this.materializedFrames == null) {
List frames = new ArrayList<>();
for (StackFrame frame : getPolyglotStackTrace()) {
frames.add(frame);
}
this.materializedFrames = Collections.unmodifiableList(frames);
}
}
public StackTraceElement[] getStackTrace() {
return getJavaStackTrace().clone();
}
public boolean isInternalError() {
return internal;
}
public Iterable getPolyglotStackTrace() {
if (materializedFrames != null) {
return materializedFrames;
} else {
return new Iterable() {
public Iterator iterator() {
return createStackFrameIterator(PolyglotExceptionImpl.this);
}
};
}
}
public boolean isCancelled() {
return cancelled;
}
public boolean isExit() {
return exit;
}
public boolean isIncompleteSource() {
return incompleteSource;
}
public int getExitStatus() {
return exitStatus;
}
public boolean isSyntaxError() {
return syntaxError;
}
public Value getGuestObject() {
return guestObject;
}
static String printStackToString(PolyglotLanguageContext context, Node node) {
StackTraceException stack = new StackTraceException(node);
TruffleStackTrace.fillIn(stack);
PolyglotException e = PolyglotImpl.guestToHostException(context, stack, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(out));
return new String(out.toByteArray());
}
@SuppressWarnings("serial")
static class StackTraceException extends AbstractTruffleException {
StackTraceException(Node location) {
super(location);
}
}
Object getFileSystemContext(PolyglotLanguage language) {
if (context == null) {
return null;
}
synchronized (context) {
/*
* Synchronized on polyglot context, otherwise isCreated() can change before
* getInternalFileSystemContext is called.
*/
PolyglotLanguageContext languageContext = context.getContext(language);
if (!languageContext.isCreated()) {
return null;
}
return languageContext.getInternalFileSystemContext();
}
}
/**
* Wrapper class for PrintStream and PrintWriter to enable a single implementation of
* printStackTrace.
*/
private abstract static class PrintStreamOrWriter {
/**
* Returns the object to be locked when using this StreamOrWriter.
*/
abstract Object lock();
/**
* Prints the specified string.
*/
abstract void print(Object o);
/**
* Prints the specified string as a line on this StreamOrWriter.
*/
abstract void println(Object o);
abstract void printStackTrace(Throwable t);
}
private static class WrappedPrintStream extends PrintStreamOrWriter {
private final PrintStream printStream;
WrappedPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
@Override
Object lock() {
return printStream;
}
@Override
void print(Object o) {
printStream.print(o);
}
@Override
void println(Object o) {
printStream.println(o);
}
@Override
void printStackTrace(Throwable t) {
t.printStackTrace(printStream);
}
}
private static class WrappedPrintWriter extends PrintStreamOrWriter {
private final PrintWriter printWriter;
WrappedPrintWriter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
@Override
Object lock() {
return printWriter;
}
@Override
void print(Object o) {
printWriter.print(o);
}
@Override
void println(Object o) {
printWriter.println(o);
}
@Override
void printStackTrace(Throwable t) {
t.printStackTrace(printWriter);
}
}
static Iterator createStackFrameIterator(PolyglotExceptionImpl impl) {
APIAccess apiAccess = impl.polyglot.getAPIAccess();
Throwable cause = findCause(impl.engine, impl.exception);
StackTraceElement[] hostStack;
if (EngineAccessor.LANGUAGE.isTruffleStackTrace(cause)) {
hostStack = EngineAccessor.LANGUAGE.getInternalStackTraceElements(cause);
} else if (cause.getStackTrace() == null || cause.getStackTrace().length == 0) {
hostStack = impl.exception.getStackTrace();
} else {
hostStack = cause.getStackTrace();
}
Iterator guestFrames = impl.guestFrames == null ? Collections.emptyIterator() : impl.guestFrames.iterator();
// we always start in some host stack frame
boolean inHostLanguage = impl.isHostException() || impl.isInternalError();
if (TRACE_STACK_TRACE_WALKING) {
// To mark the beginning of the stack trace and separate from the previous one
PrintStream out = System.out;
out.println();
}
return new MergedHostGuestIterator<>(impl.engine, hostStack, guestFrames, inHostLanguage, new Function() {
@Override
public StackFrame apply(StackTraceElement element) {
return apiAccess.newPolyglotStackTraceElement(PolyglotExceptionFrame.createHost(impl, element), impl.api);
}
}, new Function() {
private boolean firstGuestFrame = true;
@Override
public StackFrame apply(TruffleStackTraceElement guestFrame) {
boolean first = this.firstGuestFrame;
this.firstGuestFrame = false;
PolyglotExceptionFrame guest = PolyglotExceptionFrame.createGuest(impl, guestFrame, first);
if (guest != null) {
return apiAccess.newPolyglotStackTraceElement(guest, impl.api);
} else {
return null;
}
}
});
}
private static Throwable findCause(PolyglotEngineImpl engine, Throwable throwable) {
Throwable cause = throwable;
if (isHostException(engine, cause)) {
return findCause(engine, engine.host.unboxHostException(cause));
} else if (EngineAccessor.EXCEPTION.isException(cause)) {
return EngineAccessor.EXCEPTION.getLazyStackTrace(cause);
} else {
while (cause.getCause() != null && cause.getStackTrace().length == 0) {
if (isHostException(engine, cause)) {
cause = engine.host.unboxHostException(cause);
} else {
cause = cause.getCause();
}
}
return cause;
}
}
private static boolean isHostException(PolyglotEngineImpl engine, Throwable cause) {
/*
* Note that engine.host can be null if the error happens during initialization.
*/
return engine != null && engine.host != null && engine.host.isHostException(cause);
}
static class MergedHostGuestIterator implements Iterator {
private static final String POLYGLOT_PACKAGE = Engine.class.getName().substring(0, Engine.class.getName().lastIndexOf('.') + 1);
private static final String HOST_INTEROP_PACKAGE = "com.oracle.truffle.polyglot.";
private static final String[] JAVA_INTEROP_HOST_TO_GUEST = {
HOST_INTEROP_PACKAGE + "PolyglotMap",
HOST_INTEROP_PACKAGE + "PolyglotList",
HOST_INTEROP_PACKAGE + "PolyglotFunction",
HOST_INTEROP_PACKAGE + "PolyglotMapAndFunction",
HOST_INTEROP_PACKAGE + "PolyglotFunctionProxyHandler",
HOST_INTEROP_PACKAGE + "PolyglotObjectProxyHandler"
};
private final PolyglotEngineImpl engine;
private final Iterator guestFrames;
private final StackTraceElement[] hostStack;
private final ListIterator hostFrames;
private final Function hostFrameConvertor;
private final Function guestFrameConvertor;
private boolean inHostLanguage;
private T fetchedNext;
MergedHostGuestIterator(PolyglotEngineImpl engine, StackTraceElement[] hostStack, Iterator guestFrames, boolean inHostLanguage, Function hostFrameConvertor,
Function guestFrameConvertor) {
this.engine = engine;
this.hostStack = hostStack;
this.hostFrames = Arrays.asList(hostStack).listIterator();
this.guestFrames = guestFrames;
this.inHostLanguage = inHostLanguage;
this.hostFrameConvertor = hostFrameConvertor;
this.guestFrameConvertor = guestFrameConvertor;
}
@Override
public boolean hasNext() {
return fetchNext() != null;
}
@Override
public T next() {
T next = fetchNext();
if (next == null) {
throw new NoSuchElementException();
}
fetchedNext = null;
return next;
}
T fetchNext() {
if (fetchedNext != null) {
return fetchedNext;
}
while (hostFrames.hasNext()) {
StackTraceElement element = hostFrames.next();
traceStackTraceElement(element);
// we need to flip inHostLanguage state in opposite order as the stack is top to
// bottom.
if (inHostLanguage) {
int guestToHost = findGuestToHostFrame(engine, element, hostStack, hostFrames.nextIndex());
if (guestToHost >= 0) {
assert !isHostToGuest(element);
inHostLanguage = false;
for (int i = 0; i < guestToHost; i++) {
element = hostFrames.next();
traceStackTraceElement(element);
}
}
} else {
if (isHostToGuest(element)) {
inHostLanguage = true;
// skip extra host-to-guest frames
while (hostFrames.hasNext()) {
StackTraceElement next = hostFrames.next();
traceStackTraceElement(next);
if (isHostToGuest(next)) {
element = next;
} else {
hostFrames.previous();
break;
}
}
}
}
if (isGuestCall(element)) {
inHostLanguage = false;
// construct guest frame
if (guestFrames.hasNext()) {
G guestFrame = guestFrames.next();
T frame = guestFrameConvertor.apply(guestFrame);
if (frame != null) {
fetchedNext = frame;
return fetchedNext;
}
}
} else if (inHostLanguage) {
// construct host frame
fetchedNext = hostFrameConvertor.apply(element);
return fetchedNext;
} else {
// skip stack frame that is part of guest language stack
}
}
// consume guest frames
while (guestFrames.hasNext()) {
G guestFrame = guestFrames.next();
T frame = guestFrameConvertor.apply(guestFrame);
if (frame != null) {
fetchedNext = frame;
return fetchedNext;
}
}
return null;
}
static boolean isLazyStackTraceElement(StackTraceElement element) {
return element == null;
}
static boolean isGuestCall(StackTraceElement element) {
return isLazyStackTraceElement(element) || EngineAccessor.RUNTIME.isGuestCallStackFrame(element);
}
static boolean isHostToGuest(StackTraceElement element) {
if (isLazyStackTraceElement(element)) {
return false;
}
if (element.getClassName().startsWith(POLYGLOT_PACKAGE) && element.getClassName().indexOf('.', POLYGLOT_PACKAGE.length()) < 0) {
return true;
} else if (element.getClassName().startsWith(HOST_INTEROP_PACKAGE)) {
for (String hostToGuestClassName : JAVA_INTEROP_HOST_TO_GUEST) {
if (element.getClassName().equals(hostToGuestClassName)) {
return true;
}
}
}
return false;
}
// Return the number of frames with reflective calls to skip
static int findGuestToHostFrame(PolyglotEngineImpl engine, StackTraceElement firstElement, StackTraceElement[] hostStack, int nextElementIndex) {
if (isLazyStackTraceElement(firstElement)) {
return -1;
}
if (engine == null || engine.host == null) {
return -1;
}
return engine.host.findNextGuestToHostStackTraceElement(firstElement, hostStack, nextElementIndex);
}
private void traceStackTraceElement(StackTraceElement element) {
if (TRACE_STACK_TRACE_WALKING) {
PrintStream out = System.out;
out.printf("host: %5s, guestToHost: %2s, hostToGuest: %5s, guestCall: %5s, -- %s %n", inHostLanguage,
findGuestToHostFrame(engine, element, hostStack, hostFrames.nextIndex()), isHostToGuest(element),
isGuestCall(element), element);
}
}
}
}