diff --git a/emsdk/Makefile b/emsdk/Makefile index 09f87acbb..424479644 100644 --- a/emsdk/Makefile +++ b/emsdk/Makefile @@ -11,6 +11,7 @@ emsdk/emsdk: ./emsdk install --build=Release sdk-tag-1.38.4-64bit binaryen-tag-1.38.4-64bit ; \ cd .. ; \ (cat patches/*.patch | patch -p1) ; \ + cp files/* emsdk/emscripten/tag-1.38.4/src/ ; \ cd emsdk/binaryen/tag-1.38.4_64bit_binaryen/ ; \ make ; \ cd ../.. ; \ diff --git a/emsdk/files/library.js b/emsdk/files/library.js new file mode 100644 index 000000000..82ebcfd61 --- /dev/null +++ b/emsdk/files/library.js @@ -0,0 +1,4799 @@ +//"use strict"; + +// An implementation of basic necessary libraries for the web. This integrates +// with a compiled libc and with the rest of the JS runtime. +// +// We search the Library object when there is an external function. If the +// entry in the Library is a function, we insert it. If it is a string, we +// do another lookup in the library (a simple way to write a function once, +// if it can be called by different names). We also allow dependencies, +// using __deps. Initialization code to be run after allocating all +// global constants can be defined by __postset. +// +// Note that the full function name will be '_' + the name in the Library +// object. For convenience, the short name appears here. Note that if you add a +// new function with an '_', it will not be found. + +// Memory allocated during startup, in postsets, should only be ALLOC_STATIC + +LibraryManager.library = { + // keep this low in memory, because we flatten arrays with them in them +#if USE_PTHREADS + stdin: '; if (ENVIRONMENT_IS_PTHREAD) _stdin = PthreadWorkerInit._stdin; else PthreadWorkerInit._stdin = _stdin = allocate(1, "i32*", ALLOC_STATIC)', + stdout: '; if (ENVIRONMENT_IS_PTHREAD) _stdout = PthreadWorkerInit._stdout; else PthreadWorkerInit._stdout = _stdout = allocate(1, "i32*", ALLOC_STATIC)', + stderr: '; if (ENVIRONMENT_IS_PTHREAD) _stderr = PthreadWorkerInit._stderr; else PthreadWorkerInit._stderr = _stderr = allocate(1, "i32*", ALLOC_STATIC)', + _impure_ptr: '; if (ENVIRONMENT_IS_PTHREAD) __impure_ptr = PthreadWorkerInit.__impure_ptr; else PthreadWorkerInit.__impure_ptr __impure_ptr = allocate(1, "i32*", ALLOC_STATIC)', + __dso_handle: '; if (ENVIRONMENT_IS_PTHREAD) ___dso_handle = PthreadWorkerInit.___dso_handle; else PthreadWorkerInit.___dso_handle = ___dso_handle = allocate(1, "i32*", ALLOC_STATIC)', +#else + stdin: '{{{ makeStaticAlloc(1) }}}', + stdout: '{{{ makeStaticAlloc(1) }}}', + stderr: '{{{ makeStaticAlloc(1) }}}', + _impure_ptr: '{{{ makeStaticAlloc(1) }}}', + __dso_handle: '{{{ makeStaticAlloc(1) }}}', +#endif + + $PROCINFO: { + // permissions + /* + uid: 0, + gid: 0, + euid: 0, + egid: 0, + suid: 0, + sgid: 0, + fsuid: 0, + fsgid: 0, + */ + // process identification + ppid: 1, + pid: 42, + sid: 42, + pgid: 42 + }, + + // ========================================================================== + // utime.h + // ========================================================================== + + utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], + utime__proxy: 'sync', + utime__sig: 'iii', + utime: function(path, times) { + // int utime(const char *path, const struct utimbuf *times); + // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/utime.h.html + var time; + if (times) { + // NOTE: We don't keep track of access timestamps. + var offset = {{{ C_STRUCTS.utimbuf.modtime }}}; + time = {{{ makeGetValue('times', 'offset', 'i32') }}}; + time *= 1000; + } else { + time = Date.now(); + } + path = Pointer_stringify(path); + try { + FS.utime(path, time, time); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } + }, + + utimes__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], + utimes__proxy: 'sync', + utimes__sig: 'iii', + utimes: function(path, times) { + var time; + if (times) { + var offset = {{{ C_STRUCTS.timeval.__size__ }}} + {{{ C_STRUCTS.timeval.tv_sec }}}; + time = {{{ makeGetValue('times', 'offset', 'i32') }}} * 1000; + offset = {{{ C_STRUCTS.timeval.__size__ }}} + {{{ C_STRUCTS.timeval.tv_usec }}}; + time += {{{ makeGetValue('times', 'offset', 'i32') }}} / 1000; + } else { + time = Date.now(); + } + path = Pointer_stringify(path); + try { + FS.utime(path, time, time); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } + }, + + // ========================================================================== + // sys/file.h + // ========================================================================== + + flock: function(fd, operation) { + // int flock(int fd, int operation); + // Pretend to succeed + return 0; + }, + + chroot__deps: ['__setErrNo', '$ERRNO_CODES'], + chroot__proxy: 'sync', + chroot__sig: 'ii', + chroot: function(path) { + // int chroot(const char *path); + // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html + ___setErrNo(ERRNO_CODES.EACCES); + return -1; + }, + + fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'], + fpathconf__proxy: 'sync', + fpathconf__sig: 'iii', + fpathconf: function(fildes, name) { + // long fpathconf(int fildes, int name); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html + // NOTE: The first parameter is ignored, so pathconf == fpathconf. + // The constants here aren't real values. Just mimicking glibc. + switch (name) { + case {{{ cDefine('_PC_LINK_MAX') }}}: + return 32000; + case {{{ cDefine('_PC_MAX_CANON') }}}: + case {{{ cDefine('_PC_MAX_INPUT') }}}: + case {{{ cDefine('_PC_NAME_MAX') }}}: + return 255; + case {{{ cDefine('_PC_PATH_MAX') }}}: + case {{{ cDefine('_PC_PIPE_BUF') }}}: + case {{{ cDefine('_PC_REC_MIN_XFER_SIZE') }}}: + case {{{ cDefine('_PC_REC_XFER_ALIGN') }}}: + case {{{ cDefine('_PC_ALLOC_SIZE_MIN') }}}: + return 4096; + case {{{ cDefine('_PC_CHOWN_RESTRICTED') }}}: + case {{{ cDefine('_PC_NO_TRUNC') }}}: + case {{{ cDefine('_PC_2_SYMLINKS') }}}: + return 1; + case {{{ cDefine('_PC_VDISABLE') }}}: + return 0; + case {{{ cDefine('_PC_SYNC_IO') }}}: + case {{{ cDefine('_PC_ASYNC_IO') }}}: + case {{{ cDefine('_PC_PRIO_IO') }}}: + case {{{ cDefine('_PC_SOCK_MAXBUF') }}}: + case {{{ cDefine('_PC_REC_INCR_XFER_SIZE') }}}: + case {{{ cDefine('_PC_REC_MAX_XFER_SIZE') }}}: + case {{{ cDefine('_PC_SYMLINK_MAX') }}}: + return -1; + case {{{ cDefine('_PC_FILESIZEBITS') }}}: + return 64; + } + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + }, + pathconf: 'fpathconf', + + confstr__deps: ['__setErrNo', '$ERRNO_CODES', '$ENV'], + confstr__proxy: 'sync', + confstr__sig: 'iiii', + confstr: function(name, buf, len) { + // size_t confstr(int name, char *buf, size_t len); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/confstr.html + var value; + switch (name) { + case {{{ cDefine('_CS_PATH') }}}: + value = ENV['PATH'] || '/'; + break; + case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}: + // Mimicking glibc. + value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG'; + break; + case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}: + // This JS implementation was tested against this glibc version. + value = 'glibc 2.14'; + break; + case {{{ cDefine('_CS_GNU_LIBPTHREAD_VERSION') }}}: + // We don't support pthreads. + value = ''; + break; + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LIBS') }}}: + value = ''; + break; + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS') }}}: + value = '-m32'; + break; + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS') }}}: + value = '-m32 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'; + break; + default: + ___setErrNo(ERRNO_CODES.EINVAL); + return 0; + } + if (len == 0 || buf == 0) { + return value.length + 1; + } else { + var length = Math.min(len, value.length); + for (var i = 0; i < length; i++) { + {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}}; + } + if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}}; + return i; + } + }, + + execl__deps: ['__setErrNo', '$ERRNO_CODES'], + execl: function(/* ... */) { + // int execl(const char *path, const char *arg0, ... /*, (char *)0 */); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html + // We don't support executing external code. + ___setErrNo(ERRNO_CODES.ENOEXEC); + return -1; + }, + execle: 'execl', + execlp: 'execl', + execv: 'execl', + execve: 'execl', + execvp: 'execl', + __execvpe: 'execl', + fexecve: 'execl', + + _exit: function(status) { + // void _exit(int status); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html + Module['exit'](status); + }, + + fork__deps: ['__setErrNo', '$ERRNO_CODES'], + fork: function() { + // pid_t fork(void); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html + // We don't support multiple processes. + ___setErrNo(ERRNO_CODES.EAGAIN); + return -1; + }, + vfork: 'fork', + posix_spawn: 'fork', + posix_spawnp: 'fork', + + setgroups__deps: ['__setErrNo', '$ERRNO_CODES', 'sysconf'], + setgroups: function(ngroups, gidset) { + // int setgroups(int ngroups, const gid_t *gidset); + // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html + if (ngroups < 1 || ngroups > _sysconf({{{ cDefine('_SC_NGROUPS_MAX') }}})) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } else { + // We have just one process/user/group, so it makes no sense to set groups. + ___setErrNo(ERRNO_CODES.EPERM); + return -1; + } + }, + getpagesize: function() { + // int getpagesize(void); + return PAGE_SIZE; + }, + + sysconf__deps: ['__setErrNo', '$ERRNO_CODES'], + sysconf__proxy: 'sync', + sysconf__sig: 'ii', + sysconf: function(name) { + // long sysconf(int name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/sysconf.html + switch(name) { + case {{{ cDefine('_SC_PAGE_SIZE') }}}: return PAGE_SIZE; + case {{{ cDefine('_SC_PHYS_PAGES') }}}: +#if WASM + var maxHeapSize = 2*1024*1024*1024 - 65536; +#else + var maxHeapSize = 2*1024*1024*1024 - 16777216; +#endif +#if WASM_MEM_MAX != -1 + maxHeapSize = {{{ WASM_MEM_MAX }}}; +#endif +#if !ALLOW_MEMORY_GROWTH + maxHeapSize = HEAPU8.length; +#endif + return maxHeapSize / PAGE_SIZE; + case {{{ cDefine('_SC_ADVISORY_INFO') }}}: + case {{{ cDefine('_SC_BARRIERS') }}}: + case {{{ cDefine('_SC_ASYNCHRONOUS_IO') }}}: + case {{{ cDefine('_SC_CLOCK_SELECTION') }}}: + case {{{ cDefine('_SC_CPUTIME') }}}: + case {{{ cDefine('_SC_FSYNC') }}}: + case {{{ cDefine('_SC_IPV6') }}}: + case {{{ cDefine('_SC_MAPPED_FILES') }}}: + case {{{ cDefine('_SC_MEMLOCK') }}}: + case {{{ cDefine('_SC_MEMLOCK_RANGE') }}}: + case {{{ cDefine('_SC_MEMORY_PROTECTION') }}}: + case {{{ cDefine('_SC_MESSAGE_PASSING') }}}: + case {{{ cDefine('_SC_MONOTONIC_CLOCK') }}}: + case {{{ cDefine('_SC_PRIORITIZED_IO') }}}: + case {{{ cDefine('_SC_PRIORITY_SCHEDULING') }}}: + case {{{ cDefine('_SC_RAW_SOCKETS') }}}: + case {{{ cDefine('_SC_READER_WRITER_LOCKS') }}}: + case {{{ cDefine('_SC_REALTIME_SIGNALS') }}}: + case {{{ cDefine('_SC_SEMAPHORES') }}}: + case {{{ cDefine('_SC_SHARED_MEMORY_OBJECTS') }}}: + case {{{ cDefine('_SC_SPAWN') }}}: + case {{{ cDefine('_SC_SPIN_LOCKS') }}}: + case {{{ cDefine('_SC_SYNCHRONIZED_IO') }}}: + case {{{ cDefine('_SC_THREAD_ATTR_STACKADDR') }}}: + case {{{ cDefine('_SC_THREAD_ATTR_STACKSIZE') }}}: + case {{{ cDefine('_SC_THREAD_CPUTIME') }}}: + case {{{ cDefine('_SC_THREAD_PRIO_INHERIT') }}}: + case {{{ cDefine('_SC_THREAD_PRIO_PROTECT') }}}: + case {{{ cDefine('_SC_THREAD_PROCESS_SHARED') }}}: + case {{{ cDefine('_SC_THREAD_SAFE_FUNCTIONS') }}}: + case {{{ cDefine('_SC_THREADS') }}}: + case {{{ cDefine('_SC_TIMEOUTS') }}}: + case {{{ cDefine('_SC_TIMERS') }}}: + case {{{ cDefine('_SC_VERSION') }}}: + case {{{ cDefine('_SC_2_C_BIND') }}}: + case {{{ cDefine('_SC_2_C_DEV') }}}: + case {{{ cDefine('_SC_2_CHAR_TERM') }}}: + case {{{ cDefine('_SC_2_LOCALEDEF') }}}: + case {{{ cDefine('_SC_2_SW_DEV') }}}: + case {{{ cDefine('_SC_2_VERSION') }}}: + return 200809; + case {{{ cDefine('_SC_THREAD_PRIORITY_SCHEDULING') }}}: + return 0; + case {{{ cDefine('_SC_MQ_OPEN_MAX') }}}: + case {{{ cDefine('_SC_XOPEN_STREAMS') }}}: + case {{{ cDefine('_SC_XBS5_LP64_OFF64') }}}: + case {{{ cDefine('_SC_XBS5_LPBIG_OFFBIG') }}}: + case {{{ cDefine('_SC_AIO_LISTIO_MAX') }}}: + case {{{ cDefine('_SC_AIO_MAX') }}}: + case {{{ cDefine('_SC_SPORADIC_SERVER') }}}: + case {{{ cDefine('_SC_THREAD_SPORADIC_SERVER') }}}: + case {{{ cDefine('_SC_TRACE') }}}: + case {{{ cDefine('_SC_TRACE_EVENT_FILTER') }}}: + case {{{ cDefine('_SC_TRACE_EVENT_NAME_MAX') }}}: + case {{{ cDefine('_SC_TRACE_INHERIT') }}}: + case {{{ cDefine('_SC_TRACE_LOG') }}}: + case {{{ cDefine('_SC_TRACE_NAME_MAX') }}}: + case {{{ cDefine('_SC_TRACE_SYS_MAX') }}}: + case {{{ cDefine('_SC_TRACE_USER_EVENT_MAX') }}}: + case {{{ cDefine('_SC_TYPED_MEMORY_OBJECTS') }}}: + case {{{ cDefine('_SC_V6_LP64_OFF64') }}}: + case {{{ cDefine('_SC_V6_LPBIG_OFFBIG') }}}: + case {{{ cDefine('_SC_2_FORT_DEV') }}}: + case {{{ cDefine('_SC_2_FORT_RUN') }}}: + case {{{ cDefine('_SC_2_PBS') }}}: + case {{{ cDefine('_SC_2_PBS_ACCOUNTING') }}}: + case {{{ cDefine('_SC_2_PBS_CHECKPOINT') }}}: + case {{{ cDefine('_SC_2_PBS_LOCATE') }}}: + case {{{ cDefine('_SC_2_PBS_MESSAGE') }}}: + case {{{ cDefine('_SC_2_PBS_TRACK') }}}: + case {{{ cDefine('_SC_2_UPE') }}}: + case {{{ cDefine('_SC_THREAD_THREADS_MAX') }}}: + case {{{ cDefine('_SC_SEM_NSEMS_MAX') }}}: + case {{{ cDefine('_SC_SYMLOOP_MAX') }}}: + case {{{ cDefine('_SC_TIMER_MAX') }}}: + return -1; + case {{{ cDefine('_SC_V6_ILP32_OFF32') }}}: + case {{{ cDefine('_SC_V6_ILP32_OFFBIG') }}}: + case {{{ cDefine('_SC_JOB_CONTROL') }}}: + case {{{ cDefine('_SC_REGEXP') }}}: + case {{{ cDefine('_SC_SAVED_IDS') }}}: + case {{{ cDefine('_SC_SHELL') }}}: + case {{{ cDefine('_SC_XBS5_ILP32_OFF32') }}}: + case {{{ cDefine('_SC_XBS5_ILP32_OFFBIG') }}}: + case {{{ cDefine('_SC_XOPEN_CRYPT') }}}: + case {{{ cDefine('_SC_XOPEN_ENH_I18N') }}}: + case {{{ cDefine('_SC_XOPEN_LEGACY') }}}: + case {{{ cDefine('_SC_XOPEN_REALTIME') }}}: + case {{{ cDefine('_SC_XOPEN_REALTIME_THREADS') }}}: + case {{{ cDefine('_SC_XOPEN_SHM') }}}: + case {{{ cDefine('_SC_XOPEN_UNIX') }}}: + return 1; + case {{{ cDefine('_SC_THREAD_KEYS_MAX') }}}: + case {{{ cDefine('_SC_IOV_MAX') }}}: + case {{{ cDefine('_SC_GETGR_R_SIZE_MAX') }}}: + case {{{ cDefine('_SC_GETPW_R_SIZE_MAX') }}}: + case {{{ cDefine('_SC_OPEN_MAX') }}}: + return 1024; + case {{{ cDefine('_SC_RTSIG_MAX') }}}: + case {{{ cDefine('_SC_EXPR_NEST_MAX') }}}: + case {{{ cDefine('_SC_TTY_NAME_MAX') }}}: + return 32; + case {{{ cDefine('_SC_ATEXIT_MAX') }}}: + case {{{ cDefine('_SC_DELAYTIMER_MAX') }}}: + case {{{ cDefine('_SC_SEM_VALUE_MAX') }}}: + return 2147483647; + case {{{ cDefine('_SC_SIGQUEUE_MAX') }}}: + case {{{ cDefine('_SC_CHILD_MAX') }}}: + return 47839; + case {{{ cDefine('_SC_BC_SCALE_MAX') }}}: + case {{{ cDefine('_SC_BC_BASE_MAX') }}}: + return 99; + case {{{ cDefine('_SC_LINE_MAX') }}}: + case {{{ cDefine('_SC_BC_DIM_MAX') }}}: + return 2048; + case {{{ cDefine('_SC_ARG_MAX') }}}: return 2097152; + case {{{ cDefine('_SC_NGROUPS_MAX') }}}: return 65536; + case {{{ cDefine('_SC_MQ_PRIO_MAX') }}}: return 32768; + case {{{ cDefine('_SC_RE_DUP_MAX') }}}: return 32767; + case {{{ cDefine('_SC_THREAD_STACK_MIN') }}}: return 16384; + case {{{ cDefine('_SC_BC_STRING_MAX') }}}: return 1000; + case {{{ cDefine('_SC_XOPEN_VERSION') }}}: return 700; + case {{{ cDefine('_SC_LOGIN_NAME_MAX') }}}: return 256; + case {{{ cDefine('_SC_COLL_WEIGHTS_MAX') }}}: return 255; + case {{{ cDefine('_SC_CLK_TCK') }}}: return 100; + case {{{ cDefine('_SC_HOST_NAME_MAX') }}}: return 64; + case {{{ cDefine('_SC_AIO_PRIO_DELTA_MAX') }}}: return 20; + case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16; + case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6; + case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4; + case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: { + if (typeof navigator === 'object') return navigator['hardwareConcurrency'] || 1; + return 1; + } + } + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + }, + + // Implement a Linux-like 'memory area' for our 'process'. + // Changes the size of the memory area by |bytes|; returns the + // address of the previous top ('break') of the memory area + // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP + sbrk__asm: true, + sbrk__sig: ['ii'], + sbrk__deps: ['__setErrNo'], + sbrk: function(increment) { + increment = increment|0; + var oldDynamicTop = 0; + var oldDynamicTopOnChange = 0; + var newDynamicTop = 0; + var totalMemory = 0; +#if USE_PTHREADS + totalMemory = getTotalMemory()|0; + + // Perform a compare-and-swap loop to update the new dynamic top value. This is because + // this function can becalled simultaneously in multiple threads. + do { + oldDynamicTop = Atomics_load(HEAP32, DYNAMICTOP_PTR>>2)|0; + newDynamicTop = oldDynamicTop + increment | 0; + // Asking to increase dynamic top to a too high value? In pthreads builds we cannot + // enlarge memory, so this needs to fail. + if (((increment|0) > 0 & (newDynamicTop|0) < (oldDynamicTop|0)) // Detect and fail if we would wrap around signed 32-bit int. + | (newDynamicTop|0) < 0 // Also underflow, sbrk() should be able to be used to subtract. + | (newDynamicTop|0) > (totalMemory|0)) { +#if ABORTING_MALLOC + abortOnCannotGrowMemory()|0; +#else + ___setErrNo({{{ cDefine('ENOMEM') }}}); + return -1; +#endif + } + // Attempt to update the dynamic top to new value. Another thread may have beat this thread to the update, + // in which case we will need to start over by iterating the loop body again. + oldDynamicTopOnChange = Atomics_compareExchange(HEAP32, DYNAMICTOP_PTR>>2, oldDynamicTop|0, newDynamicTop|0)|0; + } while((oldDynamicTopOnChange|0) != (oldDynamicTop|0)); +#else // singlethreaded build: (-s USE_PTHREADS=0) + oldDynamicTop = HEAP32[DYNAMICTOP_PTR>>2]|0; + newDynamicTop = oldDynamicTop + increment | 0; + + if (((increment|0) > 0 & (newDynamicTop|0) < (oldDynamicTop|0)) // Detect and fail if we would wrap around signed 32-bit int. + | (newDynamicTop|0) < 0) { // Also underflow, sbrk() should be able to be used to subtract. +#if ABORTING_MALLOC + abortOnCannotGrowMemory()|0; +#endif + ___setErrNo({{{ cDefine('ENOMEM') }}}); + return -1; + } + + HEAP32[DYNAMICTOP_PTR>>2] = newDynamicTop; + totalMemory = getTotalMemory()|0; + if ((newDynamicTop|0) > (totalMemory|0)) { + if ((enlargeMemory()|0) == 0) { + HEAP32[DYNAMICTOP_PTR>>2] = oldDynamicTop; + ___setErrNo({{{ cDefine('ENOMEM') }}}); + return -1; + } + } +#endif + return oldDynamicTop|0; + }, + + brk__asm: true, + brk__sig: ['ii'], + brk: function(newDynamicTop) { + newDynamicTop = newDynamicTop|0; + var oldDynamicTop = 0; + var totalMemory = 0; +#if USE_PTHREADS + totalMemory = getTotalMemory()|0; + // Asking to increase dynamic top to a too high value? In pthreads builds we cannot + // enlarge memory, so this needs to fail. + if ((newDynamicTop|0) < 0 | (newDynamicTop|0) > (totalMemory|0)) { +#if ABORTING_MALLOC + abortOnCannotGrowMemory()|0; +#else + ___setErrNo({{{ cDefine('ENOMEM') }}}); + return -1; +#endif + } + Atomics_store(HEAP32, DYNAMICTOP_PTR>>2, newDynamicTop|0)|0; +#else // singlethreaded build: (-s USE_PTHREADS=0) + if ((newDynamicTop|0) < 0) { +#if ABORTING_MALLOC + abortOnCannotGrowMemory()|0; +#endif + ___setErrNo({{{ cDefine('ENOMEM') }}}); + return -1; + } + + oldDynamicTop = HEAP32[DYNAMICTOP_PTR>>2]|0; + HEAP32[DYNAMICTOP_PTR>>2] = newDynamicTop; + totalMemory = getTotalMemory()|0; + if ((newDynamicTop|0) > (totalMemory|0)) { + if ((enlargeMemory()|0) == 0) { + ___setErrNo({{{ cDefine('ENOMEM') }}}); + HEAP32[DYNAMICTOP_PTR>>2] = oldDynamicTop; + return -1; + } + } +#endif + return 0; + }, + + system__deps: ['__setErrNo', '$ERRNO_CODES'], + system: function(command) { + // int system(const char *command); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html + // Can't call external programs. + ___setErrNo(ERRNO_CODES.EAGAIN); + return -1; + }, + + // ========================================================================== + // stdlib.h + // ========================================================================== + + // tiny, fake malloc/free implementation. If the program actually uses malloc, + // a compiled version will be used; this will only be used if the runtime + // needs to allocate something, for which this is good enough if otherwise + // no malloc is needed. + malloc: function(bytes) { + /* Over-allocate to make sure it is byte-aligned by 8. + * This will leak memory, but this is only the dummy + * implementation (replaced by dlmalloc normally) so + * not an issue. + */ +#if ASSERTIONS == 2 + warnOnce('using stub malloc (reference it from C to have the real one included)'); +#endif + var ptr = dynamicAlloc(bytes + 8); + return (ptr+8) & 0xFFFFFFF8; + }, + free: function() { +#if ASSERTIONS == 2 + warnOnce('using stub free (reference it from C to have the real one included)'); +#endif +}, + + abs: 'Math_abs', + labs: 'Math_abs', + + exit__deps: ['_exit'], + exit: function(status) { + __exit(status); + }, + _Exit__deps: ['exit'], + _Exit: function(status) { + __exit(status); + }, + + _ZSt9terminatev__deps: ['exit'], + _ZSt9terminatev: function() { + _exit(-1234); + }, + + atexit__proxy: 'sync', + atexit__sig: 'ii', + atexit: function(func, arg) { +#if ASSERTIONS +#if NO_EXIT_RUNTIME == 1 + warnOnce('atexit() called, but NO_EXIT_RUNTIME is set, so atexits() will not be called. set NO_EXIT_RUNTIME to 0 (see the FAQ)'); +#endif +#endif + __ATEXIT__.unshift({ func: func, arg: arg }); + }, + __cxa_atexit: 'atexit', + + // used in rust, clang when doing thread_local statics + __cxa_thread_atexit: 'atexit', + __cxa_thread_atexit_impl: 'atexit', + + abort: function() { + Module['abort'](); + }, + + environ__deps: ['$ENV'], +#if USE_PTHREADS + environ: '; if (ENVIRONMENT_IS_PTHREAD) _environ = PthreadWorkerInit._environ; else PthreadWorkerInit._environ = _environ = allocate(1, "i32*", ALLOC_STATIC)', +#else + environ: '{{{ makeStaticAlloc(1) }}}', +#endif + __environ__deps: ['environ'], + __environ: 'environ', + __buildEnvironment__deps: ['__environ'], + __buildEnvironment: function(env) { + // WARNING: Arbitrary limit! + var MAX_ENV_VALUES = 64; + var TOTAL_ENV_SIZE = 1024; + + // Statically allocate memory for the environment. + var poolPtr; + var envPtr; + if (!___buildEnvironment.called) { + ___buildEnvironment.called = true; + // Set default values. Use string keys for Closure Compiler compatibility. + ENV['USER'] = ENV['LOGNAME'] = 'web_user'; + ENV['PATH'] = '/'; + ENV['PWD'] = '/'; + ENV['HOME'] = '/home/web_user'; + ENV['LANG'] = 'C.UTF-8'; + ENV['_'] = Module['thisProgram']; + // Allocate memory. + poolPtr = staticAlloc(TOTAL_ENV_SIZE); + envPtr = staticAlloc(MAX_ENV_VALUES * {{{ Runtime.POINTER_SIZE }}}); + {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}}; + {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; + } else { + envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; + poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}}; + } + + // Collect key=value lines. + var strings = []; + var totalSize = 0; + for (var key in env) { + if (typeof env[key] === 'string') { + var line = key + '=' + env[key]; + strings.push(line); + totalSize += line.length; + } + } + if (totalSize > TOTAL_ENV_SIZE) { + throw new Error('Environment size exceeded TOTAL_ENV_SIZE!'); + } + + // Make new. + var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}}; + for (var i = 0; i < strings.length; i++) { + var line = strings[i]; + writeAsciiToMemory(line, poolPtr); + {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}}; + poolPtr += line.length + 1; + } + {{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}}; + }, + $ENV__deps: ['__buildEnvironment'], +#if USE_PTHREADS + $ENV__postset: 'if (!ENVIRONMENT_IS_PTHREAD) ___buildEnvironment(ENV);', +#else + $ENV__postset: '___buildEnvironment(ENV);', +#endif + $ENV: {}, + getenv__deps: ['$ENV'], + getenv__proxy: 'sync', + getenv__sig: 'ii', + getenv: function(name) { + // char *getenv(const char *name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html + if (name === 0) return 0; + name = Pointer_stringify(name); + if (!ENV.hasOwnProperty(name)) return 0; + + if (_getenv.ret) _free(_getenv.ret); + _getenv.ret = allocateUTF8(ENV[name]); + return _getenv.ret; + }, + clearenv__deps: ['$ENV', '__buildEnvironment'], + clearenv__proxy: 'sync', + clearenv__sig: 'i', + clearenv: function() { + // int clearenv (void); + // http://www.gnu.org/s/hello/manual/libc/Environment-Access.html#index-clearenv-3107 + ENV = {}; + ___buildEnvironment(ENV); + return 0; + }, + setenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], + setenv__proxy: 'sync', + setenv__sig: 'iiii', + setenv: function(envname, envval, overwrite) { + // int setenv(const char *envname, const char *envval, int overwrite); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/setenv.html + if (envname === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + var name = Pointer_stringify(envname); + var val = Pointer_stringify(envval); + if (name === '' || name.indexOf('=') !== -1) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + if (ENV.hasOwnProperty(name) && !overwrite) return 0; + ENV[name] = val; + ___buildEnvironment(ENV); + return 0; + }, + unsetenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], + unsetenv__proxy: 'sync', + unsetenv__sig: 'ii', + unsetenv: function(name) { + // int unsetenv(const char *name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/unsetenv.html + if (name === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + name = Pointer_stringify(name); + if (name === '' || name.indexOf('=') !== -1) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + if (ENV.hasOwnProperty(name)) { + delete ENV[name]; + ___buildEnvironment(ENV); + } + return 0; + }, + putenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], + putenv__proxy: 'sync', + putenv__sig: 'ii', + putenv: function(string) { + // int putenv(char *string); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html + // WARNING: According to the standard (and the glibc implementation), the + // string is taken by reference so future changes are reflected. + // We copy it instead, possibly breaking some uses. + if (string === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + string = Pointer_stringify(string); + var splitPoint = string.indexOf('=') + if (string === '' || string.indexOf('=') === -1) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + var name = string.slice(0, splitPoint); + var value = string.slice(splitPoint + 1); + if (!(name in ENV) || ENV[name] !== value) { + ENV[name] = value; + ___buildEnvironment(ENV); + } + return 0; + }, + + getloadavg: function(loadavg, nelem) { + // int getloadavg(double loadavg[], int nelem); + // http://linux.die.net/man/3/getloadavg + var limit = Math.min(nelem, 3); + var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}}; + for (var i = 0; i < limit; i++) { + {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}}; + } + return limit; + }, + + // For compatibility, call to rand() when code requests arc4random(), although this is *not* at all + // as strong as rc4 is. See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/arc4random.3.html + arc4random: 'rand', + + // ========================================================================== + // string.h + // ========================================================================== + + memcpy__inline: function(dest, src, num, align) { + var ret = ''; + ret += makeCopyValues(dest, src, num, 'null', null, align); + return ret; + }, + + emscripten_memcpy_big: function(dest, src, num) { + HEAPU8.set(HEAPU8.subarray(src, src+num), dest); + return dest; + }, + + memcpy__asm: true, + memcpy__sig: 'iiii', + memcpy__deps: ['emscripten_memcpy_big'], + memcpy: function(dest, src, num) { + dest = dest|0; src = src|0; num = num|0; + var ret = 0; + var aligned_dest_end = 0; + var block_aligned_dest_end = 0; + var dest_end = 0; + // Test against a benchmarked cutoff limit for when HEAPU8.set() becomes faster to use. + if ((num|0) >= +#if SIMD + 196608 +#else + 8192 +#endif + ) { + return _emscripten_memcpy_big(dest|0, src|0, num|0)|0; + } + + ret = dest|0; + dest_end = (dest + num)|0; + if ((dest&3) == (src&3)) { + // The initial unaligned < 4-byte front. + while (dest & 3) { + if ((num|0) == 0) return ret|0; + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + dest = (dest+1)|0; + src = (src+1)|0; + num = (num-1)|0; + } + aligned_dest_end = (dest_end & -4)|0; + block_aligned_dest_end = (aligned_dest_end - 64)|0; + while ((dest|0) <= (block_aligned_dest_end|0) ) { +#if SIMD + SIMD_Int32x4_store(HEAPU8, dest, SIMD_Int32x4_load(HEAPU8, src)); + SIMD_Int32x4_store(HEAPU8, dest+16, SIMD_Int32x4_load(HEAPU8, src+16)); + SIMD_Int32x4_store(HEAPU8, dest+32, SIMD_Int32x4_load(HEAPU8, src+32)); + SIMD_Int32x4_store(HEAPU8, dest+48, SIMD_Int32x4_load(HEAPU8, src+48)); +#else + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 4, makeGetValueAsm('src', 4, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 8, makeGetValueAsm('src', 8, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 12, makeGetValueAsm('src', 12, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 16, makeGetValueAsm('src', 16, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 20, makeGetValueAsm('src', 20, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 24, makeGetValueAsm('src', 24, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 28, makeGetValueAsm('src', 28, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 32, makeGetValueAsm('src', 32, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 36, makeGetValueAsm('src', 36, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 40, makeGetValueAsm('src', 40, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 44, makeGetValueAsm('src', 44, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 48, makeGetValueAsm('src', 48, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 52, makeGetValueAsm('src', 52, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 56, makeGetValueAsm('src', 56, 'i32'), 'i32') }}}; + {{{ makeSetValueAsm('dest', 60, makeGetValueAsm('src', 60, 'i32'), 'i32') }}}; +#endif + dest = (dest+64)|0; + src = (src+64)|0; + } + while ((dest|0) < (aligned_dest_end|0) ) { + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i32'), 'i32') }}}; + dest = (dest+4)|0; + src = (src+4)|0; + } + } else { + // In the unaligned copy case, unroll a bit as well. + aligned_dest_end = (dest_end - 4)|0; + while ((dest|0) < (aligned_dest_end|0) ) { + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + {{{ makeSetValueAsm('dest', 1, makeGetValueAsm('src', 1, 'i8'), 'i8') }}}; + {{{ makeSetValueAsm('dest', 2, makeGetValueAsm('src', 2, 'i8'), 'i8') }}}; + {{{ makeSetValueAsm('dest', 3, makeGetValueAsm('src', 3, 'i8'), 'i8') }}}; + dest = (dest+4)|0; + src = (src+4)|0; + } + } + // The remaining unaligned < 4 byte tail. + while ((dest|0) < (dest_end|0)) { + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + dest = (dest+1)|0; + src = (src+1)|0; + } + return ret|0; + }, + + llvm_memcpy_i32: 'memcpy', + llvm_memcpy_i64: 'memcpy', + llvm_memcpy_p0i8_p0i8_i32: 'memcpy', + llvm_memcpy_p0i8_p0i8_i64: 'memcpy', + + memmove__sig: 'iiii', + memmove__asm: true, + memmove__deps: ['memcpy'], + memmove: function(dest, src, num) { + dest = dest|0; src = src|0; num = num|0; + var ret = 0; + if (((src|0) < (dest|0)) & ((dest|0) < ((src + num)|0))) { + // Unlikely case: Copy backwards in a safe manner + ret = dest; + src = (src + num)|0; + dest = (dest + num)|0; + while ((num|0) > 0) { + dest = (dest - 1)|0; + src = (src - 1)|0; + num = (num - 1)|0; + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + } + dest = ret; + } else { + _memcpy(dest, src, num) | 0; + } + return dest | 0; + }, + llvm_memmove_i32: 'memmove', + llvm_memmove_i64: 'memmove', + llvm_memmove_p0i8_p0i8_i32: 'memmove', + llvm_memmove_p0i8_p0i8_i64: 'memmove', + + memset__inline: function(ptr, value, num, align) { + return makeSetValues(ptr, 0, value, 'null', num, align); + }, + memset__sig: 'iiii', + memset__asm: true, + memset: function(ptr, value, num) { + ptr = ptr|0; value = value|0; num = num|0; + var end = 0, aligned_end = 0, block_aligned_end = 0, value4 = 0; +#if SIMD + var value16 = SIMD_Int32x4(0,0,0,0); +#endif + end = (ptr + num)|0; + + value = value & 0xff; + if ((num|0) >= 67 /* 64 bytes for an unrolled loop + 3 bytes for unaligned head*/) { + while ((ptr&3) != 0) { + {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; + ptr = (ptr+1)|0; + } + + aligned_end = (end & -4)|0; + block_aligned_end = (aligned_end - 64)|0; + value4 = value | (value << 8) | (value << 16) | (value << 24); +#if SIMD + value16 = SIMD_Int32x4_splat(value4); +#endif + + while((ptr|0) <= (block_aligned_end|0)) { +#if SIMD + SIMD_Int32x4_store(HEAPU8, ptr, value16); + SIMD_Int32x4_store(HEAPU8, ptr+16, value16); + SIMD_Int32x4_store(HEAPU8, ptr+32, value16); + SIMD_Int32x4_store(HEAPU8, ptr+48, value16); +#else + {{{ makeSetValueAsm('ptr', 0, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 4, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 8, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 12, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 16, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 20, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 24, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 28, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 32, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 36, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 40, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 44, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 48, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 52, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 56, 'value4', 'i32') }}}; + {{{ makeSetValueAsm('ptr', 60, 'value4', 'i32') }}}; +#endif + ptr = (ptr + 64)|0; + } + + while ((ptr|0) < (aligned_end|0) ) { + {{{ makeSetValueAsm('ptr', 0, 'value4', 'i32') }}}; + ptr = (ptr+4)|0; + } + } + // The remaining bytes. + while ((ptr|0) < (end|0)) { + {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; + ptr = (ptr+1)|0; + } + return (end-num)|0; + }, + llvm_memset_i32: 'memset', + llvm_memset_p0i8_i32: 'memset', + llvm_memset_p0i8_i64: 'memset', + + // ========================================================================== + // GCC/LLVM specifics + // ========================================================================== + __builtin_prefetch: function(){}, + + // ========================================================================== + // LLVM specifics + // ========================================================================== + + llvm_va_start__inline: function(ptr) { + // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp' + // 2-word structure: struct { void* start; void* currentOffset; } + return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, Runtime.QUANTUM_SIZE, 0, 'void*'); + }, + + llvm_va_end: function() {}, + + llvm_va_copy: function(ppdest, ppsrc) { + // copy the list start + {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; + + // copy the list's current offset (will be advanced with each call to va_arg) + {{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; + }, + + llvm_bswap_i16__asm: true, + llvm_bswap_i16__sig: 'ii', + llvm_bswap_i16: function(x) { + x = x|0; + return (((x&0xff)<<8) | ((x>>8)&0xff))|0; + }, + + llvm_bswap_i32__asm: true, + llvm_bswap_i32__sig: 'ii', + llvm_bswap_i32: function(x) { + x = x|0; + return (((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24))|0; + }, + + llvm_bswap_i64__deps: ['llvm_bswap_i32'], + llvm_bswap_i64: function(l, h) { + var retl = _llvm_bswap_i32(h)>>>0; + var reth = _llvm_bswap_i32(l)>>>0; + {{{ makeStructuralReturn(['retl', 'reth']) }}}; + }, + + llvm_ctlz_i8__asm: true, + llvm_ctlz_i8__sig: 'ii', + llvm_ctlz_i8: function(x, isZeroUndef) { + x = x | 0; + isZeroUndef = isZeroUndef | 0; + return (Math_clz32(x & 0xff) | 0) - 24 | 0; + }, + + llvm_ctlz_i16__asm: true, + llvm_ctlz_i16__sig: 'ii', + llvm_ctlz_i16: function(x, isZeroUndef) { + x = x | 0; + isZeroUndef = isZeroUndef | 0; + return (Math_clz32(x & 0xffff) | 0) - 16 | 0 + }, + + llvm_ctlz_i64__asm: true, + llvm_ctlz_i64__sig: 'iii', + llvm_ctlz_i64: function(l, h, isZeroUndef) { + l = l | 0; + h = h | 0; + isZeroUndef = isZeroUndef | 0; + var ret = 0; + ret = Math_clz32(h) | 0; + if ((ret | 0) == 32) ret = ret + (Math_clz32(l) | 0) | 0; + {{{ makeSetTempRet0('0') }}}; + return ret | 0; + }, + +#if WASM == 0 // binaryen will convert these calls to wasm anyhow + llvm_cttz_i32__asm: true, +#endif + llvm_cttz_i32__sig: 'ii', + llvm_cttz_i32: function(x) { // Note: Currently doesn't take isZeroUndef() + x = x | 0; + return (x ? (31 - (Math_clz32((x ^ (x - 1))) | 0) | 0) : 32) | 0; + }, + + llvm_cttz_i64__deps: ['llvm_cttz_i32'], + llvm_cttz_i64: function(l, h) { + var ret = _llvm_cttz_i32(l); + if (ret == 32) ret += _llvm_cttz_i32(h); + {{{ makeStructuralReturn(['ret', '0']) }}}; + }, + + llvm_ctpop_i32__asm: true, + llvm_ctpop_i32__sig: 'ii', + llvm_ctpop_i32: function(x) { + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + // http://bits.stephan-brumme.com/countBits.html + x = x | 0; + x = x - ((x >>> 1) & 0x55555555) | 0; + x = (x & 0x33333333) + ((x >>> 2) & 0x33333333) | 0; + return (Math_imul((x + (x >>> 4) & 252645135 /* 0xF0F0F0F, but hits uglify parse bug? */), 0x1010101) >>> 24) | 0; + }, + + llvm_ctpop_i64__deps: ['llvm_ctpop_i32'], + llvm_ctpop_i64__asm: true, + llvm_ctpop_i64__sig: 'iii', + llvm_ctpop_i64: function(l, h) { + l = l | 0; + h = h | 0; + return (_llvm_ctpop_i32(l) | 0) + (_llvm_ctpop_i32(h) | 0) | 0; + }, + + llvm_trap: function() { + abort('trap!'); + }, + + llvm_prefetch: function(){}, + + __assert_fail: function(condition, filename, line, func) { + abort('Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function']); + }, + + __assert_func: function(filename, line, func, condition) { + abort('Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function']); + }, + + $EXCEPTIONS: { + last: 0, + caught: [], + infos: {}, + deAdjust: function(adjusted) { + if (!adjusted || EXCEPTIONS.infos[adjusted]) return adjusted; + for (var key in EXCEPTIONS.infos) { + var ptr = +key; // the iteration key is a string, and if we throw this, it must be an integer as that is what we look for + var info = EXCEPTIONS.infos[ptr]; + if (info.adjusted === adjusted) { +#if EXCEPTION_DEBUG + Module.printErr('de-adjusted exception ptr ' + adjusted + ' to ' + ptr); +#endif + return ptr; + } + } +#if EXCEPTION_DEBUG + Module.printErr('no de-adjustment for unknown exception ptr ' + adjusted); +#endif + return adjusted; + }, + addRef: function(ptr) { +#if EXCEPTION_DEBUG + Module.printErr('addref ' + ptr); +#endif + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + info.refcount++; + }, + decRef: function(ptr) { +#if EXCEPTION_DEBUG + Module.printErr('decref ' + ptr); +#endif + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + assert(info.refcount > 0); + info.refcount--; + // A rethrown exception can reach refcount 0; it must not be discarded + // Its next handler will clear the rethrown flag and addRef it, prior to + // final decRef and destruction here + if (info.refcount === 0 && !info.rethrown) { + if (info.destructor) { +#if WASM_BACKEND == 0 + Module['dynCall_vi'](info.destructor, ptr); +#else + // In Wasm, destructors return 'this' as in ARM + Module['dynCall_ii'](info.destructor, ptr); +#endif + } + delete EXCEPTIONS.infos[ptr]; + ___cxa_free_exception(ptr); +#if EXCEPTION_DEBUG + Module.printErr('decref freeing exception ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); +#endif + } + }, + clearRef: function(ptr) { + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + info.refcount = 0; + }, + }, + + // Exceptions + __cxa_allocate_exception__deps: ['malloc'], + __cxa_allocate_exception: function(size) { + return _malloc(size); + }, + __cxa_free_exception__deps: ['free'], + __cxa_free_exception: function(ptr) { + try { + return _free(ptr); + } catch(e) { // XXX FIXME +#if ASSERTIONS + Module.printErr('exception during cxa_free_exception: ' + e); +#endif + } + }, + __cxa_increment_exception_refcount__deps: ['$EXCEPTIONS'], + __cxa_increment_exception_refcount: function(ptr) { + EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr)); + }, + __cxa_decrement_exception_refcount__deps: ['$EXCEPTIONS'], + __cxa_decrement_exception_refcount: function(ptr) { + EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr)); + }, + // 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__sig: 'viii', + __cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '$EXCEPTIONS'], + __cxa_throw: function(ptr, type, destructor) { +#if EXCEPTION_DEBUG + Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor]); +#endif + EXCEPTIONS.infos[ptr] = { + ptr: ptr, + adjusted: ptr, + type: type, + destructor: destructor, + refcount: 0, + caught: false, + rethrown: false + }; + EXCEPTIONS.last = ptr; + if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { + __ZSt18uncaught_exceptionv.uncaught_exception = 1; + } else { + __ZSt18uncaught_exceptionv.uncaught_exception++; + } + {{{ makeThrow('ptr') }}} + }, + // 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: ['__cxa_end_catch', '$EXCEPTIONS'], + __cxa_rethrow: function() { + var ptr = EXCEPTIONS.caught.pop(); + ptr = EXCEPTIONS.deAdjust(ptr); + if (!EXCEPTIONS.infos[ptr].rethrown) { + // Only pop if the corresponding push was through rethrow_primary_exception + EXCEPTIONS.caught.push(ptr) + EXCEPTIONS.infos[ptr].rethrown = true; + } +#if EXCEPTION_DEBUG + Module.printErr('Compiled code RE-throwing an exception, popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); +#endif + EXCEPTIONS.last = ptr; + {{{ makeThrow('ptr') }}} + }, + llvm_eh_exception__deps: ['$EXCEPTIONS'], + llvm_eh_exception: function() { + return EXCEPTIONS.last; + }, + llvm_eh_selector__jsargs: true, + llvm_eh_selector__deps: ['$EXCEPTIONS'], + llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { + var type = EXCEPTIONS.last; + for (var i = 2; i < arguments.length; i++) { + if (arguments[i] == type) return type; + } + return 0; + }, + llvm_eh_typeid_for: function(type) { + return type; + }, + __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv', '$EXCEPTIONS'], + __cxa_begin_catch: function(ptr) { + var info = EXCEPTIONS.infos[ptr]; + if (info && !info.caught) { + info.caught = true; + __ZSt18uncaught_exceptionv.uncaught_exception--; + } + if (info) info.rethrown = false; + EXCEPTIONS.caught.push(ptr); +#if EXCEPTION_DEBUG + Module.printErr('cxa_begin_catch ' + [ptr, 'stack', EXCEPTIONS.caught]); +#endif + EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr)); + return 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: ['__cxa_free_exception', '$EXCEPTIONS'], + __cxa_end_catch: function() { + // Clear state flag. + Module['setThrew'](0); + // Call destructor if one is registered then clear it. + var ptr = EXCEPTIONS.caught.pop(); +#if EXCEPTION_DEBUG + Module.printErr('cxa_end_catch popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); +#endif + if (ptr) { + EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr)); + EXCEPTIONS.last = 0; // XXX in decRef? + } + }, + __cxa_get_exception_ptr: function(ptr) { +#if EXCEPTION_DEBUG + Module.printErr('cxa_get_exception_ptr ' + ptr); +#endif + // TODO: use info.adjusted? + return ptr; + }, + _ZSt18uncaught_exceptionv: function() { // std::uncaught_exception() + return !!__ZSt18uncaught_exceptionv.uncaught_exception; + }, + __cxa_uncaught_exception__deps: ['_ZSt18uncaught_exceptionv'], + __cxa_uncaught_exception: function() { + return !!__ZSt18uncaught_exceptionv.uncaught_exception; + }, + + __cxa_call_unexpected: function(exception) { + Module.printErr('Unexpected exception thrown, this is not properly supported - aborting'); + ABORT = true; + throw exception; + }, + + __cxa_current_primary_exception: function() { + var ret = EXCEPTIONS.caught[EXCEPTIONS.caught.length-1] || 0; + if (ret) EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ret)); + return ret; + }, + + __cxa_rethrow_primary_exception__deps: ['__cxa_rethrow'], + __cxa_rethrow_primary_exception: function(ptr) { + if (!ptr) return; + EXCEPTIONS.caught.push(ptr); + EXCEPTIONS.infos[ptr].rethrown = true; + ___cxa_rethrow(); + }, + + terminate: '__cxa_call_unexpected', + + __gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], + __gxx_personality_v0: function() { + }, + + __gcc_personality_v0: function() { + }, + + // 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. + + __cxa_find_matching_catch__deps: ['__resumeException', '$EXCEPTIONS'], + __cxa_find_matching_catch: function() { + var thrown = EXCEPTIONS.last; + if (!thrown) { + // just pass through the null ptr + {{{ makeStructuralReturn([0, 0]) }}}; + } + var info = EXCEPTIONS.infos[thrown]; + var throwntype = info.type; + if (!throwntype) { + // just pass through the thrown ptr + {{{ makeStructuralReturn(['thrown', 0]) }}}; + } + var typeArray = Array.prototype.slice.call(arguments); + + var pointer = Module['___cxa_is_pointer_type'](throwntype); + // can_catch receives a **, add indirection + if (!___cxa_find_matching_catch.buffer) ___cxa_find_matching_catch.buffer = _malloc(4); +#if EXCEPTION_DEBUG + Module.print("can_catch on " + [thrown]); +#endif + {{{ makeSetValue('___cxa_find_matching_catch.buffer', '0', 'thrown', '*') }}}; + thrown = ___cxa_find_matching_catch.buffer; + // 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 i = 0; i < typeArray.length; i++) { + if (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) { + thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection + info.adjusted = thrown; +#if EXCEPTION_DEBUG + Module.print(" can_catch found " + [thrown, typeArray[i]]); +#endif + {{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}}; + } + } + // Shouldn't happen unless we have bogus data in typeArray + // or encounter a type for which emscripten doesn't have suitable + // typeinfo defined. Best-efforts match just in case. + thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection + {{{ makeStructuralReturn(['thrown', 'throwntype']) }}}; + }, + + __resumeException__deps: ['$EXCEPTIONS', function() { Functions.libraryFunctions['___resumeException'] = 1 }], // will be called directly from compiled code + __resumeException: function(ptr) { +#if EXCEPTION_DEBUG + Module.print("Resuming exception " + [ptr, EXCEPTIONS.last]); +#endif + if (!EXCEPTIONS.last) { EXCEPTIONS.last = ptr; } + {{{ makeThrow('ptr') }}} + }, + + llvm_stacksave: function() { + var self = _llvm_stacksave; + if (!self.LLVM_SAVEDSTACKS) { + self.LLVM_SAVEDSTACKS = []; + } + self.LLVM_SAVEDSTACKS.push(stackSave()); + return self.LLVM_SAVEDSTACKS.length-1; + }, + llvm_stackrestore: function(p) { + var self = _llvm_stacksave; + var ret = self.LLVM_SAVEDSTACKS[p]; + self.LLVM_SAVEDSTACKS.splice(p, 1); + stackRestore(ret); + }, + + __cxa_pure_virtual: function() { + ABORT = true; + throw 'Pure virtual function called!'; + }, + + llvm_flt_rounds: function() { + return -1; // 'indeterminable' for FLT_ROUNDS + }, + + llvm_expect_i32__inline: function(val, expected) { + return '(' + val + ')'; + }, + + llvm_objectsize_i32: function() { return -1 }, // TODO: support this + + llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning + + llvm_bitreverse_i32__asm: true, + llvm_bitreverse_i32__sig: 'ii', + llvm_bitreverse_i32: function(x) { + x = x|0; + x = ((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1); + x = ((x & 0xcccccccc) >>> 2) | ((x & 0x33333333) << 2); + x = ((x & 0xf0f0f0f0) >>> 4) | ((x & 0x0f0f0f0f) << 4); + x = ((x & 0xff00ff00) >>> 8) | ((x & 0x00ff00ff) << 8); + return (x >>> 16) | (x << 16); + }, + + // llvm-nacl + + llvm_nacl_atomic_store_i32__inline: true, + + llvm_nacl_atomic_cmpxchg_i8__inline: true, + llvm_nacl_atomic_cmpxchg_i16__inline: true, + llvm_nacl_atomic_cmpxchg_i32__inline: true, + + // ========================================================================== + // llvm-mono integration + // ========================================================================== + + llvm_mono_load_i8_p0i8: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i8') }}}; + }, + + llvm_mono_store_i8_p0i8: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i8') }}}; + }, + + llvm_mono_load_i16_p0i16: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i16') }}}; + }, + + llvm_mono_store_i16_p0i16: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i16') }}}; + }, + + llvm_mono_load_i32_p0i32: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i32') }}}; + }, + + llvm_mono_store_i32_p0i32: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i32') }}}; + }, + + // ========================================================================== + // math.h + // ========================================================================== + + cos: 'Math_cos', + cosf: 'Math_cos', + cosl: 'Math_cos', + sin: 'Math_sin', + sinf: 'Math_sin', + sinl: 'Math_sin', + tan: 'Math_tan', + tanf: 'Math_tan', + tanl: 'Math_tan', + acos: 'Math_acos', + acosf: 'Math_acos', + acosl: 'Math_acos', + asin: 'Math_asin', + asinf: 'Math_asin', + asinl: 'Math_asin', + atan: 'Math_atan', + atanf: 'Math_atan', + atanl: 'Math_atan', + atan2: 'Math_atan2', + atan2f: 'Math_atan2', + atan2l: 'Math_atan2', + exp: 'Math_exp', + expf: 'Math_exp', + expl: 'Math_exp', + log: 'Math_log', + logf: 'Math_log', + logl: 'Math_log', + sqrt: 'Math_sqrt', + sqrtf: 'Math_sqrt', + sqrtl: 'Math_sqrt', + fabs: 'Math_abs', + fabsf: 'Math_abs', + fabsl: 'Math_abs', + llvm_fabs_f32: 'Math_abs', + llvm_fabs_f64: 'Math_abs', + ceil: 'Math_ceil', + ceilf: 'Math_ceil', + ceill: 'Math_ceil', + floor: 'Math_floor', + floorf: 'Math_floor', + floorl: 'Math_floor', + pow: 'Math_pow', + powf: 'Math_pow', + powl: 'Math_pow', + llvm_sqrt_f32: 'Math_sqrt', + llvm_sqrt_f64: 'Math_sqrt', + llvm_pow_f32: 'Math_pow', + llvm_pow_f64: 'Math_pow', + llvm_powi_f32: 'Math_pow', + llvm_powi_f64: 'Math_pow', + llvm_log_f32: 'Math_log', + llvm_log_f64: 'Math_log', + llvm_exp_f32: 'Math_exp', + llvm_exp_f64: 'Math_exp', + llvm_cos_f32: 'Math_cos', + llvm_cos_f64: 'Math_cos', + llvm_sin_f32: 'Math_sin', + llvm_sin_f64: 'Math_sin', + llvm_trunc_f32: 'Math_trunc', + llvm_trunc_f64: 'Math_trunc', + llvm_ceil_f32: 'Math_ceil', + llvm_ceil_f64: 'Math_ceil', + llvm_floor_f32: 'Math_floor', + llvm_floor_f64: 'Math_floor', + + llvm_exp2_f32: function(x) { + return Math.pow(2, x); + }, + llvm_exp2_f64: 'llvm_exp2_f32', + + llvm_log2_f32: function(x) { + return Math.log(x) / Math.LN2; // TODO: Math.log2, when browser support is there + }, + llvm_log2_f64: 'llvm_log2_f32', + + llvm_log10_f32: function(x) { + return Math.log(x) / Math.LN10; // TODO: Math.log10, when browser support is there + }, + llvm_log10_f64: 'llvm_log10_f32', + + llvm_copysign_f32: function(x, y) { + return y < 0 || (y === 0 && 1/y < 0) ? -Math_abs(x) : Math_abs(x); + }, + + llvm_copysign_f64: function(x, y) { + return y < 0 || (y === 0 && 1/y < 0) ? -Math_abs(x) : Math_abs(x); + }, + + round__asm: true, + round__sig: 'dd', + round: function(d) { + d = +d; + return d >= +0 ? +Math_floor(d + +0.5) : +Math_ceil(d - +0.5); + }, + + roundf__asm: true, + roundf__sig: 'ff', + roundf: function(d) { + d = +d; + return d >= +0 ? +Math_floor(d + +0.5) : +Math_ceil(d - +0.5); + }, + + llvm_round_f64__asm: true, + llvm_round_f64__sig: 'dd', + llvm_round_f64: function(d) { + d = +d; + return d >= +0 ? +Math_floor(d + +0.5) : +Math_ceil(d - +0.5); + }, + + llvm_round_f32__asm: true, + llvm_round_f32__sig: 'ff', + llvm_round_f32: function(f) { + f = +f; + return f >= +0 ? +Math_floor(f + +0.5) : +Math_ceil(f - +0.5); // TODO: use fround? + }, + + rintf__asm: true, + rintf__sig: 'ff', + rintf__deps: ['round'], + rintf: function(f) { + f = +f; + return (f - +Math_floor(f) != .5) ? +_round(f) : +_round(f / +2) * +2; + }, + + // TODO: fround? + llvm_rint_f32__asm: true, + llvm_rint_f32__sig: 'ff', + llvm_rint_f32__deps: ['roundf'], + llvm_rint_f32: function(f) { + f = +f; + return (f - +Math_floor(f) != .5) ? +_roundf(f) : +_roundf(f / +2) * +2; + }, + + llvm_rint_f64__asm: true, + llvm_rint_f64__sig: 'dd', + llvm_rint_f64__deps: ['round'], + llvm_rint_f64: function(f) { + f = +f; + return (f - +Math_floor(f) != .5) ? +_round(f) : +_round(f / +2) * +2; + }, + + // TODO: fround? + llvm_nearbyint_f32__asm: true, + llvm_nearbyint_f32__sig: 'ff', + llvm_nearbyint_f32__deps: ['roundf'], + llvm_nearbyint_f32: function(f) { + f = +f; + return (f - +Math_floor(f) != .5) ? +_roundf(f) : +_roundf(f / +2) * +2; + }, + + llvm_nearbyint_f64__asm: true, + llvm_nearbyint_f64__sig: 'dd', + llvm_nearbyint_f64__deps: ['round'], + llvm_nearbyint_f64: function(f) { + f = +f; + return (f - +Math_floor(f) != .5) ? +_round(f) : +_round(f / +2) * +2; + }, + + // min/max num do not quite match the behavior of JS and wasm min/max: + // llvm and libc return the non-NaN if one is NaN, while JS and wasm + // return the NaN :( + // see also https://github.com/WebAssembly/design/issues/214 + llvm_minnum_f32__asm: true, + llvm_minnum_f32__sig: 'ff', + llvm_minnum_f32: function(x, y) { + x = +x; + y = +y; + if (x != x) return +y; + if (y != y) return +x; + return +Math_min(+x, +y); + }, + + llvm_minnum_f64__asm: true, + llvm_minnum_f64__sig: 'dd', + llvm_minnum_f64: function(x, y) { + x = +x; + y = +y; + if (x != x) return +y; + if (y != y) return +x; + return +Math_min(+x, +y); + }, + + llvm_maxnum_f32__asm: true, + llvm_maxnum_f32__sig: 'ff', + llvm_maxnum_f32: function(x, y) { + x = +x; + y = +y; + if (x != x) return +y; + if (y != y) return +x; + return +Math_max(+x, +y); + }, + + llvm_maxnum_f64__asm: true, + llvm_maxnum_f64__sig: 'dd', + llvm_maxnum_f64: function(x, y) { + x = +x; + y = +y; + if (x != x) return +y; + if (y != y) return +x; + return +Math_max(+x, +y); + }, + + _reallyNegative: function(x) { + return x < 0 || (x === 0 && (1/x) === -Infinity); + }, + + // ========================================================================== + // dlfcn.h - Dynamic library loading + // + // Some limitations: + // + // * Minification on each file separately may not work, as they will + // have different shortened names. You can in theory combine them, then + // minify, then split... perhaps. + // + // * LLVM optimizations may fail. If the child wants to access a function + // in the parent, LLVM opts may remove it from the parent when it is + // being compiled. Not sure how to tell LLVM to not do so. + // ========================================================================== + + $DLFCN: { + error: null, + errorMsg: null, + loadedLibs: {}, // handle -> [refcount, name, lib_object] + loadedLibNames: {}, // name -> handle + }, + // void* dlopen(const char* filename, int flag); + dlopen__deps: ['$DLFCN', '$FS', '$ENV'], + dlopen__proxy: 'sync', + dlopen__sig: 'iii', + dlopen: function(filename, flag) { +#if MAIN_MODULE == 0 + abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/kripken/emscripten/wiki/Linking"); +#endif + // void *dlopen(const char *file, int mode); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html + var searchpaths = []; + if (filename === 0) { + filename = '__self__'; + } else { + var strfilename = Pointer_stringify(filename); + var isValidFile = function (filename) { + var target = FS.findObject(filename); + return target && !target.isFolder && !target.isDevice; + }; + + if (isValidFile(strfilename)) { + filename = strfilename; + } else { + if (ENV['LD_LIBRARY_PATH']) { + searchpaths = ENV['LD_LIBRARY_PATH'].split(':'); + } + + for (var ident in searchpaths) { + var searchfile = PATH.join2(searchpaths[ident],strfilename); + if (isValidFile(searchfile)) { + filename = searchfile; + break; + } + } + } + } + + if (DLFCN.loadedLibNames[filename]) { + // Already loaded; increment ref count and return. + var handle = DLFCN.loadedLibNames[filename]; + DLFCN.loadedLibs[handle].refcount++; + return handle; + } + + var lib_module; + if (filename === '__self__') { + var handle = -1; + lib_module = Module; + } else { + if (Module['preloadedWasm'] !== undefined && + Module['preloadedWasm'][filename] !== undefined) { + lib_module = Module['preloadedWasm'][filename]; + } else { + var target = FS.findObject(filename); + if (!target || target.isFolder || target.isDevice) { + DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename; + return 0; + } + FS.forceLoadFile(target); + + try { +#if WASM + // the shared library is a shared wasm library (see tools/shared.py WebAssembly.make_shared_library) + var lib_data = FS.readFile(filename, { encoding: 'binary' }); + if (!(lib_data instanceof Uint8Array)) lib_data = new Uint8Array(lib_data); + //Module.printErr('libfile ' + filename + ' size: ' + lib_data.length); + lib_module = loadWebAssemblyModule(lib_data); +#else + // the shared library is a JS file, which we eval + var lib_data = FS.readFile(filename, { encoding: 'utf8' }); + lib_module = eval(lib_data)( + alignFunctionTables(), + Module + ); +#endif + } catch (e) { +#if ASSERTIONS + Module.printErr('Error in loading dynamic library: ' + e); +#endif + DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename + '\n' + e; + return 0; + } + } + + // Not all browsers support Object.keys(). + var handle = 1; + for (var key in DLFCN.loadedLibs) { + if (DLFCN.loadedLibs.hasOwnProperty(key)) handle++; + } + + // We don't care about RTLD_NOW and RTLD_LAZY. + if (flag & 256) { // RTLD_GLOBAL + for (var ident in lib_module) { + if (lib_module.hasOwnProperty(ident)) { + // When RTLD_GLOBAL is enable, the symbols defined by this shared object will be made + // available for symbol resolution of subsequently loaded shared objects. + // + // We should copy the symbols (which include methods and variables) from SIDE_MODULE to MAIN_MODULE. + // + // Module of SIDE_MODULE has not only the symbols (which should be copied) + // but also others (print*, asmGlobal*, FUNCTION_TABLE_**, NAMED_GLOBALS, and so on). + // + // When the symbol (which should be copied) is method, Module._* 's type becomes function. + // When the symbol (which should be copied) is variable, Module._* 's type becomes number. + // + // Except for the symbol prefix (_), there is no difference in the symbols (which should be copied) and others. + // So this just copies over compiled symbols (which start with _). + if (ident[0] == '_') { + Module[ident] = lib_module[ident]; + } + } + } + } + } + DLFCN.loadedLibs[handle] = { + refcount: 1, + name: filename, + module: lib_module + }; + DLFCN.loadedLibNames[filename] = handle; + + return handle; + }, + // int dlclose(void* handle); + dlclose__deps: ['$DLFCN'], + dlclose__proxy: 'sync', + dlclose__sig: 'ii', + dlclose: function(handle) { + // int dlclose(void *handle); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html + if (!DLFCN.loadedLibs[handle]) { + DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle; + return 1; + } else { + var lib_record = DLFCN.loadedLibs[handle]; + if (--lib_record.refcount == 0) { + if (lib_record.module.cleanups) { + lib_record.module.cleanups.forEach(function(cleanup) { cleanup() }); + } + delete DLFCN.loadedLibNames[lib_record.name]; + delete DLFCN.loadedLibs[handle]; + } + return 0; + } + }, + // void* dlsym(void* handle, const char* symbol); + dlsym__deps: ['$DLFCN'], + dlsym__proxy: 'sync', + dlsym__sig: 'iii', + dlsym: function(handle, symbol) { + // void *dlsym(void *restrict handle, const char *restrict name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html + symbol = Pointer_stringify(symbol); + + if (!DLFCN.loadedLibs[handle]) { + DLFCN.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle; + return 0; + } else { + var lib = DLFCN.loadedLibs[handle]; + symbol = '_' + symbol; + if (!lib.module.hasOwnProperty(symbol)) { + DLFCN.errorMsg = ('Tried to lookup unknown symbol "' + symbol + + '" in dynamic lib: ' + lib.name); + return 0; + } else { + var result = lib.module[symbol]; + if (typeof result === 'function') { +#if WASM +#if EMULATED_FUNCTION_POINTERS + // for wasm with emulated function pointers, the i64 ABI is used for all + // function calls, so we can't just call addFunction on something JS + // can call (which does not use that ABI), as the function pointer would + // not be usable from wasm. instead, the wasm has exported function pointers + // for everything we need, with prefix fp$, use those + result = lib.module['fp$' + symbol]; + if (typeof result === 'object') { + // a breaking change in the wasm spec, globals are now objects + // https://github.com/WebAssembly/mutable-global/issues/1 + result = result.value; + } +#if ASSERTIONS + assert(typeof result === 'number', 'could not find function pointer for ' + symbol); +#endif // ASSERTIONS + return result; +#endif // EMULATED_FUNCTION_POINTERS +#endif // WASM + // convert the exported function into a function pointer using our generic + // JS mechanism. + return addFunction(result); + } + return result; + } + } + }, + // char* dlerror(void); + dlerror__deps: ['$DLFCN'], + dlerror__proxy: 'sync', + dlerror__sig: 'i', + dlerror: function() { + // char *dlerror(void); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html + if (DLFCN.errorMsg === null) { + return 0; + } else { + if (DLFCN.error) _free(DLFCN.error); + var msgArr = intArrayFromString(DLFCN.errorMsg); + DLFCN.error = allocate(msgArr, 'i8', ALLOC_NORMAL); + DLFCN.errorMsg = null; + return DLFCN.error; + } + }, + + dladdr__proxy: 'sync', + dladdr__sig: 'iii', + dladdr: function(addr, info) { + // report all function pointers as coming from this program itself XXX not really correct in any way + var fname = allocate(intArrayFromString(Module['thisProgram'] || './this.program'), 'i8', ALLOC_NORMAL); // XXX leak + {{{ makeSetValue('info', 0, 'fname', 'i32') }}}; + {{{ makeSetValue('info', QUANTUM_SIZE, '0', 'i32') }}}; + {{{ makeSetValue('info', QUANTUM_SIZE*2, '0', 'i32') }}}; + {{{ makeSetValue('info', QUANTUM_SIZE*3, '0', 'i32') }}}; + return 1; + }, + + // ========================================================================== + // pwd.h + // ========================================================================== + + // TODO: Implement. + // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/pwd.h.html + getpwuid: function(uid) { + return 0; // NULL + }, + + + // ========================================================================== + // time.h + // ========================================================================== + + clock: function() { + if (_clock.start === undefined) _clock.start = Date.now(); + return ((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}} / 1000))|0; + }, + + time: function(ptr) { + var ret = (Date.now()/1000)|0; + if (ptr) { + {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; + } + return ret; + }, + + difftime: function(time1, time0) { + return time1 - time0; + }, + + // Statically allocated time struct. +#if USE_PTHREADS + __tm_current: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_current = PthreadWorkerInit.___tm_current; else PthreadWorkerInit.___tm_current = ___tm_current = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', + __tm_timezone: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_timezone = PthreadWorkerInit.___tm_timezone; else PthreadWorkerInit.___tm_timezone = ___tm_timezone = allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', + __tm_formatted: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_formatted = PthreadWorkerInit.___tm_formatted; else PthreadWorkerInit.___tm_formatted = ___tm_formatted = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', +#else + __tm_current: '{{{ makeStaticAlloc(C_STRUCTS.tm.__size__) }}}', + // Statically allocated copy of the string "GMT" for gmtime() to point to + __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', + // Statically allocated time strings. + __tm_formatted: '{{{ makeStaticAlloc(C_STRUCTS.tm.__size__) }}}', +#endif + mktime__deps: ['tzset'], + mktime: function(tmPtr) { + _tzset(); + var date = new Date({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, + 0); + + // There's an ambiguous hour when the time goes back; the tm_isdst field is + // used to disambiguate it. Date() basically guesses, so we fix it up if it + // guessed wrong, or fill in tm_isdst with the guess if it's -1. + var dst = {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'i32') }}}; + var guessedOffset = date.getTimezoneOffset(); + var start = new Date(date.getFullYear(), 0, 1); + var summerOffset = new Date(2000, 6, 1).getTimezoneOffset(); + var winterOffset = start.getTimezoneOffset(); + var dstOffset = Math.min(winterOffset, summerOffset); // DST is in December in South + if (dst < 0) { + // Attention: some regions don't have DST at all. + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'Number(summerOffset != winterOffset && dstOffset == guessedOffset)', 'i32') }}}; + } else if ((dst > 0) != (dstOffset == guessedOffset)) { + var nonDstOffset = Math.max(winterOffset, summerOffset); + var trueOffset = dst > 0 ? dstOffset : nonDstOffset; + // Don't try setMinutes(date.getMinutes() + ...) -- it's messed up. + date.setTime(date.getTime() + (trueOffset - guessedOffset)*60000); + } + + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; + var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + + return (date.getTime() / 1000)|0; + }, + timelocal: 'mktime', + + gmtime__deps: ['__tm_current', 'gmtime_r'], + gmtime: function(time) { + return _gmtime_r(time, ___tm_current); + }, + + gmtime_r__deps: ['__tm_timezone'], + gmtime_r: function(time, tmPtr) { + var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; + + return tmPtr; + }, + timegm__deps: ['tzset'], + timegm: function(tmPtr) { + _tzset(); + var time = Date.UTC({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, + 0); + var date = new Date(time); + + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + + return (date.getTime() / 1000)|0; + }, + + localtime__deps: ['__tm_current', 'localtime_r'], + localtime: function(time) { + return _localtime_r(time, ___tm_current); + }, + + localtime_r__deps: ['__tm_timezone', 'tzset'], + localtime_r: function(time, tmPtr) { + _tzset(); + var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; + + var start = new Date(date.getFullYear(), 0, 1); + var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '-(date.getTimezoneOffset() * 60)', 'i32') }}}; + + // Attention: DST is in December in South, and some regions don't have DST at all. + var summerOffset = new Date(2000, 6, 1).getTimezoneOffset(); + var winterOffset = start.getTimezoneOffset(); + var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}}; + + var zonePtr = {{{ makeGetValue('__get_tzname()', 'dst ? ' + Runtime.QUANTUM_SIZE + ' : 0', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, 'zonePtr', 'i32') }}}; + + return tmPtr; + }, + + asctime__deps: ['__tm_formatted', 'asctime_r'], + asctime: function(tmPtr) { + return _asctime_r(tmPtr, ___tm_formatted); + }, + + asctime_r__deps: ['__tm_formatted', 'mktime'], + asctime_r: function(tmPtr, buf) { + var date = { + tm_sec: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, + tm_min: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + tm_hour: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + tm_mday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + tm_mon: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + tm_year: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}}, + tm_wday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'i32') }}} + }; + var days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]; + var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; + var s = days[date.tm_wday] + ' ' + months[date.tm_mon] + + (date.tm_mday < 10 ? ' ' : ' ') + date.tm_mday + + (date.tm_hour < 10 ? ' 0' : ' ') + date.tm_hour + + (date.tm_min < 10 ? ':0' : ':') + date.tm_min + + (date.tm_sec < 10 ? ':0' : ':') + date.tm_sec + + ' ' + (1900 + date.tm_year) + "\n"; + + // asctime_r is specced to behave in an undefined manner if the algorithm would attempt + // to write out more than 26 bytes (including the null terminator). + // See http://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html + // Our undefined behavior is to truncate the write to at most 26 bytes, including null terminator. + stringToUTF8(s, buf, 26); + return buf; + }, + + ctime__deps: ['__tm_current', 'ctime_r'], + ctime: function(timer) { + return _ctime_r(timer, ___tm_current); + }, + + ctime_r__deps: ['localtime_r', 'asctime_r'], + ctime_r: function(time, buf) { + var stack = stackSave(); + var rv = _asctime_r(_localtime_r(time, stackAlloc({{{ C_STRUCTS.tm.__size__ }}})), buf); + stackRestore(stack); + return rv; + }, + + dysize: function(year) { + var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))); + return leap ? 366 : 365; + }, + + // TODO: Initialize these to defaults on startup from system settings. + // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) + tzset__proxy: 'sync', + tzset__sig: 'v', + tzset: function() { + // TODO: Use (malleable) environment variables instead of system settings. + if (_tzset.called) return; + _tzset.called = true; + + // timezone is specified as seconds west of UTC ("The external variable + // `timezone` shall be set to the difference, in seconds, between + // Coordinated Universal Time (UTC) and local standard time."), the same + // as returned by getTimezoneOffset(). + // See http://pubs.opengroup.org/onlinepubs/009695399/functions/tzset.html + {{{ makeSetValue('__get_timezone()', '0', '(new Date()).getTimezoneOffset() * 60', 'i32') }}}; + + var winter = new Date(2000, 0, 1); + var summer = new Date(2000, 6, 1); + {{{ makeSetValue('__get_daylight()', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}; + + function extractZone(date) { + var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); + return match ? match[1] : "GMT"; + }; + var winterName = extractZone(winter); + var summerName = extractZone(summer); + var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); + var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); + if (summer.getTimezoneOffset() < winter.getTimezoneOffset()) { + // Northern hemisphere + {{{ makeSetValue('__get_tzname()', '0', 'winterNamePtr', 'i32') }}}; + {{{ makeSetValue('__get_tzname()', Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}; + } else { + {{{ makeSetValue('__get_tzname()', '0', 'summerNamePtr', 'i32') }}}; + {{{ makeSetValue('__get_tzname()', Runtime.QUANTUM_SIZE, 'winterNamePtr', 'i32') }}}; + } + }, + + stime__deps: ['$ERRNO_CODES', '__setErrNo'], + stime: function(when) { + ___setErrNo(ERRNO_CODES.EPERM); + return -1; + }, + + __map_file__deps: ['$ERRNO_CODES', '__setErrNo'], + __map_file: function(pathname, size) { + ___setErrNo(ERRNO_CODES.EPERM); + return -1; + }, + + _MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + _MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + _isLeapYear: function(year) { + return year%4 === 0 && (year%100 !== 0 || year%400 === 0); + }, + + _arraySum: function(array, index) { + var sum = 0; + for (var i = 0; i <= index; sum += array[i++]); + return sum; + }, + + _addDays__deps: ['_isLeapYear', '_MONTH_DAYS_LEAP', '_MONTH_DAYS_REGULAR'], + _addDays: function(date, days) { + var newDate = new Date(date.getTime()); + while(days > 0) { + var leap = __isLeapYear(newDate.getFullYear()); + var currentMonth = newDate.getMonth(); + var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; + + if (days > daysInCurrentMonth-newDate.getDate()) { + // we spill over to next month + days -= (daysInCurrentMonth-newDate.getDate()+1); + newDate.setDate(1); + if (currentMonth < 11) { + newDate.setMonth(currentMonth+1) + } else { + newDate.setMonth(0); + newDate.setFullYear(newDate.getFullYear()+1); + } + } else { + // we stay in current month + newDate.setDate(newDate.getDate()+days); + return newDate; + } + } + + return newDate; + }, + + strftime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strftime: function(s, maxsize, format, tm) { + // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html + + var tm_zone = {{{ makeGetValue('tm', C_STRUCTS.tm.tm_zone, 'i32') }}}; + + var date = { + tm_sec: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}}, + tm_min: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32') }}}, + tm_hour: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32') }}}, + tm_mday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32') }}}, + tm_mon: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32') }}}, + tm_year: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32') }}}, + tm_wday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_wday, 'i32') }}}, + tm_yday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_yday, 'i32') }}}, + tm_isdst: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_isdst, 'i32') }}}, + tm_gmtoff: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_gmtoff, 'i32') }}}, + tm_zone: tm_zone ? Pointer_stringify(tm_zone) : '' + }; + + var pattern = Pointer_stringify(format); + + // expand format + var EXPANSION_RULES_1 = { + '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 + '%D': '%m/%d/%y', // Equivalent to %m / %d / %y + '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d + '%h': '%b', // Equivalent to %b + '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation + '%R': '%H:%M', // Replaced by the time in 24-hour notation + '%T': '%H:%M:%S', // Replaced by the time + '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation + '%X': '%H:%M:%S' // Replaced by the locale's appropriate date representation + }; + for (var rule in EXPANSION_RULES_1) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); + } + + var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + function leadingSomething(value, digits, character) { + var str = typeof value === 'number' ? value.toString() : (value || ''); + while (str.length < digits) { + str = character[0]+str; + } + return str; + }; + + function leadingNulls(value, digits) { + return leadingSomething(value, digits, '0'); + }; + + function compareByDay(date1, date2) { + function sgn(value) { + return value < 0 ? -1 : (value > 0 ? 1 : 0); + }; + + var compare; + if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { + if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { + compare = sgn(date1.getDate()-date2.getDate()); + } + } + return compare; + }; + + function getFirstWeekStartDate(janFourth) { + switch (janFourth.getDay()) { + case 0: // Sunday + return new Date(janFourth.getFullYear()-1, 11, 29); + case 1: // Monday + return janFourth; + case 2: // Tuesday + return new Date(janFourth.getFullYear(), 0, 3); + case 3: // Wednesday + return new Date(janFourth.getFullYear(), 0, 2); + case 4: // Thursday + return new Date(janFourth.getFullYear(), 0, 1); + case 5: // Friday + return new Date(janFourth.getFullYear()-1, 11, 31); + case 6: // Saturday + return new Date(janFourth.getFullYear()-1, 11, 30); + } + }; + + function getWeekBasedYear(date) { + var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); + + var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); + var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); + + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + + if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { + // this date is after the start of the first week of this year + if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { + return thisDate.getFullYear()+1; + } else { + return thisDate.getFullYear(); + } + } else { + return thisDate.getFullYear()-1; + } + }; + + var EXPANSION_RULES_2 = { + '%a': function(date) { + return WEEKDAYS[date.tm_wday].substring(0,3); + }, + '%A': function(date) { + return WEEKDAYS[date.tm_wday]; + }, + '%b': function(date) { + return MONTHS[date.tm_mon].substring(0,3); + }, + '%B': function(date) { + return MONTHS[date.tm_mon]; + }, + '%C': function(date) { + var year = date.tm_year+1900; + return leadingNulls((year/100)|0,2); + }, + '%d': function(date) { + return leadingNulls(date.tm_mday, 2); + }, + '%e': function(date) { + return leadingSomething(date.tm_mday, 2, ' '); + }, + '%g': function(date) { + // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. + // In this system, weeks begin on a Monday and week 1 of the year is the week that includes + // January 4th, which is also the week that includes the first Thursday of the year, and + // is also the first week that contains at least four days in the year. + // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of + // the last week of the preceding year; thus, for Saturday 2nd January 1999, + // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, + // or 31st is a Monday, it and any following days are part of week 1 of the following year. + // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. + + return getWeekBasedYear(date).toString().substring(2); + }, + '%G': function(date) { + return getWeekBasedYear(date); + }, + '%H': function(date) { + return leadingNulls(date.tm_hour, 2); + }, + '%I': function(date) { + var twelveHour = date.tm_hour; + if (twelveHour == 0) twelveHour = 12; + else if (twelveHour > 12) twelveHour -= 12; + return leadingNulls(twelveHour, 2); + }, + '%j': function(date) { + // Day of the year (001-366) + return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); + }, + '%m': function(date) { + return leadingNulls(date.tm_mon+1, 2); + }, + '%M': function(date) { + return leadingNulls(date.tm_min, 2); + }, + '%n': function() { + return '\n'; + }, + '%p': function(date) { + if (date.tm_hour >= 0 && date.tm_hour < 12) { + return 'AM'; + } else { + return 'PM'; + } + }, + '%S': function(date) { + return leadingNulls(date.tm_sec, 2); + }, + '%t': function() { + return '\t'; + }, + '%u': function(date) { + var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); + return day.getDay() || 7; + }, + '%U': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Sunday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year+1900, 0, 1); + var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Sunday? + if (compareByDay(firstSunday, endDate) < 0) { + // calculate difference in days between first Sunday and endDate + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstSundayUntilEndJanuary = 31-firstSunday.getDate(); + var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + + return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; + }, + '%V': function(date) { + // Replaced by the week number of the year (Monday as the first day of the week) + // as a decimal number [01,53]. If the week containing 1 January has four + // or more days in the new year, then it is considered week 1. + // Otherwise, it is the last week of the previous year, and the next week is week 1. + // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] + var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); + var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); + + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + + var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); + + if (compareByDay(endDate, firstWeekStartThisYear) < 0) { + // if given date is before this years first week, then it belongs to the 53rd week of last year + return '53'; + } + + if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { + // if given date is after next years first week, then it belongs to the 01th week of next year + return '01'; + } + + // given date is in between CW 01..53 of this calendar year + var daysDifference; + if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) { + // first CW of this year starts last year + daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate() + } else { + // first CW of this year starts this year + daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate(); + } + return leadingNulls(Math.ceil(daysDifference/7), 2); + }, + '%w': function(date) { + var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); + return day.getDay(); + }, + '%W': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Monday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year, 0, 1); + var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Monday? + if (compareByDay(firstMonday, endDate) < 0) { + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstMondayUntilEndJanuary = 31-firstMonday.getDate(); + var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + return compareByDay(firstMonday, janFirst) === 0 ? '01': '00'; + }, + '%y': function(date) { + // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] + return (date.tm_year+1900).toString().substring(2); + }, + '%Y': function(date) { + // Replaced by the year as a decimal number (for example, 1997). [ tm_year] + return date.tm_year+1900; + }, + '%z': function(date) { + // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). + // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). + var off = date.tm_gmtoff; + var ahead = off >= 0; + off = Math.abs(off) / 60; + // convert from minutes into hhmm format (which means 60 minutes = 100 units) + off = (off / 60)*100 + (off % 60); + return (ahead ? '+' : '-') + String("0000" + off).slice(-4); + }, + '%Z': function(date) { + return date.tm_zone; + }, + '%%': function() { + return '%'; + } + }; + for (var rule in EXPANSION_RULES_2) { + if (pattern.indexOf(rule) >= 0) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); + } + } + + var bytes = intArrayFromString(pattern, false); + if (bytes.length > maxsize) { + return 0; + } + + writeArrayToMemory(bytes, s); + return bytes.length-1; + }, + strftime_l__deps: ['strftime'], + strftime_l: function(s, maxsize, format, tm) { + return _strftime(s, maxsize, format, tm); // no locale support yet + }, + + strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strptime: function(buf, format, tm) { + // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html + var pattern = Pointer_stringify(format); + + // escape special characters + // TODO: not sure we really need to escape all of these in JS regexps + var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; + for (var i=0, ii=SPECIAL_CHARS.length; i=0; i=pattern.indexOf('%')) { + capture.push(pattern[i+1]); + pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), ''); + } + + var matches = new RegExp('^'+pattern, "i").exec(Pointer_stringify(buf)) + // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches)); + + function initDate() { + function fixup(value, min, max) { + return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); + }; + return { + year: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999), + month: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32', 0, 0, 1) }}}, 0, 11), + day: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32', 0, 0, 1) }}}, 1, 31), + hour: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32', 0, 0, 1) }}}, 0, 23), + min: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32', 0, 0, 1) }}}, 0, 59), + sec: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32', 0, 0, 1) }}}, 0, 59) + }; + }; + + if (matches) { + var date = initDate(); + var value; + + function getMatch(symbol) { + var pos = capture.indexOf(symbol); + // check if symbol appears in regexp + if (pos >= 0) { + // return matched value or null (falsy!) for non-matches + return matches[pos+1]; + } + return; + } + + // seconds + if ((value=getMatch('S'))) { + date.sec = parseInt(value); + } + + // minutes + if ((value=getMatch('M'))) { + date.min = parseInt(value); + } + + // hours + if ((value=getMatch('H'))) { + // 24h clock + date.hour = parseInt(value); + } else if ((value = getMatch('I'))) { + // AM/PM clock + var hour = parseInt(value); + if ((value=getMatch('p'))) { + hour += value.toUpperCase()[0] === 'P' ? 12 : 0; + } + date.hour = hour; + } + + // year + if ((value=getMatch('Y'))) { + // parse from four-digit year + date.year = parseInt(value); + } else if ((value=getMatch('y'))) { + // parse from two-digit year... + var year = parseInt(value); + if ((value=getMatch('C'))) { + // ...and century + year += parseInt(value)*100; + } else { + // ...and rule-of-thumb + year += year<69 ? 2000 : 1900; + } + date.year = year; + } + + // month + if ((value=getMatch('m'))) { + // parse from month number + date.month = parseInt(value)-1; + } else if ((value=getMatch('b'))) { + // parse from month name + date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; + // TODO: derive month from day in year+year, week number+day of week+year + } + + // day + if ((value=getMatch('d'))) { + // get day of month directly + date.day = parseInt(value); + } else if ((value=getMatch('j'))) { + // get day of month from day of year ... + var day = parseInt(value); + var leapYear = __isLeapYear(date.year); + for (var month=0; month<12; ++month) { + var daysUntilMonth = __arraySum(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, month-1); + if (day<=daysUntilMonth+(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[month]) { + date.day = day-daysUntilMonth; + } + } + } else if ((value=getMatch('a'))) { + // get day of month from weekday ... + var weekDay = value.substring(0,3).toUpperCase(); + if ((value=getMatch('U'))) { + // ... and week number (Sunday being first day of week) + // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Sunday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; + var weekNumber = parseInt(value); + + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay() === 0) { + // Jan 1st is a Sunday, and, hence in the 1st CW + endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + } else { + // Jan 1st is not a Sunday, and, hence still in the 0th CW + endDate = __addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); + } + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } else if ((value=getMatch('W'))) { + // ... and week number (Monday being first day of week) + // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Monday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; + var weekNumber = parseInt(value); + + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay()===1) { + // Jan 1st is a Monday, and, hence in the 1st CW + endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + } else { + // Jan 1st is not a Monday, and, hence still in the 0th CW + endDate = __addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); + } + + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } + } + + /* + tm_sec int seconds after the minute 0-61* + tm_min int minutes after the hour 0-59 + tm_hour int hours since midnight 0-23 + tm_mday int day of the month 1-31 + tm_mon int months since January 0-11 + tm_year int years since 1900 + tm_wday int days since Sunday 0-6 + tm_yday int days since January 1 0-365 + tm_isdst int Daylight Saving Time flag + */ + + var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; + + // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F + // TODO: not sure that intArrayFromString handles all unicode characters correctly + return buf+intArrayFromString(matches[0]).length-1; + } + + return 0; + }, + strptime_l__deps: ['strptime'], + strptime_l: function(buf, format, tm) { + return _strptime(buf, format, tm); // no locale support yet + }, + + getdate: function(string) { + // struct tm *getdate(const char *string); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/getdate.html + // TODO: Implement. + return 0; + }, + + // ========================================================================== + // sys/time.h + // ========================================================================== + + clock_gettime__deps: ['emscripten_get_now', 'emscripten_get_now_is_monotonic', '$ERRNO_CODES', '__setErrNo'], + clock_gettime: function(clk_id, tp) { + // int clock_gettime(clockid_t clk_id, struct timespec *tp); + var now; + if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { + now = Date.now(); + } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic()) { + now = _emscripten_get_now(); + } else { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds + {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, '((now % 1000)*1000*1000)|0', 'i32') }}}; // nanoseconds + return 0; + }, + __clock_gettime: 'clock_gettime', // musl internal alias + clock_settime__deps: ['$ERRNO_CODES', '__setErrNo'], + clock_settime: function(clk_id, tp) { + // int clock_settime(clockid_t clk_id, const struct timespec *tp); + // Nothing. + ___setErrNo(clk_id === {{{ cDefine('CLOCK_REALTIME') }}} ? ERRNO_CODES.EPERM + : ERRNO_CODES.EINVAL); + return -1; + }, + clock_getres__deps: ['emscripten_get_now_res', 'emscripten_get_now_is_monotonic', '$ERRNO_CODES', '__setErrNo'], + clock_getres: function(clk_id, res) { + // int clock_getres(clockid_t clk_id, struct timespec *res); + var nsec; + if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { + nsec = 1000 * 1000; // educated guess that it's milliseconds + } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic()) { + nsec = _emscripten_get_now_res(); + } else { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '(nsec/1000000000)|0', 'i32') }}}; + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is nanoseconds + return 0; + }, + clock_getcpuclockid__deps: ['$PROCINFO'], + clock_getcpuclockid: function(pid, clk_id) { + if (pid < 0) return ERRNO_CODES.ESRCH; + if (pid !== 0 && pid !== PROCINFO.pid) return ERRNO_CODES.ENOSYS; + if (clk_id) {{{ makeSetValue('clk_id', 0, 2/*CLOCK_PROCESS_CPUTIME_ID*/, 'i32') }}}; + return 0; + }, + // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html + gettimeofday: function(ptr) { + var now = Date.now(); + {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds + {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_usec, '((now % 1000)*1000)|0', 'i32') }}}; // microseconds + return 0; + }, + + // ========================================================================== + // sys/timeb.h + // ========================================================================== + + ftime: function(p) { + var millis = Date.now(); + {{{ makeSetValue('p', C_STRUCTS.timeb.time, '(millis/1000)|0', 'i32') }}}; + {{{ makeSetValue('p', C_STRUCTS.timeb.millitm, 'millis % 1000', 'i16') }}}; + {{{ makeSetValue('p', C_STRUCTS.timeb.timezone, '0', 'i16') }}}; // Obsolete field + {{{ makeSetValue('p', C_STRUCTS.timeb.dstflag, '0', 'i16') }}}; // Obsolete field + return 0; + }, + + // ========================================================================== + // sys/times.h + // ========================================================================== + + times__deps: ['memset'], + times: function(buffer) { + // clock_t times(struct tms *buffer); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html + // NOTE: This is fake, since we can't calculate real CPU time usage in JS. + if (buffer !== 0) { + _memset(buffer, 0, {{{ C_STRUCTS.tms.__size__ }}}); + } + return 0; + }, + + // ========================================================================== + // sys/types.h + // ========================================================================== + // http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html + makedev: function(maj, min) { + return ((maj) << 8 | (min)); + }, + gnu_dev_makedev: 'makedev', + major: function(dev) { + return ((dev) >> 8); + }, + gnu_dev_major: 'major', + minor: function(dev) { + return ((dev) & 0xff); + }, + gnu_dev_minor: 'minor', + + // ========================================================================== + // setjmp.h + // ========================================================================== + + // asm.js-style setjmp/longjmp support for wasm binaryen backend. + // In asm.js compilation, various variables including setjmpId will be + // generated within 'var asm' in emscripten.py, while in wasm compilation, + // wasm side is considered as 'asm' so they are not generated. But + // saveSetjmp() needs setjmpId and no other functions in wasm side needs it. + // So we declare it here if WASM_BACKEND=1. +#if WASM_BACKEND == 1 + $setjmpId: 0, +#endif + + saveSetjmp__asm: true, + saveSetjmp__sig: 'iii', +#if WASM_BACKEND == 1 + saveSetjmp__deps: ['realloc', '$setjmpId'], +#else + saveSetjmp__deps: ['realloc'], +#endif + saveSetjmp: function(env, label, table, size) { + // Not particularly fast: slow table lookup of setjmpId to label. But setjmp + // prevents relooping anyhow, so slowness is to be expected. And typical case + // is 1 setjmp per invocation, or less. + env = env|0; + label = label|0; + table = table|0; + size = size|0; + var i = 0; + setjmpId = (setjmpId+1)|0; + {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; + while ((i|0) < (size|0)) { + if ({{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}} == 0) { + {{{ makeSetValueAsm('table', '(i<<3)', 'setjmpId', 'i32') }}}; + {{{ makeSetValueAsm('table', '(i<<3)+4', 'label', 'i32') }}}; + // prepare next slot + {{{ makeSetValueAsm('table', '(i<<3)+8', '0', 'i32') }}}; + {{{ makeSetTempRet0('size') }}}; + return table | 0; + } + i = i+1|0; + } + // grow the table + size = (size*2)|0; + table = _realloc(table|0, 8*(size+1|0)|0) | 0; + table = _saveSetjmp(env|0, label|0, table|0, size|0) | 0; + {{{ makeSetTempRet0('size') }}}; + return table | 0; + }, + + testSetjmp__asm: true, + testSetjmp__sig: 'iii', + testSetjmp: function(id, table, size) { + id = id|0; + table = table|0; + size = size|0; + var i = 0, curr = 0; + while ((i|0) < (size|0)) { + curr = {{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}}; + if ((curr|0) == 0) break; + if ((curr|0) == (id|0)) { + return {{{ makeGetValueAsm('table', '(i<<3)+4', 'i32') }}}; + } + i = i+1|0; + } + return 0; + }, + + setjmp__deps: ['saveSetjmp', 'testSetjmp'], + setjmp__inline: function(env) { + // Save the label + return '_saveSetjmp(' + env + ', label, setjmpTable)|0'; + }, + + longjmp__deps: ['saveSetjmp', 'testSetjmp'], + longjmp: function(env, value) { + Module['setThrew'](env, value || 1); + throw 'longjmp'; + }, + emscripten_longjmp__deps: ['longjmp'], + emscripten_longjmp: function(env, value) { + _longjmp(env, value); + }, + + // ========================================================================== + // sys/wait.h + // ========================================================================== + + wait__deps: ['$ERRNO_CODES', '__setErrNo'], + wait: function(stat_loc) { + // pid_t wait(int *stat_loc); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/wait.html + // Makes no sense in a single-process environment. + ___setErrNo(ERRNO_CODES.ECHILD); + return -1; + }, + // NOTE: These aren't really the same, but we use the same stub for them all. + waitid: 'wait', + waitpid: 'wait', + wait3: 'wait', + wait4: 'wait', + + // ========================================================================== + // errno.h + // ========================================================================== + + $ERRNO_CODES: { + EPERM: {{{ cDefine('EPERM') }}}, + ENOENT: {{{ cDefine('ENOENT') }}}, + ESRCH: {{{ cDefine('ESRCH') }}}, + EINTR: {{{ cDefine('EINTR') }}}, + EIO: {{{ cDefine('EIO') }}}, + ENXIO: {{{ cDefine('ENXIO') }}}, + E2BIG: {{{ cDefine('E2BIG') }}}, + ENOEXEC: {{{ cDefine('ENOEXEC') }}}, + EBADF: {{{ cDefine('EBADF') }}}, + ECHILD: {{{ cDefine('ECHILD') }}}, + EAGAIN: {{{ cDefine('EAGAIN') }}}, + EWOULDBLOCK: {{{ cDefine('EWOULDBLOCK') }}}, + ENOMEM: {{{ cDefine('ENOMEM') }}}, + EACCES: {{{ cDefine('EACCES') }}}, + EFAULT: {{{ cDefine('EFAULT') }}}, + ENOTBLK: {{{ cDefine('ENOTBLK') }}}, + EBUSY: {{{ cDefine('EBUSY') }}}, + EEXIST: {{{ cDefine('EEXIST') }}}, + EXDEV: {{{ cDefine('EXDEV') }}}, + ENODEV: {{{ cDefine('ENODEV') }}}, + ENOTDIR: {{{ cDefine('ENOTDIR') }}}, + EISDIR: {{{ cDefine('EISDIR') }}}, + EINVAL: {{{ cDefine('EINVAL') }}}, + ENFILE: {{{ cDefine('ENFILE') }}}, + EMFILE: {{{ cDefine('EMFILE') }}}, + ENOTTY: {{{ cDefine('ENOTTY') }}}, + ETXTBSY: {{{ cDefine('ETXTBSY') }}}, + EFBIG: {{{ cDefine('EFBIG') }}}, + ENOSPC: {{{ cDefine('ENOSPC') }}}, + ESPIPE: {{{ cDefine('ESPIPE') }}}, + EROFS: {{{ cDefine('EROFS') }}}, + EMLINK: {{{ cDefine('EMLINK') }}}, + EPIPE: {{{ cDefine('EPIPE') }}}, + EDOM: {{{ cDefine('EDOM') }}}, + ERANGE: {{{ cDefine('ERANGE') }}}, + ENOMSG: {{{ cDefine('ENOMSG') }}}, + EIDRM: {{{ cDefine('EIDRM') }}}, + ECHRNG: {{{ cDefine('ECHRNG') }}}, + EL2NSYNC: {{{ cDefine('EL2NSYNC') }}}, + EL3HLT: {{{ cDefine('EL3HLT') }}}, + EL3RST: {{{ cDefine('EL3RST') }}}, + ELNRNG: {{{ cDefine('ELNRNG') }}}, + EUNATCH: {{{ cDefine('EUNATCH') }}}, + ENOCSI: {{{ cDefine('ENOCSI') }}}, + EL2HLT: {{{ cDefine('EL2HLT') }}}, + EDEADLK: {{{ cDefine('EDEADLK') }}}, + ENOLCK: {{{ cDefine('ENOLCK') }}}, + EBADE: {{{ cDefine('EBADE') }}}, + EBADR: {{{ cDefine('EBADR') }}}, + EXFULL: {{{ cDefine('EXFULL') }}}, + ENOANO: {{{ cDefine('ENOANO') }}}, + EBADRQC: {{{ cDefine('EBADRQC') }}}, + EBADSLT: {{{ cDefine('EBADSLT') }}}, + EDEADLOCK: {{{ cDefine('EDEADLOCK') }}}, + EBFONT: {{{ cDefine('EBFONT') }}}, + ENOSTR: {{{ cDefine('ENOSTR') }}}, + ENODATA: {{{ cDefine('ENODATA') }}}, + ETIME: {{{ cDefine('ETIME') }}}, + ENOSR: {{{ cDefine('ENOSR') }}}, + ENONET: {{{ cDefine('ENONET') }}}, + ENOPKG: {{{ cDefine('ENOPKG') }}}, + EREMOTE: {{{ cDefine('EREMOTE') }}}, + ENOLINK: {{{ cDefine('ENOLINK') }}}, + EADV: {{{ cDefine('EADV') }}}, + ESRMNT: {{{ cDefine('ESRMNT') }}}, + ECOMM: {{{ cDefine('ECOMM') }}}, + EPROTO: {{{ cDefine('EPROTO') }}}, + EMULTIHOP: {{{ cDefine('EMULTIHOP') }}}, + EDOTDOT: {{{ cDefine('EDOTDOT') }}}, + EBADMSG: {{{ cDefine('EBADMSG') }}}, + ENOTUNIQ: {{{ cDefine('ENOTUNIQ') }}}, + EBADFD: {{{ cDefine('EBADFD') }}}, + EREMCHG: {{{ cDefine('EREMCHG') }}}, + ELIBACC: {{{ cDefine('ELIBACC') }}}, + ELIBBAD: {{{ cDefine('ELIBBAD') }}}, + ELIBSCN: {{{ cDefine('ELIBSCN') }}}, + ELIBMAX: {{{ cDefine('ELIBMAX') }}}, + ELIBEXEC: {{{ cDefine('ELIBEXEC') }}}, + ENOSYS: {{{ cDefine('ENOSYS') }}}, + ENOTEMPTY: {{{ cDefine('ENOTEMPTY') }}}, + ENAMETOOLONG: {{{ cDefine('ENAMETOOLONG') }}}, + ELOOP: {{{ cDefine('ELOOP') }}}, + EOPNOTSUPP: {{{ cDefine('EOPNOTSUPP') }}}, + EPFNOSUPPORT: {{{ cDefine('EPFNOSUPPORT') }}}, + ECONNRESET: {{{ cDefine('ECONNRESET') }}}, + ENOBUFS: {{{ cDefine('ENOBUFS') }}}, + EAFNOSUPPORT: {{{ cDefine('EAFNOSUPPORT') }}}, + EPROTOTYPE: {{{ cDefine('EPROTOTYPE') }}}, + ENOTSOCK: {{{ cDefine('ENOTSOCK') }}}, + ENOPROTOOPT: {{{ cDefine('ENOPROTOOPT') }}}, + ESHUTDOWN: {{{ cDefine('ESHUTDOWN') }}}, + ECONNREFUSED: {{{ cDefine('ECONNREFUSED') }}}, + EADDRINUSE: {{{ cDefine('EADDRINUSE') }}}, + ECONNABORTED: {{{ cDefine('ECONNABORTED') }}}, + ENETUNREACH: {{{ cDefine('ENETUNREACH') }}}, + ENETDOWN: {{{ cDefine('ENETDOWN') }}}, + ETIMEDOUT: {{{ cDefine('ETIMEDOUT') }}}, + EHOSTDOWN: {{{ cDefine('EHOSTDOWN') }}}, + EHOSTUNREACH: {{{ cDefine('EHOSTUNREACH') }}}, + EINPROGRESS: {{{ cDefine('EINPROGRESS') }}}, + EALREADY: {{{ cDefine('EALREADY') }}}, + EDESTADDRREQ: {{{ cDefine('EDESTADDRREQ') }}}, + EMSGSIZE: {{{ cDefine('EMSGSIZE') }}}, + EPROTONOSUPPORT: {{{ cDefine('EPROTONOSUPPORT') }}}, + ESOCKTNOSUPPORT: {{{ cDefine('ESOCKTNOSUPPORT') }}}, + EADDRNOTAVAIL: {{{ cDefine('EADDRNOTAVAIL') }}}, + ENETRESET: {{{ cDefine('ENETRESET') }}}, + EISCONN: {{{ cDefine('EISCONN') }}}, + ENOTCONN: {{{ cDefine('ENOTCONN') }}}, + ETOOMANYREFS: {{{ cDefine('ETOOMANYREFS') }}}, + EUSERS: {{{ cDefine('EUSERS') }}}, + EDQUOT: {{{ cDefine('EDQUOT') }}}, + ESTALE: {{{ cDefine('ESTALE') }}}, + ENOTSUP: {{{ cDefine('ENOTSUP') }}}, + ENOMEDIUM: {{{ cDefine('ENOMEDIUM') }}}, + EILSEQ: {{{ cDefine('EILSEQ') }}}, + EOVERFLOW: {{{ cDefine('EOVERFLOW') }}}, + ECANCELED: {{{ cDefine('ECANCELED') }}}, + ENOTRECOVERABLE: {{{ cDefine('ENOTRECOVERABLE') }}}, + EOWNERDEAD: {{{ cDefine('EOWNERDEAD') }}}, + ESTRPIPE: {{{ cDefine('ESTRPIPE') }}}, + }, + $ERRNO_MESSAGES: { + 0: 'Success', + {{{ cDefine('EPERM') }}}: 'Not super-user', + {{{ cDefine('ENOENT') }}}: 'No such file or directory', + {{{ cDefine('ESRCH') }}}: 'No such process', + {{{ cDefine('EINTR') }}}: 'Interrupted system call', + {{{ cDefine('EIO') }}}: 'I/O error', + {{{ cDefine('ENXIO') }}}: 'No such device or address', + {{{ cDefine('E2BIG') }}}: 'Arg list too long', + {{{ cDefine('ENOEXEC') }}}: 'Exec format error', + {{{ cDefine('EBADF') }}}: 'Bad file number', + {{{ cDefine('ECHILD') }}}: 'No children', + {{{ cDefine('EWOULDBLOCK') }}}: 'No more processes', + {{{ cDefine('ENOMEM') }}}: 'Not enough core', + {{{ cDefine('EACCES') }}}: 'Permission denied', + {{{ cDefine('EFAULT') }}}: 'Bad address', + {{{ cDefine('ENOTBLK') }}}: 'Block device required', + {{{ cDefine('EBUSY') }}}: 'Mount device busy', + {{{ cDefine('EEXIST') }}}: 'File exists', + {{{ cDefine('EXDEV') }}}: 'Cross-device link', + {{{ cDefine('ENODEV') }}}: 'No such device', + {{{ cDefine('ENOTDIR') }}}: 'Not a directory', + {{{ cDefine('EISDIR') }}}: 'Is a directory', + {{{ cDefine('EINVAL') }}}: 'Invalid argument', + {{{ cDefine('ENFILE') }}}: 'Too many open files in system', + {{{ cDefine('EMFILE') }}}: 'Too many open files', + {{{ cDefine('ENOTTY') }}}: 'Not a typewriter', + {{{ cDefine('ETXTBSY') }}}: 'Text file busy', + {{{ cDefine('EFBIG') }}}: 'File too large', + {{{ cDefine('ENOSPC') }}}: 'No space left on device', + {{{ cDefine('ESPIPE') }}}: 'Illegal seek', + {{{ cDefine('EROFS') }}}: 'Read only file system', + {{{ cDefine('EMLINK') }}}: 'Too many links', + {{{ cDefine('EPIPE') }}}: 'Broken pipe', + {{{ cDefine('EDOM') }}}: 'Math arg out of domain of func', + {{{ cDefine('ERANGE') }}}: 'Math result not representable', + {{{ cDefine('ENOMSG') }}}: 'No message of desired type', + {{{ cDefine('EIDRM') }}}: 'Identifier removed', + {{{ cDefine('ECHRNG') }}}: 'Channel number out of range', + {{{ cDefine('EL2NSYNC') }}}: 'Level 2 not synchronized', + {{{ cDefine('EL3HLT') }}}: 'Level 3 halted', + {{{ cDefine('EL3RST') }}}: 'Level 3 reset', + {{{ cDefine('ELNRNG') }}}: 'Link number out of range', + {{{ cDefine('EUNATCH') }}}: 'Protocol driver not attached', + {{{ cDefine('ENOCSI') }}}: 'No CSI structure available', + {{{ cDefine('EL2HLT') }}}: 'Level 2 halted', + {{{ cDefine('EDEADLK') }}}: 'Deadlock condition', + {{{ cDefine('ENOLCK') }}}: 'No record locks available', + {{{ cDefine('EBADE') }}}: 'Invalid exchange', + {{{ cDefine('EBADR') }}}: 'Invalid request descriptor', + {{{ cDefine('EXFULL') }}}: 'Exchange full', + {{{ cDefine('ENOANO') }}}: 'No anode', + {{{ cDefine('EBADRQC') }}}: 'Invalid request code', + {{{ cDefine('EBADSLT') }}}: 'Invalid slot', + {{{ cDefine('EDEADLOCK') }}}: 'File locking deadlock error', + {{{ cDefine('EBFONT') }}}: 'Bad font file fmt', + {{{ cDefine('ENOSTR') }}}: 'Device not a stream', + {{{ cDefine('ENODATA') }}}: 'No data (for no delay io)', + {{{ cDefine('ETIME') }}}: 'Timer expired', + {{{ cDefine('ENOSR') }}}: 'Out of streams resources', + {{{ cDefine('ENONET') }}}: 'Machine is not on the network', + {{{ cDefine('ENOPKG') }}}: 'Package not installed', + {{{ cDefine('EREMOTE') }}}: 'The object is remote', + {{{ cDefine('ENOLINK') }}}: 'The link has been severed', + {{{ cDefine('EADV') }}}: 'Advertise error', + {{{ cDefine('ESRMNT') }}}: 'Srmount error', + {{{ cDefine('ECOMM') }}}: 'Communication error on send', + {{{ cDefine('EPROTO') }}}: 'Protocol error', + {{{ cDefine('EMULTIHOP') }}}: 'Multihop attempted', + {{{ cDefine('EDOTDOT') }}}: 'Cross mount point (not really error)', + {{{ cDefine('EBADMSG') }}}: 'Trying to read unreadable message', + {{{ cDefine('ENOTUNIQ') }}}: 'Given log. name not unique', + {{{ cDefine('EBADFD') }}}: 'f.d. invalid for this operation', + {{{ cDefine('EREMCHG') }}}: 'Remote address changed', + {{{ cDefine('ELIBACC') }}}: 'Can access a needed shared lib', + {{{ cDefine('ELIBBAD') }}}: 'Accessing a corrupted shared lib', + {{{ cDefine('ELIBSCN') }}}: '.lib section in a.out corrupted', + {{{ cDefine('ELIBMAX') }}}: 'Attempting to link in too many libs', + {{{ cDefine('ELIBEXEC') }}}: 'Attempting to exec a shared library', + {{{ cDefine('ENOSYS') }}}: 'Function not implemented', + {{{ cDefine('ENOTEMPTY') }}}: 'Directory not empty', + {{{ cDefine('ENAMETOOLONG') }}}: 'File or path name too long', + {{{ cDefine('ELOOP') }}}: 'Too many symbolic links', + {{{ cDefine('EOPNOTSUPP') }}}: 'Operation not supported on transport endpoint', + {{{ cDefine('EPFNOSUPPORT') }}}: 'Protocol family not supported', + {{{ cDefine('ECONNRESET') }}}: 'Connection reset by peer', + {{{ cDefine('ENOBUFS') }}}: 'No buffer space available', + {{{ cDefine('EAFNOSUPPORT') }}}: 'Address family not supported by protocol family', + {{{ cDefine('EPROTOTYPE') }}}: 'Protocol wrong type for socket', + {{{ cDefine('ENOTSOCK') }}}: 'Socket operation on non-socket', + {{{ cDefine('ENOPROTOOPT') }}}: 'Protocol not available', + {{{ cDefine('ESHUTDOWN') }}}: 'Can\'t send after socket shutdown', + {{{ cDefine('ECONNREFUSED') }}}: 'Connection refused', + {{{ cDefine('EADDRINUSE') }}}: 'Address already in use', + {{{ cDefine('ECONNABORTED') }}}: 'Connection aborted', + {{{ cDefine('ENETUNREACH') }}}: 'Network is unreachable', + {{{ cDefine('ENETDOWN') }}}: 'Network interface is not configured', + {{{ cDefine('ETIMEDOUT') }}}: 'Connection timed out', + {{{ cDefine('EHOSTDOWN') }}}: 'Host is down', + {{{ cDefine('EHOSTUNREACH') }}}: 'Host is unreachable', + {{{ cDefine('EINPROGRESS') }}}: 'Connection already in progress', + {{{ cDefine('EALREADY') }}}: 'Socket already connected', + {{{ cDefine('EDESTADDRREQ') }}}: 'Destination address required', + {{{ cDefine('EMSGSIZE') }}}: 'Message too long', + {{{ cDefine('EPROTONOSUPPORT') }}}: 'Unknown protocol', + {{{ cDefine('ESOCKTNOSUPPORT') }}}: 'Socket type not supported', + {{{ cDefine('EADDRNOTAVAIL') }}}: 'Address not available', + {{{ cDefine('ENETRESET') }}}: 'Connection reset by network', + {{{ cDefine('EISCONN') }}}: 'Socket is already connected', + {{{ cDefine('ENOTCONN') }}}: 'Socket is not connected', + {{{ cDefine('ETOOMANYREFS') }}}: 'Too many references', + {{{ cDefine('EUSERS') }}}: 'Too many users', + {{{ cDefine('EDQUOT') }}}: 'Quota exceeded', + {{{ cDefine('ESTALE') }}}: 'Stale file handle', + {{{ cDefine('ENOTSUP') }}}: 'Not supported', + {{{ cDefine('ENOMEDIUM') }}}: 'No medium (in tape drive)', + {{{ cDefine('EILSEQ') }}}: 'Illegal byte sequence', + {{{ cDefine('EOVERFLOW') }}}: 'Value too large for defined data type', + {{{ cDefine('ECANCELED') }}}: 'Operation canceled', + {{{ cDefine('ENOTRECOVERABLE') }}}: 'State not recoverable', + {{{ cDefine('EOWNERDEAD') }}}: 'Previous owner died', + {{{ cDefine('ESTRPIPE') }}}: 'Streams pipe error', + }, + __setErrNo: function(value) { + if (Module['___errno_location']) {{{ makeSetValue("Module['___errno_location']()", 0, 'value', 'i32') }}}; +#if ASSERTIONS + else Module.printErr('failed to set errno from JS'); +#endif + return value; + }, + + // ========================================================================== + // sched.h (stubs only - no thread support yet!) + // ========================================================================== + sched_yield: function() { + return 0; + }, + + // ========================================================================== + // arpa/inet.h + // ========================================================================== + + // old ipv4 only functions + inet_addr__deps: ['_inet_pton4_raw'], + inet_addr: function(ptr) { + var addr = __inet_pton4_raw(Pointer_stringify(ptr)); + if (addr === null) { + return -1; + } + return addr; + }, + + // ========================================================================== + // netinet/in.h + // ========================================================================== + +#if USE_PTHREADS + in6addr_any: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_any = PthreadWorkerInit._in6addr_any; else PthreadWorkerInit._in6addr_any = _in6addr_any = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', + in6addr_loopback: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_loopback = PthreadWorkerInit._in6addr_loopback; else PthreadWorkerInit._in6addr_loopback = _in6addr_loopback = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', +#else + in6addr_any: + 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', + in6addr_loopback: + 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', +#endif + + // ========================================================================== + // netdb.h + // ========================================================================== + + _inet_pton4_raw: function(str) { + var b = str.split('.'); + for (var i = 0; i < 4; i++) { + var tmp = Number(b[i]); + if (isNaN(tmp)) return null; + b[i] = tmp; + } + return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0; + }, + _inet_ntop4_raw: function(addr) { + return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) + }, + _inet_pton6_raw__deps: ['htons'], + _inet_pton6_raw: function(str) { + var words; + var w, offset, z, i; + /* http://home.deds.nl/~aeron/regex/ */ + var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i + var parts = []; + if (!valid6regx.test(str)) { + return null; + } + if (str === "::") { + return [0, 0, 0, 0, 0, 0, 0, 0]; + } + // Z placeholder to keep track of zeros when splitting the string on ":" + if (str.indexOf("::") === 0) { + str = str.replace("::", "Z:"); // leading zeros case + } else { + str = str.replace("::", ":Z:"); + } + + if (str.indexOf(".") > 0) { + // parse IPv4 embedded stress + str = str.replace(new RegExp('[.]', 'g'), ":"); + words = str.split(":"); + words[words.length-4] = parseInt(words[words.length-4]) + parseInt(words[words.length-3])*256; + words[words.length-3] = parseInt(words[words.length-2]) + parseInt(words[words.length-1])*256; + words = words.slice(0, words.length-2); + } else { + words = str.split(":"); + } + + offset = 0; z = 0; + for (w=0; w < words.length; w++) { + if (typeof words[w] === 'string') { + if (words[w] === 'Z') { + // compressed zeros - write appropriate number of zero words + for (z = 0; z < (8 - words.length+1); z++) { + parts[w+z] = 0; + } + offset = z-1; + } else { + // parse hex to field to 16-bit value and write it in network byte-order + parts[w+offset] = _htons(parseInt(words[w],16)); + } + } else { + // parsed IPv4 words + parts[w+offset] = words[w]; + } + } + return [ + (parts[1] << 16) | parts[0], + (parts[3] << 16) | parts[2], + (parts[5] << 16) | parts[4], + (parts[7] << 16) | parts[6] + ]; + }, + _inet_pton6__deps: ['_inet_pton6_raw'], + _inet_pton6: function(src, dst) { + var ints = __inet_pton6_raw(Pointer_stringify(src)); + if (ints === null) { + return 0; + } + for (var i = 0; i < 4; i++) { + {{{ makeSetValue('dst', 'i*4', 'ints[i]', 'i32') }}}; + } + return 1; + }, + _inet_ntop6_raw__deps: ['_inet_ntop4_raw'], + _inet_ntop6_raw: function(ints) { + // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 + // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses + // 128-bits are split into eight 16-bit words + // stored in network byte order (big-endian) + // | 80 bits | 16 | 32 bits | + // +-----------------------------------------------------------------+ + // | 10 bytes | 2 | 4 bytes | + // +--------------------------------------+--------------------------+ + // + 5 words | 1 | 2 words | + // +--------------------------------------+--------------------------+ + // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) + // +--------------------------------------+----+---------------------+ + // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) + // +--------------------------------------+----+---------------------+ + var str = ""; + var word = 0; + var longest = 0; + var lastzero = 0; + var zstart = 0; + var len = 0; + var i = 0; + var parts = [ + ints[0] & 0xffff, + (ints[0] >> 16), + ints[1] & 0xffff, + (ints[1] >> 16), + ints[2] & 0xffff, + (ints[2] >> 16), + ints[3] & 0xffff, + (ints[3] >> 16) + ]; + + // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses + + var hasipv4 = true; + var v4part = ""; + // check if the 10 high-order bytes are all zeros (first 5 words) + for (i = 0; i < 5; i++) { + if (parts[i] !== 0) { hasipv4 = false; break; } + } + + if (hasipv4) { + // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) + v4part = __inet_ntop4_raw(parts[6] | (parts[7] << 16)); + // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) + if (parts[5] === -1) { + str = "::ffff:"; + str += v4part; + return str; + } + // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) + if (parts[5] === 0) { + str = "::"; + //special case IPv6 addresses + if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address + if(v4part === "0.0.0.1") v4part = "1";// loopback address + str += v4part; + return str; + } + } + + // Handle all other IPv6 addresses + + // first run to find the longest contiguous zero words + for (word = 0; word < 8; word++) { + if (parts[word] === 0) { + if (word - lastzero > 1) { + len = 0; + } + lastzero = word; + len++; + } + if (len > longest) { + longest = len; + zstart = word - longest + 1; + } + } + + for (word = 0; word < 8; word++) { + if (longest > 1) { + // compress contiguous zeros - to produce "::" + if (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) { + if (word === zstart) { + str += ":"; + if (zstart === 0) str += ":"; //leading zeros case + } + continue; + } + } + // converts 16-bit words from big-endian to little-endian before converting to hex string + str += Number(_ntohs(parts[word] & 0xffff)).toString(16); + str += word < 7 ? ":" : ""; + } + return str; + }, + + _read_sockaddr__deps: ['$Sockets', '_inet_ntop4_raw', '_inet_ntop6_raw'], + _read_sockaddr: function (sa, salen) { + // family / port offsets are common to both sockaddr_in and sockaddr_in6 + var family = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'i16') }}}; + var port = _ntohs({{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_port, 'i16') }}}); + var addr; + + switch (family) { + case {{{ cDefine('AF_INET') }}}: + if (salen !== {{{ C_STRUCTS.sockaddr_in.__size__ }}}) { + return { errno: ERRNO_CODES.EINVAL }; + } + addr = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'i32') }}}; + addr = __inet_ntop4_raw(addr); + break; + case {{{ cDefine('AF_INET6') }}}: + if (salen !== {{{ C_STRUCTS.sockaddr_in6.__size__ }}}) { + return { errno: ERRNO_CODES.EINVAL }; + } + addr = [ + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'i32') }}} + ]; + addr = __inet_ntop6_raw(addr); + break; + default: + return { errno: ERRNO_CODES.EAFNOSUPPORT }; + } + + return { family: family, addr: addr, port: port }; + }, + _write_sockaddr__deps: ['$Sockets', '_inet_pton4_raw', '_inet_pton6_raw'], + _write_sockaddr: function (sa, family, addr, port) { + switch (family) { + case {{{ cDefine('AF_INET') }}}: + addr = __inet_pton4_raw(addr); + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'family', 'i16') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'addr', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_port, '_htons(port)', 'i16') }}}; + break; + case {{{ cDefine('AF_INET6') }}}: + addr = __inet_pton6_raw(addr); + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_family, 'family', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'addr[0]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'addr[1]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'addr[2]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'addr[3]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(port)', 'i16') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_flowinfo, '0', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_scope_id, '0', 'i32') }}}; + break; + default: + return { errno: ERRNO_CODES.EAFNOSUPPORT }; + } + // kind of lame, but let's match _read_sockaddr's interface + return {}; + }, + + // We can't actually resolve hostnames in the browser, so instead + // we're generating fake IP addresses with lookup_name that we can + // resolve later on with lookup_addr. + // We do the aliasing in 172.29.*.*, giving us 65536 possibilities. + $DNS__deps: ['_inet_pton4_raw', '_inet_pton6_raw'], + $DNS: { + address_map: { + id: 1, + addrs: {}, + names: {} + }, + + lookup_name: function (name) { + // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. + var res = __inet_pton4_raw(name); + if (res !== null) { + return name; + } + res = __inet_pton6_raw(name); + if (res !== null) { + return name; + } + + // See if this name is already mapped. + var addr; + + if (DNS.address_map.addrs[name]) { + addr = DNS.address_map.addrs[name]; + } else { + var id = DNS.address_map.id++; + assert(id < 65535, 'exceeded max address mappings of 65535'); + + addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); + + DNS.address_map.names[addr] = name; + DNS.address_map.addrs[name] = addr; + } + + return addr; + }, + + lookup_addr: function (addr) { + if (DNS.address_map.names[addr]) { + return DNS.address_map.names[addr]; + } + + return null; + } + }, + + // note: lots of leaking here! + gethostbyaddr__deps: ['$DNS', 'gethostbyname', '_inet_ntop4_raw'], + gethostbyaddr__proxy: 'sync', + gethostbyaddr__sig: 'iiii', + gethostbyaddr: function (addr, addrlen, type) { + if (type !== {{{ cDefine('AF_INET') }}}) { + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + // TODO: set h_errno + return null; + } + addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr + var host = __inet_ntop4_raw(addr); + var lookup = DNS.lookup_addr(host); + if (lookup) { + host = lookup; + } + var hostp = allocate(intArrayFromString(host), 'i8', ALLOC_STACK); + return _gethostbyname(hostp); + }, + + gethostbyname__deps: ['$DNS', '_inet_pton4_raw'], + gethostbyname__proxy: 'sync', + gethostbyname__sig: 'ii', + gethostbyname: function(name) { + name = Pointer_stringify(name); + + // generate hostent + var ret = _malloc({{{ C_STRUCTS.hostent.__size__ }}}); // XXX possibly leaked, as are others here + var nameBuf = _malloc(name.length+1); + stringToUTF8(name, nameBuf, name.length+1); + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}}; + var aliasesBuf = _malloc(4); + {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}}; + var afinet = {{{ cDefine('AF_INET') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}}; + var addrListBuf = _malloc(12); + {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}}; + return ret; + }, + + gethostbyname_r__deps: ['gethostbyname'], + gethostbyname_r__proxy: 'sync', + gethostbyname_r__sig: 'iiiiiii', + gethostbyname_r: function(name, ret, buf, buflen, out, err) { + var data = _gethostbyname(name); + _memcpy(ret, data, {{{ C_STRUCTS.hostent.__size__ }}}); + _free(data); + {{{ makeSetValue('err', '0', '0', 'i32') }}}; + {{{ makeSetValue('out', '0', 'ret', '*') }}}; + return 0; + }, + + getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr'], + getaddrinfo__proxy: 'sync', + getaddrinfo__sig: 'iiiii', + getaddrinfo: function(node, service, hint, out) { + // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL + // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we + // really should provide a linked list of suitable addrinfo values. + var addrs = []; + var canon = null; + var addr = 0; + var port = 0; + var flags = 0; + var family = {{{ cDefine('AF_UNSPEC') }}}; + var type = 0; + var proto = 0; + var ai, last; + + function allocaddrinfo(family, type, proto, canon, addr, port) { + var sa, salen, ai; + var res; + + salen = family === {{{ cDefine('AF_INET6') }}} ? + {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : + {{{ C_STRUCTS.sockaddr_in.__size__ }}}; + addr = family === {{{ cDefine('AF_INET6') }}} ? + __inet_ntop6_raw(addr) : + __inet_ntop4_raw(addr); + sa = _malloc(salen); + res = __write_sockaddr(sa, family, addr, port); + assert(!res.errno); + + ai = _malloc({{{ C_STRUCTS.addrinfo.__size__ }}}); + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_family, 'family', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_socktype, 'type', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_protocol, 'proto', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_canonname, 'canon', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addr, 'sa', '*') }}}; + if (family === {{{ cDefine('AF_INET6') }}}) { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; + } else { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; + } + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; + + return ai; + } + + if (hint) { + flags = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}}; + family = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_family, 'i32') }}}; + type = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_socktype, 'i32') }}}; + proto = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_protocol, 'i32') }}}; + } + if (type && !proto) { + proto = type === {{{ cDefine('SOCK_DGRAM') }}} ? {{{ cDefine('IPPROTO_UDP') }}} : {{{ cDefine('IPPROTO_TCP') }}}; + } + if (!type && proto) { + type = proto === {{{ cDefine('IPPROTO_UDP') }}} ? {{{ cDefine('SOCK_DGRAM') }}} : {{{ cDefine('SOCK_STREAM') }}}; + } + + // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for + // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints. + if (proto === 0) { + proto = {{{ cDefine('IPPROTO_TCP') }}}; + } + if (type === 0) { + type = {{{ cDefine('SOCK_STREAM') }}}; + } + + if (!node && !service) { + return {{{ cDefine('EAI_NONAME') }}}; + } + if (flags & ~({{{ cDefine('AI_PASSIVE') }}}|{{{ cDefine('AI_CANONNAME') }}}|{{{ cDefine('AI_NUMERICHOST') }}}| + {{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) { + return {{{ cDefine('EAI_BADFLAGS') }}}; + } + if (hint !== 0 && ({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) { + return {{{ cDefine('EAI_BADFLAGS') }}}; + } + if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) { + // TODO + return {{{ cDefine('EAI_NONAME') }}}; + } + if (type !== 0 && type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) { + return {{{ cDefine('EAI_SOCKTYPE') }}}; + } + if (family !== {{{ cDefine('AF_UNSPEC') }}} && family !== {{{ cDefine('AF_INET') }}} && family !== {{{ cDefine('AF_INET6') }}}) { + return {{{ cDefine('EAI_FAMILY') }}}; + } + + if (service) { + service = Pointer_stringify(service); + port = parseInt(service, 10); + + if (isNaN(port)) { + if (flags & {{{ cDefine('AI_NUMERICSERV') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + // TODO support resolving well-known service names from: + // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt + return {{{ cDefine('EAI_SERVICE') }}}; + } + } + + if (!node) { + if (family === {{{ cDefine('AF_UNSPEC') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } + if ((flags & {{{ cDefine('AI_PASSIVE') }}}) === 0) { + if (family === {{{ cDefine('AF_INET') }}}) { + addr = _htonl({{{ cDefine('INADDR_LOOPBACK') }}}); + } else { + addr = [0, 0, 0, 1]; + } + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + } + + // + // try as a numeric address + // + node = Pointer_stringify(node); + addr = __inet_pton4_raw(node); + if (addr !== null) { + // incoming node is a valid ipv4 address + if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } + else if (family === {{{ cDefine('AF_INET6') }}} && (flags & {{{ cDefine('AI_V4MAPPED') }}})) { + addr = [0, 0, _htonl(0xffff), addr]; + family = {{{ cDefine('AF_INET6') }}}; + } else { + return {{{ cDefine('EAI_NONAME') }}}; + } + } else { + addr = __inet_pton6_raw(node); + if (addr !== null) { + // incoming node is a valid ipv6 address + if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET6') }}}) { + family = {{{ cDefine('AF_INET6') }}}; + } else { + return {{{ cDefine('EAI_NONAME') }}}; + } + } + } + if (addr != null) { + ai = allocaddrinfo(family, type, proto, node, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + } + if (flags & {{{ cDefine('AI_NUMERICHOST') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + + // + // try as a hostname + // + // resolve the hostname to a temporary fake address + node = DNS.lookup_name(node); + addr = __inet_pton4_raw(node); + if (family === {{{ cDefine('AF_UNSPEC') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } else if (family === {{{ cDefine('AF_INET6') }}}) { + addr = [0, 0, _htonl(0xffff), addr]; + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + }, + + getnameinfo__deps: ['$Sockets', '$DNS', '_read_sockaddr'], + getnameinfo: function (sa, salen, node, nodelen, serv, servlen, flags) { + var info = __read_sockaddr(sa, salen); + if (info.errno) { + return {{{ cDefine('EAI_FAMILY') }}}; + } + var port = info.port; + var addr = info.addr; + + var overflowed = false; + + if (node && nodelen) { + var lookup; + if ((flags & {{{ cDefine('NI_NUMERICHOST') }}}) || !(lookup = DNS.lookup_addr(addr))) { + if (flags & {{{ cDefine('NI_NAMEREQD') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + } else { + addr = lookup; + } + var numBytesWrittenExclNull = stringToUTF8(addr, node, nodelen); + + if (numBytesWrittenExclNull+1 >= nodelen) { + overflowed = true; + } + } + + if (serv && servlen) { + port = '' + port; + var numBytesWrittenExclNull = stringToUTF8(port, serv, servlen); + + if (numBytesWrittenExclNull+1 >= servlen) { + overflowed = true; + } + } + + if (overflowed) { + // Note: even when we overflow, getnameinfo() is specced to write out the truncated results. + return {{{ cDefine('EAI_OVERFLOW') }}}; + } + + return 0; + }, + // Can't use a literal for $GAI_ERRNO_MESSAGES as was done for $ERRNO_MESSAGES as the keys (e.g. EAI_BADFLAGS) + // are actually negative numbers and you can't have expressions as keys in JavaScript literals. + $GAI_ERRNO_MESSAGES: {}, + + gai_strerror__deps: ['$GAI_ERRNO_MESSAGES'], + gai_strerror: function(val) { + var buflen = 256; + + // On first call to gai_strerror we initialise the buffer and populate the error messages. + if (!_gai_strerror.buffer) { + _gai_strerror.buffer = _malloc(buflen); + + GAI_ERRNO_MESSAGES['0'] = 'Success'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_BADFLAGS') }}}] = 'Invalid value for \'ai_flags\' field'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_NONAME') }}}] = 'NAME or SERVICE is unknown'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_AGAIN') }}}] = 'Temporary failure in name resolution'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAIL') }}}] = 'Non-recoverable failure in name res'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAMILY') }}}] = '\'ai_family\' not supported'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SOCKTYPE') }}}] = '\'ai_socktype\' not supported'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SERVICE') }}}] = 'SERVICE not supported for \'ai_socktype\''; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_MEMORY') }}}] = 'Memory allocation failure'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SYSTEM') }}}] = 'System error returned in \'errno\''; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_OVERFLOW') }}}] = 'Argument buffer overflow'; + } + + var msg = 'Unknown error'; + + if (val in GAI_ERRNO_MESSAGES) { + if (GAI_ERRNO_MESSAGES[val].length > buflen - 1) { + msg = 'Message too long'; // EMSGSIZE message. This should never occur given the GAI_ERRNO_MESSAGES above. + } else { + msg = GAI_ERRNO_MESSAGES[val]; + } + } + + writeAsciiToMemory(msg, _gai_strerror.buffer); + return _gai_strerror.buffer; + }, + + // Implement netdb.h protocol entry (getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent) + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/getprotobyname.html + // The Protocols object holds our 'fake' protocols 'database'. + $Protocols: { + list: [], + map: {} + }, + setprotoent__deps: ['$Protocols'], + setprotoent: function(stayopen) { + // void setprotoent(int stayopen); + + // Allocate and populate a protoent structure given a name, protocol number and array of aliases + function allocprotoent(name, proto, aliases) { + // write name into buffer + var nameBuf = _malloc(name.length + 1); + writeAsciiToMemory(name, nameBuf); + + // write aliases into buffer + var j = 0; + var length = aliases.length; + var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. + + for (var i = 0; i < length; i++, j += 4) { + var alias = aliases[i]; + var aliasBuf = _malloc(alias.length + 1); + writeAsciiToMemory(alias, aliasBuf); + {{{ makeSetValue('aliasListBuf', 'j', 'aliasBuf', 'i8*') }}}; + } + {{{ makeSetValue('aliasListBuf', 'j', '0', 'i8*') }}}; // Terminating NULL pointer. + + // generate protoent + var pe = _malloc({{{ C_STRUCTS.protoent.__size__ }}}); + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_name, 'nameBuf', 'i8*') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_aliases, 'aliasListBuf', 'i8**') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_proto, 'proto', 'i32') }}}; + return pe; + }; + + // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial + // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. + var list = Protocols.list; + var map = Protocols.map; + if (list.length === 0) { + var entry = allocprotoent('tcp', 6, ['TCP']); + list.push(entry); + map['tcp'] = map['6'] = entry; + entry = allocprotoent('udp', 17, ['UDP']); + list.push(entry); + map['udp'] = map['17'] = entry; + } + + _setprotoent.index = 0; + }, + + endprotoent: function() { + // void endprotoent(void); + // We're not using a real protocol database so we don't do a real close. + }, + + getprotoent__deps: ['setprotoent', '$Protocols'], + getprotoent: function(number) { + // struct protoent *getprotoent(void); + // reads the next entry from the protocols 'database' or return NULL if 'eof' + if (_setprotoent.index === Protocols.list.length) { + return 0; + } else { + var result = Protocols.list[_setprotoent.index++]; + return result; + } + }, + + getprotobyname__deps: ['setprotoent', '$Protocols'], + getprotobyname: function(name) { + // struct protoent *getprotobyname(const char *); + name = Pointer_stringify(name); + _setprotoent(true); + var result = Protocols.map[name]; + return result; + }, + + getprotobynumber__deps: ['setprotoent', '$Protocols'], + getprotobynumber: function(number) { + // struct protoent *getprotobynumber(int proto); + _setprotoent(true); + var result = Protocols.map[number]; + return result; + }, + + // ========================================================================== + // sockets. Note that the implementation assumes all sockets are always + // nonblocking + // ========================================================================== +#if SOCKET_WEBRTC + $Sockets__deps: ['__setErrNo', '$ERRNO_CODES', + function() { return 'var SocketIO = ' + read('socket.io.js') + ';\n' }, + function() { return 'var Peer = ' + read('wrtcp.js') + ';\n' }], +#else + $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], +#endif + $Sockets: { + BUFFER_SIZE: 10*1024, // initial size + MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer + + nextFd: 1, + fds: {}, + nextport: 1, + maxport: 65535, + peer: null, + connections: {}, + portmap: {}, + localAddr: 0xfe00000a, // Local address is always 10.0.0.254 + addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, + 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, + 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ + }, + + // pwd.h + + getpwnam: function() { throw 'getpwnam: TODO' }, + setpwent: function() { throw 'setpwent: TODO' }, + getpwent: function() { throw 'getpwent: TODO' }, + endpwent: function() { throw 'endpwent: TODO' }, + + // ========================================================================== + // emscripten.h + // ========================================================================== + + emscripten_run_script: function(ptr) { + {{{ makeEval('eval(Pointer_stringify(ptr));') }}} + }, + + emscripten_run_script_int: function(ptr) { + {{{ makeEval('return eval(Pointer_stringify(ptr))|0;') }}} + }, + + emscripten_run_script_string: function(ptr) { + {{{ makeEval("var s = eval(Pointer_stringify(ptr)) + '';") }}} + var me = _emscripten_run_script_string; + var len = lengthBytesUTF8(s); + if (!me.bufferSize || me.bufferSize < len+1) { + if (me.bufferSize) _free(me.buffer); + me.bufferSize = len+1; + me.buffer = _malloc(me.bufferSize); + } + stringToUTF8(s, me.buffer, me.bufferSize); + return me.buffer; + }, + + emscripten_random: function() { + return Math.random(); + }, + + emscripten_get_now: function() { abort() }, // replaced by the postset at startup time + emscripten_get_now__postset: "if (ENVIRONMENT_IS_NODE) {\n" + + " _emscripten_get_now = function _emscripten_get_now_actual() {\n" + + " var t = process['hrtime']();\n" + + " return t[0] * 1e3 + t[1] / 1e6;\n" + + " };\n" + +#if USE_PTHREADS +// Pthreads need their clocks synchronized to the execution of the main thread, so give them a special form of the function. + "} else if (ENVIRONMENT_IS_PTHREAD) {\n" + + " _emscripten_get_now = function() { return performance['now']() - __performance_now_clock_drift; };\n" + +#endif + "} else if (typeof dateNow !== 'undefined') {\n" + + " _emscripten_get_now = dateNow;\n" + + "} else if (typeof self === 'object' && self['performance'] && typeof self['performance']['now'] === 'function') {\n" + + " _emscripten_get_now = function() { return self['performance']['now'](); };\n" + + "} else if (typeof performance === 'object' && typeof performance['now'] === 'function') {\n" + + " _emscripten_get_now = function() { return performance['now'](); };\n" + + "} else {\n" + + " _emscripten_get_now = Date.now;\n" + + "}", + + emscripten_get_now_res: function() { // return resolution of get_now, in nanoseconds + if (ENVIRONMENT_IS_NODE) { + return 1; // nanoseconds + } else if (typeof dateNow !== 'undefined' || + ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now'])) { + return 1000; // microseconds (1/1000 of a millisecond) + } else { + return 1000*1000; // milliseconds + } + }, + + emscripten_get_now_is_monotonic__deps: ['emscripten_get_now'], + emscripten_get_now_is_monotonic: function() { + // return whether emscripten_get_now is guaranteed monotonic; the Date.now + // implementation is not :( + return ENVIRONMENT_IS_NODE || (typeof dateNow !== 'undefined') || + ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now']); + }, + + // Returns [parentFuncArguments, functionName, paramListName] + _emscripten_traverse_stack: function(args) { + if (!args || !args.callee || !args.callee.name) { + return [null, '', '']; + } + + var funstr = args.callee.toString(); + var funcname = args.callee.name; + var str = '('; + var first = true; + for (var i in args) { + var a = args[i]; + if (!first) { + str += ", "; + } + first = false; + if (typeof a === 'number' || typeof a === 'string') { + str += a; + } else { + str += '(' + typeof a + ')'; + } + } + str += ')'; + var caller = args.callee.caller; + args = caller ? caller.arguments : []; + if (first) + str = ''; + return [args, funcname, str]; + }, + + emscripten_get_callstack_js__deps: ['_emscripten_traverse_stack'], + emscripten_get_callstack_js: function(flags) { + var callstack = jsStackTrace(); + + // Find the symbols in the callstack that corresponds to the functions that report callstack information, and remove everyhing up to these from the output. + var iThisFunc = callstack.lastIndexOf('_emscripten_log'); + var iThisFunc2 = callstack.lastIndexOf('_emscripten_get_callstack'); + var iNextLine = callstack.indexOf('\n', Math.max(iThisFunc, iThisFunc2))+1; + callstack = callstack.slice(iNextLine); + + // If user requested to see the original source stack, but no source map information is available, just fall back to showing the JS stack. + if (flags & 8/*EM_LOG_C_STACK*/ && typeof emscripten_source_map === 'undefined') { + warnOnce('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.'); + flags ^= 8/*EM_LOG_C_STACK*/; + flags |= 16/*EM_LOG_JS_STACK*/; + } + + var stack_args = null; + if (flags & 128 /*EM_LOG_FUNC_PARAMS*/) { + // To get the actual parameters to the functions, traverse the stack via the unfortunately deprecated 'arguments.callee' method, if it works: + stack_args = __emscripten_traverse_stack(arguments); + while (stack_args[1].indexOf('_emscripten_') >= 0) + stack_args = __emscripten_traverse_stack(stack_args[0]); + } + + // Process all lines: + var lines = callstack.split('\n'); + callstack = ''; + var newFirefoxRe = new RegExp('\\s*(.*?)@(.*?):([0-9]+):([0-9]+)'); // New FF30 with column info: extract components of form ' Object._main@http://server.com:4324:12' + var firefoxRe = new RegExp('\\s*(.*?)@(.*):(.*)(:(.*))?'); // Old FF without column info: extract components of form ' Object._main@http://server.com:4324' + var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)'); // Extract components of form ' at Object._main (http://server.com/file.html:4324:12)' + + for (var l in lines) { + var line = lines[l]; + + var jsSymbolName = ''; + var file = ''; + var lineno = 0; + var column = 0; + + var parts = chromeRe.exec(line); + if (parts && parts.length == 5) { + jsSymbolName = parts[1]; + file = parts[2]; + lineno = parts[3]; + column = parts[4]; + } else { + parts = newFirefoxRe.exec(line); + if (!parts) parts = firefoxRe.exec(line); + if (parts && parts.length >= 4) { + jsSymbolName = parts[1]; + file = parts[2]; + lineno = parts[3]; + column = parts[4]|0; // Old Firefox doesn't carry column information, but in new FF30, it is present. See https://bugzilla.mozilla.org/show_bug.cgi?id=762556 + } else { + // Was not able to extract this line for demangling/sourcemapping purposes. Output it as-is. + callstack += line + '\n'; + continue; + } + } + + // Try to demangle the symbol, but fall back to showing the original JS symbol name if not available. + var cSymbolName = (flags & 32/*EM_LOG_DEMANGLE*/) ? demangle(jsSymbolName) : jsSymbolName; + if (!cSymbolName) { + cSymbolName = jsSymbolName; + } + + var haveSourceMap = false; + + if (flags & 8/*EM_LOG_C_STACK*/) { + var orig = emscripten_source_map.originalPositionFor({line: lineno, column: column}); + haveSourceMap = (orig && orig.source); + if (haveSourceMap) { + if (flags & 64/*EM_LOG_NO_PATHS*/) { + orig.source = orig.source.substring(orig.source.replace(/\\/g, "/").lastIndexOf('/')+1); + } + callstack += ' at ' + cSymbolName + ' (' + orig.source + ':' + orig.line + ':' + orig.column + ')\n'; + } + } + if ((flags & 16/*EM_LOG_JS_STACK*/) || !haveSourceMap) { + if (flags & 64/*EM_LOG_NO_PATHS*/) { + file = file.substring(file.replace(/\\/g, "/").lastIndexOf('/')+1); + } + callstack += (haveSourceMap ? (' = '+jsSymbolName) : (' at '+cSymbolName)) + ' (' + file + ':' + lineno + ':' + column + ')\n'; + } + + // If we are still keeping track with the callstack by traversing via 'arguments.callee', print the function parameters as well. + if (flags & 128 /*EM_LOG_FUNC_PARAMS*/ && stack_args[0]) { + if (stack_args[1] == jsSymbolName && stack_args[2].length > 0) { + callstack = callstack.replace(/\s+$/, ''); + callstack += ' with values: ' + stack_args[1] + stack_args[2] + '\n'; + } + stack_args = __emscripten_traverse_stack(stack_args[0]); + } + } + // Trim extra whitespace at the end of the output. + callstack = callstack.replace(/\s+$/, ''); + return callstack; + }, + + emscripten_get_callstack__deps: ['emscripten_get_callstack_js'], + emscripten_get_callstack: function(flags, str, maxbytes) { + var callstack = _emscripten_get_callstack_js(flags); + // User can query the required amount of bytes to hold the callstack. + if (!str || maxbytes <= 0) { + return lengthBytesUTF8(callstack)+1; + } + // Output callstack string as C string to HEAP. + var bytesWrittenExcludingNull = stringToUTF8(callstack, str, maxbytes); + + // Return number of bytes written, including null. + return bytesWrittenExcludingNull+1; + }, + + emscripten_log_js__deps: ['emscripten_get_callstack_js'], + emscripten_log_js: function(flags, str) { + if (flags & 24/*EM_LOG_C_STACK | EM_LOG_JS_STACK*/) { + str = str.replace(/\s+$/, ''); // Ensure the message and the callstack are joined cleanly with exactly one newline. + str += (str.length > 0 ? '\n' : '') + _emscripten_get_callstack_js(flags); + } + + if (flags & 1 /*EM_LOG_CONSOLE*/) { + if (flags & 4 /*EM_LOG_ERROR*/) { + console.error(str); + } else if (flags & 2 /*EM_LOG_WARN*/) { + console.warn(str); + } else { + console.log(str); + } + } else if (flags & 6 /*EM_LOG_ERROR|EM_LOG_WARN*/) { + Module.printErr(str); + } else { + Module.print(str); + } + }, + + emscripten_log__deps: ['_formatString', 'emscripten_log_js'], + emscripten_log: function(flags, varargs) { + // Extract the (optionally-existing) printf format specifier field from varargs. + var format = {{{ makeGetValue('varargs', '0', 'i32', undefined, undefined, true) }}}; + varargs += {{{ Math.max(Runtime.getNativeFieldSize('i32'), Runtime.getAlignSize('i32', null, true)) }}}; + var str = ''; + if (format) { + var result = __formatString(format, varargs); + for(var i = 0 ; i < result.length; ++i) { + str += String.fromCharCode(result[i]); + } + } + _emscripten_log_js(flags, str); + }, + + emscripten_get_compiler_setting: function(name) { + name = Pointer_stringify(name); + + var ret = getCompilerSetting(name); + if (typeof ret === 'number') return ret; + + if (!_emscripten_get_compiler_setting.cache) _emscripten_get_compiler_setting.cache = {}; + var cache = _emscripten_get_compiler_setting.cache; + var fullname = name + '__str'; + var fullret = cache[fullname]; + if (fullret) return fullret; + return cache[fullname] = allocate(intArrayFromString(ret + ''), 'i8', ALLOC_NORMAL); + }, + + emscripten_debugger: function() { + debugger; + }, + + emscripten_print_double: function(x, to, max) { + var str = x + ''; + if (to) return stringToUTF8(str, to, max); + else return lengthBytesUTF8(str); + }, + + //============================ + // i64 math + //============================ + + i64Add__asm: true, + i64Add__sig: 'iiiii', + i64Add: function(a, b, c, d) { + /* + x = a + b*2^32 + y = c + d*2^32 + result = l + h*2^32 + */ + a = a|0; b = b|0; c = c|0; d = d|0; + var l = 0, h = 0; + l = (a + c)>>>0; + h = (b + d + (((l>>>0) < (a>>>0))|0))>>>0; // Add carry from low word to high word on overflow. + {{{ makeStructuralReturn(['l|0', 'h'], true) }}}; + }, + + i64Subtract__asm: true, + i64Subtract__sig: 'iiiii', + i64Subtract: function(a, b, c, d) { + a = a|0; b = b|0; c = c|0; d = d|0; + var l = 0, h = 0; + l = (a - c)>>>0; + h = (b - d)>>>0; + h = (b - d - (((c>>>0) > (a>>>0))|0))>>>0; // Borrow one from high word to low word on underflow. + {{{ makeStructuralReturn(['l|0', 'h'], true) }}}; + }, + + bitshift64Shl__asm: true, + bitshift64Shl__sig: 'iiii', + bitshift64Shl: function(low, high, bits) { + low = low|0; high = high|0; bits = bits|0; + var ander = 0; + if ((bits|0) < 32) { + ander = ((1 << bits) - 1)|0; + {{{ makeSetTempRet0('(high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))') }}}; + return low << bits; + } + {{{ makeSetTempRet0('low << (bits - 32)') }}}; + return 0; + }, + bitshift64Ashr__asm: true, + bitshift64Ashr__sig: 'iiii', + bitshift64Ashr: function(low, high, bits) { + low = low|0; high = high|0; bits = bits|0; + var ander = 0; + if ((bits|0) < 32) { + ander = ((1 << bits) - 1)|0; + {{{ makeSetTempRet0('high >> bits') }}}; + return (low >>> bits) | ((high&ander) << (32 - bits)); + } + {{{ makeSetTempRet0('(high|0) < 0 ? -1 : 0') }}}; + return (high >> (bits - 32))|0; + }, + bitshift64Lshr__asm: true, + bitshift64Lshr__sig: 'iiii', + bitshift64Lshr: function(low, high, bits) { + low = low|0; high = high|0; bits = bits|0; + var ander = 0; + if ((bits|0) < 32) { + ander = ((1 << bits) - 1)|0; + {{{ makeSetTempRet0('high >>> bits') }}}; + return (low >>> bits) | ((high&ander) << (32 - bits)); + } + {{{ makeSetTempRet0('0') }}}; + return (high >>> (bits - 32))|0; + }, + + // misc shims for musl + __lock: function() {}, + __unlock: function() {}, + __lockfile: function() { return 1 }, + __unlockfile: function(){}, + + // ubsan (undefined behavior sanitizer) support + __ubsan_handle_float_cast_overflow: function(id, post) { + abort('Undefined behavior! ubsan_handle_float_cast_overflow: ' + [id, post]); + }, + + // USE_FULL_LIBRARY hacks + realloc: function() { throw 'bad' }, + + // libunwind + + _Unwind_Backtrace__deps: ['emscripten_get_callstack_js'], + _Unwind_Backtrace: function(func, arg) { + var trace = _emscripten_get_callstack_js(); + var parts = trace.split('\n'); + for (var i = 0; i < parts.length; i++) { + var ret = Module['dynCall_iii'](func, 0, arg); + if (ret !== 0) return; + } + }, + + _Unwind_GetIPInfo: function() { + abort('Unwind_GetIPInfo'); + }, + + _Unwind_FindEnclosingFunction: function() { + return 0; // we cannot succeed + }, + + _Unwind_RaiseException__deps: ['__cxa_throw'], + _Unwind_RaiseException: function(ex) { + Module.printErr('Warning: _Unwind_RaiseException is not correctly implemented'); + return ___cxa_throw(ex, 0, 0); + }, + + _Unwind_DeleteException: function(ex) { + Module.printErr('TODO: Unwind_DeleteException'); + }, + + // autodebugging + + emscripten_autodebug_i64: function(line, valuel, valueh) { + Module.print('AD:' + [line, valuel, valueh]); + }, + emscripten_autodebug_i32: function(line, value) { + Module.print('AD:' + [line, value]); + }, + emscripten_autodebug_i16: function(line, value) { + Module.print('AD:' + [line, value]); + }, + emscripten_autodebug_i8: function(line, value) { + Module.print('AD:' + [line, value]); + }, + emscripten_autodebug_float: function(line, value) { + Module.print('AD:' + [line, value]); + }, + emscripten_autodebug_double: function(line, value) { + Module.print('AD:' + [line, value]); + }, + + // misc definitions to avoid unnecessary unresolved symbols from fastcomp + emscripten_prep_setjmp: true, + emscripten_cleanup_setjmp: true, + emscripten_check_longjmp: true, + emscripten_get_longjmp_result: true, + emscripten_setjmp: true, + emscripten_preinvoke: true, + emscripten_postinvoke: true, + emscripten_resume: true, + emscripten_landingpad: true, + getHigh32: true, + setHigh32: true, + FtoILow: true, + FtoIHigh: true, + DtoILow: true, + DtoIHigh: true, + BDtoILow: true, + BDtoIHigh: true, + SItoF: true, + UItoF: true, + SItoD: true, + UItoD: true, + BItoD: true, + llvm_dbg_value: true, + llvm_debugtrap: true, + llvm_ctlz_i32: true, + emscripten_asm_const: true, + emscripten_asm_const_int: true, + emscripten_asm_const_double: true, + emscripten_asm_const_int_sync_on_main_thread: true, + emscripten_asm_const_double_sync_on_main_thread: true, + emscripten_asm_const_async_on_main_thread: true, + + // ======== compiled code from system/lib/compiler-rt , see readme therein + __muldsi3__asm: true, + __muldsi3__sig: 'iii', + __muldsi3: function($a, $b) { + $a = $a | 0; + $b = $b | 0; + var $1 = 0, $2 = 0, $3 = 0, $6 = 0, $8 = 0, $11 = 0, $12 = 0; + $1 = $a & 65535; + $2 = $b & 65535; + $3 = Math_imul($2, $1) | 0; + $6 = $a >>> 16; + $8 = ($3 >>> 16) + (Math_imul($2, $6) | 0) | 0; + $11 = $b >>> 16; + $12 = Math_imul($11, $1) | 0; + return ({{{ makeSetTempRet0('(($8 >>> 16) + (Math_imul($11, $6) | 0) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0') }}}, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; + }, + __divdi3__sig: 'iiiii', + __divdi3__asm: true, + __divdi3__deps: ['__udivmoddi4', 'i64Subtract'], + __divdi3: function($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $1$0 = 0, $1$1 = 0, $2$0 = 0, $2$1 = 0, $4$0 = 0, $4$1 = 0, $6$0 = 0, $7$0 = 0, $7$1 = 0, $8$0 = 0, $10$0 = 0; + $1$0 = $a$1 >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $1$1 = (($a$1 | 0) < 0 ? -1 : 0) >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $2$0 = $b$1 >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $2$1 = (($b$1 | 0) < 0 ? -1 : 0) >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $4$0 = _i64Subtract($1$0 ^ $a$0 | 0, $1$1 ^ $a$1 | 0, $1$0 | 0, $1$1 | 0) | 0; + $4$1 = {{{ makeGetTempRet0() }}}; + $6$0 = _i64Subtract($2$0 ^ $b$0 | 0, $2$1 ^ $b$1 | 0, $2$0 | 0, $2$1 | 0) | 0; + $7$0 = $2$0 ^ $1$0; + $7$1 = $2$1 ^ $1$1; + $8$0 = ___udivmoddi4($4$0, $4$1, $6$0, {{{ makeGetTempRet0() }}}, 0) | 0; + $10$0 = _i64Subtract($8$0 ^ $7$0 | 0, {{{ makeGetTempRet0() }}} ^ $7$1 | 0, $7$0 | 0, $7$1 | 0) | 0; + return $10$0 | 0; + }, + __remdi3__sig: 'iiiii', + __remdi3__asm: true, + __remdi3__deps: ['__udivmoddi4', 'i64Subtract'], + __remdi3: function($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $rem = 0, $1$0 = 0, $1$1 = 0, $2$0 = 0, $2$1 = 0, $4$0 = 0, $4$1 = 0, $6$0 = 0, $10$0 = 0, $10$1 = 0, __stackBase__ = 0; + __stackBase__ = STACKTOP; + STACKTOP = STACKTOP + 16 | 0; + $rem = __stackBase__ | 0; + $1$0 = $a$1 >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $1$1 = (($a$1 | 0) < 0 ? -1 : 0) >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $2$0 = $b$1 >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $2$1 = (($b$1 | 0) < 0 ? -1 : 0) >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $4$0 = _i64Subtract($1$0 ^ $a$0 | 0, $1$1 ^ $a$1 | 0, $1$0 | 0, $1$1 | 0) | 0; + $4$1 = {{{ makeGetTempRet0() }}}; + $6$0 = _i64Subtract($2$0 ^ $b$0 | 0, $2$1 ^ $b$1 | 0, $2$0 | 0, $2$1 | 0) | 0; + ___udivmoddi4($4$0, $4$1, $6$0, {{{ makeGetTempRet0() }}}, $rem) | 0; + $10$0 = _i64Subtract(HEAP32[$rem >> 2] ^ $1$0 | 0, HEAP32[$rem + 4 >> 2] ^ $1$1 | 0, $1$0 | 0, $1$1 | 0) | 0; + $10$1 = {{{ makeGetTempRet0() }}}; + STACKTOP = __stackBase__; + return ({{{ makeSetTempRet0('$10$1') }}}, $10$0) | 0; + }, + __muldi3__sig: 'iiiii', + __muldi3__asm: true, + __muldi3__deps: ['__muldsi3'], + __muldi3: function($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $x_sroa_0_0_extract_trunc = 0, $y_sroa_0_0_extract_trunc = 0, $1$0 = 0, $1$1 = 0, $2 = 0; + $x_sroa_0_0_extract_trunc = $a$0; + $y_sroa_0_0_extract_trunc = $b$0; + $1$0 = ___muldsi3($x_sroa_0_0_extract_trunc, $y_sroa_0_0_extract_trunc) | 0; + $1$1 = {{{ makeGetTempRet0() }}}; + $2 = Math_imul($a$1, $y_sroa_0_0_extract_trunc) | 0; + return ({{{ makeSetTempRet0('((Math_imul($b$1, $x_sroa_0_0_extract_trunc) | 0) + $2 | 0) + $1$1 | $1$1 & 0') }}}, 0 | $1$0 & -1) | 0; + }, + __udivdi3__sig: 'iiiii', + __udivdi3__asm: true, + __udivdi3__deps: ['__udivmoddi4'], + __udivdi3: function($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $1$0 = 0; + $1$0 = ___udivmoddi4($a$0, $a$1, $b$0, $b$1, 0) | 0; + return $1$0 | 0; + }, + __uremdi3__sig: 'iiiii', + __uremdi3__asm: true, + __uremdi3__deps: ['__udivmoddi4'], + __uremdi3: function($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $rem = 0, __stackBase__ = 0; + __stackBase__ = STACKTOP; + STACKTOP = STACKTOP + 16 | 0; + $rem = __stackBase__ | 0; + ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) | 0; + STACKTOP = __stackBase__; + return ({{{ makeSetTempRet0('HEAP32[$rem + 4 >> 2] | 0') }}}, HEAP32[$rem >> 2] | 0) | 0; + }, + __udivmoddi4__sig: 'iiiiii', + __udivmoddi4__asm: true, + __udivmoddi4__deps: ['i64Add', 'i64Subtract', 'llvm_cttz_i32'], + __udivmoddi4: function($a$0, $a$1, $b$0, $b$1, $rem) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + $rem = $rem | 0; + var $n_sroa_0_0_extract_trunc = 0, $n_sroa_1_4_extract_shift$0 = 0, $n_sroa_1_4_extract_trunc = 0, $d_sroa_0_0_extract_trunc = 0, $d_sroa_1_4_extract_shift$0 = 0, $d_sroa_1_4_extract_trunc = 0, $4 = 0, $17 = 0, $37 = 0, $49 = 0, $51 = 0, $57 = 0, $58 = 0, $66 = 0, $78 = 0, $86 = 0, $88 = 0, $89 = 0, $91 = 0, $92 = 0, $95 = 0, $105 = 0, $117 = 0, $119 = 0, $125 = 0, $126 = 0, $130 = 0, $q_sroa_1_1_ph = 0, $q_sroa_0_1_ph = 0, $r_sroa_1_1_ph = 0, $r_sroa_0_1_ph = 0, $sr_1_ph = 0, $d_sroa_0_0_insert_insert99$0 = 0, $d_sroa_0_0_insert_insert99$1 = 0, $137$0 = 0, $137$1 = 0, $carry_0203 = 0, $sr_1202 = 0, $r_sroa_0_1201 = 0, $r_sroa_1_1200 = 0, $q_sroa_0_1199 = 0, $q_sroa_1_1198 = 0, $147 = 0, $149 = 0, $r_sroa_0_0_insert_insert42$0 = 0, $r_sroa_0_0_insert_insert42$1 = 0, $150$1 = 0, $151$0 = 0, $152 = 0, $154$0 = 0, $r_sroa_0_0_extract_trunc = 0, $r_sroa_1_4_extract_trunc = 0, $155 = 0, $carry_0_lcssa$0 = 0, $carry_0_lcssa$1 = 0, $r_sroa_0_1_lcssa = 0, $r_sroa_1_1_lcssa = 0, $q_sroa_0_1_lcssa = 0, $q_sroa_1_1_lcssa = 0, $q_sroa_0_0_insert_ext75$0 = 0, $q_sroa_0_0_insert_ext75$1 = 0, $q_sroa_0_0_insert_insert77$1 = 0, $_0$0 = 0, $_0$1 = 0; + $n_sroa_0_0_extract_trunc = $a$0; + $n_sroa_1_4_extract_shift$0 = $a$1; + $n_sroa_1_4_extract_trunc = $n_sroa_1_4_extract_shift$0; + $d_sroa_0_0_extract_trunc = $b$0; + $d_sroa_1_4_extract_shift$0 = $b$1; + $d_sroa_1_4_extract_trunc = $d_sroa_1_4_extract_shift$0; + if (($n_sroa_1_4_extract_trunc | 0) == 0) { + $4 = ($rem | 0) != 0; + if (($d_sroa_1_4_extract_trunc | 0) == 0) { + if ($4) { + HEAP32[$rem >> 2] = ($n_sroa_0_0_extract_trunc >>> 0) % ($d_sroa_0_0_extract_trunc >>> 0); + HEAP32[$rem + 4 >> 2] = 0; + } + $_0$1 = 0; + $_0$0 = ($n_sroa_0_0_extract_trunc >>> 0) / ($d_sroa_0_0_extract_trunc >>> 0) >>> 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } else { + if (!$4) { + $_0$1 = 0; + $_0$0 = 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + HEAP32[$rem >> 2] = $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $a$1 & 0; + $_0$1 = 0; + $_0$0 = 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + } + $17 = ($d_sroa_1_4_extract_trunc | 0) == 0; + do { + if (($d_sroa_0_0_extract_trunc | 0) == 0) { + if ($17) { + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = ($n_sroa_1_4_extract_trunc >>> 0) % ($d_sroa_0_0_extract_trunc >>> 0); + HEAP32[$rem + 4 >> 2] = 0; + } + $_0$1 = 0; + $_0$0 = ($n_sroa_1_4_extract_trunc >>> 0) / ($d_sroa_0_0_extract_trunc >>> 0) >>> 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + if (($n_sroa_0_0_extract_trunc | 0) == 0) { + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = 0; + HEAP32[$rem + 4 >> 2] = ($n_sroa_1_4_extract_trunc >>> 0) % ($d_sroa_1_4_extract_trunc >>> 0); + } + $_0$1 = 0; + $_0$0 = ($n_sroa_1_4_extract_trunc >>> 0) / ($d_sroa_1_4_extract_trunc >>> 0) >>> 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + $37 = $d_sroa_1_4_extract_trunc - 1 | 0; + if (($37 & $d_sroa_1_4_extract_trunc | 0) == 0) { + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = 0 | $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $37 & $n_sroa_1_4_extract_trunc | $a$1 & 0; + } + $_0$1 = 0; + $_0$0 = $n_sroa_1_4_extract_trunc >>> ((_llvm_cttz_i32($d_sroa_1_4_extract_trunc | 0) | 0) >>> 0); + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + $49 = Math_clz32($d_sroa_1_4_extract_trunc | 0) | 0; + $51 = $49 - (Math_clz32($n_sroa_1_4_extract_trunc | 0) | 0) | 0; + if ($51 >>> 0 <= 30) { + $57 = $51 + 1 | 0; + $58 = 31 - $51 | 0; + $sr_1_ph = $57; + $r_sroa_0_1_ph = $n_sroa_1_4_extract_trunc << $58 | $n_sroa_0_0_extract_trunc >>> ($57 >>> 0); + $r_sroa_1_1_ph = $n_sroa_1_4_extract_trunc >>> ($57 >>> 0); + $q_sroa_0_1_ph = 0; + $q_sroa_1_1_ph = $n_sroa_0_0_extract_trunc << $58; + break; + } + if (($rem | 0) == 0) { + $_0$1 = 0; + $_0$0 = 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + HEAP32[$rem >> 2] = 0 | $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $n_sroa_1_4_extract_shift$0 | $a$1 & 0; + $_0$1 = 0; + $_0$0 = 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } else { + if (!$17) { + $117 = Math_clz32($d_sroa_1_4_extract_trunc | 0) | 0; + $119 = $117 - (Math_clz32($n_sroa_1_4_extract_trunc | 0) | 0) | 0; + if ($119 >>> 0 <= 31) { + $125 = $119 + 1 | 0; + $126 = 31 - $119 | 0; + $130 = $119 - 31 >> 31; + $sr_1_ph = $125; + $r_sroa_0_1_ph = $n_sroa_0_0_extract_trunc >>> ($125 >>> 0) & $130 | $n_sroa_1_4_extract_trunc << $126; + $r_sroa_1_1_ph = $n_sroa_1_4_extract_trunc >>> ($125 >>> 0) & $130; + $q_sroa_0_1_ph = 0; + $q_sroa_1_1_ph = $n_sroa_0_0_extract_trunc << $126; + break; + } + if (($rem | 0) == 0) { + $_0$1 = 0; + $_0$0 = 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + HEAP32[$rem >> 2] = 0 | $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $n_sroa_1_4_extract_shift$0 | $a$1 & 0; + $_0$1 = 0; + $_0$0 = 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + $66 = $d_sroa_0_0_extract_trunc - 1 | 0; + if (($66 & $d_sroa_0_0_extract_trunc | 0) != 0) { + $86 = (Math_clz32($d_sroa_0_0_extract_trunc | 0) | 0) + 33 | 0; + $88 = $86 - (Math_clz32($n_sroa_1_4_extract_trunc | 0) | 0) | 0; + $89 = 64 - $88 | 0; + $91 = 32 - $88 | 0; + $92 = $91 >> 31; + $95 = $88 - 32 | 0; + $105 = $95 >> 31; + $sr_1_ph = $88; + $r_sroa_0_1_ph = $91 - 1 >> 31 & $n_sroa_1_4_extract_trunc >>> ($95 >>> 0) | ($n_sroa_1_4_extract_trunc << $91 | $n_sroa_0_0_extract_trunc >>> ($88 >>> 0)) & $105; + $r_sroa_1_1_ph = $105 & $n_sroa_1_4_extract_trunc >>> ($88 >>> 0); + $q_sroa_0_1_ph = $n_sroa_0_0_extract_trunc << $89 & $92; + $q_sroa_1_1_ph = ($n_sroa_1_4_extract_trunc << $89 | $n_sroa_0_0_extract_trunc >>> ($95 >>> 0)) & $92 | $n_sroa_0_0_extract_trunc << $91 & $88 - 33 >> 31; + break; + } + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = $66 & $n_sroa_0_0_extract_trunc; + HEAP32[$rem + 4 >> 2] = 0; + } + if (($d_sroa_0_0_extract_trunc | 0) == 1) { + $_0$1 = $n_sroa_1_4_extract_shift$0 | $a$1 & 0; + $_0$0 = 0 | $a$0 & -1; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } else { + $78 = _llvm_cttz_i32($d_sroa_0_0_extract_trunc | 0) | 0; + $_0$1 = 0 | $n_sroa_1_4_extract_trunc >>> ($78 >>> 0); + $_0$0 = $n_sroa_1_4_extract_trunc << 32 - $78 | $n_sroa_0_0_extract_trunc >>> ($78 >>> 0) | 0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + } + } + } while (0); + if (($sr_1_ph | 0) == 0) { + $q_sroa_1_1_lcssa = $q_sroa_1_1_ph; + $q_sroa_0_1_lcssa = $q_sroa_0_1_ph; + $r_sroa_1_1_lcssa = $r_sroa_1_1_ph; + $r_sroa_0_1_lcssa = $r_sroa_0_1_ph; + $carry_0_lcssa$1 = 0; + $carry_0_lcssa$0 = 0; + } else { + $d_sroa_0_0_insert_insert99$0 = 0 | $b$0 & -1; + $d_sroa_0_0_insert_insert99$1 = $d_sroa_1_4_extract_shift$0 | $b$1 & 0; + $137$0 = _i64Add($d_sroa_0_0_insert_insert99$0 | 0, $d_sroa_0_0_insert_insert99$1 | 0, -1, -1) | 0; + $137$1 = {{{ makeGetTempRet0() }}}; + $q_sroa_1_1198 = $q_sroa_1_1_ph; + $q_sroa_0_1199 = $q_sroa_0_1_ph; + $r_sroa_1_1200 = $r_sroa_1_1_ph; + $r_sroa_0_1201 = $r_sroa_0_1_ph; + $sr_1202 = $sr_1_ph; + $carry_0203 = 0; + while (1) { + $147 = $q_sroa_0_1199 >>> 31 | $q_sroa_1_1198 << 1; + $149 = $carry_0203 | $q_sroa_0_1199 << 1; + $r_sroa_0_0_insert_insert42$0 = 0 | ($r_sroa_0_1201 << 1 | $q_sroa_1_1198 >>> 31); + $r_sroa_0_0_insert_insert42$1 = $r_sroa_0_1201 >>> 31 | $r_sroa_1_1200 << 1 | 0; + _i64Subtract($137$0 | 0, $137$1 | 0, $r_sroa_0_0_insert_insert42$0 | 0, $r_sroa_0_0_insert_insert42$1 | 0) | 0; + $150$1 = {{{ makeGetTempRet0() }}}; + $151$0 = $150$1 >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1; + $152 = $151$0 & 1; + $154$0 = _i64Subtract($r_sroa_0_0_insert_insert42$0 | 0, $r_sroa_0_0_insert_insert42$1 | 0, $151$0 & $d_sroa_0_0_insert_insert99$0 | 0, ((($150$1 | 0) < 0 ? -1 : 0) >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1) & $d_sroa_0_0_insert_insert99$1 | 0) | 0; + $r_sroa_0_0_extract_trunc = $154$0; + $r_sroa_1_4_extract_trunc = {{{ makeGetTempRet0() }}}; + $155 = $sr_1202 - 1 | 0; + if (($155 | 0) == 0) { + break; + } else { + $q_sroa_1_1198 = $147; + $q_sroa_0_1199 = $149; + $r_sroa_1_1200 = $r_sroa_1_4_extract_trunc; + $r_sroa_0_1201 = $r_sroa_0_0_extract_trunc; + $sr_1202 = $155; + $carry_0203 = $152; + } + } + $q_sroa_1_1_lcssa = $147; + $q_sroa_0_1_lcssa = $149; + $r_sroa_1_1_lcssa = $r_sroa_1_4_extract_trunc; + $r_sroa_0_1_lcssa = $r_sroa_0_0_extract_trunc; + $carry_0_lcssa$1 = 0; + $carry_0_lcssa$0 = $152; + } + $q_sroa_0_0_insert_ext75$0 = $q_sroa_0_1_lcssa; + $q_sroa_0_0_insert_ext75$1 = 0; + $q_sroa_0_0_insert_insert77$1 = $q_sroa_1_1_lcssa | $q_sroa_0_0_insert_ext75$1; + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = 0 | $r_sroa_0_1_lcssa; + HEAP32[$rem + 4 >> 2] = $r_sroa_1_1_lcssa | 0; + } + $_0$1 = (0 | $q_sroa_0_0_insert_ext75$0) >>> 31 | $q_sroa_0_0_insert_insert77$1 << 1 | ($q_sroa_0_0_insert_ext75$1 << 1 | $q_sroa_0_0_insert_ext75$0 >>> 31) & 0 | $carry_0_lcssa$1; + $_0$0 = ($q_sroa_0_0_insert_ext75$0 << 1 | 0 >>> 31) & -2 | $carry_0_lcssa$0; + return ({{{ makeSetTempRet0('$_0$1') }}}, $_0$0) | 0; + }, + // ======================================================================= + +}; + +function autoAddDeps(object, name) { + name = [name]; + for (var item in object) { + if (item.substr(-6) != '__deps') { + if (!object[item + '__deps']) { + object[item + '__deps'] = name; + } else { + object[item + '__deps'].push(name[0]); // add to existing list + } + } + } +} diff --git a/emsdk/files/library_browser.js b/emsdk/files/library_browser.js new file mode 100644 index 000000000..e2ac2763e --- /dev/null +++ b/emsdk/files/library_browser.js @@ -0,0 +1,1519 @@ +//"use strict"; + +// Utilities for browser environments +var LibraryBrowser = { + $Browser__deps: ['emscripten_set_main_loop', 'emscripten_set_main_loop_timing'], + $Browser__postset: 'Module["requestFullScreen"] = function Module_requestFullScreen(lockPointer, resizeCanvas, vrDevice) { Module.printErr("Module.requestFullScreen is deprecated. Please call Module.requestFullscreen instead."); Module["requestFullScreen"] = Module["requestFullscreen"]; Browser.requestFullScreen(lockPointer, resizeCanvas, vrDevice) };\n' + // exports + 'Module["requestFullscreen"] = function Module_requestFullscreen(lockPointer, resizeCanvas, vrDevice) { Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice) };\n' + // exports + 'Module["requestAnimationFrame"] = function Module_requestAnimationFrame(func) { Browser.requestAnimationFrame(func) };\n' + + 'Module["setCanvasSize"] = function Module_setCanvasSize(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) };\n' + + 'Module["pauseMainLoop"] = function Module_pauseMainLoop() { Browser.mainLoop.pause() };\n' + + 'Module["resumeMainLoop"] = function Module_resumeMainLoop() { Browser.mainLoop.resume() };\n' + + 'Module["getUserMedia"] = function Module_getUserMedia() { Browser.getUserMedia() }\n' + + 'Module["createContext"] = function Module_createContext(canvas, useWebGL, setInModule, webGLContextAttributes) { return Browser.createContext(canvas, useWebGL, setInModule, webGLContextAttributes) }', + $Browser: { + mainLoop: { + scheduler: null, + method: '', + // Each main loop is numbered with a ID in sequence order. Only one main loop can run at a time. This variable stores the ordinal number of the main loop that is currently + // allowed to run. All previous main loops will quit themselves. This is incremented whenever a new main loop is created. + currentlyRunningMainloop: 0, + func: null, // The main loop tick function that will be called at each iteration. + arg: 0, // The argument that will be passed to the main loop. (of type void*) + timingMode: 0, + timingValue: 0, + currentFrameNumber: 0, + queue: [], + pause: function() { + Browser.mainLoop.scheduler = null; + Browser.mainLoop.currentlyRunningMainloop++; // Incrementing this signals the previous main loop that it's now become old, and it must return. + }, + resume: function() { + Browser.mainLoop.currentlyRunningMainloop++; + var timingMode = Browser.mainLoop.timingMode; + var timingValue = Browser.mainLoop.timingValue; + var func = Browser.mainLoop.func; + Browser.mainLoop.func = null; + _emscripten_set_main_loop(func, 0, false, Browser.mainLoop.arg, true /* do not set timing and call scheduler, we will do it on the next lines */); + _emscripten_set_main_loop_timing(timingMode, timingValue); + Browser.mainLoop.scheduler(); + }, + updateStatus: function() { + if (Module['setStatus']) { + var message = Module['statusMessage'] || 'Please wait...'; + var remaining = Browser.mainLoop.remainingBlockers; + var expected = Browser.mainLoop.expectedBlockers; + if (remaining) { + if (remaining < expected) { + Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')'); + } else { + Module['setStatus'](message); + } + } else { + Module['setStatus'](''); + } + } + }, + runIter: function(func) { + if (ABORT) return; + if (Module['preMainLoop']) { + var preRet = Module['preMainLoop'](); + if (preRet === false) { + return; // |return false| skips a frame + } + } + try { + func(); + } catch (e) { + if (e instanceof ExitStatus) { + return; + } else { + if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]); + throw e; + } + } + if (Module['postMainLoop']) Module['postMainLoop'](); + } + }, + isFullscreen: false, + pointerLock: false, + moduleContextCreatedCallbacks: [], + workers: [], + + init: function() { + if (!Module["preloadPlugins"]) Module["preloadPlugins"] = []; // needs to exist even in workers + + if (Browser.initted) return; + Browser.initted = true; + + try { + new Blob(); + Browser.hasBlobConstructor = true; + } catch(e) { + Browser.hasBlobConstructor = false; + console.log("warning: no blob constructor, cannot create blobs with mimetypes"); + } + Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null)); + Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : undefined; + if (!Module.noImageDecoding && typeof Browser.URLObject === 'undefined') { + console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available."); + Module.noImageDecoding = true; + } + + // Support for plugins that can process preloaded files. You can add more of these to + // your app by creating and appending to Module.preloadPlugins. + // + // Each plugin is asked if it can handle a file based on the file's name. If it can, + // it is given the file's raw data. When it is done, it calls a callback with the file's + // (possibly modified) data. For example, a plugin might decompress a file, or it + // might create some side data structure for use later (like an Image element, etc.). + + var imagePlugin = {}; + imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { + return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); + }; + imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) { + var b = null; + if (Browser.hasBlobConstructor) { + try { + b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + if (b.size !== byteArray.length) { // Safari bug #118630 + // Safari's Blob can only take an ArrayBuffer + b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); + } + } catch(e) { + warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder'); + } + } + if (!b) { + var bb = new Browser.BlobBuilder(); + bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range + b = bb.getBlob(); + } + var url = Browser.URLObject.createObjectURL(b); +#if ASSERTIONS + assert(typeof url == 'string', 'createObjectURL must return a url as a string'); +#endif + var img = new Image(); + img.onload = function img_onload() { + assert(img.complete, 'Image ' + name + ' could not be decoded'); + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + Module["preloadedImages"][name] = canvas; + Browser.URLObject.revokeObjectURL(url); + if (onload) onload(byteArray); + }; + img.onerror = function img_onerror(event) { + console.log('Image ' + url + ' could not be decoded'); + if (onerror) onerror(); + }; + img.src = url; + }; + Module['preloadPlugins'].push(imagePlugin); + + var audioPlugin = {}; + audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { + return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; + }; + audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) { + var done = false; + function finish(audio) { + if (done) return; + done = true; + Module["preloadedAudios"][name] = audio; + if (onload) onload(byteArray); + } + function fail() { + if (done) return; + done = true; + Module["preloadedAudios"][name] = new Audio(); // empty shim + if (onerror) onerror(); + } + if (Browser.hasBlobConstructor) { + try { + var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + } catch(e) { + return fail(); + } + var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this! +#if ASSERTIONS + assert(typeof url == 'string', 'createObjectURL must return a url as a string'); +#endif + var audio = new Audio(); + audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926 + audio.onerror = function audio_onerror(event) { + if (done) return; + console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach'); + function encode64(data) { + var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var PAD = '='; + var ret = ''; + var leftchar = 0; + var leftbits = 0; + for (var i = 0; i < data.length; i++) { + leftchar = (leftchar << 8) | data[i]; + leftbits += 8; + while (leftbits >= 6) { + var curr = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + ret += BASE[curr]; + } + } + if (leftbits == 2) { + ret += BASE[(leftchar&3) << 4]; + ret += PAD + PAD; + } else if (leftbits == 4) { + ret += BASE[(leftchar&0xf) << 2]; + ret += PAD; + } + return ret; + } + audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray); + finish(audio); // we don't wait for confirmation this worked - but it's worth trying + }; + audio.src = url; + // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror + Browser.safeSetTimeout(function() { + finish(audio); // try to use it even though it is not necessarily ready to play + }, 10000); + } else { + return fail(); + } + }; + Module['preloadPlugins'].push(audioPlugin); + +#if WASM +#if MAIN_MODULE + var wasmPlugin = {}; + wasmPlugin['asyncWasmLoadPromise'] = new Promise( + function(resolve, reject) { return resolve(); }); + wasmPlugin['canHandle'] = function(name) { + return !Module.noWasmDecoding && (name.endsWith('.so') || name.endsWith('.wasm')); + }; + wasmPlugin['handle'] = function(byteArray, name, onload, onerror) { + // loadWebAssemblyModule can not load modules out-of-order, so rather + // than just running the promises in parallel, this makes a chain of + // promises to run in series. + this['asyncWasmLoadPromise'] = this['asyncWasmLoadPromise'].then( + function() { + return loadWebAssemblyModule(byteArray, true); + }).then( + function(module) { + Module['preloadedWasm'][name] = module; + onload(); + }, + function(err) { + console.warn("Couldn't instantiate wasm: " + name + " '" + err + "'"); + onerror(); + }); + }; + Module['preloadPlugins'].push(wasmPlugin); +#endif // MAIN_MODULE +#endif // WASM + + // Canvas event setup + + function pointerLockChange() { + Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] || + document['mozPointerLockElement'] === Module['canvas'] || + document['webkitPointerLockElement'] === Module['canvas'] || + document['msPointerLockElement'] === Module['canvas']; + } + var canvas = Module['canvas']; + if (canvas) { + // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module + // Module['forcedAspectRatio'] = 4 / 3; + + canvas.requestPointerLock = canvas['requestPointerLock'] || + canvas['mozRequestPointerLock'] || + canvas['webkitRequestPointerLock'] || + canvas['msRequestPointerLock'] || + function(){}; + canvas.exitPointerLock = document['exitPointerLock'] || + document['mozExitPointerLock'] || + document['webkitExitPointerLock'] || + document['msExitPointerLock'] || + function(){}; // no-op if function does not exist + canvas.exitPointerLock = canvas.exitPointerLock.bind(document); + + document.addEventListener('pointerlockchange', pointerLockChange, false); + document.addEventListener('mozpointerlockchange', pointerLockChange, false); + document.addEventListener('webkitpointerlockchange', pointerLockChange, false); + document.addEventListener('mspointerlockchange', pointerLockChange, false); + + if (Module['elementPointerLock']) { + canvas.addEventListener("click", function(ev) { + if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { + Module['canvas'].requestPointerLock(); + ev.preventDefault(); + } + }, false); + } + } + }, + + createContext: function(canvas, useWebGL, setInModule, webGLContextAttributes) { + if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas. + + var ctx; + var contextHandle; + if (useWebGL) { + // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. + var contextAttributes = { + antialias: false, + alpha: false + }; + + if (webGLContextAttributes) { + for (var attribute in webGLContextAttributes) { + contextAttributes[attribute] = webGLContextAttributes[attribute]; + } + } + + contextHandle = GL.createContext(canvas, contextAttributes); + if (contextHandle) { + ctx = GL.getContext(contextHandle).GLctx; + } + } else { + ctx = canvas.getContext('2d'); + } + + if (!ctx) return null; + + if (setInModule) { + if (!useWebGL) assert(typeof GLctx === 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); + + Module.ctx = ctx; + if (useWebGL) GL.makeContextCurrent(contextHandle); + Module.useWebGL = useWebGL; + Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() }); + Browser.init(); + } + return ctx; + }, + + destroyContext: function(canvas, useWebGL, setInModule) {}, + + fullscreenHandlersInstalled: false, + lockPointer: undefined, + resizeCanvas: undefined, + requestFullscreen: function(lockPointer, resizeCanvas, vrDevice) { + Browser.lockPointer = lockPointer; + Browser.resizeCanvas = resizeCanvas; + Browser.vrDevice = vrDevice; + if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true; + if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false; + if (typeof Browser.vrDevice === 'undefined') Browser.vrDevice = null; + + var canvas = Module['canvas']; + function fullscreenChange() { + Browser.isFullscreen = false; + var canvasContainer = canvas.parentNode; + if ((document['fullscreenElement'] || document['mozFullScreenElement'] || + document['msFullscreenElement'] || document['webkitFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvasContainer) { + canvas.exitFullscreen = document['exitFullscreen'] || + document['cancelFullScreen'] || + document['mozCancelFullScreen'] || + document['msExitFullscreen'] || + document['webkitCancelFullScreen'] || + function() {}; + canvas.exitFullscreen = canvas.exitFullscreen.bind(document); + if (Browser.lockPointer) canvas.requestPointerLock(); + Browser.isFullscreen = true; + if (Browser.resizeCanvas) { + Browser.setFullscreenCanvasSize(); + } else { + Browser.updateCanvasDimensions(canvas); + } + } else { + // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen + canvasContainer.parentNode.insertBefore(canvas, canvasContainer); + canvasContainer.parentNode.removeChild(canvasContainer); + + if (Browser.resizeCanvas) { + Browser.setWindowedCanvasSize(); + } else { + Browser.updateCanvasDimensions(canvas); + } + } + if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullscreen); + if (Module['onFullscreen']) Module['onFullscreen'](Browser.isFullscreen); + } + + if (!Browser.fullscreenHandlersInstalled) { + Browser.fullscreenHandlersInstalled = true; + document.addEventListener('fullscreenchange', fullscreenChange, false); + document.addEventListener('mozfullscreenchange', fullscreenChange, false); + document.addEventListener('webkitfullscreenchange', fullscreenChange, false); + document.addEventListener('MSFullscreenChange', fullscreenChange, false); + } + + // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root + var canvasContainer = document.createElement("div"); + canvas.parentNode.insertBefore(canvasContainer, canvas); + canvasContainer.appendChild(canvas); + + // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) + canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || + canvasContainer['mozRequestFullScreen'] || + canvasContainer['msRequestFullscreen'] || + (canvasContainer['webkitRequestFullscreen'] ? function() { canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null) || + (canvasContainer['webkitRequestFullScreen'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null); + + if (vrDevice) { + canvasContainer.requestFullscreen({ vrDisplay: vrDevice }); + } else { + canvasContainer.requestFullscreen(); + } + }, + + requestFullScreen: function(lockPointer, resizeCanvas, vrDevice) { + Module.printErr('Browser.requestFullScreen() is deprecated. Please call Browser.requestFullscreen instead.'); + Browser.requestFullScreen = function(lockPointer, resizeCanvas, vrDevice) { + return Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + } + return Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + }, + + nextRAF: 0, + + fakeRequestAnimationFrame: function(func) { + // try to keep 60fps between calls to here + var now = Date.now(); + if (Browser.nextRAF === 0) { + Browser.nextRAF = now + 1000/60; + } else { + while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 + Browser.nextRAF += 1000/60; + } + } + var delay = Math.max(Browser.nextRAF - now, 0); + setTimeout(func, delay); + }, + + requestAnimationFrame: function requestAnimationFrame(func) { + if (typeof window === 'undefined') { // Provide fallback to setTimeout if window is undefined (e.g. in Node.js) + Browser.fakeRequestAnimationFrame(func); + } else { + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = window['requestAnimationFrame'] || + window['mozRequestAnimationFrame'] || + window['webkitRequestAnimationFrame'] || + window['msRequestAnimationFrame'] || + window['oRequestAnimationFrame'] || + Browser.fakeRequestAnimationFrame; + } + window.requestAnimationFrame(func); + } + }, + + // generic abort-aware wrapper for an async callback + safeCallback: function(func) { + return function() { + if (!ABORT) return func.apply(null, arguments); + }; + }, + + // abort and pause-aware versions TODO: build main loop on top of this? + + allowAsyncCallbacks: true, + queuedAsyncCallbacks: [], + + pauseAsyncCallbacks: function() { + Browser.allowAsyncCallbacks = false; + }, + resumeAsyncCallbacks: function() { // marks future callbacks as ok to execute, and synchronously runs any remaining ones right now + Browser.allowAsyncCallbacks = true; + if (Browser.queuedAsyncCallbacks.length > 0) { + var callbacks = Browser.queuedAsyncCallbacks; + Browser.queuedAsyncCallbacks = []; + callbacks.forEach(function(func) { + func(); + }); + } + }, + + safeRequestAnimationFrame: function(func) { + return Browser.requestAnimationFrame(function() { + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } + }); + }, + safeSetTimeout: function(func, timeout) { + Module['noExitRuntime'] = true; + return setTimeout(function() { + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } + }, timeout); + }, + safeSetInterval: function(func, timeout) { + Module['noExitRuntime'] = true; + return setInterval(function() { + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } // drop it on the floor otherwise, next interval will kick in + }, timeout); + }, + + getMimetype: function(name) { + return { + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'png': 'image/png', + 'bmp': 'image/bmp', + 'ogg': 'audio/ogg', + 'wav': 'audio/wav', + 'mp3': 'audio/mpeg' + }[name.substr(name.lastIndexOf('.')+1)]; + }, + + getUserMedia: function(func) { + if(!window.getUserMedia) { + window.getUserMedia = navigator['getUserMedia'] || + navigator['mozGetUserMedia']; + } + window.getUserMedia(func); + }, + + + getMovementX: function(event) { + return event['movementX'] || + event['mozMovementX'] || + event['webkitMovementX'] || + 0; + }, + + getMovementY: function(event) { + return event['movementY'] || + event['mozMovementY'] || + event['webkitMovementY'] || + 0; + }, + + // Browsers specify wheel direction according to the page CSS pixel Y direction: + // Scrolling mouse wheel down (==towards user/away from screen) on Windows/Linux (and OSX without 'natural scroll' enabled) + // is the positive wheel direction. Scrolling mouse wheel up (towards the screen) is the negative wheel direction. + // This function returns the wheel direction in the browser page coordinate system (+: down, -: up). Note that this is often the + // opposite of native code: In native APIs the positive scroll direction is to scroll up (away from the user). + // NOTE: The mouse wheel delta is a decimal number, and can be a fractional value within -1 and 1. If you need to represent + // this as an integer, don't simply cast to int, or you may receive scroll events for wheel delta == 0. + getMouseWheelDelta: function(event) { + var delta = 0; + switch (event.type) { + case 'DOMMouseScroll': + delta = event.detail; + break; + case 'mousewheel': + delta = event.wheelDelta; + break; + case 'wheel': + delta = event['deltaY']; + break; + default: + throw 'unrecognized mouse wheel event: ' + event.type; + } + return delta; + }, + + mouseX: 0, + mouseY: 0, + mouseMovementX: 0, + mouseMovementY: 0, + touches: {}, + lastTouches: {}, + + calculateMouseEvent: function(event) { // event should be mousemove, mousedown or mouseup + if (Browser.pointerLock) { + // When the pointer is locked, calculate the coordinates + // based on the movement of the mouse. + // Workaround for Firefox bug 764498 + if (event.type != 'mousemove' && + ('mozMovementX' in event)) { + Browser.mouseMovementX = Browser.mouseMovementY = 0; + } else { + Browser.mouseMovementX = Browser.getMovementX(event); + Browser.mouseMovementY = Browser.getMovementY(event); + } + + // check if SDL is available + if (typeof SDL != "undefined") { + Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; + Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; + } else { + // just add the mouse delta to the current absolut mouse position + // FIXME: ideally this should be clamped against the canvas size and zero + Browser.mouseX += Browser.mouseMovementX; + Browser.mouseY += Browser.mouseMovementY; + } + } else { + // Otherwise, calculate the movement based on the changes + // in the coordinates. + var rect = Module["canvas"].getBoundingClientRect(); + var cw = Module["canvas"].width; + var ch = Module["canvas"].height; + + // Neither .scrollX or .pageXOffset are defined in a spec, but + // we prefer .scrollX because it is currently in a spec draft. + // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) + var scrollX = ((typeof window.scrollX !== 'undefined') ? window.scrollX : window.pageXOffset); + var scrollY = ((typeof window.scrollY !== 'undefined') ? window.scrollY : window.pageYOffset); +#if ASSERTIONS + // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset + // and we have no viable fallback. + assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); +#endif + + if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { + var touch = event.touch; + if (touch === undefined) { + return; // the "touch" property is only defined in SDL + + } + var adjustedX = touch.pageX - (scrollX + rect.left); + var adjustedY = touch.pageY - (scrollY + rect.top); + + adjustedX = adjustedX * (cw / rect.width); + adjustedY = adjustedY * (ch / rect.height); + + var coords = { x: adjustedX, y: adjustedY }; + + if (event.type === 'touchstart') { + Browser.lastTouches[touch.identifier] = coords; + Browser.touches[touch.identifier] = coords; + } else if (event.type === 'touchend' || event.type === 'touchmove') { + var last = Browser.touches[touch.identifier]; + if (!last) last = coords; + Browser.lastTouches[touch.identifier] = last; + Browser.touches[touch.identifier] = coords; + } + return; + } + + var x = event.pageX - (scrollX + rect.left); + var y = event.pageY - (scrollY + rect.top); + + // the canvas might be CSS-scaled compared to its backbuffer; + // SDL-using content will want mouse coordinates in terms + // of backbuffer units. + x = x * (cw / rect.width); + y = y * (ch / rect.height); + + Browser.mouseMovementX = x - Browser.mouseX; + Browser.mouseMovementY = y - Browser.mouseY; + Browser.mouseX = x; + Browser.mouseY = y; + } + }, + + asyncLoad: function(url, onload, onerror, noRunDep) { + var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : ''; + Module['readAsync'](url, function(arrayBuffer) { + assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); + onload(new Uint8Array(arrayBuffer)); + if (dep) removeRunDependency(dep); + }, function(event) { + if (onerror) { + onerror(); + } else { + throw 'Loading data file "' + url + '" failed.'; + } + }); + if (dep) addRunDependency(dep); + }, + + resizeListeners: [], + + updateResizeListeners: function() { + var canvas = Module['canvas']; + Browser.resizeListeners.forEach(function(listener) { + listener(canvas.width, canvas.height); + }); + }, + + setCanvasSize: function(width, height, noUpdates) { + var canvas = Module['canvas']; + Browser.updateCanvasDimensions(canvas, width, height); + if (!noUpdates) Browser.updateResizeListeners(); + }, + + windowedWidth: 0, + windowedHeight: 0, + setFullscreenCanvasSize: function() { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = {{{ makeGetValue('SDL.screen', '0', 'i32', 0, 1) }}}; + flags = flags | 0x00800000; // set SDL_FULLSCREEN flag + {{{ makeSetValue('SDL.screen', '0', 'flags', 'i32') }}} + } + Browser.updateCanvasDimensions(Module['canvas']); + Browser.updateResizeListeners(); + }, + + setWindowedCanvasSize: function() { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = {{{ makeGetValue('SDL.screen', '0', 'i32', 0, 1) }}}; + flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag + {{{ makeSetValue('SDL.screen', '0', 'flags', 'i32') }}} + } + Browser.updateCanvasDimensions(Module['canvas']); + Browser.updateResizeListeners(); + }, + + updateCanvasDimensions : function(canvas, wNative, hNative) { + if (wNative && hNative) { + canvas.widthNative = wNative; + canvas.heightNative = hNative; + } else { + wNative = canvas.widthNative; + hNative = canvas.heightNative; + } + var w = wNative; + var h = hNative; + if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) { + if (w/h < Module['forcedAspectRatio']) { + w = Math.round(h * Module['forcedAspectRatio']); + } else { + h = Math.round(w / Module['forcedAspectRatio']); + } + } + if (((document['fullscreenElement'] || document['mozFullScreenElement'] || + document['msFullscreenElement'] || document['webkitFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) { + var factor = Math.min(screen.width / w, screen.height / h); + w = Math.round(w * factor); + h = Math.round(h * factor); + } + if (Browser.resizeCanvas) { + if (canvas.width != w) canvas.width = w; + if (canvas.height != h) canvas.height = h; + if (typeof canvas.style != 'undefined') { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } else { + if (canvas.width != wNative) canvas.width = wNative; + if (canvas.height != hNative) canvas.height = hNative; + if (typeof canvas.style != 'undefined') { + if (w != wNative || h != hNative) { + canvas.style.setProperty( "width", w + "px", "important"); + canvas.style.setProperty("height", h + "px", "important"); + } else { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } + } + }, + + wgetRequests: {}, + nextWgetRequestHandle: 0, + + getNextWgetRequestHandle: function() { + var handle = Browser.nextWgetRequestHandle; + Browser.nextWgetRequestHandle++; + return handle; + } + }, + + emscripten_async_wget__deps: ['$PATH'], + emscripten_async_wget__proxy: 'sync', + emscripten_async_wget__sig: 'viiii', + emscripten_async_wget: function(url, file, onload, onerror) { + Module['noExitRuntime'] = true; + + var _url = Pointer_stringify(url); + var _file = Pointer_stringify(file); + _file = PATH.resolve(FS.cwd(), _file); + function doCallback(callback) { + if (callback) { + var stack = stackSave(); + Module['dynCall_vi'](callback, allocate(intArrayFromString(_file), 'i8', ALLOC_STACK)); + stackRestore(stack); + } + } + var destinationDirectory = PATH.dirname(_file); + FS.createPreloadedFile( + destinationDirectory, + PATH.basename(_file), + _url, true, true, + function() { + doCallback(onload); + }, + function() { + doCallback(onerror); + }, + false, // dontCreateFile + false, // canOwn + function() { // preFinish + // if a file exists there, we overwrite it + try { + FS.unlink(_file); + } catch (e) {} + // if the destination directory does not yet exist, create it + FS.mkdirTree(destinationDirectory); + } + ); + }, + + emscripten_async_wget_data__proxy: 'sync', + emscripten_async_wget_data__sig: 'viiii', + emscripten_async_wget_data: function(url, arg, onload, onerror) { + Browser.asyncLoad(Pointer_stringify(url), function(byteArray) { + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + Module['dynCall_viii'](onload, arg, buffer, byteArray.length); + _free(buffer); + }, function() { + if (onerror) Module['dynCall_vi'](onerror, arg); + }, true /* no need for run dependency, this is async but will not do any prepare etc. step */ ); + }, + + emscripten_async_wget2__proxy: 'sync', + emscripten_async_wget2__sig: 'iiiiiiiii', + emscripten_async_wget2: function(url, file, request, param, arg, onload, onerror, onprogress) { + Module['noExitRuntime'] = true; + + var _url = Pointer_stringify(url); + var _file = Pointer_stringify(file); + _file = PATH.resolve(FS.cwd(), _file); + var _request = Pointer_stringify(request); + var _param = Pointer_stringify(param); + var index = _file.lastIndexOf('/'); + + var http = new XMLHttpRequest(); + http.open(_request, _url, true); + http.responseType = 'arraybuffer'; + + var handle = Browser.getNextWgetRequestHandle(); + + var destinationDirectory = PATH.dirname(_file); + + // LOAD + http.onload = function http_onload(e) { + if (http.status == 200) { + // if a file exists there, we overwrite it + try { + FS.unlink(_file); + } catch (e) {} + // if the destination directory does not yet exist, create it + FS.mkdirTree(destinationDirectory); + + FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true, false); + if (onload) { + var stack = stackSave(); + Module['dynCall_viii'](onload, handle, arg, allocate(intArrayFromString(_file), 'i8', ALLOC_STACK)); + stackRestore(stack); + } + } else { + if (onerror) Module['dynCall_viii'](onerror, handle, arg, http.status); + } + + delete Browser.wgetRequests[handle]; + }; + + // ERROR + http.onerror = function http_onerror(e) { + if (onerror) Module['dynCall_viii'](onerror, handle, arg, http.status); + delete Browser.wgetRequests[handle]; + }; + + // PROGRESS + http.onprogress = function http_onprogress(e) { + if (e.lengthComputable || (e.lengthComputable === undefined && e.total != 0)) { + var percentComplete = (e.loaded / e.total)*100; + if (onprogress) Module['dynCall_viii'](onprogress, handle, arg, percentComplete); + } + }; + + // ABORT + http.onabort = function http_onabort(e) { + delete Browser.wgetRequests[handle]; + }; + + if (_request == "POST") { + //Send the proper header information along with the request + http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + http.send(_param); + } else { + http.send(null); + } + + Browser.wgetRequests[handle] = http; + + return handle; + }, + + emscripten_async_wget2_data__proxy: 'sync', + emscripten_async_wget2_data__sig: 'iiiiiiiii', + emscripten_async_wget2_data: function(url, request, param, arg, free, onload, onerror, onprogress) { + var _url = Pointer_stringify(url); + var _request = Pointer_stringify(request); + var _param = Pointer_stringify(param); + + var http = new XMLHttpRequest(); + http.open(_request, _url, true); + http.responseType = 'arraybuffer'; + + var handle = Browser.getNextWgetRequestHandle(); + + // LOAD + http.onload = function http_onload(e) { + if (http.status == 200 || _url.substr(0,4).toLowerCase() != "http") { + var byteArray = new Uint8Array(http.response); + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + if (onload) Module['dynCall_viiii'](onload, handle, arg, buffer, byteArray.length); + if (free) _free(buffer); + } else { + if (onerror) Module['dynCall_viiii'](onerror, handle, arg, http.status, http.statusText); + } + delete Browser.wgetRequests[handle]; + }; + + // ERROR + http.onerror = function http_onerror(e) { + if (onerror) { + Module['dynCall_viiii'](onerror, handle, arg, http.status, http.statusText); + } + delete Browser.wgetRequests[handle]; + }; + + // PROGRESS + http.onprogress = function http_onprogress(e) { + if (onprogress) Module['dynCall_viiii'](onprogress, handle, arg, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0); + }; + + // ABORT + http.onabort = function http_onabort(e) { + delete Browser.wgetRequests[handle]; + }; + + if (_request == "POST") { + //Send the proper header information along with the request + http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + http.send(_param); + } else { + http.send(null); + } + + Browser.wgetRequests[handle] = http; + + return handle; + }, + + emscripten_async_wget2_abort__proxy: 'sync', + emscripten_async_wget2_abort__sig: 'vi', + emscripten_async_wget2_abort: function(handle) { + var http = Browser.wgetRequests[handle]; + if (http) { + http.abort(); + } + }, + + emscripten_run_preload_plugins__deps: ['$PATH'], + emscripten_run_preload_plugins__proxy: 'sync', + emscripten_run_preload_plugins__sig: 'iiii', + emscripten_run_preload_plugins: function(file, onload, onerror) { + Module['noExitRuntime'] = true; + + var _file = Pointer_stringify(file); + var data = FS.analyzePath(_file); + if (!data.exists) return -1; + FS.createPreloadedFile( + PATH.dirname(_file), + PATH.basename(_file), + new Uint8Array(data.object.contents), true, true, + function() { + if (onload) Module['dynCall_vi'](onload, file); + }, + function() { + if (onerror) Module['dynCall_vi'](onerror, file); + }, + true // don'tCreateFile - it's already there + ); + return 0; + }, + + emscripten_run_preload_plugins_data__proxy: 'sync', + emscripten_run_preload_plugins_data__sig: 'viiiiii', + emscripten_run_preload_plugins_data: function(data, size, suffix, arg, onload, onerror) { + Module['noExitRuntime'] = true; + + var _suffix = Pointer_stringify(suffix); + if (!Browser.asyncPrepareDataCounter) Browser.asyncPrepareDataCounter = 0; + var name = 'prepare_data_' + (Browser.asyncPrepareDataCounter++) + '.' + _suffix; + var lengthAsUTF8 = lengthBytesUTF8(name); + var cname = _malloc(lengthAsUTF8+1); + stringToUTF8(name, cname, lengthAsUTF8+1); + FS.createPreloadedFile( + '/', + name, + {{{ makeHEAPView('U8', 'data', 'data + size') }}}, + true, true, + function() { + if (onload) Module['dynCall_vii'](onload, arg, cname); + }, + function() { + if (onerror) Module['dynCall_vi'](onerror, arg); + }, + true // don'tCreateFile - it's already there + ); + }, + + // Callable from pthread, executes in pthread context. + emscripten_async_run_script__deps: ['emscripten_run_script'], + emscripten_async_run_script: function(script, millis) { + Module['noExitRuntime'] = true; + + // TODO: cache these to avoid generating garbage + Browser.safeSetTimeout(function() { + _emscripten_run_script(script); + }, millis); + }, + + // TODO: currently not callable from a pthread, but immediately calls onerror() if not on main thread. + emscripten_async_load_script: function(url, onload, onerror) { + onload = getFuncWrapper(onload, 'v'); + onerror = getFuncWrapper(onerror, 'v'); + +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) { + console.error('emscripten_async_load_script("' + Pointer_stringify(url) + '") failed, emscripten_async_load_script is currently not available in pthreads!'); + return onerror ? onerror() : undefined; + } +#endif + Module['noExitRuntime'] = true; + + assert(runDependencies === 0, 'async_load_script must be run when no other dependencies are active'); + var script = document.createElement('script'); + if (onload) { + script.onload = function script_onload() { + if (runDependencies > 0) { + dependenciesFulfilled = onload; + } else { + onload(); + } + }; + } + if (onerror) script.onerror = onerror; + script.src = Pointer_stringify(url); + document.body.appendChild(script); + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_get_main_loop_timing: function(mode, value) { + if (mode) {{{ makeSetValue('mode', 0, 'Browser.mainLoop.timingMode', 'i32') }}}; + if (value) {{{ makeSetValue('value', 0, 'Browser.mainLoop.timingValue', 'i32') }}}; + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_set_main_loop_timing: function(mode, value) { + Browser.mainLoop.timingMode = mode; + Browser.mainLoop.timingValue = value; + + if (!Browser.mainLoop.func) { +#if ASSERTIONS + console.error('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); +#endif + return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. + } + + if (mode == 0 /*EM_TIMING_SETTIMEOUT*/) { + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() { + var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now())|0; + setTimeout(Browser.mainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop + }; + Browser.mainLoop.method = 'timeout'; + } else if (mode == 1 /*EM_TIMING_RAF*/) { + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() { + Browser.requestAnimationFrame(Browser.mainLoop.runner); + }; + Browser.mainLoop.method = 'rAF'; + } else if (mode == 2 /*EM_TIMING_SETIMMEDIATE*/) { + if (typeof setImmediate === 'undefined') { + // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) + var setImmediates = []; + var emscriptenMainLoopMessageId = 'setimmediate'; + function Browser_setImmediate_messageHandler(event) { + // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, + // so check for both cases. + if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) { + event.stopPropagation(); + setImmediates.shift()(); + } + } + addEventListener("message", Browser_setImmediate_messageHandler, true); + setImmediate = function Browser_emulated_setImmediate(func) { + setImmediates.push(func); + if (ENVIRONMENT_IS_WORKER) { + if (Module['setImmediates'] === undefined) Module['setImmediates'] = []; + Module['setImmediates'].push(func); + postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js + } else postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. + } + } + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() { + setImmediate(Browser.mainLoop.runner); + }; + Browser.mainLoop.method = 'immediate'; + } + return 0; + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_set_main_loop__deps: ['emscripten_set_main_loop_timing', 'emscripten_get_now'], + emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop, arg, noSetTiming) { + Module['noExitRuntime'] = true; + + assert(!Browser.mainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); + + Browser.mainLoop.func = func; + Browser.mainLoop.arg = arg; + + var browserIterationFunc; + if (typeof arg !== 'undefined') { + browserIterationFunc = function() { + Module['dynCall_vi'](func, arg); + }; + } else { + browserIterationFunc = function() { + Module['dynCall_v'](func); + }; + } + + var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop; + + Browser.mainLoop.runner = function Browser_mainLoop_runner() { + if (ABORT) return; + if (Browser.mainLoop.queue.length > 0) { + var start = Date.now(); + var blocker = Browser.mainLoop.queue.shift(); + blocker.func(blocker.arg); + if (Browser.mainLoop.remainingBlockers) { + var remaining = Browser.mainLoop.remainingBlockers; + var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); + if (blocker.counted) { + Browser.mainLoop.remainingBlockers = next; + } else { + // not counted, but move the progress along a tiny bit + next = next + 0.5; // do not steal all the next one's progress + Browser.mainLoop.remainingBlockers = (8*remaining + next)/9; + } + } + console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers); + Browser.mainLoop.updateStatus(); + + // catches pause/resume main loop from blocker execution + if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; + + setTimeout(Browser.mainLoop.runner, 0); + return; + } + + // catch pauses from non-main loop sources + if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; + + // Implement very basic swap interval control + Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0; + if (Browser.mainLoop.timingMode == 1/*EM_TIMING_RAF*/ && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) { + // Not the scheduled time to render this frame - skip. + Browser.mainLoop.scheduler(); + return; + } else if (Browser.mainLoop.timingMode == 0/*EM_TIMING_SETTIMEOUT*/) { + Browser.mainLoop.tickStartTime = _emscripten_get_now(); + } + + // Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize + // VBO double-buffering and reduce GPU stalls. +#if USES_GL_EMULATION + GL.newRenderingFrameStarted(); +#endif + +#if OFFSCREENCANVAS_SUPPORT + // If the current GL context is an OffscreenCanvas, but it was initialized with implicit swap mode, perform the swap + // in behalf of the user. + if (typeof GL !== 'undefined' && GL.currentContext && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) { + GL.currentContext.GLctx.commit(); + } +#endif + + if (Browser.mainLoop.method === 'timeout' && Module.ctx) { + Module.printErr('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); + Browser.mainLoop.method = ''; // just warn once per call to set main loop + } + + Browser.mainLoop.runIter(browserIterationFunc); + +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif + + // catch pauses from the main loop itself + if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; + + // Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able + // to queue the newest produced audio samples. + // TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData() + // do not need to be hardcoded into this function, but can be more generic. + if (typeof SDL === 'object' && SDL.audio && SDL.audio.queueNewAudioData) SDL.audio.queueNewAudioData(); + + Browser.mainLoop.scheduler(); + } + + if (!noSetTiming) { + if (fps && fps > 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 1000.0 / fps); + else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, 1); // Do rAF by rendering each frame (no decimating) + + Browser.mainLoop.scheduler(); + } + + if (simulateInfiniteLoop) { + throw 'SimulateInfiniteLoop'; + } + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_set_main_loop_arg__deps: ['emscripten_set_main_loop'], + emscripten_set_main_loop_arg: function(func, arg, fps, simulateInfiniteLoop) { + _emscripten_set_main_loop(func, fps, simulateInfiniteLoop, arg); + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_cancel_main_loop: function() { + Browser.mainLoop.pause(); + Browser.mainLoop.func = null; + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_pause_main_loop: function() { + Browser.mainLoop.pause(); + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_resume_main_loop: function() { + Browser.mainLoop.resume(); + }, + + // Runs natively in pthread, no __proxy needed. + _emscripten_push_main_loop_blocker: function(func, arg, name) { + Browser.mainLoop.queue.push({ func: function() { + Module['dynCall_vi'](func, arg); + }, name: Pointer_stringify(name), counted: true }); + Browser.mainLoop.updateStatus(); + }, + + // Runs natively in pthread, no __proxy needed. + _emscripten_push_uncounted_main_loop_blocker: function(func, arg, name) { + Browser.mainLoop.queue.push({ func: function() { + Module['dynCall_vi'](func, arg); + }, name: Pointer_stringify(name), counted: false }); + Browser.mainLoop.updateStatus(); + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_set_main_loop_expected_blockers: function(num) { + Browser.mainLoop.expectedBlockers = num; + Browser.mainLoop.remainingBlockers = num; + Browser.mainLoop.updateStatus(); + }, + + // Runs natively in pthread, no __proxy needed. + emscripten_async_call: function(func, arg, millis) { + Module['noExitRuntime'] = true; + + function wrapper() { + getFuncWrapper(func, 'vi')(arg); + } + + if (millis >= 0) { + Browser.safeSetTimeout(wrapper, millis); + } else { + Browser.safeRequestAnimationFrame(wrapper); + } + }, + + // Callable in pthread without __proxy needed. + emscripten_exit_with_live_runtime: function() { + Module['noExitRuntime'] = true; + throw 'SimulateInfiniteLoop'; + }, + + emscripten_force_exit__proxy: 'sync', + emscripten_force_exit__sig: 'vi', + emscripten_force_exit: function(status) { +#if NO_EXIT_RUNTIME +#if ASSERTIONS + warnOnce('emscripten_force_exit cannot actually shut down the runtime, as the build has NO_EXIT_RUNTIME set'); +#endif +#endif + Module['noExitRuntime'] = false; + Module['exit'](status); + }, + + emscripten_get_device_pixel_ratio__proxy: 'sync', + emscripten_get_device_pixel_ratio__sig: 'd', + emscripten_get_device_pixel_ratio: function() { + return window.devicePixelRatio || 1.0; + }, + + emscripten_hide_mouse__proxy: 'sync', + emscripten_hide_mouse__sig: 'v', + emscripten_hide_mouse: function() { + var styleSheet = document.styleSheets[0]; + var rules = styleSheet.cssRules; + for (var i = 0; i < rules.length; i++) { + if (rules[i].cssText.substr(0, 6) == 'canvas') { + styleSheet.deleteRule(i); + i--; + } + } + styleSheet.insertRule('canvas.emscripten { border: 1px solid black; cursor: none; }', 0); + }, + + emscripten_set_canvas_size__proxy: 'sync', + emscripten_set_canvas_size__sig: 'vii', + emscripten_set_canvas_size: function(width, height) { + Browser.setCanvasSize(width, height); + }, + + emscripten_get_canvas_size__proxy: 'sync', + emscripten_get_canvas_size__sig: 'viii', + emscripten_get_canvas_size: function(width, height, isFullscreen) { + var canvas = Module['canvas']; + {{{ makeSetValue('width', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('height', '0', 'canvas.height', 'i32') }}}; + {{{ makeSetValue('isFullscreen', '0', 'Browser.isFullscreen ? 1 : 0', 'i32') }}}; + }, + + // To avoid creating worker parent->child chains, always proxies to execute on the main thread. + emscripten_create_worker__proxy: 'sync', + emscripten_create_worker__sig: 'ii', + emscripten_create_worker: function(url) { + url = Pointer_stringify(url); + var id = Browser.workers.length; + var info = { + worker: new Worker(url), + callbacks: [], + awaited: 0, + buffer: 0, + bufferSize: 0 + }; + info.worker.onmessage = function info_worker_onmessage(msg) { + if (ABORT) return; + var info = Browser.workers[id]; + if (!info) return; // worker was destroyed meanwhile + var callbackId = msg.data['callbackId']; + var callbackInfo = info.callbacks[callbackId]; + if (!callbackInfo) return; // no callback or callback removed meanwhile + // Don't trash our callback state if we expect additional calls. + if (msg.data['finalResponse']) { + info.awaited--; + info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this + } + var data = msg.data['data']; + if (data) { + if (!data.byteLength) data = new Uint8Array(data); + if (!info.buffer || info.bufferSize < data.length) { + if (info.buffer) _free(info.buffer); + info.bufferSize = data.length; + info.buffer = _malloc(data.length); + } + HEAPU8.set(data, info.buffer); + callbackInfo.func(info.buffer, data.length, callbackInfo.arg); + } else { + callbackInfo.func(0, 0, callbackInfo.arg); + } + }; + Browser.workers.push(info); + return id; + }, + + emscripten_destroy_worker__proxy: 'sync', + emscripten_destroy_worker__sig: 'vi', + emscripten_destroy_worker: function(id) { + var info = Browser.workers[id]; + info.worker.terminate(); + if (info.buffer) _free(info.buffer); + Browser.workers[id] = null; + }, + + emscripten_call_worker__proxy: 'sync', + emscripten_call_worker__sig: 'viiiiii', + emscripten_call_worker: function(id, funcName, data, size, callback, arg) { + Module['noExitRuntime'] = true; // should we only do this if there is a callback? + + funcName = Pointer_stringify(funcName); + var info = Browser.workers[id]; + var callbackId = -1; + if (callback) { + callbackId = info.callbacks.length; + info.callbacks.push({ + func: getFuncWrapper(callback, 'viii'), + arg: arg + }); + info.awaited++; + } + var transferObject = { + 'funcName': funcName, + 'callbackId': callbackId, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 + }; + if (data) { + info.worker.postMessage(transferObject, [transferObject.data.buffer]); + } else { + info.worker.postMessage(transferObject); + } + }, + + emscripten_worker_respond_provisionally__proxy: 'sync', + emscripten_worker_respond_provisionally__sig: 'vii', + emscripten_worker_respond_provisionally: function(data, size) { + if (workerResponded) throw 'already responded with final response!'; + var transferObject = { + 'callbackId': workerCallbackId, + 'finalResponse': false, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 + }; + if (data) { + postMessage(transferObject, [transferObject.data.buffer]); + } else { + postMessage(transferObject); + } + }, + + emscripten_worker_respond__proxy: 'sync', + emscripten_worker_respond__sig: 'vii', + emscripten_worker_respond: function(data, size) { + if (workerResponded) throw 'already responded with final response!'; + workerResponded = true; + var transferObject = { + 'callbackId': workerCallbackId, + 'finalResponse': true, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 + }; + if (data) { + postMessage(transferObject, [transferObject.data.buffer]); + } else { + postMessage(transferObject); + } + }, + + emscripten_get_worker_queue_size__proxy: 'sync', + emscripten_get_worker_queue_size__sig: 'i', + emscripten_get_worker_queue_size: function(id) { + var info = Browser.workers[id]; + if (!info) return -1; + return info.awaited; + }, + + emscripten_get_preloaded_image_data__deps: ['$PATH'], + emscripten_get_preloaded_image_data__proxy: 'sync', + emscripten_get_preloaded_image_data__sig: 'iiii', + emscripten_get_preloaded_image_data: function(path, w, h) { + if (typeof path === "number") { + path = Pointer_stringify(path); + } + + path = PATH.resolve(path); + + var canvas = Module["preloadedImages"][path]; + if (canvas) { + var ctx = canvas.getContext("2d"); + var image = ctx.getImageData(0, 0, canvas.width, canvas.height); + var buf = _malloc(canvas.width * canvas.height * 4); + + HEAPU8.set(image.data, buf); + + {{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}}; + return buf; + } + + return 0; + }, + + emscripten_get_preloaded_image_data_from_FILE__deps: ['emscripten_get_preloaded_image_data'], + emscripten_get_preloaded_image_data_from_FILE__proxy: 'sync', + emscripten_get_preloaded_image_data_from_FILE__sig: 'iiii', + emscripten_get_preloaded_image_data_from_FILE: function(file, w, h) { + var fd = Module['_fileno'](file); + var stream = FS.getStream(fd); + if (stream) { + return _emscripten_get_preloaded_image_data(stream.path, w, h); + } + + return 0; + } +}; + +autoAddDeps(LibraryBrowser, '$Browser'); + +mergeInto(LibraryManager.library, LibraryBrowser); + +/* Useful stuff for browser debugging + +function slowLog(label, text) { + if (!slowLog.labels) slowLog.labels = {}; + if (!slowLog.labels[label]) slowLog.labels[label] = 0; + var now = Date.now(); + if (now - slowLog.labels[label] > 1000) { + Module.print(label + ': ' + text); + slowLog.labels[label] = now; + } +} + +*/ diff --git a/emsdk/files/preamble.js b/emsdk/files/preamble.js new file mode 100644 index 000000000..c7b1f596f --- /dev/null +++ b/emsdk/files/preamble.js @@ -0,0 +1,2510 @@ +// === Preamble library stuff === + +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html + +#if BENCHMARK +Module.realPrint = Module.print; +Module.print = Module.printErr = function(){}; +#endif + +#if SAFE_HEAP +function getSafeHeapType(bytes, isFloat) { + switch (bytes) { + case 1: return 'i8'; + case 2: return 'i16'; + case 4: return isFloat ? 'float' : 'i32'; + case 8: return 'double'; + default: assert(0); + } +} + +#if SAFE_HEAP_LOG +var SAFE_HEAP_COUNTER = 0; +#endif + +function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { +#if SAFE_HEAP_LOG + Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat, SAFE_HEAP_COUNTER++]); +#endif + if (dest <= 0) abort('segmentation fault storing ' + bytes + ' bytes to address ' + dest); + if (dest % bytes !== 0) abort('alignment error storing to address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes); + if (staticSealed) { + if (dest + bytes > HEAP32[DYNAMICTOP_PTR>>2]) abort('segmentation fault, exceeded the top of the available dynamic heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + HEAP32[DYNAMICTOP_PTR>>2]); + assert(DYNAMICTOP_PTR); + assert(HEAP32[DYNAMICTOP_PTR>>2] <= TOTAL_MEMORY); + } else { + if (dest + bytes > STATICTOP) abort('segmentation fault, exceeded the top of the available static heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP); + } + setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); +} +function SAFE_HEAP_STORE_D(dest, value, bytes) { + SAFE_HEAP_STORE(dest, value, bytes, true); +} + +function SAFE_HEAP_LOAD(dest, bytes, unsigned, isFloat) { + if (dest <= 0) abort('segmentation fault loading ' + bytes + ' bytes from address ' + dest); + if (dest % bytes !== 0) abort('alignment error loading from address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes); + if (staticSealed) { + if (dest + bytes > HEAP32[DYNAMICTOP_PTR>>2]) abort('segmentation fault, exceeded the top of the available dynamic heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + HEAP32[DYNAMICTOP_PTR>>2]); + assert(DYNAMICTOP_PTR); + assert(HEAP32[DYNAMICTOP_PTR>>2] <= TOTAL_MEMORY); + } else { + if (dest + bytes > STATICTOP) abort('segmentation fault, exceeded the top of the available static heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP); + } + var type = getSafeHeapType(bytes, isFloat); + var ret = getValue(dest, type, 1); + if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); +#if SAFE_HEAP_LOG + Module.print('SAFE_HEAP load: ' + [dest, ret, bytes, isFloat, unsigned, SAFE_HEAP_COUNTER++]); +#endif + return ret; +} +function SAFE_HEAP_LOAD_D(dest, bytes, unsigned) { + return SAFE_HEAP_LOAD(dest, bytes, unsigned, true); +} + +function SAFE_FT_MASK(value, mask) { + var ret = value & mask; + if (ret !== value) { + abort('Function table mask error: function pointer is ' + value + ' which is masked by ' + mask + ', the likely cause of this is that the function pointer is being called by the wrong type.'); + } + return ret; +} + +function segfault() { + abort('segmentation fault'); +} +function alignfault() { + abort('alignment fault'); +} +function ftfault() { + abort('Function table mask error'); +} +#endif + +//======================================== +// Runtime essentials +//======================================== + +var ABORT = 0; // whether we are quitting the application. no code should run after this. set in exit() and abort() +var EXITSTATUS = 0; + +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed: ' + text); + } +} + +var globalScope = this; + +// Returns the C function with a specified identifier (for C++, you need to do manual name mangling) +function getCFunc(ident) { + var func = Module['_' + ident]; // closure exported function + assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); + return func; +} + +var JSfuncs = { + // Helpers for cwrap -- it can't refer to Runtime directly because it might + // be renamed by closure, instead it calls JSfuncs['stackSave'].body to find + // out what the minified function name is. + 'stackSave': function() { + stackSave() + }, + 'stackRestore': function() { + stackRestore() + }, + // type conversion from js to c + 'arrayToC' : function(arr) { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + }, + 'stringToC' : function(str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { // null string + // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' + var len = (str.length << 2) + 1; + ret = stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + } +}; + +// For fast lookup of conversion functions +var toC = { + 'string': JSfuncs['stringToC'], 'array': JSfuncs['arrayToC'] +}; + +// C calling interface. +function ccall (ident, returnType, argTypes, args, opts) { + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; +#if ASSERTIONS + assert(returnType !== 'array', 'Return type should not be "array".'); +#endif + if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]]; + if (converter) { + if (stack === 0) stack = stackSave(); + cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + } + var ret = func.apply(null, cArgs); +#if ASSERTIONS +#if EMTERPRETIFY_ASYNC + if ((!opts || !opts.async) && typeof EmterpreterAsync === 'object') { + assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling ccall'); + } + if (opts && opts.async) assert(!returnType, 'async ccalls cannot return values'); +#endif +#endif + if (returnType === 'string') ret = Pointer_stringify(ret); + else if (returnType === 'boolean') ret = Boolean(ret); + if (stack !== 0) { +#if EMTERPRETIFY_ASYNC + if (opts && opts.async) { + EmterpreterAsync.asyncFinalizers.push(function() { + stackRestore(stack); + }); + return; + } +#endif + stackRestore(stack); + } + return ret; +} + +function cwrap (ident, returnType, argTypes) { + argTypes = argTypes || []; + var cfunc = getCFunc(ident); + // When the function takes numbers and returns a number, we can just return + // the original function + var numericArgs = argTypes.every(function(type){ return type === 'number'}); + var numericRet = returnType !== 'string'; + if (numericRet && numericArgs) { + return cfunc; + } + return function() { + return ccall(ident, returnType, argTypes, arguments); + } +} + +/** @type {function(number, number, string, boolean=)} */ +function setValue(ptr, value, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit +#if SAFE_HEAP + if (noSafe) { + switch(type) { + case 'i1': {{{ makeSetValue('ptr', '0', 'value', 'i1', undefined, undefined, undefined, '1') }}}; break; + case 'i8': {{{ makeSetValue('ptr', '0', 'value', 'i8', undefined, undefined, undefined, '1') }}}; break; + case 'i16': {{{ makeSetValue('ptr', '0', 'value', 'i16', undefined, undefined, undefined, '1') }}}; break; + case 'i32': {{{ makeSetValue('ptr', '0', 'value', 'i32', undefined, undefined, undefined, '1') }}}; break; + case 'i64': {{{ makeSetValue('ptr', '0', 'value', 'i64', undefined, undefined, undefined, '1') }}}; break; + case 'float': {{{ makeSetValue('ptr', '0', 'value', 'float', undefined, undefined, undefined, '1') }}}; break; + case 'double': {{{ makeSetValue('ptr', '0', 'value', 'double', undefined, undefined, undefined, '1') }}}; break; + default: abort('invalid type for setValue: ' + type); + } + } else { +#endif + switch(type) { + case 'i1': {{{ makeSetValue('ptr', '0', 'value', 'i1') }}}; break; + case 'i8': {{{ makeSetValue('ptr', '0', 'value', 'i8') }}}; break; + case 'i16': {{{ makeSetValue('ptr', '0', 'value', 'i16') }}}; break; + case 'i32': {{{ makeSetValue('ptr', '0', 'value', 'i32') }}}; break; + case 'i64': {{{ makeSetValue('ptr', '0', 'value', 'i64') }}}; break; + case 'float': {{{ makeSetValue('ptr', '0', 'value', 'float') }}}; break; + case 'double': {{{ makeSetValue('ptr', '0', 'value', 'double') }}}; break; + default: abort('invalid type for setValue: ' + type); + } +#if SAFE_HEAP + } +#endif +} + +/** @type {function(number, string, boolean=)} */ +function getValue(ptr, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit +#if SAFE_HEAP + if (noSafe) { + switch(type) { + case 'i1': return {{{ makeGetValue('ptr', '0', 'i1', undefined, undefined, undefined, undefined, '1') }}}; + case 'i8': return {{{ makeGetValue('ptr', '0', 'i8', undefined, undefined, undefined, undefined, '1') }}}; + case 'i16': return {{{ makeGetValue('ptr', '0', 'i16', undefined, undefined, undefined, undefined, '1') }}}; + case 'i32': return {{{ makeGetValue('ptr', '0', 'i32', undefined, undefined, undefined, undefined, '1') }}}; + case 'i64': return {{{ makeGetValue('ptr', '0', 'i64', undefined, undefined, undefined, undefined, '1') }}}; + case 'float': return {{{ makeGetValue('ptr', '0', 'float', undefined, undefined, undefined, undefined, '1') }}}; + case 'double': return {{{ makeGetValue('ptr', '0', 'double', undefined, undefined, undefined, undefined, '1') }}}; + default: abort('invalid type for getValue: ' + type); + } + } else { +#endif + switch(type) { + case 'i1': return {{{ makeGetValue('ptr', '0', 'i1') }}}; + case 'i8': return {{{ makeGetValue('ptr', '0', 'i8') }}}; + case 'i16': return {{{ makeGetValue('ptr', '0', 'i16') }}}; + case 'i32': return {{{ makeGetValue('ptr', '0', 'i32') }}}; + case 'i64': return {{{ makeGetValue('ptr', '0', 'i64') }}}; + case 'float': return {{{ makeGetValue('ptr', '0', 'float') }}}; + case 'double': return {{{ makeGetValue('ptr', '0', 'double') }}}; + default: abort('invalid type for getValue: ' + type); + } +#if SAFE_HEAP + } +#endif + return null; +} + +var ALLOC_NORMAL = 0; // Tries to use _malloc() +var ALLOC_STACK = 1; // Lives for the duration of the current function call +var ALLOC_STATIC = 2; // Cannot be freed +var ALLOC_DYNAMIC = 3; // Cannot be freed except through sbrk +var ALLOC_NONE = 4; // Do not allocate + +// allocate(): This is for internal use. You can use it yourself as well, but the interface +// is a little tricky (see docs right below). The reason is that it is optimized +// for multiple syntaxes to save space in generated code. So you should +// normally not use allocate(), and instead allocate memory using _malloc(), +// initialize it with setValue(), and so forth. +// @slab: An array of data, or a number. If a number, then the size of the block to allocate, +// in *bytes* (note that this is sometimes confusing: the next parameter does not +// affect this!) +// @types: Either an array of types, one for each byte (or 0 if no type at that position), +// or a single type which is used for the entire block. This only matters if there +// is initial data - if @slab is a number, then this does not matter at all and is +// ignored. +// @allocator: How to allocate memory, see ALLOC_* +/** @type {function((TypedArray|Array|number), string, number, number=)} */ +function allocate(slab, types, allocator, ptr) { + var zeroinit, size; + if (typeof slab === 'number') { + zeroinit = true; + size = slab; + } else { + zeroinit = false; + size = slab.length; + } + + var singleType = typeof types === 'string' ? types : null; + + var ret; + if (allocator == ALLOC_NONE) { + ret = ptr; + } else { + ret = [typeof _malloc === 'function' ? _malloc : staticAlloc, stackAlloc, staticAlloc, dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + } + + if (zeroinit) { + var stop; + ptr = ret; + assert((ret & 3) == 0); + stop = ret + (size & ~3); + for (; ptr < stop; ptr += 4) { + {{{ makeSetValue('ptr', '0', '0', 'i32', null, true) }}}; + } + stop = ret + size; + while (ptr < stop) { + {{{ makeSetValue('ptr++', '0', '0', 'i8', null, true) }}}; + } + return ret; + } + + if (singleType === 'i8') { + if (slab.subarray || slab.slice) { + HEAPU8.set(/** @type {!Uint8Array} */ (slab), ret); + } else { + HEAPU8.set(new Uint8Array(slab), ret); + } + return ret; + } + + var i = 0, type, typeSize, previousType; + while (i < size) { + var curr = slab[i]; + + type = singleType || types[i]; + if (type === 0) { + i++; + continue; + } +#if ASSERTIONS + assert(type, 'Must know what type to store in allocate!'); +#endif + + if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later + + setValue(ret+i, curr, type); + + // no need to look up size unless type changes, so cache it + if (previousType !== type) { + typeSize = getNativeTypeSize(type); + previousType = type; + } + i += typeSize; + } + + return ret; +} + +// Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready +function getMemory(size) { + if (!staticSealed) return staticAlloc(size); + if (!runtimeInitialized) return dynamicAlloc(size); + return _malloc(size); +} + +/** @type {function(number, number=)} */ +function Pointer_stringify(ptr, length) { + if (length === 0 || !ptr) return ''; + // TODO: use TextDecoder + // Find the length, and check for UTF while doing so + var hasUtf = 0; + var t; + var i = 0; + while (1) { +#if ASSERTIONS + assert(ptr + i < TOTAL_MEMORY); +#endif + t = {{{ makeGetValue('ptr', 'i', 'i8', 0, 1) }}}; + hasUtf |= t; + if (t == 0 && !length) break; + i++; + if (length && i == length) break; + } + if (!length) length = i; + + var ret = ''; + + if (hasUtf < 128) { + var MAX_CHUNK = 1024; // split up into chunks, because .apply on a huge string can overflow the stack + var curr; + while (length > 0) { + curr = String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK))); + ret = ret ? ret + curr : curr; + ptr += MAX_CHUNK; + length -= MAX_CHUNK; + } + return ret; + } + return UTF8ToString(ptr); +} + +// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +function AsciiToString(ptr) { + var str = ''; + while (1) { + var ch = {{{ makeGetValue('ptr++', 0, 'i8') }}}; + if (!ch) return str; + str += String.fromCharCode(ch); + } +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. + +function stringToAscii(str, outPtr) { + return writeAsciiToMemory(str, outPtr, false); +} + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns +// a copy of that string as a Javascript String object. + +#if TEXTDECODER +var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; +#endif +function UTF8ArrayToString(u8Array, idx) { +#if TEXTDECODER + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + while (u8Array[endPtr]) ++endPtr; + + if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) { + return UTF8Decoder.decode(u8Array.subarray(idx, endPtr)); + } else { +#endif + var u0, u1, u2, u3, u4, u5; + + var str = ''; + while (1) { + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + u0 = u8Array[idx++]; + if (!u0) return str; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + u1 = u8Array[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + u2 = u8Array[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u3 = u8Array[idx++] & 63; + if ((u0 & 0xF8) == 0xF0) { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | u3; + } else { + u4 = u8Array[idx++] & 63; + if ((u0 & 0xFC) == 0xF8) { + u0 = ((u0 & 3) << 24) | (u1 << 18) | (u2 << 12) | (u3 << 6) | u4; + } else { + u5 = u8Array[idx++] & 63; + u0 = ((u0 & 1) << 30) | (u1 << 24) | (u2 << 18) | (u3 << 12) | (u4 << 6) | u5; + } + } + } + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } +#if TEXTDECODER + } +#endif +} + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +function UTF8ToString(ptr) { + return UTF8ArrayToString({{{ heapAndOffset('HEAPU8', 'ptr') }}}); +} + +// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', +// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outU8Array: the array to copy to. Each index in this array is assumed to be one 8-byte element. +// outIdx: The starting offset in the array to begin the copying. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. +// maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + outU8Array[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + outU8Array[outIdx++] = 0xC0 | (u >> 6); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + outU8Array[outIdx++] = 0xE0 | (u >> 12); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x1FFFFF) { + if (outIdx + 3 >= endIdx) break; + outU8Array[outIdx++] = 0xF0 | (u >> 18); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x3FFFFFF) { + if (outIdx + 4 >= endIdx) break; + outU8Array[outIdx++] = 0xF8 | (u >> 24); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 5 >= endIdx) break; + outU8Array[outIdx++] = 0xFC | (u >> 30); + outU8Array[outIdx++] = 0x80 | ((u >> 24) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + outU8Array[outIdx] = 0; + return outIdx - startIdx; +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8(str, outPtr, maxBytesToWrite) { +#if ASSERTIONS + assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); +#endif + return stringToUTF8Array(str, {{{ heapAndOffset('HEAPU8', 'outPtr') }}}, maxBytesToWrite); +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) { + ++len; + } else if (u <= 0x7FF) { + len += 2; + } else if (u <= 0xFFFF) { + len += 3; + } else if (u <= 0x1FFFFF) { + len += 4; + } else if (u <= 0x3FFFFFF) { + len += 5; + } else { + len += 6; + } + } + return len; +} + +// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; +function UTF16ToString(ptr) { +#if ASSERTIONS + assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!'); +#endif +#if TEXTDECODER + var endPtr = ptr; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + var idx = endPtr >> 1; + while (HEAP16[idx]) ++idx; + endPtr = idx << 1; + + if (endPtr - ptr > 32 && UTF16Decoder) { + return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); + } else { +#endif + var i = 0; + + var str = ''; + while (1) { + var codeUnit = {{{ makeGetValue('ptr', 'i*2', 'i16') }}}; + if (codeUnit == 0) return str; + ++i; + // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. + str += String.fromCharCode(codeUnit); + } +#if TEXTDECODER + } +#endif +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. +// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. +// maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF16(str, outPtr, maxBytesToWrite) { +#if ASSERTIONS + assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!'); +#endif +#if ASSERTIONS + assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); +#endif + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 2) return 0; + maxBytesToWrite -= 2; // Null terminator. + var startPtr = outPtr; + var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; + for (var i = 0; i < numCharsToWrite; ++i) { + // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + {{{ makeSetValue('outPtr', 0, 'codeUnit', 'i16') }}}; + outPtr += 2; + } + // Null-terminate the pointer to the HEAP. + {{{ makeSetValue('outPtr', 0, 0, 'i16') }}}; + return outPtr - startPtr; +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF16(str) { + return str.length*2; +} + +function UTF32ToString(ptr) { +#if ASSERTIONS + assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!'); +#endif + var i = 0; + + var str = ''; + while (1) { + var utf32 = {{{ makeGetValue('ptr', 'i*4', 'i32') }}}; + if (utf32 == 0) + return str; + ++i; + // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + if (utf32 >= 0x10000) { + var ch = utf32 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } else { + str += String.fromCharCode(utf32); + } + } +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. +// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. +// maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF32(str, outPtr, maxBytesToWrite) { +#if ASSERTIONS + assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!'); +#endif +#if ASSERTIONS + assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); +#endif + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 4) return 0; + var startPtr = outPtr; + var endPtr = startPtr + maxBytesToWrite - 4; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { + var trailSurrogate = str.charCodeAt(++i); + codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); + } + {{{ makeSetValue('outPtr', 0, 'codeUnit', 'i32') }}}; + outPtr += 4; + if (outPtr + 4 > endPtr) break; + } + // Null-terminate the pointer to the HEAP. + {{{ makeSetValue('outPtr', 0, 0, 'i32') }}}; + return outPtr - startPtr; +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF32(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. + len += 4; + } + + return len; +} + +// Allocate heap space for a JS string, and write it there. +// It is the responsibility of the caller to free() that memory. +function allocateUTF8(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = _malloc(size); + if (ret) stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} + +// Allocate stack space for a JS string, and write it there. +function allocateUTF8OnStack(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} + +function demangle(func) { +#if DEMANGLE_SUPPORT + var __cxa_demangle_func = Module['___cxa_demangle'] || Module['__cxa_demangle']; + assert(__cxa_demangle_func); + try { + var s = +#if WASM_BACKEND + func; +#else + func.substr(1); +#endif + var len = lengthBytesUTF8(s)+1; + var buf = _malloc(len); + stringToUTF8(s, buf, len); + var status = _malloc(4); + var ret = __cxa_demangle_func(buf, 0, 0, status); + if ({{{ makeGetValue('status', '0', 'i32') }}} === 0 && ret) { + return Pointer_stringify(ret); + } + // otherwise, libcxxabi failed + } catch(e) { + // ignore problems here + } finally { + if (buf) _free(buf); + if (status) _free(status); + if (ret) _free(ret); + } + // failure when using libcxxabi, don't demangle + return func; +#else // DEMANGLE_SUPPORT +#if ASSERTIONS + warnOnce('warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling'); +#endif // ASSERTIONS + return func; +#endif // DEMANGLE_SUPPORT +} + +function demangleAll(text) { + var regex = +#if WASM_BACKEND + /_Z[\w\d_]+/g; +#else + /__Z[\w\d_]+/g; +#endif + return text.replace(regex, + function(x) { + var y = demangle(x); + return x === y ? x : (x + ' [' + y + ']'); + }); +} + +function jsStackTrace() { + var err = new Error(); + if (!err.stack) { + // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, + // so try that as a special-case. + try { + throw new Error(0); + } catch(e) { + err = e; + } + if (!err.stack) { + return '(no stack trace available)'; + } + } + return err.stack.toString(); +} + +function stackTrace() { + var js = jsStackTrace(); + if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); + return demangleAll(js); +} + +// Memory management + +var PAGE_SIZE = 16384; +var WASM_PAGE_SIZE = 65536; +var ASMJS_PAGE_SIZE = 16777216; +var MIN_TOTAL_MEMORY = 16777216; + +function alignUp(x, multiple) { + if (x % multiple > 0) { + x += multiple - (x % multiple); + } + return x; +} + +var HEAP, +/** @type {ArrayBuffer} */ + buffer, +/** @type {Int8Array} */ + HEAP8, +/** @type {Uint8Array} */ + HEAPU8, +/** @type {Int16Array} */ + HEAP16, +/** @type {Uint16Array} */ + HEAPU16, +/** @type {Int32Array} */ + HEAP32, +/** @type {Uint32Array} */ + HEAPU32, +/** @type {Float32Array} */ + HEAPF32, +/** @type {Float64Array} */ + HEAPF64; + +function updateGlobalBuffer(buf) { + Module['buffer'] = buffer = buf; +} + +function updateGlobalBufferViews() { + Module['HEAP8'] = HEAP8 = new Int8Array(buffer); + Module['HEAP16'] = HEAP16 = new Int16Array(buffer); + Module['HEAP32'] = HEAP32 = new Int32Array(buffer); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(buffer); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(buffer); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(buffer); + Module['HEAPF32'] = HEAPF32 = new Float32Array(buffer); + Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer); +} + +var STATIC_BASE, STATICTOP, staticSealed; // static area +var STACK_BASE, STACKTOP, STACK_MAX; // stack area +var DYNAMIC_BASE, DYNAMICTOP_PTR; // dynamic area handled by sbrk + +#if USE_PTHREADS +if (!ENVIRONMENT_IS_PTHREAD) { // Pthreads have already initialized these variables in src/pthread-main.js, where they were passed to the thread worker at startup time +#endif + STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0; + staticSealed = false; +#if USE_PTHREADS +} +#endif + +#if USE_PTHREADS +if (ENVIRONMENT_IS_PTHREAD) { + staticSealed = true; // The static memory area has been initialized already in the main thread, pthreads skip this. +#if SEPARATE_ASM != 0 + importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js +#endif +} +#endif + +#if STACK_OVERFLOW_CHECK +// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. +function writeStackCookie() { + assert((STACK_MAX & 3) == 0); + HEAPU32[(STACK_MAX >> 2)-1] = 0x02135467; + HEAPU32[(STACK_MAX >> 2)-2] = 0x89BACDFE; +} + +function checkStackCookie() { + if (HEAPU32[(STACK_MAX >> 2)-1] != 0x02135467 || HEAPU32[(STACK_MAX >> 2)-2] != 0x89BACDFE) { + abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x02135467, but received 0x' + HEAPU32[(STACK_MAX >> 2)-2].toString(16) + ' ' + HEAPU32[(STACK_MAX >> 2)-1].toString(16)); + } +#if !SAFE_SPLIT_MEMORY + // Also test the global address 0 for integrity. This check is not compatible with SAFE_SPLIT_MEMORY though, since that mode already tests all address 0 accesses on its own. + if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) throw 'Runtime error: The application has corrupted its heap memory area (address zero)!'; +#endif +} + +function abortStackOverflow(allocSize) { + abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - stackSave() + allocSize) + ' bytes available!'); +} +#endif + +#if ABORTING_MALLOC +function abortOnCannotGrowMemory() { +#if WASM + abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 '); +#else + abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 '); +#endif +} +#endif + +#if ALLOW_MEMORY_GROWTH +if (!Module['reallocBuffer']) Module['reallocBuffer'] = function(size) { + var ret; + try { + if (ArrayBuffer.transfer) { + ret = ArrayBuffer.transfer(buffer, size); + } else { + var oldHEAP8 = HEAP8; + ret = new ArrayBuffer(size); + var temp = new Int8Array(ret); + temp.set(oldHEAP8); + } + } catch(e) { + return false; + } + var success = _emscripten_replace_memory(ret); + if (!success) return false; + return ret; +}; +#endif + +function enlargeMemory() { +#if USE_PTHREADS + abort('Cannot enlarge memory arrays, since compiling with pthreads support enabled (-s USE_PTHREADS=1).'); +#else +#if ALLOW_MEMORY_GROWTH == 0 +#if ABORTING_MALLOC + abortOnCannotGrowMemory(); +#else + return false; // malloc will report failure +#endif +#else + // TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top. +#if ASSERTIONS + assert(HEAP32[DYNAMICTOP_PTR>>2] > TOTAL_MEMORY); // This function should only ever be called after the ceiling of the dynamic heap has already been bumped to exceed the current total size of the asm.js heap. +#endif + +#if EMSCRIPTEN_TRACING + // Report old layout one last time + _emscripten_trace_report_memory_layout(); +#endif + + var PAGE_MULTIPLE = Module["usingWasm"] ? WASM_PAGE_SIZE : ASMJS_PAGE_SIZE; // In wasm, heap size must be a multiple of 64KB. In asm.js, they need to be multiples of 16MB. + var LIMIT = 2147483648 - PAGE_MULTIPLE; // We can do one page short of 2GB as theoretical maximum. + + if (HEAP32[DYNAMICTOP_PTR>>2] > LIMIT) { +#if ASSERTIONS + Module.printErr('Cannot enlarge memory, asked to go up to ' + HEAP32[DYNAMICTOP_PTR>>2] + ' bytes, but the limit is ' + LIMIT + ' bytes!'); +#endif + return false; + } + + var OLD_TOTAL_MEMORY = TOTAL_MEMORY; + TOTAL_MEMORY = Math.max(TOTAL_MEMORY, MIN_TOTAL_MEMORY); // So the loop below will not be infinite, and minimum asm.js memory size is 16MB. + + while (TOTAL_MEMORY < HEAP32[DYNAMICTOP_PTR>>2]) { // Keep incrementing the heap size as long as it's less than what is requested. + if (TOTAL_MEMORY <= 536870912) { + TOTAL_MEMORY = alignUp(2 * TOTAL_MEMORY, PAGE_MULTIPLE); // Simple heuristic: double until 1GB... + } else { + // ..., but after that, add smaller increments towards 2GB, which we cannot reach + TOTAL_MEMORY = Math.min(alignUp((3 * TOTAL_MEMORY + 2147483648) / 4, PAGE_MULTIPLE), LIMIT); +#if ASSERTIONS + if (TOTAL_MEMORY === OLD_TOTAL_MEMORY) { + warnOnce('Cannot ask for more memory since we reached the practical limit in browsers (which is just below 2GB), so the request would have failed. Requesting only ' + TOTAL_MEMORY); + } +#endif + } + } + +#if ASSERTIONS + var start = Date.now(); +#endif + + var replacement = Module['reallocBuffer'](TOTAL_MEMORY); + if (!replacement || replacement.byteLength != TOTAL_MEMORY) { +#if ASSERTIONS + Module.printErr('Failed to grow the heap from ' + OLD_TOTAL_MEMORY + ' bytes to ' + TOTAL_MEMORY + ' bytes, not enough memory!'); + if (replacement) { + Module.printErr('Expected to get back a buffer of size ' + TOTAL_MEMORY + ' bytes, but instead got back a buffer of size ' + replacement.byteLength); + } +#endif + // restore the state to before this call, we failed + TOTAL_MEMORY = OLD_TOTAL_MEMORY; + return false; + } + + // everything worked + + updateGlobalBuffer(replacement); + updateGlobalBufferViews(); + +#if ASSERTIONS + if (!Module["usingWasm"]) { + Module.printErr('Warning: Enlarging memory arrays, this is not fast! ' + [OLD_TOTAL_MEMORY, TOTAL_MEMORY]); + } +#endif + +#if EMSCRIPTEN_TRACING + _emscripten_trace_js_log_message("Emscripten", "Enlarging memory arrays from " + OLD_TOTAL_MEMORY + " to " + TOTAL_MEMORY); + // And now report the new layout + _emscripten_trace_report_memory_layout(); +#endif + + return true; +#endif // ALLOW_MEMORY_GROWTH +#endif // USE_PTHREADS +} + +#if ALLOW_MEMORY_GROWTH +var byteLength; +try { + byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get); + byteLength(new ArrayBuffer(4)); // can fail on older ie +} catch(e) { // can fail on older node/v8 + byteLength = function(buffer) { return buffer.byteLength; }; +} +#endif + +var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}}; +var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}}; +if (TOTAL_MEMORY < TOTAL_STACK) Module.printErr('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); + +// Initialize the runtime's memory +#if ASSERTIONS +// check for full engine support (use string 'subarray' to avoid closure compiler confusion) +assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined, + 'JS engine does not provide full typed array support'); +#endif + +#if IN_TEST_HARNESS + +// Test runs in browsers should always be free from uncaught exceptions. If an uncaught exception is thrown, we fail browser test execution in the REPORT_RESULT() macro to output an error value. +if (ENVIRONMENT_IS_WEB) { + window.addEventListener('error', function(e) { + if (e.message.indexOf('SimulateInfiniteLoop') != -1) return; + console.error('Page threw an exception ' + e); + Module['pageThrewException'] = true; + }); +} + +#if USE_PTHREADS == 1 +if (typeof SharedArrayBuffer === 'undefined' || typeof Atomics === 'undefined') { + xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://localhost:8888/report_result?skipped:%20SharedArrayBuffer%20is%20not%20supported!'); + xhr.send(); + setTimeout(function() { window.close() }, 2000); +} +#endif +#endif + +#if USE_PTHREADS +#if !WASM +if (typeof SharedArrayBuffer !== 'undefined') { + if (!ENVIRONMENT_IS_PTHREAD) buffer = new SharedArrayBuffer(TOTAL_MEMORY); + // Currently SharedArrayBuffer does not have a slice() operation, so polyfill it in. + // Adapted from https://github.com/ttaubert/node-arraybuffer-slice, (c) 2014 Tim Taubert + // arraybuffer-slice may be freely distributed under the MIT license. + (function (undefined) { + "use strict"; + function clamp(val, length) { + val = (val|0) || 0; + if (val < 0) return Math.max(val + length, 0); + return Math.min(val, length); + } + if (typeof SharedArrayBuffer !== 'undefined' && !SharedArrayBuffer.prototype.slice) { + SharedArrayBuffer.prototype.slice = function (from, to) { + var length = this.byteLength; + var begin = clamp(from, length); + var end = length; + if (to !== undefined) end = clamp(to, length); + if (begin > end) return new ArrayBuffer(0); + var num = end - begin; + var target = new ArrayBuffer(num); + var targetArray = new Uint8Array(target); + var sourceArray = new Uint8Array(this, begin, num); + targetArray.set(sourceArray); + return target; + }; + } + })(); +} else { + if (!ENVIRONMENT_IS_PTHREAD) buffer = new ArrayBuffer(TOTAL_MEMORY); +} +updateGlobalBufferViews(); + +if (typeof Atomics === 'undefined') { + // Polyfill singlethreaded atomics ops from http://lars-t-hansen.github.io/ecmascript_sharedmem/shmem.html#Atomics.add + // No thread-safety needed since we don't have multithreading support. + Atomics = {}; + Atomics['add'] = function(t, i, v) { var w = t[i]; t[i] += v; return w; } + Atomics['and'] = function(t, i, v) { var w = t[i]; t[i] &= v; return w; } + Atomics['compareExchange'] = function(t, i, e, r) { var w = t[i]; if (w == e) t[i] = r; return w; } + Atomics['exchange'] = function(t, i, v) { var w = t[i]; t[i] = v; return w; } + Atomics['wait'] = function(t, i, v, o) { if (t[i] != v) return 'not-equal'; else return 'timed-out'; } + Atomics['wake'] = function(t, i, c) { return 0; } + Atomics['wakeOrRequeue'] = function(t, i1, c, i2, v) { return 0; } + Atomics['isLockFree'] = function(s) { return true; } + Atomics['load'] = function(t, i) { return t[i]; } + Atomics['or'] = function(t, i, v) { var w = t[i]; t[i] |= v; return w; } + Atomics['store'] = function(t, i, v) { t[i] = v; return v; } + Atomics['sub'] = function(t, i, v) { var w = t[i]; t[i] -= v; return w; } + Atomics['xor'] = function(t, i, v) { var w = t[i]; t[i] ^= v; return w; } +} + +#else +if (!ENVIRONMENT_IS_PTHREAD) { +#if ALLOW_MEMORY_GROWTH + Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE , 'maximum': {{{ WASM_MEM_MAX }}} / WASM_PAGE_SIZE, 'shared': true }); +#else + Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE , 'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE, 'shared': true }); +#endif + buffer = Module['wasmMemory'].buffer; +} + +updateGlobalBufferViews(); +#endif // !WASM +#else // USE_PTHREADS + +#if SPLIT_MEMORY == 0 +// Use a provided buffer, if there is one, or else allocate a new one +if (Module['buffer']) { + buffer = Module['buffer']; +#if ASSERTIONS + assert(buffer.byteLength === TOTAL_MEMORY, 'provided buffer should be ' + TOTAL_MEMORY + ' bytes, but it is ' + buffer.byteLength); +#endif +} else { + // Use a WebAssembly memory where available +#if WASM + if (typeof WebAssembly === 'object' && typeof WebAssembly.Memory === 'function') { +#if ASSERTIONS + assert(TOTAL_MEMORY % WASM_PAGE_SIZE === 0); +#endif // ASSERTIONS +#if ALLOW_MEMORY_GROWTH +#if WASM_MEM_MAX +#if ASSERTIONS + assert({{{ WASM_MEM_MAX }}} % WASM_PAGE_SIZE == 0); +#endif + Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE, 'maximum': {{{ WASM_MEM_MAX }}} / WASM_PAGE_SIZE }); +#else + Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE }); +#endif // BINARYEN_MEM_MAX +#else + Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE, 'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE }); +#endif // ALLOW_MEMORY_GROWTH + buffer = Module['wasmMemory'].buffer; + } else +#endif // WASM + { + buffer = new ArrayBuffer(TOTAL_MEMORY); + } +#if ASSERTIONS + assert(buffer.byteLength === TOTAL_MEMORY); +#endif // ASSERTIONS + Module['buffer'] = buffer; +} +updateGlobalBufferViews(); +#else // SPLIT_MEMORY +// make sure total memory is a multiple of the split memory size +var SPLIT_MEMORY = {{{ SPLIT_MEMORY }}}; +var SPLIT_MEMORY_MASK = SPLIT_MEMORY - 1; +var SPLIT_MEMORY_BITS = -1; +var ALLOW_MEMORY_GROWTH = {{{ ALLOW_MEMORY_GROWTH }}}; +var ABORTING_MALLOC = {{{ ABORTING_MALLOC }}}; + +Module['SPLIT_MEMORY'] = SPLIT_MEMORY; + +totalMemory = TOTAL_MEMORY; +if (totalMemory % SPLIT_MEMORY) { + totalMemory += SPLIT_MEMORY - (totalMemory % SPLIT_MEMORY); +} +if (totalMemory === SPLIT_MEMORY) totalMemory *= 2; +if (totalMemory !== TOTAL_MEMORY) { + TOTAL_MEMORY = totalMemory; +#if ASSERTIONS == 2 + Module.printErr('increasing TOTAL_MEMORY to ' + TOTAL_MEMORY + ' to be a multiple>1 of the split memory size ' + SPLIT_MEMORY + ')'); +#endif +} + +var buffers = [], HEAP8s = [], HEAP16s = [], HEAP32s = [], HEAPU8s = [], HEAPU16s = [], HEAPU32s = [], HEAPF32s = [], HEAPF64s = []; + +// Allocates a split chunk, a range of memory of size SPLIT_MEMORY. Generally data is not provided, and a new +// buffer is allocated, this is what happens when malloc works. However, you can provide your own buffer, +// which then lets you access it at address [ i*SPLIT_MEMORY, (i+1)*SPLIT_MEMORY ). +// The function returns true if it succeeds. It can also throw an exception if no data is provided and +// the browser fails to allocate the buffer. +function allocateSplitChunk(i, data) { + if (buffers[i]) return false; // already taken + // any of these allocations might fail; do them all before writing anything to global state + var currBuffer = data ? data : new ArrayBuffer(SPLIT_MEMORY); +#if ASSERTIONS + assert(currBuffer instanceof ArrayBuffer); +#endif + var currHEAP8s = new Int8Array(currBuffer); + var currHEAP16s = new Int16Array(currBuffer); + var currHEAP32s = new Int32Array(currBuffer); + var currHEAPU8s = new Uint8Array(currBuffer); + var currHEAPU16s = new Uint16Array(currBuffer); + var currHEAPU32s = new Uint32Array(currBuffer); + var currHEAPF32s = new Float32Array(currBuffer); + var currHEAPF64s = new Float64Array(currBuffer); + buffers[i] = currBuffer; + HEAP8s[i] = currHEAP8s; + HEAP16s[i] = currHEAP16s; + HEAP32s[i] = currHEAP32s; + HEAPU8s[i] = currHEAPU8s; + HEAPU16s[i] = currHEAPU16s; + HEAPU32s[i] = currHEAPU32s; + HEAPF32s[i] = currHEAPF32s; + HEAPF64s[i] = currHEAPF64s; + return true; +} +function freeSplitChunk(i) { +#if ASSERTIONS + assert(buffers[i] && HEAP8s[i]); + assert(i > 0); // cannot free the first chunk +#endif + buffers[i] = HEAP8s[i] = HEAP16s[i] = HEAP32s[i] = HEAPU8s[i] = HEAPU16s[i] = HEAPU32s[i] = HEAPF32s[i] = HEAPF64s[i] = null; +} + +(function() { + for (var i = 0; i < TOTAL_MEMORY / SPLIT_MEMORY; i++) { + buffers[i] = HEAP8s[i] = HEAP16s[i] = HEAP32s[i] = HEAPU8s[i] = HEAPU16s[i] = HEAPU32s[i] = HEAPF32s[i] = HEAPF64s[i] = null; + } + + var temp = SPLIT_MEMORY; + while (temp) { + temp >>= 1; + SPLIT_MEMORY_BITS++; + } + + allocateSplitChunk(0); // first chunk is for core runtime, static, stack, etc., always must be initialized + + // support HEAP8.subarray etc. + var SHIFT_TABLE = [0, 0, 1, 0, 2, 0, 0, 0, 3]; + function fake(real) { + var bytes = real[0].BYTES_PER_ELEMENT; + var shifts = SHIFT_TABLE[bytes]; +#if ASSERTIONS + assert(shifts > 0 || bytes == 1); +#endif + var that = { + BYTES_PER_ELEMENT: bytes, + set: function(array, offset) { + if (offset === undefined) offset = 0; + // potentially split over multiple chunks + while (array.length > 0) { + var chunk = offset >> SPLIT_MEMORY_BITS; + var relative = offset & SPLIT_MEMORY_MASK; + if (relative + (array.length << shifts) < SPLIT_MEMORY) { + real[chunk].set(array, relative); // all fits in this chunk + break; + } else { + var currSize = SPLIT_MEMORY - relative; +#if ASSERTIONS + assert(currSize % that.BYTES_PER_ELEMENT === 0); +#endif + var lastIndex = currSize >> shifts; + real[chunk].set(array.subarray(0, lastIndex), relative); + // increments + array = array.subarray(lastIndex); + offset += currSize; + } + } + }, + subarray: function(from, to) { + from = from << shifts; + var start = from >> SPLIT_MEMORY_BITS; + if (to === undefined) { + to = (start + 1) << SPLIT_MEMORY_BITS; + } else { + to = to << shifts; + } + to = Math.max(from, to); // if to is smaller, we'll get nothing anyway, same as to == from + if (from < to) { + var end = (to - 1) >> SPLIT_MEMORY_BITS; // -1, since we do not actually read the last address +#if ASSERTIONS + assert(start === end, 'subarray cannot span split chunks'); +#endif + } + if (to > from && (to & SPLIT_MEMORY_MASK) == 0) { + // avoid the mask on the next line giving 0 for the end + return real[start].subarray((from & SPLIT_MEMORY_MASK) >> shifts); // just return to the end of the chunk + } + return real[start].subarray((from & SPLIT_MEMORY_MASK) >> shifts, (to & SPLIT_MEMORY_MASK) >> shifts); + }, + buffer: { + slice: function(from, to) { +#if ASSERTIONS + assert(to, 'TODO: this is an actual copy, so we could support a slice across multiple chunks'); +#endif + return new Uint8Array(HEAPU8.subarray(from, to)).buffer; + }, + }, + }; + return that; + } + HEAP8 = fake(HEAP8s); + HEAP16 = fake(HEAP16s); + HEAP32 = fake(HEAP32s); + HEAPU8 = fake(HEAPU8s); + HEAPU16 = fake(HEAPU16s); + HEAPU32 = fake(HEAPU32s); + HEAPF32 = fake(HEAPF32s); + HEAPF64 = fake(HEAPF64s); +})(); + +#if SAFE_SPLIT_MEMORY +function checkPtr(ptr, shifts) { + if (ptr <= 0) abort('segmentation fault storing to address ' + ptr); + if (ptr !== ((ptr >> shifts) << shifts)) abort('alignment error storing to address ' + ptr + ', which was expected to be aligned to a shift of ' + shifts); + if ((ptr >> SPLIT_MEMORY_BITS) !== (ptr + Math.pow(2, shifts) - 1 >> SPLIT_MEMORY_BITS)) abort('segmentation fault, write spans split chunks ' + [ptr, shifts]); +} +#endif + +function get8(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 0); +#endif + return HEAP8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] | 0; +} +function get16(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 1); +#endif + return HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; +} +function get32(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 2); +#endif + return HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] | 0; +} +function getU8(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 0); +#endif + return HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] | 0; +} +function getU16(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 1); +#endif + return HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] | 0; +} +function getU32(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 2); +#endif + return HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] >>> 0; +} +function getF32(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 2); +#endif + return +HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2]; +} +function getF64(ptr) { + ptr = ptr | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 3); +#endif + return +HEAPF64s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 3]; +} +function set8(ptr, value) { + ptr = ptr | 0; + value = value | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 0); +#endif + HEAP8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] = value; +} +function set16(ptr, value) { + ptr = ptr | 0; + value = value | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 1); +#endif + HEAP16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] = value; +} +function set32(ptr, value) { + ptr = ptr | 0; + value = value | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 2); +#endif + HEAP32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; +} +function setU8(ptr, value) { + ptr = ptr | 0; + value = value | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 0); +#endif + HEAPU8s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 0] = value; +} +function setU16(ptr, value) { + ptr = ptr | 0; + value = value | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 1); +#endif + HEAPU16s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 1] = value; +} +function setU32(ptr, value) { + ptr = ptr | 0; + value = value | 0; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 2); +#endif + HEAPU32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; +} +function setF32(ptr, value) { + ptr = ptr | 0; + value = +value; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 2); +#endif + HEAPF32s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 2] = value; +} +function setF64(ptr, value) { + ptr = ptr | 0; + value = +value; +#if SAFE_SPLIT_MEMORY + checkPtr(ptr, 3); +#endif + HEAPF64s[ptr >> SPLIT_MEMORY_BITS][(ptr & SPLIT_MEMORY_MASK) >> 3] = value; +} +#endif // SPLIT_MEMORY + +#endif // USE_PTHREADS + +function getTotalMemory() { + return TOTAL_MEMORY; +} + +// Endianness check (note: assumes compiler arch was little-endian) +#if SAFE_SPLIT_MEMORY == 0 +#if USE_PTHREADS +if (!ENVIRONMENT_IS_PTHREAD) { +#endif + HEAP32[0] = 0x63736d65; /* 'emsc' */ +#if USE_PTHREADS +} else { + if (HEAP32[0] !== 0x63736d65) throw 'Runtime error: The application has corrupted its heap memory area (address zero)!'; +} +#endif +HEAP16[1] = 0x6373; +if (HEAPU8[2] !== 0x73 || HEAPU8[3] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; +#endif + +function callRuntimeCallbacks(callbacks) { + while(callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == 'function') { + callback(); + continue; + } + var func = callback.func; + if (typeof func === 'number') { + if (callback.arg === undefined) { + Module['dynCall_v'](func); + } else { + Module['dynCall_vi'](func, callback.arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } +} + +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the runtime has exited + +var runtimeInitialized = false; +var runtimeExited = false; + +#if USE_PTHREADS +if (ENVIRONMENT_IS_PTHREAD) runtimeInitialized = true; // The runtime is hosted in the main thread, and bits shared to pthreads via SharedArrayBuffer. No need to init again in pthread. +#endif + +function preRun() { +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread. +#endif + // compatibility - merge in anything from Module['preRun'] at this time + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); +} + +function ensureInitRuntime() { +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread. +#endif + if (runtimeInitialized) return; + runtimeInitialized = true; +#if USE_PTHREADS + // Pass the thread address inside the asm.js scope to store it for fast access that avoids the need for a FFI out. + __register_pthread_ptr(PThread.mainThreadBlock, /*isMainBrowserThread=*/!ENVIRONMENT_IS_WORKER, /*isMainRuntimeThread=*/1); +#endif + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread. +#endif + callRuntimeCallbacks(__ATMAIN__); +} + +function exitRuntime() { +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread. +#endif + callRuntimeCallbacks(__ATEXIT__); + runtimeExited = true; +} + +function postRun() { +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread. +#endif + // compatibility - merge in anything from Module['postRun'] at this time + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} + +function addOnExit(cb) { + __ATEXIT__.unshift(cb); +} + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} + +// Deprecated: This function should not be called because it is unsafe and does not provide +// a maximum length limit of how many bytes it is allowed to write. Prefer calling the +// function stringToUTF8Array() instead, which takes in a maximum length that can be used +// to be secure from out of bounds writes. +/** @deprecated */ +function writeStringToMemory(string, buffer, dontAddNull) { + warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8Array always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. +} + +function writeArrayToMemory(array, buffer) { +#if ASSERTIONS + assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') +#endif + HEAP8.set(array, buffer); +} + +function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { +#if ASSERTIONS + assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); +#endif + {{{ makeSetValue('buffer++', 0, 'str.charCodeAt(i)', 'i8') }}}; + } + // Null-terminate the pointer to the HEAP. + if (!dontAddNull) {{{ makeSetValue('buffer', 0, 0, 'i8') }}}; +} + +{{{ unSign }}} +{{{ reSign }}} + +#if LEGACY_VM_SUPPORT +// check for imul support, and also for correctness ( https://bugs.webkit.org/show_bug.cgi?id=126345 ) +if (!Math['imul'] || Math['imul'](0xffffffff, 5) !== -5) Math['imul'] = function imul(a, b) { + var ah = a >>> 16; + var al = a & 0xffff; + var bh = b >>> 16; + var bl = b & 0xffff; + return (al*bl + ((ah*bl + al*bh) << 16))|0; +}; +Math.imul = Math['imul']; + +#if PRECISE_F32 +#if PRECISE_F32 == 1 +if (!Math['fround']) { + var froundBuffer = new Float32Array(1); + Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] }; +} +#else // 2 +if (!Math['fround']) Math['fround'] = function(x) { return x }; +#endif +Math.fround = Math['fround']; +#else +#if SIMD +if (!Math['fround']) Math['fround'] = function(x) { return x }; +#endif +#endif + +if (!Math['clz32']) Math['clz32'] = function(x) { + x = x >>> 0; + for (var i = 0; i < 32; i++) { + if (x & (1 << (31 - i))) return i; + } + return 32; +}; +Math.clz32 = Math['clz32'] + +if (!Math['trunc']) Math['trunc'] = function(x) { + return x < 0 ? Math.ceil(x) : Math.floor(x); +}; +Math.trunc = Math['trunc']; +#else // LEGACY_VM_SUPPORT +#if ASSERTIONS +assert(Math['imul'] && Math['fround'] && Math['clz32'] && Math['trunc'], 'this is a legacy browser, build with LEGACY_VM_SUPPORT'); +#endif +#endif // LEGACY_VM_SUPPORT + +var Math_abs = Math.abs; +var Math_cos = Math.cos; +var Math_sin = Math.sin; +var Math_tan = Math.tan; +var Math_acos = Math.acos; +var Math_asin = Math.asin; +var Math_atan = Math.atan; +var Math_atan2 = Math.atan2; +var Math_exp = Math.exp; +var Math_log = Math.log; +var Math_sqrt = Math.sqrt; +var Math_ceil = Math.ceil; +var Math_floor = Math.floor; +var Math_pow = Math.pow; +var Math_imul = Math.imul; +var Math_fround = Math.fround; +var Math_round = Math.round; +var Math_min = Math.min; +var Math_max = Math.max; +var Math_clz32 = Math.clz32; +var Math_trunc = Math.trunc; + +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// PRE_RUN_ADDITIONS (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled +#if ASSERTIONS +var runDependencyTracking = {}; +#endif + +function getUniqueRunDependency(id) { +#if ASSERTIONS + var orig = id; + while (1) { + if (!runDependencyTracking[id]) return id; + id = orig + Math.random(); + } +#endif + return id; +} + +function addRunDependency(id) { +#if USE_PTHREADS + // We should never get here in pthreads (could no-op this out if called in pthreads, but that might indicate a bug in caller side, + // so good to be very explicit) + assert(!ENVIRONMENT_IS_PTHREAD); +#endif + runDependencies++; + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } +#if ASSERTIONS + if (id) { + assert(!runDependencyTracking[id]); + runDependencyTracking[id] = 1; + if (runDependencyWatcher === null && typeof setInterval !== 'undefined') { + // Check for missing dependencies every few seconds + runDependencyWatcher = setInterval(function() { + if (ABORT) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + return; + } + var shown = false; + for (var dep in runDependencyTracking) { + if (!shown) { + shown = true; + Module.printErr('still waiting on run dependencies:'); + } + Module.printErr('dependency: ' + dep); + } + if (shown) { + Module.printErr('(end of list)'); + } + }, 10000); + } + } else { + Module.printErr('warning: run dependency added without ID'); + } +#endif +} + +function removeRunDependency(id) { + runDependencies--; + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } +#if ASSERTIONS + if (id) { + assert(runDependencyTracking[id]); + delete runDependencyTracking[id]; + } else { + Module.printErr('warning: run dependency removed without ID'); + } +#endif + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } +} + +Module["preloadedImages"] = {}; // maps url to image data +Module["preloadedAudios"] = {}; // maps url to audio data +#if WASM +#if MAIN_MODULE +Module["preloadedWasm"] = {}; // maps url to wasm instance exports +#endif // MAIN_MODULE +#endif // WASM + +#if PGO +var PGOMonitor = { + called: {}, + dump: function() { + var dead = []; + for (var i = 0; i < this.allGenerated.length; i++) { + var func = this.allGenerated[i]; + if (!this.called[func]) dead.push(func); + } + Module.print('-s DEAD_FUNCTIONS=\'' + JSON.stringify(dead) + '\'\n'); + } +}; +Module['PGOMonitor'] = PGOMonitor; +__ATEXIT__.push(function() { PGOMonitor.dump() }); +addOnPreRun(function() { addRunDependency('pgo') }); +#endif + +#if RELOCATABLE +{{{ +(function() { + // add in RUNTIME_LINKED_LIBS, if provided + if (RUNTIME_LINKED_LIBS.length > 0) { + return "if (!Module['dynamicLibraries']) Module['dynamicLibraries'] = [];\n" + + "Module['dynamicLibraries'] = " + JSON.stringify(RUNTIME_LINKED_LIBS) + ".concat(Module['dynamicLibraries']);\n"; + } + return ''; +})() +}}} + +addOnPreRun(function() { + function loadDynamicLibraries(libs) { + if (libs) { + libs.forEach(function(lib) { + loadDynamicLibrary(lib); + }); + } + if (Module['asm']['runPostSets']) { + Module['asm']['runPostSets'](); + } + } + // if we can load dynamic libraries synchronously, do so, otherwise, preload +#if WASM + if (Module['dynamicLibraries'] && Module['dynamicLibraries'].length > 0 && !Module['readBinary']) { + // we can't read binary data synchronously, so preload + addRunDependency('preload_dynamicLibraries'); + var binaries = []; + Module['dynamicLibraries'].forEach(function(lib) { + fetch(lib, { credentials: 'same-origin' }).then(function(response) { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + lib + "'"; + } + return response['arrayBuffer'](); + }).then(function(buffer) { + var binary = new Uint8Array(buffer); + binaries.push(binary); + if (binaries.length === Module['dynamicLibraries'].length) { + // we got them all, wonderful + loadDynamicLibraries(binaries); + removeRunDependency('preload_dynamicLibraries'); + } + }); + }); + return; + } +#endif + loadDynamicLibraries(Module['dynamicLibraries']); +}); + +#if ASSERTIONS +function lookupSymbol(ptr) { // for a pointer, print out all symbols that resolve to it + var ret = []; + for (var i in Module) { + if (Module[i] === ptr) ret.push(i); + } + print(ptr + ' is ' + ret); +} +#endif +#endif + +var memoryInitializer = null; + +#if USE_PTHREADS +#if PTHREAD_HINT_NUM_CORES < 0 +if (!ENVIRONMENT_IS_PTHREAD) addOnPreRun(function() { + addRunDependency('pthreads_querycores'); + + var bg = document.createElement('div'); + bg.style = "position: absolute; top: 0%; left: 0%; width: 100%; height: 100%; background-color: black; z-index:1001; -moz-opacity: 0.8; opacity:.80; filter: alpha(opacity=80);"; + var div = document.createElement('div'); + var default_num_cores = navigator.hardwareConcurrency || 4; + var hwConcurrency = navigator.hardwareConcurrency ? ("says " + navigator.hardwareConcurrency) : "is not available"; + var html = '
Thread setup

Number of logical cores:
(navigator.hardwareConcurrency ' + + hwConcurrency + ')
'; +#if PTHREAD_POOL_SIZE < 0 + html += 'PThread pool size:
'; +#endif + html += '
'; + div.innerHTML = html; + div.style = 'position: absolute; top: 35%; left: 35%; width: 30%; height: 150px; padding: 16px; border: 16px solid gray; background-color: white; z-index:1002; overflow: auto;'; + document.body.appendChild(bg); + document.body.appendChild(div); + var goButton = document.getElementById('thread_setup_button_go'); + goButton.onclick = function() { + var num_logical_cores = parseInt(document.getElementById('thread_setup_num_logical_cores').value); + _emscripten_force_num_logical_cores(num_logical_cores); +#if PTHREAD_POOL_SIZE < 0 + var pthread_pool_size = parseInt(document.getElementById('thread_setup_pthread_pool_size').value); + PThread.allocateUnusedWorkers(pthread_pool_size, function() { removeRunDependency('pthreads_querycores'); }); +#else + removeRunDependency('pthreads_querycores'); +#endif + document.body.removeChild(bg); + document.body.removeChild(div); + } +}); +#endif +#endif + +#if PTHREAD_POOL_SIZE > 0 +// To work around https://bugzilla.mozilla.org/show_bug.cgi?id=1049079, warm up a worker pool before starting up the application. +if (!ENVIRONMENT_IS_PTHREAD) addOnPreRun(function() { if (typeof SharedArrayBuffer !== 'undefined') { addRunDependency('pthreads'); PThread.allocateUnusedWorkers({{{PTHREAD_POOL_SIZE}}}, function() { removeRunDependency('pthreads'); }); }}); +#endif + +#if ASSERTIONS +#if NO_FILESYSTEM +var /* show errors on likely calls to FS when it was not included */ FS = { + error: function() { + abort('Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with -s FORCE_FILESYSTEM=1'); + }, + init: function() { FS.error() }, + createDataFile: function() { FS.error() }, + createPreloadedFile: function() { FS.error() }, + createLazyFile: function() { FS.error() }, + open: function() { FS.error() }, + mkdev: function() { FS.error() }, + registerDevice: function() { FS.error() }, + analyzePath: function() { FS.error() }, + loadFilesFromDB: function() { FS.error() }, + + ErrnoError: function ErrnoError() { FS.error() }, +}; +Module['FS_createDataFile'] = FS.createDataFile; +Module['FS_createPreloadedFile'] = FS.createPreloadedFile; +#endif +#endif + +#if CYBERDWARF +var cyberDWARFFile = '{{{ BUNDLED_CD_DEBUG_FILE }}}'; +#endif + +#include "URIUtils.js" + +#if WASM +function integrateWasmJS() { + // wasm.js has several methods for creating the compiled code module here: + // * 'native-wasm' : use native WebAssembly support in the browser + // * 'interpret-s-expr': load s-expression code from a .wast and interpret + // * 'interpret-binary': load binary wasm and interpret + // * 'interpret-asm2wasm': load asm.js code, translate to wasm, and interpret + // * 'asmjs': no wasm, just load the asm.js code and use that (good for testing) + // The method is set at compile time (BINARYEN_METHOD) + // The method can be a comma-separated list, in which case, we will try the + // options one by one. Some of them can fail gracefully, and then we can try + // the next. + + // inputs + + var method = '{{{ BINARYEN_METHOD }}}'; + + var wasmTextFile = '{{{ WASM_TEXT_FILE }}}'; + var wasmBinaryFile = '{{{ WASM_BINARY_FILE }}}'; + var asmjsCodeFile = '{{{ ASMJS_CODE_FILE }}}'; + + if (typeof Module['locateFile'] === 'function') { + if (!isDataURI(wasmTextFile)) { + wasmTextFile = Module['locateFile'](wasmTextFile); + } + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = Module['locateFile'](wasmBinaryFile); + } + if (!isDataURI(asmjsCodeFile)) { + asmjsCodeFile = Module['locateFile'](asmjsCodeFile); + } + } + + // utilities + + var wasmPageSize = 64*1024; + + var info = { + 'global': null, + 'env': null, + 'asm2wasm': { // special asm2wasm imports + "f64-rem": function(x, y) { + return x % y; + }, + "debugger": function() { + debugger; + } +#if NEED_ALL_ASM2WASM_IMPORTS + , + "f64-to-int": function(x) { + return x | 0; + }, + "i32s-div": function(x, y) { + return ((x | 0) / (y | 0)) | 0; + }, + "i32u-div": function(x, y) { + return ((x >>> 0) / (y >>> 0)) >>> 0; + }, + "i32s-rem": function(x, y) { + return ((x | 0) % (y | 0)) | 0; + }, + "i32u-rem": function(x, y) { + return ((x >>> 0) % (y >>> 0)) >>> 0; + } +#endif // NEED_ALL_ASM2WASM_IMPORTS + }, + 'parent': Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program. + }; + + var exports = null; + +#if BINARYEN_METHOD != 'native-wasm' + function lookupImport(mod, base) { + var lookup = info; + if (mod.indexOf('.') < 0) { + lookup = (lookup || {})[mod]; + } else { + var parts = mod.split('.'); + lookup = (lookup || {})[parts[0]]; + lookup = (lookup || {})[parts[1]]; + } + if (base) { + lookup = (lookup || {})[base]; + } + if (lookup === undefined) { + abort('bad lookupImport to (' + mod + ').' + base); + } + return lookup; + } +#endif // BINARYEN_METHOD != 'native-wasm' + + function mergeMemory(newBuffer) { + // The wasm instance creates its memory. But static init code might have written to + // buffer already, including the mem init file, and we must copy it over in a proper merge. + // TODO: avoid this copy, by avoiding such static init writes + // TODO: in shorter term, just copy up to the last static init write + var oldBuffer = Module['buffer']; + if (newBuffer.byteLength < oldBuffer.byteLength) { + Module['printErr']('the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here'); + } + var oldView = new Int8Array(oldBuffer); + var newView = new Int8Array(newBuffer); + +#if MEM_INIT_IN_WASM == 0 + // If we have a mem init file, do not trample it + if (!memoryInitializer) { + oldView.set(newView.subarray(Module['STATIC_BASE'], Module['STATIC_BASE'] + Module['STATIC_BUMP']), Module['STATIC_BASE']); + } +#endif + + newView.set(oldView); + updateGlobalBuffer(newBuffer); + updateGlobalBufferViews(); + } + + function fixImports(imports) { +#if WASM_BACKEND + var ret = {}; + for (var i in imports) { + var fixed = i; + if (fixed[0] == '_') fixed = fixed.substr(1); + ret[fixed] = imports[i]; + } + return ret; +#else + return imports; +#endif // WASM_BACKEND + } + + function getBinary() { + try { + if (Module['wasmBinary']) { + return new Uint8Array(Module['wasmBinary']); + } +#if SUPPORT_BASE64_EMBEDDING + var binary = tryParseAsDataURI(wasmBinaryFile); + if (binary) { + return binary; + } +#endif + if (Module['readBinary']) { + return Module['readBinary'](wasmBinaryFile); + } else { + throw "on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"; + } + } + catch (err) { + abort(err); + } + } + + function getBinaryPromise() { + // if we don't have the binary yet, and have the Fetch api, use that + // in some environments, like Electron's render process, Fetch api may be present, but have a different context than expected, let's only use it on the Web + if (!Module['wasmBinary'] && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && typeof fetch === 'function') { + return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; + } + return response['arrayBuffer'](); + }).catch(function () { + return getBinary(); + }); + } + // Otherwise, getBinary should be able to get it synchronously + return new Promise(function(resolve, reject) { + resolve(getBinary()); + }); + } + + // do-method functions + +#if BINARYEN_METHOD != 'native-wasm' + function doJustAsm(global, env, providedBuffer) { + // if no Module.asm, or it's the method handler helper (see below), then apply + // the asmjs + if (typeof Module['asm'] !== 'function' || Module['asm'] === methodHandler) { + if (!Module['asmPreload']) { + // you can load the .asm.js file before this, to avoid this sync xhr and eval + {{{ makeEval("eval(Module['read'](asmjsCodeFile));") }}} // set Module.asm + } else { + Module['asm'] = Module['asmPreload']; + } + } + if (typeof Module['asm'] !== 'function') { + Module['printErr']('asm evalling did not set the module properly'); + return false; + } + return Module['asm'](global, env, providedBuffer); + } +#endif // BINARYEN_METHOD != 'native-wasm' + + function doNativeWasm(global, env, providedBuffer) { + if (typeof WebAssembly !== 'object') { +#if BINARYEN_METHOD == 'native-wasm' +#if ASSERTIONS + // when the method is just native-wasm, our error message can be very specific + abort('No WebAssembly support found. Build with -s WASM=0 to target JavaScript instead.'); +#endif +#endif + Module['printErr']('no native wasm support detected'); + return false; + } + // prepare memory import + if (!(Module['wasmMemory'] instanceof WebAssembly.Memory)) { + Module['printErr']('no native wasm Memory in use'); + return false; + } + env['memory'] = Module['wasmMemory']; + // Load the wasm module and create an instance of using native support in the JS engine. + info['global'] = { + 'NaN': NaN, + 'Infinity': Infinity + }; + info['global.Math'] = Math; + info['env'] = env; + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + function receiveInstance(instance, module) { + exports = instance.exports; + if (exports.memory) mergeMemory(exports.memory); + Module['asm'] = exports; + Module["usingWasm"] = true; +#if WASM_BACKEND + // wasm backend stack goes down + STACKTOP = STACK_BASE + TOTAL_STACK; + STACK_MAX = STACK_BASE; + // can't call stackRestore() here since this function can be called + // synchronously before stackRestore() is declared. + Module["asm"]["stackRestore"](STACKTOP); +#endif +#if USE_PTHREADS + // Keep a reference to the compiled module so we can post it to the workers. + Module['wasmModule'] = module; + // Instantiation is synchronous in pthreads and we assert on run dependencies. + if(!ENVIRONMENT_IS_PTHREAD) removeRunDependency('wasm-instantiate'); +#else + removeRunDependency('wasm-instantiate'); +#endif + } +#if USE_PTHREADS + if (!ENVIRONMENT_IS_PTHREAD) { + addRunDependency('wasm-instantiate'); // we can't run yet (except in a pthread, where we have a custom sync instantiator) + } +#else + addRunDependency('wasm-instantiate'); +#endif + + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel + // to any other async startup actions they are performing. + if (Module['instantiateWasm']) { + try { + return Module['instantiateWasm'](info, receiveInstance); + } catch(e) { + Module['printErr']('Module.instantiateWasm callback failed with error: ' + e); + return false; + } + } + +#if BINARYEN_ASYNC_COMPILATION +#if RUNTIME_LOGGING + Module['printErr']('asynchronously preparing wasm'); +#endif +#if ASSERTIONS + // Async compilation can be confusing when an error on the page overwrites Module + // (for example, if the order of elements is wrong, and the one defining Module is + // later), so we save Module and check it later. + var trueModule = Module; +#endif + function receiveInstantiatedSource(output) { + // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called +#if ASSERTIONS + assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); + trueModule = null; +#endif + receiveInstance(output['instance'], output['module']); + } + function instantiateArrayBuffer(receiver) { + getBinaryPromise().then(function(binary) { + return WebAssembly.instantiate(binary, info); + }).then(receiver).catch(function(reason) { + Module['printErr']('failed to asynchronously prepare wasm: ' + reason); + abort(reason); + }); + } + // Prefer streaming instantiation if available. + if (!Module['wasmBinary'] && + typeof WebAssembly.instantiateStreaming === 'function' && + !isDataURI(wasmBinaryFile) && + typeof fetch === 'function') { + WebAssembly.instantiateStreaming(fetch(wasmBinaryFile, { credentials: 'same-origin' }), info) + .then(receiveInstantiatedSource) + .catch(function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + Module['printErr']('wasm streaming compile failed: ' + reason); + Module['printErr']('falling back to ArrayBuffer instantiation'); + instantiateArrayBuffer(receiveInstantiatedSource); + }); + } else { + instantiateArrayBuffer(receiveInstantiatedSource); + } + return {}; // no exports yet; we'll fill them in later +#else + var instance; + try { + instance = new WebAssembly.Instance(new WebAssembly.Module(getBinary()), info) + } catch (e) { + Module['printErr']('failed to compile wasm module: ' + e); + if (e.toString().indexOf('imported Memory with incompatible size') >= 0) { + Module['printErr']('Memory size incompatibility issues may be due to changing TOTAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set TOTAL_MEMORY at runtime to something smaller than it was at compile time).'); + } + return false; + } + receiveInstance(instance); + return exports; +#endif + } + +#if BINARYEN_METHOD != 'native-wasm' + function doWasmPolyfill(global, env, providedBuffer, method) { + if (typeof WasmJS !== 'function') { + Module['printErr']('WasmJS not detected - polyfill not bundled?'); + return false; + } + + // Use wasm.js to polyfill and execute code in a wasm interpreter. + var wasmJS = WasmJS({}); + + // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp. + wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module. + + // Information for the instance of the module. + wasmJS['info'] = info; + + wasmJS['lookupImport'] = lookupImport; + + assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way. + + info.global = global; + info.env = env; + + // polyfill interpreter expects an ArrayBuffer + assert(providedBuffer === Module['buffer']); + env['memory'] = providedBuffer; + assert(env['memory'] instanceof ArrayBuffer); + + wasmJS['providedTotalMemory'] = Module['buffer'].byteLength; + + // Prepare to generate wasm, using either asm2wasm or s-exprs + var code; + if (method === 'interpret-binary') { + code = getBinary(); + } else { + code = Module['read'](method == 'interpret-asm2wasm' ? asmjsCodeFile : wasmTextFile); + } + var temp; + if (method == 'interpret-asm2wasm') { + temp = wasmJS['_malloc'](code.length + 1); + wasmJS['writeAsciiToMemory'](code, temp); + wasmJS['_load_asm2wasm'](temp); + } else if (method === 'interpret-s-expr') { + temp = wasmJS['_malloc'](code.length + 1); + wasmJS['writeAsciiToMemory'](code, temp); + wasmJS['_load_s_expr2wasm'](temp); + } else if (method === 'interpret-binary') { + temp = wasmJS['_malloc'](code.length); + wasmJS['HEAPU8'].set(code, temp); + wasmJS['_load_binary2wasm'](temp, code.length); + } else { + throw 'what? ' + method; + } + wasmJS['_free'](temp); + + wasmJS['_instantiate'](temp); + + if (Module['newBuffer']) { + mergeMemory(Module['newBuffer']); + Module['newBuffer'] = null; + } + + exports = wasmJS['asmExports']; + + return exports; + } +#endif // BINARYEN_METHOD != 'native-wasm' + + // We may have a preloaded value in Module.asm, save it + Module['asmPreload'] = Module['asm']; + + // Memory growth integration code + + var asmjsReallocBuffer = Module['reallocBuffer']; + + var wasmReallocBuffer = function(size) { + var PAGE_MULTIPLE = Module["usingWasm"] ? WASM_PAGE_SIZE : ASMJS_PAGE_SIZE; // In wasm, heap size must be a multiple of 64KB. In asm.js, they need to be multiples of 16MB. + size = alignUp(size, PAGE_MULTIPLE); // round up to wasm page size + var old = Module['buffer']; + var oldSize = old.byteLength; + if (Module["usingWasm"]) { + // native wasm support + try { + var result = Module['wasmMemory'].grow((size - oldSize) / wasmPageSize); // .grow() takes a delta compared to the previous size + if (result !== (-1 | 0)) { + // success in native wasm memory growth, get the buffer from the memory + return Module['buffer'] = Module['wasmMemory'].buffer; + } else { + return null; + } + } catch(e) { +#if ASSERTIONS + console.error('Module.reallocBuffer: Attempted to grow from ' + oldSize + ' bytes to ' + size + ' bytes, but got error: ' + e); +#endif + return null; + } + } +#if BINARYEN_METHOD != 'native-wasm' + else { + // wasm interpreter support + exports['__growWasmMemory']((size - oldSize) / wasmPageSize); // tiny wasm method that just does grow_memory + // in interpreter, we replace Module.buffer if we allocate + return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed + } +#endif // BINARYEN_METHOD != 'native-wasm' + }; + + Module['reallocBuffer'] = function(size) { + if (finalMethod === 'asmjs') { + return asmjsReallocBuffer(size); + } else { + return wasmReallocBuffer(size); + } + }; + + // we may try more than one; this is the final one, that worked and we are using + var finalMethod = ''; + + // Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate + // the wasm module at that time, and it receives imports and provides exports and so forth, the app + // doesn't need to care that it is wasm or olyfilled wasm or asm.js. + + Module['asm'] = function(global, env, providedBuffer) { +#if BINARYEN_METHOD != 'native-wasm' + global = fixImports(global); +#endif + env = fixImports(env); + + // import table + if (!env['table']) { + var TABLE_SIZE = Module['wasmTableSize']; + if (TABLE_SIZE === undefined) TABLE_SIZE = 1024; // works in binaryen interpreter at least + var MAX_TABLE_SIZE = Module['wasmMaxTableSize']; + if (typeof WebAssembly === 'object' && typeof WebAssembly.Table === 'function') { + if (MAX_TABLE_SIZE !== undefined) { + env['table'] = new WebAssembly.Table({ 'initial': TABLE_SIZE, 'maximum': MAX_TABLE_SIZE, 'element': 'anyfunc' }); + } else { + env['table'] = new WebAssembly.Table({ 'initial': TABLE_SIZE, element: 'anyfunc' }); + } + } else { + env['table'] = new Array(TABLE_SIZE); // works in binaryen interpreter at least + } + Module['wasmTable'] = env['table']; + } + + if (!env['memoryBase']) { + env['memoryBase'] = Module['STATIC_BASE']; // tell the memory segments where to place themselves + } + if (!env['tableBase']) { + env['tableBase'] = 0; // table starts at 0 by default, in dynamic linking this will change + } + + // try the methods. each should return the exports if it succeeded + + var exports; +#if BINARYEN_METHOD == 'native-wasm' + exports = doNativeWasm(global, env, providedBuffer); +#else // native-wasm +#if BINARYEN_METHOD == 'asmjs' + exports = doJustAsm(global, env, providedBuffer); +#else + var methods = method.split(','); + + for (var i = 0; i < methods.length; i++) { + var curr = methods[i]; + +#if RUNTIME_LOGGING + Module['printErr']('trying binaryen method: ' + curr); +#endif + + finalMethod = curr; + + if (curr === 'native-wasm') { + if (exports = doNativeWasm(global, env, providedBuffer)) break; + } else if (curr === 'asmjs') { + if (exports = doJustAsm(global, env, providedBuffer)) break; + } else if (curr === 'interpret-asm2wasm' || curr === 'interpret-s-expr' || curr === 'interpret-binary') { + if (exports = doWasmPolyfill(global, env, providedBuffer, curr)) break; + } else { + abort('bad method: ' + curr); + } + } +#endif // asmjs +#endif // native-wasm + +#if ASSERTIONS + assert(exports, 'no binaryen method succeeded. consider enabling more options, like interpreting, if you want that: https://github.com/kripken/emscripten/wiki/WebAssembly#binaryen-methods'); +#else + assert(exports, 'no binaryen method succeeded.'); +#endif + +#if RUNTIME_LOGGING + Module['printErr']('binaryen method succeeded.'); +#endif + + return exports; + }; + + var methodHandler = Module['asm']; // note our method handler, as we may modify Module['asm'] later +} + +integrateWasmJS(); +#endif + +// === Body === diff --git a/emsdk/files/support.js b/emsdk/files/support.js new file mode 100644 index 000000000..99367db70 --- /dev/null +++ b/emsdk/files/support.js @@ -0,0 +1,519 @@ +// {{PREAMBLE_ADDITIONS}} + +var STACK_ALIGN = {{{ STACK_ALIGN }}}; + +#if ASSERTIONS +// stack management, and other functionality that is provided by the compiled code, +// should not be used before it is ready +stackSave = stackRestore = stackAlloc = setTempRet0 = getTempRet0 = function() { + abort('cannot use the stack before compiled code is ready to run, and has provided stack access'); +}; +#endif + +function staticAlloc(size) { + assert(!staticSealed); + var ret = STATICTOP; + STATICTOP = (STATICTOP + size + 15) & -16; + return ret; +} + +function dynamicAlloc(size) { + assert(DYNAMICTOP_PTR); + var ret = HEAP32[DYNAMICTOP_PTR>>2]; + var end = (ret + size + 15) & -16; + HEAP32[DYNAMICTOP_PTR>>2] = end; + if (end >= TOTAL_MEMORY) { + var success = enlargeMemory(); + if (!success) { + HEAP32[DYNAMICTOP_PTR>>2] = ret; + return 0; + } + } + return ret; +} + +{{{ alignMemory }}} + +{{{ getNativeTypeSize }}} + +function warnOnce(text) { + if (!warnOnce.shown) warnOnce.shown = {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + Module.printErr(text); + } +} + +#if RELOCATABLE +var loadedDynamicLibraries = []; + +function loadDynamicLibrary(lib) { + var libModule; +#if WASM + var bin; + if (lib.buffer) { + // we were provided the binary, in a typed array + bin = lib; + } else { + // load the binary synchronously + bin = Module['readBinary'](lib); + } + libModule = loadWebAssemblyModule(bin); +#else + var src = Module['read'](lib); + libModule = eval(src)( + alignFunctionTables(), + Module + ); +#endif + // add symbols into global namespace TODO: weak linking etc. + for (var sym in libModule) { + if (!Module.hasOwnProperty(sym)) { + Module[sym] = libModule[sym]; + } +#if ASSERTIONS == 2 + else if (sym[0] === '_') { + var curr = Module[sym], next = libModule[sym]; + // don't warn on functions - might be odr, linkonce_odr, etc. + if (!(typeof curr === 'function' && typeof next === 'function')) { + Module.printErr("warning: trying to dynamically load symbol '" + sym + "' (from '" + lib + "') that already exists (duplicate symbol? or weak linking, which isn't supported yet?)"); // + [curr, ' vs ', next]); + } + } +#endif + } + loadedDynamicLibraries.push(libModule); +} + +#if WASM +// Loads a side module from binary data +function loadWebAssemblyModule(binary, loadAsync) { + var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer); + assert(int32View[0] == 0x6d736100, 'need to see wasm magic number'); // \0wasm + // we should see the dylink section right after the magic number and wasm version + assert(binary[8] === 0, 'need the dylink section to be first') + var next = 9; + function getLEB() { + var ret = 0; + var mul = 1; + while (1) { + var byte = binary[next++]; + ret += ((byte & 0x7f) * mul); + mul *= 0x80; + if (!(byte & 0x80)) break; + } + return ret; + } + var sectionSize = getLEB(); + assert(binary[next] === 6); next++; // size of "dylink" string + assert(binary[next] === 'd'.charCodeAt(0)); next++; + assert(binary[next] === 'y'.charCodeAt(0)); next++; + assert(binary[next] === 'l'.charCodeAt(0)); next++; + assert(binary[next] === 'i'.charCodeAt(0)); next++; + assert(binary[next] === 'n'.charCodeAt(0)); next++; + assert(binary[next] === 'k'.charCodeAt(0)); next++; + var memorySize = getLEB(); + var memoryAlign = getLEB(); + var tableSize = getLEB(); + var tableAlign = getLEB(); + // alignments are powers of 2 + memoryAlign = Math.pow(2, memoryAlign); + tableAlign = Math.pow(2, tableAlign); + // finalize alignments and verify them + memoryAlign = Math.max(memoryAlign, STACK_ALIGN); // we at least need stack alignment + assert(tableAlign === 1); + // prepare memory + var memoryStart = alignMemory(getMemory(memorySize + memoryAlign), memoryAlign); // TODO: add to cleanups + // The static area consists of explicitly initialized data, followed by zero-initialized data. + // The latter may need zeroing out if the MAIN_MODULE has already used this memory area before + // dlopen'ing the SIDE_MODULE. Since we don't know the size of the explicitly initialized data + // here, we just zero the whole thing, which is suboptimal, but should at least resolve bugs + // from uninitialized memory. + for (var i = memoryStart; i < memoryStart + memorySize; ++i) HEAP8[i] = 0; + // prepare env imports + var env = Module['asmLibraryArg']; + // TODO: use only memoryBase and tableBase, need to update asm.js backend + var table = Module['wasmTable']; + var oldTableSize = table.length; + env['memoryBase'] = env['gb'] = memoryStart; + env['tableBase'] = env['fb'] = oldTableSize; + var originalTable = table; + table.grow(tableSize); + assert(table === originalTable); + // zero-initialize memory and table TODO: in some cases we can tell it is already zero initialized + for (var i = env['memoryBase']; i < env['memoryBase'] + memorySize; i++) { + HEAP8[i] = 0; + } + for (var i = env['tableBase']; i < env['tableBase'] + tableSize; i++) { + table.set(i, null); + } + // copy currently exported symbols so the new module can import them + for (var x in Module) { + if (!(x in env)) { + env[x] = Module[x]; + } + } + var info = { + global: { + 'NaN': NaN, + 'Infinity': Infinity, + }, + 'global.Math': Math, + env: env + }; +#if ASSERTIONS + var oldTable = []; + for (var i = 0; i < oldTableSize; i++) { + oldTable.push(table.get(i)); + } +#endif + + function postInstantiation(instance) { + var exports = {}; +#if ASSERTIONS + // the table should be unchanged + assert(table === originalTable); + assert(table === Module['wasmTable']); + if (instance.exports['table']) { + assert(table === instance.exports['table']); + } + // the old part of the table should be unchanged + for (var i = 0; i < oldTableSize; i++) { + assert(table.get(i) === oldTable[i], 'old table entries must remain the same'); + } + // verify that the new table region was filled in + for (var i = 0; i < tableSize; i++) { + assert(table.get(oldTableSize + i) !== undefined, 'table entry was not filled in'); + } +#endif + for (var e in instance.exports) { + var value = instance.exports[e]; + if (typeof value === 'object') { + // a breaking change in the wasm spec, globals are now objects + // https://github.com/WebAssembly/mutable-global/issues/1 + value = value.value; + } + if (typeof value === 'number') { + // relocate it - modules export the absolute value, they can't relocate before they export +#if EMULATED_FUNCTION_POINTERS + // it may be a function pointer + if (e.substr(0, 3) == 'fp$' && typeof instance.exports[e.substr(3)] === 'function') { + value = value + env['tableBase']; + } else { +#endif + value = value + env['memoryBase']; +#if EMULATED_FUNCTION_POINTERS + } +#endif + } + exports[e] = value; + } + // initialize the module + var init = exports['__post_instantiate']; + if (init) { + if (runtimeInitialized) { + init(); + } else { + // we aren't ready to run compiled code yet + __ATINIT__.push(init); + } + } + return exports; + } + + if (loadAsync) { + return WebAssembly.instantiate(binary, info).then(function(result) { + return postInstantiation(result.instance); + }); + } else { + var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), info); + return postInstantiation(instance); + } +} +Module['loadWebAssemblyModule'] = loadWebAssemblyModule; + +#endif // WASM +#endif // RELOCATABLE + +#if EMULATED_FUNCTION_POINTERS +function getFunctionTables(module) { + if (!module) module = Module; + var tables = {}; + for (var t in module) { + if (/^FUNCTION_TABLE_.*/.test(t)) { + var table = module[t]; + if (typeof table === 'object') tables[t.substr('FUNCTION_TABLE_'.length)] = table; + } + } + return tables; +} + +function alignFunctionTables(module) { + var tables = getFunctionTables(module); + var maxx = 0; + for (var sig in tables) { + maxx = Math.max(maxx, tables[sig].length); + } + assert(maxx >= 0); + for (var sig in tables) { + var table = tables[sig]; + while (table.length < maxx) table.push(0); + } + return maxx; +} + +#if RELOCATABLE +// register functions from a new module being loaded +function registerFunctions(sigs, newModule) { + sigs.forEach(function(sig) { + if (!Module['FUNCTION_TABLE_' + sig]) { + Module['FUNCTION_TABLE_' + sig] = []; + } + }); + var oldMaxx = alignFunctionTables(); // align the new tables we may have just added + var newMaxx = alignFunctionTables(newModule); + var maxx = oldMaxx + newMaxx; + sigs.forEach(function(sig) { + var newTable = newModule['FUNCTION_TABLE_' + sig]; + var oldTable = Module['FUNCTION_TABLE_' + sig]; + assert(newTable !== oldTable); + assert(oldTable.length === oldMaxx); + for (var i = 0; i < newTable.length; i++) { + oldTable.push(newTable[i]); + } + assert(oldTable.length === maxx); + }); + assert(maxx === alignFunctionTables()); // align the ones we didn't touch +} +// export this so side modules can use it +Module['registerFunctions'] = registerFunctions; +#endif // RELOCATABLE +#endif // EMULATED_FUNCTION_POINTERS + +#if WASM_BACKEND_WITH_RESERVED_FUNCTION_POINTERS +var jsCallStartIndex = {{{ JSCALL_START_INDEX }}}; +var jsCallSigOrder = {{{ JSON.stringify(JSCALL_SIG_ORDER) }}}; +var jsCallNumSigs = Object.keys(jsCallSigOrder).length; +var functionPointers = new Array(jsCallNumSigs * {{{ RESERVED_FUNCTION_POINTERS }}}); +#else // WASM_BACKEND_WITH_RESERVED_FUNCTION_POINTERS == 0 +var jsCallStartIndex = 1; +var functionPointers = new Array({{{ RESERVED_FUNCTION_POINTERS }}}); +#endif // WASM_BACKEND_WITH_RESERVED_FUNCTION_POINTERS + +// 'sig' parameter is only used on LLVM wasm backend +function addFunction(func, sig) { +#if WASM_BACKEND + assert(typeof sig !== 'undefined', + 'Second argument of addFunction should be a wasm function signature ' + + 'string'); +#endif // WASM_BACKEND +#if ASSERTIONS + if (typeof sig === 'undefined') { + Module.printErr('warning: addFunction(): You should provide a wasm function signature string as a second argument. This is not necessary for asm.js and asm2wasm, but is required for the LLVM wasm backend, so it is recommended for full portability.'); + } +#endif // ASSERTIONS +#if EMULATED_FUNCTION_POINTERS == 0 +#if WASM_BACKEND_WITH_RESERVED_FUNCTION_POINTERS + var base = jsCallSigOrder[sig] * {{{ RESERVED_FUNCTION_POINTERS }}}; +#else // WASM_BACKEND_WITH_RESERVED_FUNCTION_POINTERS == 0 + var base = 0; +#endif // WASM_BACKEND_WITH_RESERVED_FUNCTION_POINTERS + for (var i = base; i < base + {{{ RESERVED_FUNCTION_POINTERS }}}; i++) { + if (!functionPointers[i]) { + functionPointers[i] = func; + return jsCallStartIndex + i; + } + } + throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.'; +#else +#if WASM + // we can simply append to the wasm table + var table = Module['wasmTable']; + var ret = table.length; + table.grow(1); + table.set(ret, func); + return ret; +#else + alignFunctionTables(); // XXX we should rely on this being an invariant + var tables = getFunctionTables(); + var ret = -1; + for (var sig in tables) { + var table = tables[sig]; + if (ret < 0) ret = table.length; + else assert(ret === table.length); + table.push(func); + } + return ret; +#endif +#endif +} + +function removeFunction(index) { +#if EMULATED_FUNCTION_POINTERS == 0 + functionPointers[index-jsCallStartIndex] = null; +#else + alignFunctionTables(); // XXX we should rely on this being an invariant + var tables = getFunctionTables(); + for (var sig in tables) { + tables[sig][index] = null; + } +#endif +} + +var funcWrappers = {}; + +function getFuncWrapper(func, sig) { + if (!func) return; // on null pointer, return undefined + assert(sig); + if (!funcWrappers[sig]) { + funcWrappers[sig] = {}; + } + var sigCache = funcWrappers[sig]; + if (!sigCache[func]) { + // optimize away arguments usage in common cases + if (sig.length === 1) { + sigCache[func] = function dynCall_wrapper() { + return dynCall(sig, func); + }; + } else if (sig.length === 2) { + sigCache[func] = function dynCall_wrapper(arg) { + return dynCall(sig, func, [arg]); + }; + } else { + // general case + sigCache[func] = function dynCall_wrapper() { + return dynCall(sig, func, Array.prototype.slice.call(arguments)); + }; + } + } + return sigCache[func]; +} + +#if RUNTIME_DEBUG +var runtimeDebug = true; // Switch to false at runtime to disable logging at the right times + +var printObjectList = []; + +function prettyPrint(arg) { + if (typeof arg == 'undefined') return '!UNDEFINED!'; + if (typeof arg == 'boolean') arg = arg + 0; + if (!arg) return arg; + var index = printObjectList.indexOf(arg); + if (index >= 0) return '<' + arg + '|' + index + '>'; + if (arg.toString() == '[object HTMLImageElement]') { + return arg + '\n\n'; + } + if (arg.byteLength) { + return '{' + Array.prototype.slice.call(arg, 0, Math.min(arg.length, 400)) + '}'; // Useful for correct arrays, less so for compiled arrays, see the code below for that + var buf = new ArrayBuffer(32); + var i8buf = new Int8Array(buf); + var i16buf = new Int16Array(buf); + var f32buf = new Float32Array(buf); + switch(arg.toString()) { + case '[object Uint8Array]': + i8buf.set(arg.subarray(0, 32)); + break; + case '[object Float32Array]': + f32buf.set(arg.subarray(0, 5)); + break; + case '[object Uint16Array]': + i16buf.set(arg.subarray(0, 16)); + break; + default: + alert('unknown array for debugging: ' + arg); + throw 'see alert'; + } + var ret = '{' + arg.byteLength + ':\n'; + var arr = Array.prototype.slice.call(i8buf); + ret += 'i8:' + arr.toString().replace(/,/g, ',') + '\n'; + arr = Array.prototype.slice.call(f32buf, 0, 8); + ret += 'f32:' + arr.toString().replace(/,/g, ',') + '}'; + return ret; + } + if (typeof arg == 'object') { + printObjectList.push(arg); + return '<' + arg + '|' + (printObjectList.length-1) + '>'; + } + if (typeof arg == 'number') { + if (arg > 0) return '0x' + arg.toString(16) + ' (' + arg + ')'; + } + return arg; +} +#endif + +function makeBigInt(low, high, unsigned) { + return unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0)); +} + +function dynCall(sig, ptr, args) { + if (args && args.length) { +#if ASSERTIONS + assert(args.length == sig.length-1); +#endif +#if ASSERTIONS + assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); +#endif + return Module['dynCall_' + sig].apply(null, [ptr].concat(args)); + } else { +#if ASSERTIONS + assert(sig.length == 1); +#endif +#if ASSERTIONS + assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); +#endif + return Module['dynCall_' + sig].call(null, ptr); + } +} + +#if RELOCATABLE +// tempRet0 is normally handled in the module. but in relocatable code, +// we need to share a single one among all the modules, so they all call +// out. +var tempRet0 = 0; + +var setTempRet0 = function(value) { + tempRet0 = value; +} + +var getTempRet0 = function() { + return tempRet0; +} +#endif // RELOCATABLE + +#if RETAIN_COMPILER_SETTINGS +var compilerSettings = {{{ JSON.stringify(makeRetainedCompilerSettings()) }}} ; + +function getCompilerSetting(name) { + if (!(name in compilerSettings)) return 'invalid compiler setting: ' + name; + return compilerSettings[name]; +} +#else // RETAIN_COMPILER_SETTINGS +#if ASSERTIONS +function getCompilerSetting(name) { + throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for getCompilerSetting or emscripten_get_compiler_setting to work'; +} +#endif // ASSERTIONS +#endif // RETAIN_COMPILER_SETTINGS + +var Runtime = { + // FIXME backwards compatibility layer for ports. Support some Runtime.* + // for now, fix it there, then remove it from here. That way we + // can minimize any period of breakage. + dynCall: dynCall, // for SDL2 port +#if ASSERTIONS + // helpful errors + getTempRet0: function() { abort('getTempRet0() is now a top-level function, after removing the Runtime object. Remove "Runtime."') }, + staticAlloc: function() { abort('staticAlloc() is now a top-level function, after removing the Runtime object. Remove "Runtime."') }, + stackAlloc: function() { abort('stackAlloc() is now a top-level function, after removing the Runtime object. Remove "Runtime."') }, +#endif +}; + +// The address globals begin at. Very low in memory, for code size and optimization opportunities. +// Above 0 is static memory, starting with globals. +// Then the stack. +// Then 'dynamic' memory for sbrk. +var GLOBAL_BASE = {{{ GLOBAL_BASE }}}; + +#if RELOCATABLE +GLOBAL_BASE = alignMemory(GLOBAL_BASE, {{{ MAX_GLOBAL_ALIGN || 1 }}}); +#endif + diff --git a/emsdk/patches/async-module-loading.patch b/emsdk/patches/async-module-loading.patch deleted file mode 100644 index ecab6c75f..000000000 --- a/emsdk/patches/async-module-loading.patch +++ /dev/null @@ -1,669 +0,0 @@ -diff --git a/emsdk/emscripten/tag-1.38.4/src/library.js b/emsdk/emscripten/tag-1.38.4/src/library.js -index 5fc87ab16..82ebcfd61 100644 ---- a/emsdk/emscripten/tag-1.38.4/src/library.js -+++ b/emsdk/emscripten/tag-1.38.4/src/library.js -@@ -1012,7 +1012,7 @@ LibraryManager.library = { - llvm_va_copy: function(ppdest, ppsrc) { - // copy the list start - {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; -- -+ - // copy the list's current offset (will be advanced with each call to va_arg) - {{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; - }, -@@ -1755,39 +1755,44 @@ LibraryManager.library = { - return handle; - } - -+ var lib_module; - if (filename === '__self__') { - var handle = -1; -- var lib_module = Module; -+ lib_module = Module; - } else { -- var target = FS.findObject(filename); -- if (!target || target.isFolder || target.isDevice) { -- DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename; -- return 0; -- } -- FS.forceLoadFile(target); -+ if (Module['preloadedWasm'] !== undefined && -+ Module['preloadedWasm'][filename] !== undefined) { -+ lib_module = Module['preloadedWasm'][filename]; -+ } else { -+ var target = FS.findObject(filename); -+ if (!target || target.isFolder || target.isDevice) { -+ DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename; -+ return 0; -+ } -+ FS.forceLoadFile(target); - -- var lib_module; -- try { -+ try { - #if WASM -- // the shared library is a shared wasm library (see tools/shared.py WebAssembly.make_shared_library) -- var lib_data = FS.readFile(filename, { encoding: 'binary' }); -- if (!(lib_data instanceof Uint8Array)) lib_data = new Uint8Array(lib_data); -- //Module.printErr('libfile ' + filename + ' size: ' + lib_data.length); -- lib_module = loadWebAssemblyModule(lib_data); -+ // the shared library is a shared wasm library (see tools/shared.py WebAssembly.make_shared_library) -+ var lib_data = FS.readFile(filename, { encoding: 'binary' }); -+ if (!(lib_data instanceof Uint8Array)) lib_data = new Uint8Array(lib_data); -+ //Module.printErr('libfile ' + filename + ' size: ' + lib_data.length); -+ lib_module = loadWebAssemblyModule(lib_data); - #else -- // the shared library is a JS file, which we eval -- var lib_data = FS.readFile(filename, { encoding: 'utf8' }); -- lib_module = eval(lib_data)( -- alignFunctionTables(), -- Module -- ); -+ // the shared library is a JS file, which we eval -+ var lib_data = FS.readFile(filename, { encoding: 'utf8' }); -+ lib_module = eval(lib_data)( -+ alignFunctionTables(), -+ Module -+ ); - #endif -- } catch (e) { -+ } catch (e) { - #if ASSERTIONS -- Module.printErr('Error in loading dynamic library: ' + e); -+ Module.printErr('Error in loading dynamic library: ' + e); - #endif -- DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename + '\n' + e; -- return 0; -+ DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename + '\n' + e; -+ return 0; -+ } - } - - // Not all browsers support Object.keys(). -@@ -2224,7 +2229,7 @@ LibraryManager.library = { - newDate.setFullYear(newDate.getFullYear()+1); - } - } else { -- // we stay in current month -+ // we stay in current month - newDate.setDate(newDate.getDate()+days); - return newDate; - } -@@ -2336,7 +2341,7 @@ LibraryManager.library = { - } else { - return thisDate.getFullYear(); - } -- } else { -+ } else { - return thisDate.getFullYear()-1; - } - }; -@@ -2365,16 +2370,16 @@ LibraryManager.library = { - return leadingSomething(date.tm_mday, 2, ' '); - }, - '%g': function(date) { -- // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. -- // In this system, weeks begin on a Monday and week 1 of the year is the week that includes -- // January 4th, which is also the week that includes the first Thursday of the year, and -- // is also the first week that contains at least four days in the year. -- // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of -- // the last week of the preceding year; thus, for Saturday 2nd January 1999, -- // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, -- // or 31st is a Monday, it and any following days are part of week 1 of the following year. -+ // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. -+ // In this system, weeks begin on a Monday and week 1 of the year is the week that includes -+ // January 4th, which is also the week that includes the first Thursday of the year, and -+ // is also the first week that contains at least four days in the year. -+ // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of -+ // the last week of the preceding year; thus, for Saturday 2nd January 1999, -+ // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, -+ // or 31st is a Monday, it and any following days are part of week 1 of the following year. - // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. -- -+ - return getWeekBasedYear(date).toString().substring(2); - }, - '%G': function(date) { -@@ -2420,13 +2425,13 @@ LibraryManager.library = { - return day.getDay() || 7; - }, - '%U': function(date) { -- // Replaced by the week number of the year as a decimal number [00,53]. -- // The first Sunday of January is the first day of week 1; -+ // Replaced by the week number of the year as a decimal number [00,53]. -+ // The first Sunday of January is the first day of week 1; - // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] - var janFirst = new Date(date.tm_year+1900, 0, 1); - var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); - var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); -- -+ - // is target date after the first Sunday? - if (compareByDay(firstSunday, endDate) < 0) { - // calculate difference in days between first Sunday and endDate -@@ -2439,10 +2444,10 @@ LibraryManager.library = { - return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; - }, - '%V': function(date) { -- // Replaced by the week number of the year (Monday as the first day of the week) -- // as a decimal number [01,53]. If the week containing 1 January has four -- // or more days in the new year, then it is considered week 1. -- // Otherwise, it is the last week of the previous year, and the next week is week 1. -+ // Replaced by the week number of the year (Monday as the first day of the week) -+ // as a decimal number [01,53]. If the week containing 1 January has four -+ // or more days in the new year, then it is considered week 1. -+ // Otherwise, it is the last week of the previous year, and the next week is week 1. - // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] - var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); - var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); -@@ -2455,7 +2460,7 @@ LibraryManager.library = { - if (compareByDay(endDate, firstWeekStartThisYear) < 0) { - // if given date is before this years first week, then it belongs to the 53rd week of last year - return '53'; -- } -+ } - - if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { - // if given date is after next years first week, then it belongs to the 01th week of next year -@@ -2478,8 +2483,8 @@ LibraryManager.library = { - return day.getDay(); - }, - '%W': function(date) { -- // Replaced by the week number of the year as a decimal number [00,53]. -- // The first Monday of January is the first day of week 1; -+ // Replaced by the week number of the year as a decimal number [00,53]. -+ // The first Monday of January is the first day of week 1; - // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] - var janFirst = new Date(date.tm_year, 0, 1); - var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); -@@ -2528,7 +2533,7 @@ LibraryManager.library = { - var bytes = intArrayFromString(pattern, false); - if (bytes.length > maxsize) { - return 0; -- } -+ } - - writeArrayToMemory(bytes, s); - return bytes.length-1; -@@ -2569,7 +2574,7 @@ LibraryManager.library = { - for (var matcher in EQUIVALENT_MATCHERS) { - pattern = pattern.replace(matcher, EQUIVALENT_MATCHERS[matcher]); - } -- -+ - // TODO: take care of locale - - var DATE_PATTERNS = { -@@ -2599,7 +2604,7 @@ LibraryManager.library = { - var DAY_NUMBERS_MON_FIRST = {MON: 0, TUE: 1, WED: 2, THU: 3, FRI: 4, SAT: 5, SUN: 6}; - - for (var datePattern in DATE_PATTERNS) { -- pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')'); -+ pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')'); - } - - // take care of capturing groups -@@ -2687,7 +2692,7 @@ LibraryManager.library = { - } else if ((value=getMatch('b'))) { - // parse from month name - date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; -- // TODO: derive month from day in year+year, week number+day of week+year -+ // TODO: derive month from day in year+year, week number+day of week+year - } - - // day -@@ -2709,12 +2714,12 @@ LibraryManager.library = { - var weekDay = value.substring(0,3).toUpperCase(); - if ((value=getMatch('U'))) { - // ... and week number (Sunday being first day of week) -- // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. -+ // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Sunday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; - var weekNumber = parseInt(value); - -- // January 1st -+ // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay() === 0) { -@@ -2728,12 +2733,12 @@ LibraryManager.library = { - date.month = endDate.getMonth(); - } else if ((value=getMatch('W'))) { - // ... and week number (Monday being first day of week) -- // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. -+ // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Monday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; - var weekNumber = parseInt(value); - -- // January 1st -+ // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay()===1) { -@@ -2755,10 +2760,10 @@ LibraryManager.library = { - tm_hour int hours since midnight 0-23 - tm_mday int day of the month 1-31 - tm_mon int months since January 0-11 -- tm_year int years since 1900 -+ tm_year int years since 1900 - tm_wday int days since Sunday 0-6 - tm_yday int days since January 1 0-365 -- tm_isdst int Daylight Saving Time flag -+ tm_isdst int Daylight Saving Time flag - */ - - var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); -@@ -2775,7 +2780,7 @@ LibraryManager.library = { - // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F - // TODO: not sure that intArrayFromString handles all unicode characters correctly - return buf+intArrayFromString(matches[0]).length-1; -- } -+ } - - return 0; - }, -@@ -2854,7 +2859,7 @@ LibraryManager.library = { - // ========================================================================== - // sys/timeb.h - // ========================================================================== -- -+ - ftime: function(p) { - var millis = Date.now(); - {{{ makeSetValue('p', C_STRUCTS.timeb.time, '(millis/1000)|0', 'i32') }}}; -@@ -3884,7 +3889,7 @@ LibraryManager.library = { - - if (val in GAI_ERRNO_MESSAGES) { - if (GAI_ERRNO_MESSAGES[val].length > buflen - 1) { -- msg = 'Message too long'; // EMSGSIZE message. This should never occur given the GAI_ERRNO_MESSAGES above. -+ msg = 'Message too long'; // EMSGSIZE message. This should never occur given the GAI_ERRNO_MESSAGES above. - } else { - msg = GAI_ERRNO_MESSAGES[val]; - } -@@ -3958,7 +3963,7 @@ LibraryManager.library = { - // struct protoent *getprotoent(void); - // reads the next entry from the protocols 'database' or return NULL if 'eof' - if (_setprotoent.index === Protocols.list.length) { -- return 0; -+ return 0; - } else { - var result = Protocols.list[_setprotoent.index++]; - return result; -@@ -4140,14 +4145,14 @@ LibraryManager.library = { - while (stack_args[1].indexOf('_emscripten_') >= 0) - stack_args = __emscripten_traverse_stack(stack_args[0]); - } -- -+ - // Process all lines: - var lines = callstack.split('\n'); - callstack = ''; - var newFirefoxRe = new RegExp('\\s*(.*?)@(.*?):([0-9]+):([0-9]+)'); // New FF30 with column info: extract components of form ' Object._main@http://server.com:4324:12' - var firefoxRe = new RegExp('\\s*(.*?)@(.*):(.*)(:(.*))?'); // Old FF without column info: extract components of form ' Object._main@http://server.com:4324' - var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)'); // Extract components of form ' at Object._main (http://server.com/file.html:4324:12)' -- -+ - for (var l in lines) { - var line = lines[l]; - -@@ -4201,7 +4206,7 @@ LibraryManager.library = { - } - callstack += (haveSourceMap ? (' = '+jsSymbolName) : (' at '+cSymbolName)) + ' (' + file + ':' + lineno + ':' + column + ')\n'; - } -- -+ - // If we are still keeping track with the callstack by traversing via 'arguments.callee', print the function parameters as well. - if (flags & 128 /*EM_LOG_FUNC_PARAMS*/ && stack_args[0]) { - if (stack_args[1] == jsSymbolName && stack_args[2].length > 0) { -@@ -4792,4 +4797,3 @@ function autoAddDeps(object, name) { - } - } - } -- -diff --git a/emsdk/emscripten/tag-1.38.4/src/library_browser.js b/emsdk/emscripten/tag-1.38.4/src/library_browser.js -index 36738391e..e2ac2763e 100644 ---- a/emsdk/emscripten/tag-1.38.4/src/library_browser.js -+++ b/emsdk/emscripten/tag-1.38.4/src/library_browser.js -@@ -225,6 +225,35 @@ var LibraryBrowser = { - }; - Module['preloadPlugins'].push(audioPlugin); - -+#if WASM -+#if MAIN_MODULE -+ var wasmPlugin = {}; -+ wasmPlugin['asyncWasmLoadPromise'] = new Promise( -+ function(resolve, reject) { return resolve(); }); -+ wasmPlugin['canHandle'] = function(name) { -+ return !Module.noWasmDecoding && (name.endsWith('.so') || name.endsWith('.wasm')); -+ }; -+ wasmPlugin['handle'] = function(byteArray, name, onload, onerror) { -+ // loadWebAssemblyModule can not load modules out-of-order, so rather -+ // than just running the promises in parallel, this makes a chain of -+ // promises to run in series. -+ this['asyncWasmLoadPromise'] = this['asyncWasmLoadPromise'].then( -+ function() { -+ return loadWebAssemblyModule(byteArray, true); -+ }).then( -+ function(module) { -+ Module['preloadedWasm'][name] = module; -+ onload(); -+ }, -+ function(err) { -+ console.warn("Couldn't instantiate wasm: " + name + " '" + err + "'"); -+ onerror(); -+ }); -+ }; -+ Module['preloadPlugins'].push(wasmPlugin); -+#endif // MAIN_MODULE -+#endif // WASM -+ - // Canvas event setup - - function pointerLockChange() { -@@ -237,7 +266,7 @@ var LibraryBrowser = { - if (canvas) { - // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module - // Module['forcedAspectRatio'] = 4 / 3; -- -+ - canvas.requestPointerLock = canvas['requestPointerLock'] || - canvas['mozRequestPointerLock'] || - canvas['webkitRequestPointerLock'] || -@@ -490,7 +519,7 @@ var LibraryBrowser = { - 'mp3': 'audio/mpeg' - }[name.substr(name.lastIndexOf('.')+1)]; - }, -- -+ - getUserMedia: function(func) { - if(!window.getUserMedia) { - window.getUserMedia = navigator['getUserMedia'] || -@@ -524,13 +553,13 @@ var LibraryBrowser = { - getMouseWheelDelta: function(event) { - var delta = 0; - switch (event.type) { -- case 'DOMMouseScroll': -+ case 'DOMMouseScroll': - delta = event.detail; - break; -- case 'mousewheel': -+ case 'mousewheel': - delta = event.wheelDelta; - break; -- case 'wheel': -+ case 'wheel': - delta = event['deltaY']; - break; - default: -@@ -558,7 +587,7 @@ var LibraryBrowser = { - Browser.mouseMovementX = Browser.getMovementX(event); - Browser.mouseMovementY = Browser.getMovementY(event); - } -- -+ - // check if SDL is available - if (typeof SDL != "undefined") { - Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; -@@ -568,7 +597,7 @@ var LibraryBrowser = { - // FIXME: ideally this should be clamped against the canvas size and zero - Browser.mouseX += Browser.mouseMovementX; - Browser.mouseY += Browser.mouseMovementY; -- } -+ } - } else { - // Otherwise, calculate the movement based on the changes - // in the coordinates. -@@ -600,7 +629,7 @@ var LibraryBrowser = { - adjustedY = adjustedY * (ch / rect.height); - - var coords = { x: adjustedX, y: adjustedY }; -- -+ - if (event.type === 'touchstart') { - Browser.lastTouches[touch.identifier] = coords; - Browser.touches[touch.identifier] = coords; -@@ -609,7 +638,7 @@ var LibraryBrowser = { - if (!last) last = coords; - Browser.lastTouches[touch.identifier] = last; - Browser.touches[touch.identifier] = coords; -- } -+ } - return; - } - -@@ -1127,10 +1156,10 @@ var LibraryBrowser = { - } - console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers); - Browser.mainLoop.updateStatus(); -- -+ - // catches pause/resume main loop from blocker execution - if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; -- -+ - setTimeout(Browser.mainLoop.runner, 0); - return; - } -@@ -1300,7 +1329,7 @@ var LibraryBrowser = { - emscripten_set_canvas_size: function(width, height) { - Browser.setCanvasSize(width, height); - }, -- -+ - emscripten_get_canvas_size__proxy: 'sync', - emscripten_get_canvas_size__sig: 'viii', - emscripten_get_canvas_size: function(width, height, isFullscreen) { -@@ -1488,4 +1517,3 @@ function slowLog(label, text) { - } - - */ -- -diff --git a/emsdk/emscripten/tag-1.38.4/src/preamble.js b/emsdk/emscripten/tag-1.38.4/src/preamble.js -index a757e8300..c7b1f596f 100644 ---- a/emsdk/emscripten/tag-1.38.4/src/preamble.js -+++ b/emsdk/emscripten/tag-1.38.4/src/preamble.js -@@ -1822,6 +1822,11 @@ function removeRunDependency(id) { - - Module["preloadedImages"] = {}; // maps url to image data - Module["preloadedAudios"] = {}; // maps url to audio data -+#if WASM -+#if MAIN_MODULE -+Module["preloadedWasm"] = {}; // maps url to wasm instance exports -+#endif // MAIN_MODULE -+#endif // WASM - - #if PGO - var PGOMonitor = { -diff --git a/emsdk/emscripten/tag-1.38.4/src/support.js b/emsdk/emscripten/tag-1.38.4/src/support.js -index f6c9842ff..99367db70 100644 ---- a/emsdk/emscripten/tag-1.38.4/src/support.js -+++ b/emsdk/emscripten/tag-1.38.4/src/support.js -@@ -86,7 +86,7 @@ function loadDynamicLibrary(lib) { - - #if WASM - // Loads a side module from binary data --function loadWebAssemblyModule(binary) { -+function loadWebAssemblyModule(binary, loadAsync) { - var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer); - assert(int32View[0] == 0x6d736100, 'need to see wasm magic number'); // \0wasm - // we should see the dylink section right after the magic number and wasm version -@@ -166,59 +166,71 @@ function loadWebAssemblyModule(binary) { - oldTable.push(table.get(i)); - } - #endif -- // create a module from the instance -- var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), info); -+ -+ function postInstantiation(instance) { -+ var exports = {}; - #if ASSERTIONS -- // the table should be unchanged -- assert(table === originalTable); -- assert(table === Module['wasmTable']); -- if (instance.exports['table']) { -- assert(table === instance.exports['table']); -- } -- // the old part of the table should be unchanged -- for (var i = 0; i < oldTableSize; i++) { -- assert(table.get(i) === oldTable[i], 'old table entries must remain the same'); -- } -- // verify that the new table region was filled in -- for (var i = 0; i < tableSize; i++) { -- assert(table.get(oldTableSize + i) !== undefined, 'table entry was not filled in'); -- } --#endif -- var exports = {}; -- for (var e in instance.exports) { -- var value = instance.exports[e]; -- if (typeof value === 'object') { -- // a breaking change in the wasm spec, globals are now objects -- // https://github.com/WebAssembly/mutable-global/issues/1 -- value = value.value; -+ // the table should be unchanged -+ assert(table === originalTable); -+ assert(table === Module['wasmTable']); -+ if (instance.exports['table']) { -+ assert(table === instance.exports['table']); -+ } -+ // the old part of the table should be unchanged -+ for (var i = 0; i < oldTableSize; i++) { -+ assert(table.get(i) === oldTable[i], 'old table entries must remain the same'); -+ } -+ // verify that the new table region was filled in -+ for (var i = 0; i < tableSize; i++) { -+ assert(table.get(oldTableSize + i) !== undefined, 'table entry was not filled in'); - } -- if (typeof value === 'number') { -- // relocate it - modules export the absolute value, they can't relocate before they export -+#endif -+ for (var e in instance.exports) { -+ var value = instance.exports[e]; -+ if (typeof value === 'object') { -+ // a breaking change in the wasm spec, globals are now objects -+ // https://github.com/WebAssembly/mutable-global/issues/1 -+ value = value.value; -+ } -+ if (typeof value === 'number') { -+ // relocate it - modules export the absolute value, they can't relocate before they export - #if EMULATED_FUNCTION_POINTERS -- // it may be a function pointer -- if (e.substr(0, 3) == 'fp$' && typeof instance.exports[e.substr(3)] === 'function') { -- value = value + env['tableBase']; -- } else { -+ // it may be a function pointer -+ if (e.substr(0, 3) == 'fp$' && typeof instance.exports[e.substr(3)] === 'function') { -+ value = value + env['tableBase']; -+ } else { - #endif -- value = value + env['memoryBase']; -+ value = value + env['memoryBase']; - #if EMULATED_FUNCTION_POINTERS -- } -+ } - #endif -+ } -+ exports[e] = value; - } -- exports[e] = value; -- } -- // initialize the module -- var init = exports['__post_instantiate']; -- if (init) { -- if (runtimeInitialized) { -- init(); -- } else { -- // we aren't ready to run compiled code yet -- __ATINIT__.push(init); -+ // initialize the module -+ var init = exports['__post_instantiate']; -+ if (init) { -+ if (runtimeInitialized) { -+ init(); -+ } else { -+ // we aren't ready to run compiled code yet -+ __ATINIT__.push(init); -+ } - } -+ return exports; -+ } -+ -+ if (loadAsync) { -+ return WebAssembly.instantiate(binary, info).then(function(result) { -+ return postInstantiation(result.instance); -+ }); -+ } else { -+ var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), info); -+ return postInstantiation(instance); - } -- return exports; - } -+Module['loadWebAssemblyModule'] = loadWebAssemblyModule; -+ - #endif // WASM - #endif // RELOCATABLE - -diff --git a/emsdk/emscripten/tag-1.38.4/tests/test_browser.py b/emsdk/emscripten/tag-1.38.4/tests/test_browser.py -index d5f49d04e..c85367f72 100644 ---- a/emsdk/emscripten/tag-1.38.4/tests/test_browser.py -+++ b/emsdk/emscripten/tag-1.38.4/tests/test_browser.py -@@ -241,12 +241,12 @@ If manually bisecting: - printf("|%%s|\n", buf); - - int result = !strcmp("load me right before", buf); -- -+ - f = fopen("%s", "r"); - if (f == NULL) - result = 0; - fclose(f); -- -+ - f = fopen("%s", "r"); - if (f != NULL) - result = 0; -@@ -2273,6 +2273,43 @@ void *getBindBuffer() { - Popen([PYTHON, EMCC, path_from_root('tests', 'browser_module.cpp'), '-o', 'module.js', '-O2', '-s', 'SIDE_MODULE=1', '-s', 'DLOPEN_SUPPORT=1', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two"]']).communicate() - self.btest('browser_main.cpp', args=['-O2', '-s', 'MAIN_MODULE=1', '-s', 'DLOPEN_SUPPORT=1'], expected='8') - -+ def test_preload_module(self): -+ expected = 'hello from main\nhello from library' -+ open('library.c', 'w').write(r''' -+ #include -+ int library_func() { -+ return 42; -+ } -+ ''') -+ run_process([PYTHON, EMCC, 'library.c', '-s', 'SIDE_MODULE=1', '-O2', '-o', 'library.wasm', '-s', 'WASM=1']) -+ main = r''' -+ #include -+ #include -+ #include -+ int main() { -+ EM_ASM( -+ console.log(Object.keys(Module['preloadedWasm'])); -+ if (Module['preloadedWasm']['/library.wasm'] === undefined) { -+ throw Error("Side module not preloaded"); -+ } -+ ); -+ void *lib_handle = dlopen("/library.wasm", 0); -+ if (!lib_handle) { -+ return 1; -+ } -+ typedef int (*voidfunc)(); -+ voidfunc x = (voidfunc)dlsym(lib_handle, "library_func"); -+ if (!x) return 1; -+ if (x() != 42) return 1; -+ REPORT_RESULT(0); -+ return 0; -+ } -+ ''' -+ self.btest( -+ main, -+ args=['-s', 'MAIN_MODULE=1', '--preload-file', '.@/', '-O2', '-s', 'WASM=1', '--use-preload-plugins'], -+ expected='0') -+ - def test_mmap_file(self): - open(self.in_dir('data.dat'), 'w').write('data from the file ' + ('.' * 9000)) - for extra_args in [[], ['--no-heap-copy']]: