org.pkl.thirdparty.graalvm.polyglot.PolyglotException Maven / Gradle / Ivy
Show all versions of pkl-tools Show documentation
/*
* 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 org.pkl.thirdparty.graalvm.polyglot;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.time.Duration;
import org.pkl.thirdparty.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractExceptionDispatch;
import org.pkl.thirdparty.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractStackFrameImpl;
/**
* A polyglot exception represents errors that contain Graal guest languages on the stack trace. In
* addition to the Java stack trace it also returns a {@link #getPolyglotStackTrace() polyglot stack
* trace}. Methods like {@link #printStackTrace()} are implemented such that host and guest language
* stack traces are printed nicely.
*
* A polyglot exception may have the following properties:
*
* - {@link #isGuestException() Guest Exception}: Is
true
if the exception was raised
* in guest language code.
* - {@link #isHostException() Host Exception}: Is
true
if this exception was raised
* in host runtime code. This may happen if the polyglot runtime host runtime methods that throw an
* exception. The original host exception can be accessed using {@link #asHostException()}.
* - {@link #isCancelled() Cancelled}: Is
true
if the execution got cancelled. The
* execution may be cancelled when {@link Context#close() closing} a context, by a guest language
* intrinsic or by a tool, like the debugger.
* - {@link #isExit() Exit}: Is
true
if the execution exited. The guest language
* triggers exit events if the guest language code request to exit the VM. The exit status can be
* accessed using {@link #getExitStatus()}.
* - {@link #isSyntaxError() Syntax Error}: Is
true
if the error represents a syntax
* error. For syntax errors a {@link #getSourceLocation() location} may be available.
* - {@link #isIncompleteSource() Incomplete Source}: Is
true
if this returns a
* {@link #isSyntaxError() syntax error} that indicates that the source is incomplete.
* - {@link #isResourceExhausted() Resource exhausted}: Is
true
if a resource limit
* e.g. the maximum memory was exhausted.
* - {@link #isInternalError() Internal Error}: Is
true
if an internal implementation
* error occurred in the polyglot runtime, the guest language or an instrument. It is not
* recommended to show such errors to the user in production. Please consider filing issues for
* internal implementation errors.
*
*
* @see Context
* @see Value
* @since 19.0
*/
@SuppressWarnings("serial")
public final class PolyglotException extends RuntimeException {
final AbstractExceptionDispatch dispatch;
final Object impl;
PolyglotException(String message, AbstractExceptionDispatch dispatch, Object receiver) {
super(message);
this.dispatch = dispatch;
this.impl = receiver;
dispatch.onCreate(receiver, this);
// we need to materialize the stack if this exception is printed as cause of another error.
// unfortunately we cannot detect this easily
super.setStackTrace(getStackTrace());
}
/**
* Prints host and guest language stack frames to the standard {@link System#err error output}.
*
* @since 19.0
*/
@Override
public void printStackTrace() {
dispatch.printStackTrace(impl, System.err);
}
/**
* Prints host and guest language stack frames to specified print stream.
*
* @since 19.0
*/
@Override
public void printStackTrace(PrintStream s) {
dispatch.printStackTrace(impl, s);
}
/**
* Prints host and guest language stack frames to specified print writer.
*
* @since 19.0
*/
@Override
public void printStackTrace(PrintWriter s) {
dispatch.printStackTrace(impl, s);
}
/**
* Unsupported, {@link PolyglotException} instances are not writable therefore filling the stack
* trace has no effect for them.
*
* @since 19.0
*/
@SuppressWarnings("sync-override")
@Override
public Throwable fillInStackTrace() {
return this;
}
/**
* Gets stack trace elements for Java and guest languages. For polyglot exceptions it
* recommended to use {@link #getPolyglotStackTrace()} as the guest language stack elements do
* not always fit the Java format for stack trace elements.
*
* @since 19.0
*/
@Override
public StackTraceElement[] getStackTrace() {
return dispatch.getStackTrace(impl);
}
/**
* Gets a user readable message for the polyglot exception. In case the exception is
* {@link #isInternalError() internal} then the original java class name is included in the
* message. The message never returns null
.
*
* @since 19.0
*/
@Override
public String getMessage() {
return dispatch.getMessage(impl);
}
/**
* Gets a guest language source location of this error or null
if no source
* location is available for this exception.
*
* @since 19.0
*/
public SourceSection getSourceLocation() {
return dispatch.getSourceLocation(impl);
}
/**
* {@inheritDoc}
*
* @since 19.0
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof PolyglotException) {
return impl.equals(((PolyglotException) obj).impl);
}
return false;
}
/**
* {@inheritDoc}
*
* @since 19.0
*/
@Override
public int hashCode() {
return impl.hashCode();
}
/**
* Unsupported, {@link PolyglotException} instances are not writable therefore setting the stack
* trace has no effect for them.
*
* @since 19.0
*/
@Override
public void setStackTrace(StackTraceElement[] stackTrace) {
// validate arguments to fullfil contract
for (int i = 0; i < stackTrace.length; i++) {
if (stackTrace[i] == null) {
throw new NullPointerException("stackTrace[" + i + "]");
}
}
}
/**
* Provides programmatic access to the polyglot stack trace information printed by
* {@link #printStackTrace()}. Returns an array of stack trace elements, each representing one
* stack frame. The zeroth element of the array (assuming the array's length is non-zero)
* represents the top of the stack, which is the last invocation in the sequence. Typically,
* this is the point at which this throwable was created and thrown. The last element of the
* array (assuming the array's length is non-zero) represents the bottom of the stack, which is
* the first method invocation in the sequence.
*
* @see StackFrame
* @since 19.0
*/
public Iterable getPolyglotStackTrace() {
return dispatch.getPolyglotStackTrace(impl);
}
/**
* Returns true
if this exception originates from the Java host language. In such a
* case the first {@link #getPolyglotStackTrace() stack frame} returns a
* {@link StackFrame#isHostFrame() host frame} as zeroth element.
*
* @since 19.0
*/
public boolean isHostException() {
return dispatch.isHostException(impl);
}
/**
* Returns true
if this exception originates from a Graal guest language. In such a
* case the first {@link #getPolyglotStackTrace() stack frame} returns a
* {@link StackFrame#isGuestFrame() guest frame} as zeroth element.
*
* @since 19.0
*/
public boolean isGuestException() {
return !dispatch.isHostException(impl);
}
/**
* Returns the original Java host exception that caused this exception. The original host
* exception contains a stack trace that is hardly interpretable by users as it contains details
* of the language implementation. The polyglot exception provides information for user-friendly
* error reporting with the {@link #getPolyglotStackTrace() polyglot stack trace}.
*
* @throws UnsupportedOperationException if this exception is not a host exception. Call
* {@link #isHostException()} to ensure its originating from the host language.
* @since 19.0
*/
public Throwable asHostException() {
return dispatch.asHostException(impl);
}
/**
* Returns true
if this exception was caused by an internal implementation error.
* These errors should be reported as bugs if observed. Internal error messages are typically
* hard to understand for guest language programmers and might contain implementation specific
* details that allows guest language implementers to debug the problem.
*
* @since 19.0
*/
public boolean isInternalError() {
return dispatch.isInternalError(impl);
}
/**
* Returns true
if this exception indicates that a resource limit was exceeded,
* else false
. Resource limit exceeded errors may be raised for the following
* reasons:
*
* - The host runtime run out of memory or stack space. For example if host runtime throws
* {@link OutOfMemoryError} or {@link StackOverflowError}, then they will be translated to a
* {@link PolyglotException} that return
true
for {@link #isResourceExhausted()}.
* - A configured {@link ResourceLimits resource limit} was exceeded.
*
- A runtime specific per context resource limit was exceeded. Depending on the host runtime
* implementation additional options to restrict the resource usage of a context may be
* specified using options.
*
*
* Resource limit exceptions may be originating from the {@link #isHostException() host} or
* {@link #isGuestException() guest}. Resource limit exceeded errors are never
* {@link #isInternalError() internal}, but may have caused the context to be
* {@link #isCancelled() cancelled} such that it is no longer usable.
*
* @since 20.2
*/
public boolean isResourceExhausted() {
return dispatch.isResourceExhausted(impl);
}
/**
* Returns true
if the execution was cancelled. The execution can be cancelled by
* {@link Context#close(boolean) closing} a context, if an instrument such as a debugger decides
* to cancel the current execution or if a {@link ResourceLimits resource limit} was exceeded.
* The context that caused a cancel event becomes unusable, i.e. closed.
*
* @since 19.0
*/
public boolean isCancelled() {
return dispatch.isCancelled(impl);
}
/**
* Returns true
if the current application thread was interrupted by an
* {@link InterruptedException}, or by calling {@link Context#interrupt(Duration)} from the
* host.
*
* @since 20.3
*/
public boolean isInterrupted() {
return dispatch.isInterrupted(impl);
}
/**
* Returns true
if this exception is caused by an attempt of a guest language
* program to exit the application. The provided exit code can be accessed using
* {@link #getExitStatus()}. This can be the result of either the soft or the hard exit.
*
* @since 19.0
* @see Context
*/
public boolean isExit() {
return dispatch.isExit(impl);
}
/**
* Returns true
if this exception indicates a parser or syntax error. In such a
* case #get
*
* @since 19.0
*/
public boolean isSyntaxError() {
return dispatch.isSyntaxError(impl);
}
/**
* Returns true
if this exception indicates a syntax error that is indicating that
* the syntax is incomplete. This allows guest language programmers to find out if more code is
* expected from a given source. For example, an incomplete JavaScript program could look like
* this:
*
*
* function incompleteFunction(arg) {
*
*
* A shell might react to this exception and prompt for additional source code, if this method
* returns true
.
*
* @since 19.0
*/
public boolean isIncompleteSource() {
return dispatch.isIncompleteSource(impl);
}
/**
* Returns an additional guest language object. Returns null
if no exception object
* is available.
*
* @since 19.0
*/
public Value getGuestObject() {
return dispatch.getGuestObject(impl);
}
/**
* Returns the exit status if this exception indicates that the application was {@link #isExit()
* exited}. The exit status is intended to be passed to {@link System#exit(int)}. In case of
* hard exit the application can be configured to call {@link System#exit(int)} directly using
* {@link Context.Builder#useSystemExit(boolean)}.
*
* @see Context
* @since 19.0
*/
public int getExitStatus() {
return dispatch.getExitStatus(impl);
}
@SuppressWarnings({"static-method", "unused"})
private void writeObject(ObjectOutputStream outputStream) throws IOException {
throw new NotSerializableException(PolyglotException.class.getSimpleName() + " serialization is not supported.");
}
/**
* Represents a polyglot stack frame originating from a guest language or the host language
* Java.
*
* @since 19.0
*/
public final class StackFrame {
final AbstractStackFrameImpl impl;
StackFrame(AbstractStackFrameImpl impl) {
this.impl = impl;
}
/**
* Returns true if the stack frame originates from the host language. Host frames do not
* provide a {@link #getSourceLocation() source location}. Instead the Java stack frame can
* be accessed using {@link #toHostFrame()}.
*
* @since 19.0
*/
public boolean isHostFrame() {
return impl.isHostFrame();
}
/**
* Returns true if the stack frame originates from the guest language.
*
* @since 19.0
*/
public boolean isGuestFrame() {
return !impl.isHostFrame();
}
/**
* Returns a Java stack trace element representation of the polyglot stack trace element.
* This is supported for host stack frames as well as guest language stack frames. A
* conversion to the host frame format can be useful for interoperability.
*
* @since 19.0
*/
public StackTraceElement toHostFrame() {
return impl.toHostFrame();
}
/**
* Returns the source location of the stack frame or null
if no source location
* is available. Host frames do never provide a source location.
*
* @since 19.0
*/
public SourceSection getSourceLocation() {
return impl.getSourceLocation();
}
/**
* Returns the root name of this stack frame. In case of the host language the Java method
* name is returned. In guest languages it returns a useful identifier for code. For
* example, in JavaScript this can be the function name.
*
* @since 19.0
*/
public String getRootName() {
return impl.getRootName();
}
/**
* Returns the language of this stack frame. In case of the host language a synthetic Java
* language object is returned.
*
* @since 19.0
*/
public Language getLanguage() {
return impl.getLanguage();
}
/**
* Returns a string representation of this stack frame. The format is inspired by the Java
* stack frame format.
*
* @since 19.0
*/
@Override
public String toString() {
return impl.toStringImpl(0);
}
}
}