Use upstream C++ error formatting support (#2828)

The logic that we are using to format C++ exceptions has been upstreamed. 
This will also work correctly with wasm exceptions.
This commit is contained in:
Hood Chatham 2022-08-25 14:41:39 -07:00 committed by GitHub
parent 7ef6bf8b3e
commit 325291ab87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 109 deletions

View File

@ -29,7 +29,6 @@ dist/pyodide_py.tar: $(wildcard src/py/pyodide/*.py) $(wildcard src/py/_pyodide
dist/pyodide.asm.js: \
src/core/docstring.o \
src/core/error_handling.o \
src/core/error_handling_cpp.o \
src/core/hiwire.o \
src/core/js2python.o \
src/core/jsproxy.o \
@ -168,9 +167,6 @@ clean-all: clean
make -C emsdk clean
make -C cpython clean-all
src/core/error_handling_cpp.o: src/core/error_handling_cpp.cpp
$(CXX) -o $@ -c $< $(MAIN_MODULE_CFLAGS) -Isrc/core/
%.o: %.c $(CPYTHONLIB) $(wildcard src/core/*.h src/core/*.js)
$(CC) -o $@ -c $< $(MAIN_MODULE_CFLAGS) -Isrc/core/

View File

@ -98,6 +98,7 @@ export SIDE_MODULE_LDFLAGS= $(LDFLAGS_BASE) -s SIDE_MODULE=1
export MAIN_MODULE_LDFLAGS= $(LDFLAGS_BASE) \
-s MAIN_MODULE=1 \
-s EXPORT_NAME="'_createPyodideModule'" \
-s EXPORT_EXCEPTION_HANDLING_HELPERS \
-s EXCEPTION_CATCHING_ALLOWED=['we only want to allow exception handling in side modules'] \
-s DEMANGLE_SUPPORT=1 \
-s USE_ZLIB \

View File

@ -36,6 +36,28 @@ function ensureCaughtObjectIsError(e: any): Error {
return e;
}
class CppException extends Error {
ty: string;
constructor(ty: string, msg: string | undefined, ptr: number) {
if (!msg) {
msg = `The exception is an object of type ${ty} at address ${ptr} which does not inherit from std::exception`;
}
super(msg);
this.ty = ty;
}
}
Object.defineProperty(CppException.prototype, "name", {
get() {
return `${this.constructor.name} ${this.ty}`;
},
});
function convertCppException(e: number) {
let [ty, msg]: [string, string] = Module.getExceptionMessage(e);
return new CppException(ty, msg, e);
}
Tests.convertCppException = convertCppException;
let fatal_error_occurred = false;
/**
* Signal a fatal error.
@ -59,7 +81,7 @@ API.fatal_error = function (e: any) {
return;
}
if (typeof e === "number") {
// Hopefully a C++ exception? Have to do some conversion work.
// Hopefully a C++ exception?
e = convertCppException(e);
} else {
e = ensureCaughtObjectIsError(e);
@ -105,84 +127,6 @@ API.fatal_error = function (e: any) {
throw e;
};
class CppException extends Error {
ty: string;
constructor(ty: string, msg: string) {
super(msg);
this.ty = ty;
}
}
Object.defineProperty(CppException.prototype, "name", {
get() {
return `${this.constructor.name} ${this.ty}`;
},
});
/**
*
* Return the type name, whether the pointer inherits from exception, and the
* vtable pointer for the type.
*
* This code is based on imitating:
* 1. the implementation of __cxa_find_matching_catch
* 2. the disassembly from:
* ```C++
* try {
* ...
* } catch(exception e){
* ...
* }
*
* @param ptr
* @returns
* exc_type_name : the type name of the exception, as would be reported by
* `typeid(type).name()` but also demangled.
*
* is_exception_subclass : true if the object is a subclass of exception. In
* this case we will use `exc.what()` to get an error message.
*
* adjusted_ptr : The adjusted vtable pointer for the exception to use to invoke
* exc.what().
*
* @private
*/
function cppExceptionInfo(ptr: number): [string, boolean, number] {
const base_exception_type = Module._exc_type();
const ei = new Module.ExceptionInfo(ptr);
const caught_exception_type = ei.get_type();
const stackTop = Module.stackSave();
const exceptionThrowBuf = Module.stackAlloc(4);
Module.HEAP32[exceptionThrowBuf / 4] = ptr;
const exc_type_name = Module.demangle(
Module.UTF8ToString(Module._exc_typename(caught_exception_type))
);
const is_exception_subclass = !!Module.___cxa_can_catch(
base_exception_type,
caught_exception_type,
exceptionThrowBuf
);
const adjusted_ptr = Module.HEAP32[exceptionThrowBuf / 4];
Module.stackRestore(stackTop);
return [exc_type_name, is_exception_subclass, adjusted_ptr];
}
function convertCppException(ptr: number): CppException {
const [exc_type_name, is_exception_subclass, adjusted_ptr] =
cppExceptionInfo(ptr);
let msg;
if (is_exception_subclass) {
// If the ptr inherits from exception, we can use exception.what() to
// generate a message
const msgPtr = Module._exc_what(adjusted_ptr);
msg = Module.UTF8ToString(msgPtr);
} else {
msg = `The exception is an object of type ${exc_type_name} at address ${ptr} which does not inherit from std::exception`;
}
return new CppException(exc_type_name, msg);
}
// Expose for testing
Tests.convertCppException = convertCppException;
function isPyodideFrame(frame: ErrorStackParser.StackFrame): boolean {
if (!frame) {
return false;

View File

@ -1,26 +0,0 @@
#include <emscripten.h>
#include <exception>
#include <typeinfo>
using namespace std;
extern "C"
{
EMSCRIPTEN_KEEPALIVE
const char* exc_what(exception& e)
{
return e.what();
}
EMSCRIPTEN_KEEPALIVE
const std::type_info* exc_type()
{
return &typeid(exception);
}
EMSCRIPTEN_KEEPALIVE
const char* exc_typename(std::type_info* type)
{
return type->name();
}
}