Buckets:
| /** | |
| * @license | |
| * Copyright 2010 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| */ | |
| var LibraryExceptions = { | |
| #if !WASM_EXCEPTIONS | |
| $uncaughtExceptionCount: '0', | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| $exceptionLast: null, | |
| #endif | |
| $exceptionCaught: ' []', | |
| // This class is the exception metadata which is prepended to each thrown object (in WASM memory). | |
| // It is allocated in one block along with a thrown object in __cxa_allocate_exception and freed | |
| // in ___cxa_free_exception. It roughly corresponds to __cxa_exception structure in libcxxabi. The | |
| // class itself is just a native pointer wrapper, and contains all the necessary accessors for the | |
| // fields in the native structure. | |
| // TODO: Unfortunately this approach still cannot be considered thread-safe because single | |
| // exception object can be simultaneously thrown in several threads and its state (except | |
| // reference counter) is not protected from that. Also protection is not enough, separate state | |
| // should be allocated. libcxxabi has concept of dependent exception which is used for that | |
| // purpose, it references the primary exception. | |
| $ExceptionInfo: class { | |
| // excPtr - Thrown object pointer to wrap. Metadata pointer is calculated from it. | |
| constructor(excPtr) { | |
| this.excPtr = excPtr; | |
| this.ptr = excPtr - {{{ C_STRUCTS.__cxa_exception.__size__ }}}; | |
| } | |
| set_type(type) { | |
| {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionType, 'type', '*') }}}; | |
| } | |
| get_type() { | |
| return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionType, '*') }}}; | |
| } | |
| set_destructor(destructor) { | |
| {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionDestructor, 'destructor', '*') }}}; | |
| } | |
| get_destructor() { | |
| return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionDestructor, '*') }}}; | |
| } | |
| set_caught(caught) { | |
| caught = caught ? 1 : 0; | |
| {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.caught, 'caught', 'i8') }}}; | |
| } | |
| get_caught() { | |
| return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.caught, 'i8') }}} != 0; | |
| } | |
| set_rethrown(rethrown) { | |
| rethrown = rethrown ? 1 : 0; | |
| {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.rethrown, 'rethrown', 'i8') }}}; | |
| } | |
| get_rethrown() { | |
| return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.rethrown, 'i8') }}} != 0; | |
| } | |
| // Initialize native structure fields. Should be called once after allocated. | |
| init(type, destructor) { | |
| #if EXCEPTION_DEBUG | |
| dbg('ExceptionInfo init: ' + [type, destructor]); | |
| #endif | |
| this.set_adjusted_ptr(0); | |
| this.set_type(type); | |
| this.set_destructor(destructor); | |
| } | |
| set_adjusted_ptr(adjustedPtr) { | |
| {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.adjustedPtr, 'adjustedPtr', '*') }}}; | |
| } | |
| get_adjusted_ptr() { | |
| return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.adjustedPtr, '*') }}}; | |
| } | |
| }, | |
| // Here, we throw an exception after recording a couple of values that we need to remember | |
| // We also remember that it was the last exception thrown as we need to know that later. | |
| __cxa_throw__deps: ['$ExceptionInfo', '$uncaughtExceptionCount', | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| '$exceptionLast', | |
| '__cxa_increment_exception_refcount', | |
| #endif | |
| #if EXCEPTION_STACK_TRACES | |
| // When EXCEPTION_STACK_TRACES is enabled, the 'CppException' constructor | |
| // calls getExceptionMessage. We can't track the dependency there, so we | |
| // track it here. | |
| '$getExceptionMessage', | |
| // These functions can be necessary to prevent memory leaks from the JS | |
| // side. Even though they are not used it here directly, we export them when | |
| // 'throw' is used here. | |
| '$decrementExceptionRefcount', '$incrementExceptionRefcount', | |
| #endif | |
| ], | |
| __cxa_throw: (ptr, type, destructor) => { | |
| #if EXCEPTION_DEBUG | |
| dbg('__cxa_throw: ' + [ptrToString(ptr), type, ptrToString(destructor)]); | |
| #endif | |
| var info = new ExceptionInfo(ptr); | |
| // Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception. | |
| info.init(type, destructor); | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| ___cxa_increment_exception_refcount(ptr); | |
| exceptionLast = new CppException(ptr); | |
| #endif | |
| uncaughtExceptionCount++; | |
| {{{ makeThrow() }}} | |
| }, | |
| // This exception will be caught twice, but while begin_catch runs twice, | |
| // we early-exit from end_catch when the exception has been rethrown, so | |
| // pop that here from the caught exceptions. | |
| __cxa_rethrow__deps: ['$exceptionCaught', '$uncaughtExceptionCount', | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| '$exceptionLast', | |
| '__cxa_increment_exception_refcount', | |
| #endif | |
| ], | |
| __cxa_rethrow: () => { | |
| if (!exceptionCaught.length) { | |
| abort('no exception to throw'); | |
| } | |
| var info = exceptionCaught.at(-1); | |
| var ptr = info.excPtr; | |
| info.set_rethrown(true); | |
| info.set_caught(false); | |
| uncaughtExceptionCount++; | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| ___cxa_increment_exception_refcount(ptr); | |
| #if EXCEPTION_DEBUG | |
| dbg('__cxa_rethrow: ' + | |
| [ptrToString(ptr), exceptionLast, 'stack', exceptionCaught]); | |
| #endif | |
| exceptionLast = new CppException(ptr); | |
| #endif | |
| {{{ makeThrow() }}} | |
| }, | |
| llvm_eh_typeid_for: (type) => type, | |
| __cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_get_exception_ptr', | |
| '$uncaughtExceptionCount'], | |
| __cxa_begin_catch: (ptr) => { | |
| var info = new ExceptionInfo(ptr); | |
| if (!info.get_caught()) { | |
| info.set_caught(true); | |
| uncaughtExceptionCount--; | |
| } | |
| info.set_rethrown(false); | |
| exceptionCaught.push(info); | |
| #if EXCEPTION_DEBUG | |
| dbg('__cxa_begin_catch ' + [ptrToString(ptr), 'stack', exceptionCaught]); | |
| #endif | |
| return ___cxa_get_exception_ptr(ptr); | |
| }, | |
| // We're done with a catch. Now, we can run the destructor if there is one | |
| // and free the exception. Note that if the dynCall on the destructor fails | |
| // due to calling apply on undefined, that means that the destructor is | |
| // an invalid index into the FUNCTION_TABLE, so something has gone wrong. | |
| __cxa_end_catch__deps: ['$exceptionCaught', '__cxa_decrement_exception_refcount', 'setThrew', | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| '$exceptionLast', | |
| #endif | |
| ], | |
| __cxa_end_catch: () => { | |
| // Clear state flag. | |
| _setThrew(0, 0); | |
| #if ASSERTIONS | |
| assert(exceptionCaught.length > 0); | |
| #endif | |
| // Call destructor if one is registered then clear it. | |
| var info = exceptionCaught.pop(); | |
| #if EXCEPTION_DEBUG | |
| dbg('__cxa_end_catch popped ' + [info, 'stack', exceptionCaught]); | |
| #endif | |
| ___cxa_decrement_exception_refcount(info.excPtr); | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| exceptionLast = null; // XXX in decRef? | |
| #endif | |
| }, | |
| __cxa_uncaught_exceptions__deps: ['$uncaughtExceptionCount'], | |
| __cxa_uncaught_exceptions: () => uncaughtExceptionCount, | |
| __cxa_call_unexpected: (exception) => abort('Unexpected exception thrown, this is not properly supported - aborting'), | |
| __cxa_current_primary_exception__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount'], | |
| __cxa_current_primary_exception: () => { | |
| if (!exceptionCaught.length) { | |
| return 0; | |
| } | |
| var info = exceptionCaught[exceptionCaught.length - 1]; | |
| ___cxa_increment_exception_refcount(info.excPtr); | |
| return info.excPtr; | |
| }, | |
| __cxa_current_exception_type() { | |
| if (!exceptionCaught.length) { | |
| return 0; | |
| } | |
| var info = exceptionCaught[exceptionCaught.length - 1]; | |
| return info.get_type(); | |
| }, | |
| __cxa_rethrow_primary_exception__deps: ['$ExceptionInfo', '$uncaughtExceptionCount', | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| '$exceptionLast', | |
| '__cxa_increment_exception_refcount', | |
| #endif | |
| ], | |
| __cxa_rethrow_primary_exception: (ptr) => { | |
| if (!ptr) return; | |
| #if EXCEPTION_DEBUG | |
| dbg('__cxa_rethrow_primary_exception: ' + ptrToString(ptr)); | |
| #endif | |
| var info = new ExceptionInfo(ptr); | |
| info.set_rethrown(true); | |
| info.set_caught(false); | |
| uncaughtExceptionCount++; | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| ___cxa_increment_exception_refcount(ptr); | |
| exceptionLast = new CppException(ptr); | |
| #endif | |
| {{{ makeThrow('exceptionLast') }}} | |
| }, | |
| // Finds a suitable catch clause for when an exception is thrown. | |
| // In normal compilers, this functionality is handled by the C++ | |
| // 'personality' routine. This is passed a fairly complex structure | |
| // relating to the context of the exception and makes judgements | |
| // about how to handle it. Some of it is about matching a suitable | |
| // catch clause, and some of it is about unwinding. We already handle | |
| // unwinding using 'if' blocks around each function, so the remaining | |
| // functionality boils down to picking a suitable 'catch' block. | |
| // We'll do that here, instead, to keep things simpler. | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| $findMatchingCatch__deps: ['$exceptionLast', '$ExceptionInfo', '__cxa_can_catch', '$setTempRet0'], | |
| #endif | |
| $findMatchingCatch: (args) => { | |
| #if DISABLE_EXCEPTION_CATCHING | |
| setTempRet0(0); | |
| return 0; | |
| #else | |
| var thrown = exceptionLast?.excPtr; | |
| if (!thrown) { | |
| // just pass through the null ptr | |
| setTempRet0(0); | |
| return 0; | |
| } | |
| var info = new ExceptionInfo(thrown); | |
| info.set_adjusted_ptr(thrown); | |
| var thrownType = info.get_type(); | |
| if (!thrownType) { | |
| // just pass through the thrown ptr | |
| setTempRet0(0); | |
| return thrown; | |
| } | |
| // can_catch receives a **, add indirection | |
| #if EXCEPTION_DEBUG | |
| dbg("findMatchingCatch on " + ptrToString(thrown)); | |
| #endif | |
| // The different catch blocks are denoted by different types. | |
| // Due to inheritance, those types may not precisely match the | |
| // type of the thrown object. Find one which matches, and | |
| // return the type of the catch block which should be called. | |
| for (var caughtType of args) { | |
| if (caughtType === 0 || caughtType === thrownType) { | |
| // Catch all clause matched or exactly the same type is caught | |
| break; | |
| } | |
| var adjusted_ptr_addr = info.ptr + {{{ C_STRUCTS.__cxa_exception.adjustedPtr }}}; | |
| if (___cxa_can_catch(caughtType, thrownType, adjusted_ptr_addr)) { | |
| #if EXCEPTION_DEBUG | |
| dbg(" findMatchingCatch found " + [ptrToString(info.get_adjusted_ptr()), caughtType]); | |
| #endif | |
| setTempRet0(caughtType); | |
| return thrown; | |
| } | |
| } | |
| setTempRet0(thrownType); | |
| return thrown; | |
| #endif | |
| }, | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| __resumeException__deps: ['$exceptionLast'], | |
| #endif | |
| __resumeException: (ptr) => { | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| #if EXCEPTION_DEBUG | |
| dbg("__resumeException " + [ptrToString(ptr), exceptionLast]); | |
| #endif | |
| if (!exceptionLast) { | |
| exceptionLast = new CppException(ptr); | |
| } | |
| #endif | |
| {{{ makeThrow() }}} | |
| }, | |
| #endif | |
| #if WASM_EXCEPTIONS || !DISABLE_EXCEPTION_CATCHING | |
| $getExceptionMessageCommon__deps: ['__get_exception_message', 'free', '$stackSave', '$stackRestore', '$stackAlloc'], | |
| $getExceptionMessageCommon: (ptr) => { | |
| var sp = stackSave(); | |
| var type_addr_addr = stackAlloc({{{ POINTER_SIZE }}}); | |
| var message_addr_addr = stackAlloc({{{ POINTER_SIZE }}}); | |
| ___get_exception_message(ptr, type_addr_addr, message_addr_addr); | |
| var type_addr = {{{ makeGetValue('type_addr_addr', 0, '*') }}}; | |
| var message_addr = {{{ makeGetValue('message_addr_addr', 0, '*') }}}; | |
| var type = UTF8ToString(type_addr); | |
| _free(type_addr); | |
| var message; | |
| if (message_addr) { | |
| message = UTF8ToString(message_addr); | |
| _free(message_addr); | |
| } | |
| stackRestore(sp); | |
| return [type, message]; | |
| }, | |
| #endif | |
| #if WASM_EXCEPTIONS | |
| $getCppExceptionTag__deps: ['__cpp_exception'], | |
| // In static linking, tags are defined within the wasm module and are | |
| // exported, whereas in dynamic linking, tags are defined in libcore.js in | |
| // JS code and wasm modules import them. | |
| $getCppExceptionTag: () => ___cpp_exception, | |
| #if EXCEPTION_STACK_TRACES | |
| // Throw a WebAssembly.Exception object with the C++ tag with a stack trace | |
| // embedded. WebAssembly.Exception is a JS object representing a Wasm | |
| // exception, provided by Wasm JS API: | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Exception | |
| // In release builds, this function is not needed and the native | |
| // _Unwind_RaiseException in libunwind is used instead. | |
| __throw_exception_with_stack_trace__deps: [ | |
| '$getCppExceptionTag', '$getExceptionMessage', | |
| // These functions can be necessary to prevent memory leaks from the JS | |
| // side. Even though they are not used it here directly, we export them | |
| // when 'throw' is used here. | |
| '$decrementExceptionRefcount', '$incrementExceptionRefcount'], | |
| __throw_exception_with_stack_trace: (ex) => { | |
| var e = new WebAssembly.Exception(getCppExceptionTag(), [ex], {traceStack: true}); | |
| e.message = getExceptionMessage(e); | |
| throw e; | |
| }, | |
| #endif | |
| // Given an WebAssembly.Exception object, returns the actual user-thrown | |
| // C++ object address in the Wasm memory. | |
| $getCppExceptionThrownObjectFromWebAssemblyException__deps: ['$getCppExceptionTag', '__thrown_object_from_unwind_exception'], | |
| $getCppExceptionThrownObjectFromWebAssemblyException: (ex) => { | |
| // In Wasm EH, the value extracted from WebAssembly.Exception is a pointer | |
| // to the unwind header. Convert it to the actual thrown value. | |
| var unwind_header = ex.getArg(getCppExceptionTag(), 0); | |
| return ___thrown_object_from_unwind_exception(unwind_header); | |
| }, | |
| $incrementUncaughtExceptionCount__deps: ['__increment_uncaught_exception'], | |
| $incrementUncaughtExceptionCount: '__increment_uncaught_exception', | |
| $decrementUncaughtExceptionCount__deps: ['__decrement_uncaught_exception'], | |
| $decrementUncaughtExceptionCount: '__decrement_uncaught_exception', | |
| $incrementExceptionRefcount__deps: ['__cxa_increment_exception_refcount', '$getCppExceptionThrownObjectFromWebAssemblyException'], | |
| $incrementExceptionRefcount: (ex) => { | |
| var ptr = getCppExceptionThrownObjectFromWebAssemblyException(ex); | |
| ___cxa_increment_exception_refcount(ptr); | |
| }, | |
| $decrementExceptionRefcount__deps: ['__cxa_decrement_exception_refcount', '$getCppExceptionThrownObjectFromWebAssemblyException'], | |
| $decrementExceptionRefcount: (ex) => { | |
| var ptr = getCppExceptionThrownObjectFromWebAssemblyException(ex); | |
| ___cxa_decrement_exception_refcount(ptr); | |
| }, | |
| $getExceptionMessage__deps: ['$getCppExceptionThrownObjectFromWebAssemblyException', '$getExceptionMessageCommon'], | |
| $getExceptionMessage: (ex) => { | |
| var ptr = getCppExceptionThrownObjectFromWebAssemblyException(ex); | |
| return getExceptionMessageCommon(ptr); | |
| }, | |
| #else | |
| #if !DISABLE_EXCEPTION_THROWING | |
| $incrementUncaughtExceptionCount__deps: ['$uncaughtExceptionCount'], | |
| $incrementUncaughtExceptionCount: () => { | |
| uncaughtExceptionCount++; | |
| }, | |
| $decrementUncaughtExceptionCount__deps: ['$uncaughtExceptionCount'], | |
| $decrementUncaughtExceptionCount: () => { | |
| uncaughtExceptionCount--; | |
| }, | |
| #endif | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| $incrementExceptionRefcount__deps: ['__cxa_increment_exception_refcount'], | |
| $incrementExceptionRefcount: (exn) => ___cxa_increment_exception_refcount(exn.excPtr), | |
| $decrementExceptionRefcount__deps: ['__cxa_decrement_exception_refcount'], | |
| $decrementExceptionRefcount: (exn) => ___cxa_decrement_exception_refcount(exn.excPtr), | |
| $getExceptionMessage__deps: ['$getExceptionMessageCommon'], | |
| $getExceptionMessage: (exn) => getExceptionMessageCommon(exn.excPtr), | |
| #endif | |
| #endif | |
| }; | |
| #if !WASM_EXCEPTIONS | |
| // In LLVM, exceptions generate a set of functions of form | |
| // __cxa_find_matching_catch_2(), __cxa_find_matching_catch_3(), etc. where the | |
| // number specifies the number of arguments. In Emscripten, route all these to | |
| // a single function '__cxa_find_matching_catch' that variadically processes all | |
| // of these functions using JS 'arguments' object. | |
| addCxaCatch = (n) => { | |
| const args = []; | |
| // Confusingly, the actual number of argument is n - 2. According to the llvm | |
| // code in WebAssemblyLowerEmscriptenEHSjLj.cpp: | |
| // This is because a landingpad instruction contains two more arguments, a | |
| // personality function and a cleanup bit, and __cxa_find_matching_catch_N | |
| // functions are named after the number of arguments in the original landingpad | |
| // instruction. | |
| let sig = 'p'; | |
| for (let i = 0; i < n - 2; i++) { | |
| args.push(`arg${i}`); | |
| sig += 'p'; | |
| } | |
| const argString = args.join(','); | |
| LibraryManager.library[`__cxa_find_matching_catch_${n}__sig`] = sig; | |
| LibraryManager.library[`__cxa_find_matching_catch_${n}__deps`] = ['$findMatchingCatch']; | |
| LibraryManager.library[`__cxa_find_matching_catch_${n}`] = eval(`(${args}) => findMatchingCatch([${argString}])`); | |
| }; | |
| // Add the first 2-5 catch handlers preemptively. Others get added on demand in | |
| // jsifier. This is done here primarily so that these symbols end up with the | |
| // correct deps in the stub library that we pass to wasm-ld. | |
| // Note: __cxa_find_matching_catch_N function uses N = NumClauses + 2 so | |
| // __cxa_find_matching_catch_2 is the first such function with zero clauses. | |
| // See WebAssemblyLowerEmscriptenEHSjLj.cpp. | |
| for (let i = 2; i < 5; i++) { | |
| addCxaCatch(i) | |
| } | |
| #endif | |
| addToLibrary(LibraryExceptions); | |
Xet Storage Details
- Size:
- 17.3 kB
- Xet hash:
- 185b026672e04b5c04fc0f5c2bec7ba2cfcc885fe52095fad692761f1850d310
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.