All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.polyglot.PolyglotExceptionImpl Maven / Gradle / Ivy

/*
 * 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.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.impl.AbstractPolyglotImpl.AbstractExceptionImpl;
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.interop.ExceptionType;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.polyglot.PolyglotEngineImpl.CancelExecution;

final class PolyglotExceptionImpl extends AbstractExceptionImpl {

    private static final String CAUSE_CAPTION = "Caused by host exception: ";

    private static final boolean TRACE_STACK_TRACE_WALKING = false;

    private PolyglotException impl;

    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, boolean polyglotContextCancellingOrCancelled, Throwable original) {
        this(engine.impl, engine, polyglotContextCancellingOrCancelled, null, original);
    }

    // Exception coming from an instrument
    PolyglotExceptionImpl(PolyglotImpl polyglot, Throwable original) {
        this(polyglot, null, false, null, original);
    }

    @SuppressWarnings("deprecation")
    PolyglotExceptionImpl(PolyglotImpl polyglot, PolyglotEngineImpl engine, boolean polyglotContextCancellingOrCancelled, PolyglotLanguageContext languageContext, Throwable original) {
        super(polyglot);
        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);
        this.resourceExhausted = isResourceLimit(exception);
        InteropLibrary interop = InteropLibrary.getUncached();
        if (interop.isException(exception)) {
            try {
                ExceptionType exceptionType = interop.getExceptionType(exception);
                this.internal = false;
                this.cancelled = polyglotContextCancellingOrCancelled || isLegacyTruffleExceptionCancelled(exception);
                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 = !cancelled && exceptionType == ExceptionType.INTERRUPT;

                if (interop.hasSourceLocation(exception)) {
                    this.sourceLocation = newSourceSection(interop.getSourceLocation(exception));
                } else {
                    this.sourceLocation = null;
                }
                Object exceptionObject;
                if (languageContext != null && !(exception instanceof HostException) && (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(receiver);
                    }
                    this.guestObject = languageContext.asValue(receiver);
                } else {
                    this.guestObject = null;
                }
            } catch (UnsupportedMessageException ume) {
                throw CompilerDirectives.shouldNotReachHere(ume);
            }
        } else {
            this.cancelled = polyglotContextCancellingOrCancelled || (exception instanceof CancelExecution) || isLegacyTruffleExceptionCancelled(exception);
            /*
             * 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.
             */
            this.interrupted = (exception instanceof PolyglotEngineImpl.InterruptExecution) || (exception != null && exception.getCause() instanceof InterruptedException);
            this.internal = !interrupted && !cancelled && !resourceExhausted;
            this.syntaxError = false;
            this.incompleteSource = false;
            this.exit = isLegacyTruffleExceptionExit(exception);
            this.exitStatus = exit ? getLegacyTruffleExceptionExitStatus(exception) : 0;
            com.oracle.truffle.api.source.SourceSection location = exception instanceof CancelExecution ? ((CancelExecution) exception).getSourceLocation()
                            : getLegacyTruffleExceptionSourceLocation(exception);
            this.sourceLocation = location != null ? newSourceSection(location) : null;
            this.guestObject = getLegacyTruffleExceptionGuestObject(languageContext, exception);
        }
        if (isHostException()) {
            this.message = asHostException().getMessage();
        } else {
            if (internal) {
                this.message = exception.toString();
            } else {
                this.message = exception.getMessage();
            }
        }

        // late materialization of host frames. only needed if polyglot exceptions cross the
        // host boundary.
        EngineAccessor.LANGUAGE.materializeHostFrames(original);
    }

    private static boolean isResourceLimit(Throwable e) {
        if (e instanceof CancelExecution) {
            return ((CancelExecution) e).isResourceLimit();
        }
        Throwable toCheck;
        if (e instanceof HostException) {
            toCheck = ((HostException) e).getOriginal();
        } else {
            toCheck = e;
        }
        return toCheck instanceof StackOverflowError || toCheck instanceof OutOfMemoryError;
    }

    @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) {
            Object exceptionObject = ((com.oracle.truffle.api.TruffleException) e).getExceptionObject();
            if (exceptionObject != null) {
                if (exceptionObject instanceof Proxy) {
                    exceptionObject = languageContext.toGuestValue(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();
    }

    @Override
    public org.graalvm.polyglot.SourceSection getSourceLocation() {
        return sourceLocation;
    }

    @Override
    public void onCreate(PolyglotException instance) {
        this.impl = instance;
    }

    @Override
    public boolean isResourceExhausted() {
        return resourceExhausted;
    }

    @Override
    public boolean isInterrupted() {
        return interrupted;
    }

    @Override
    public boolean isHostException() {
        return exception instanceof HostException;
    }

    @Override
    public Throwable asHostException() {
        if (!(exception instanceof HostException)) {
            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 ((HostException) exception).getOriginal();
    }

    @Override
    public void printStackTrace(PrintWriter s) {
        printStackTrace(new WrappedPrintWriter(s));
    }

    @Override
    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(impl.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(impl);
            } else {
                s.println(getMessage());
            }

            materialize();
            int languageIdLength = 0; // java
            for (StackFrame traceElement : getPolyglotStackTrace()) {
                if (!traceElement.isHostFrame()) {
                    languageIdLength = Math.max(languageIdLength, polyglot.getAPIAccess().getImpl(traceElement).getLanguage().getId().length());
                }
            }

            for (StackFrame traceElement : getPolyglotStackTrace()) {
                s.println("\tat " + polyglot.getAPIAccess().getImpl(traceElement).toStringImpl(languageIdLength));
            }

            // Print cause, if any
            if (isHostException()) {
                s.println(CAUSE_CAPTION + asHostException());
            }
            if (isInternalError()) {
                s.println("Original Internal Error: ");
                s.printStackTrace(exception);
            }
        }
    }

    @Override
    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);
        }
    }

    @Override
    public StackTraceElement[] getStackTrace() {
        return getJavaStackTrace().clone();
    }

    @Override
    public boolean isInternalError() {
        return internal;
    }

    @Override
    public Iterable getPolyglotStackTrace() {
        if (materializedFrames != null) {
            return materializedFrames;
        } else {
            return new Iterable() {
                public Iterator iterator() {
                    return createStackFrameIterator(PolyglotExceptionImpl.this);
                }
            };
        }
    }

    @Override
    public boolean isCancelled() {
        return cancelled;
    }

    @Override
    public boolean isExit() {
        return exit;
    }

    @Override
    public boolean isIncompleteSource() {
        return incompleteSource;
    }

    @Override
    public int getExitStatus() {
        return exitStatus;
    }

    @Override
    public boolean isSyntaxError() {
        return syntaxError;
    }

    @Override
    public Value getGuestObject() {
        return guestObject;
    }

    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.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<>(hostStack, guestFrames, inHostLanguage, new Function() {
            @Override
            public StackFrame apply(StackTraceElement element) {
                return apiAccess.newPolyglotStackTraceElement(impl.impl, PolyglotExceptionFrame.createHost(impl, element));
            }
        }, 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(impl.impl, guest);
                } else {
                    return null;
                }
            }
        });
    }

    private static Throwable findCause(Throwable throwable) {
        Throwable cause = throwable;
        if (cause instanceof HostException) {
            return findCause(((HostException) cause).getOriginal());
        } else if (EngineAccessor.EXCEPTION.isException(cause)) {
            return EngineAccessor.EXCEPTION.getLazyStackTrace(cause);
        } else {
            while (cause.getCause() != null && cause.getStackTrace().length == 0) {
                if (cause instanceof HostException) {
                    cause = ((HostException) cause).getOriginal();
                } else {
                    cause = cause.getCause();
                }
            }
            return 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 + "FunctionProxyHandler",
                        HOST_INTEROP_PACKAGE + "ObjectProxyHandler"
        };

        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(StackTraceElement[] hostStack, Iterator guestFrames, boolean inHostLanguage, Function hostFrameConvertor, Function guestFrameConvertor) {
            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 = isGuestToHost(element, hostStack, hostFrames.nextIndex());
                    if (guestToHost >= 0) {
                        assert !isHostToGuest(element);
                        inHostLanguage = false;

                        for (int i = 0; i < guestToHost; i++) {
                            assert isGuestToHostReflectiveCall(element);
                            element = hostFrames.next();
                            traceStackTraceElement(element);
                        }

                        assert isGuestToHostCallFromHostInterop(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
            if (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 isGuestToHost(StackTraceElement firstElement, StackTraceElement[] hostStack, int nextElementIndex) {
            if (isLazyStackTraceElement(firstElement)) {
                return -1;
            }

            StackTraceElement element = firstElement;
            int index = nextElementIndex;
            while (isGuestToHostReflectiveCall(element) && index < hostStack.length) {
                element = hostStack[index++];
            }
            if (isGuestToHostCallFromHostInterop(element)) {
                return index - nextElementIndex;
            } else {
                return -1;
            }
        }

        private static boolean isGuestToHostCallFromHostInterop(StackTraceElement element) {
            switch (element.getClassName()) {
                case "com.oracle.truffle.polyglot.HostMethodDesc$SingleMethod$MHBase":
                    return element.getMethodName().equals("invokeHandle");
                case "com.oracle.truffle.polyglot.HostMethodDesc$SingleMethod$MethodReflectImpl":
                    return element.getMethodName().equals("reflectInvoke");
                default:
                    return element.getClassName().startsWith("com.oracle.truffle.polyglot.HostToGuestCodeCache$") && element.getMethodName().equals("executeImpl");
            }
        }

        private static boolean isGuestToHostReflectiveCall(StackTraceElement element) {
            switch (element.getClassName()) {
                case "sun.reflect.NativeMethodAccessorImpl":
                case "sun.reflect.DelegatingMethodAccessorImpl":
                case "jdk.internal.reflect.NativeMethodAccessorImpl":
                case "jdk.internal.reflect.DelegatingMethodAccessorImpl":
                case "java.lang.reflect.Method":
                    return element.getMethodName().startsWith("invoke");
                default:
                    return false;
            }
        }

        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,
                                isGuestToHost(element, hostStack, hostFrames.nextIndex()), isHostToGuest(element),
                                isGuestCall(element), element);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy