diff --git a/emcc b/emcc index 1abf0057e0481..498837678d2a8 100755 --- a/emcc +++ b/emcc @@ -858,8 +858,12 @@ try: # Apply -s settings in newargs here (after optimization levels, so they can override them) for change in settings_changes: key, value = change.split('=') + original_exported_response = False + if value[0] == '@': if key not in DEFERRED_REPONSE_FILES: + if key == 'EXPORTED_FUNCTIONS': + original_exported_response = value value = open(value[1:]).read() else: value = '"' + value + '"' @@ -867,7 +871,8 @@ try: value = value.replace('\\', '\\\\') exec('shared.Settings.' + key + ' = ' + value) if key == 'EXPORTED_FUNCTIONS': - shared.Settings.ORIGINAL_EXPORTED_FUNCTIONS = shared.Settings.EXPORTED_FUNCTIONS[:] # used for warnings in emscripten.py + # used for warnings in emscripten.py + shared.Settings.ORIGINAL_EXPORTED_FUNCTIONS = original_exported_response or shared.Settings.EXPORTED_FUNCTIONS[:] try: assert shared.Settings.ASM_JS > 0, 'ASM_JS must be enabled in fastcomp' @@ -916,14 +921,16 @@ try: if shared.Settings.MAIN_MODULE: assert not shared.Settings.SIDE_MODULE - shared.Settings.INCLUDE_FULL_LIBRARY = 1 + if shared.Settings.MAIN_MODULE != 2: + shared.Settings.INCLUDE_FULL_LIBRARY = 1 elif shared.Settings.SIDE_MODULE: assert not shared.Settings.MAIN_MODULE memory_init_file = False # memory init file is not supported with side modules, must be executable synchronously (for dlopen) if shared.Settings.MAIN_MODULE or shared.Settings.SIDE_MODULE: assert shared.Settings.ASM_JS, 'module linking requires asm.js output (-s ASM_JS=1)' - shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules + if shared.Settings.MAIN_MODULE != 2: + shared.Settings.LINKABLE = 1 shared.Settings.RELOCATABLE = 1 shared.Settings.PRECISE_I64_MATH = 1 # other might use precise math, we need to be able to print it assert not use_closure_compiler, 'cannot use closure compiler on shared modules' @@ -953,7 +960,8 @@ try: if shared.Settings.RELOCATABLE: assert shared.Settings.GLOBAL_BASE < 1 - shared.Settings.EMULATED_FUNCTION_POINTERS = 1 + if shared.Settings.EMULATED_FUNCTION_POINTERS == 0: + shared.Settings.EMULATED_FUNCTION_POINTERS = 2 # by default, use optimized function pointer emulation shared.Settings.ERROR_ON_UNDEFINED_SYMBOLS = shared.Settings.WARN_ON_UNDEFINED_SYMBOLS = 0 if not shared.Settings.SIDE_MODULE: shared.Settings.EXPORT_ALL = 1 diff --git a/emscripten-version.txt b/emscripten-version.txt index 6c1f386048777..08666e2303041 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.34.5 +1.34.6 diff --git a/emscripten.py b/emscripten.py index bf8c47fc08fc7..edb67b390705d 100755 --- a/emscripten.py +++ b/emscripten.py @@ -238,7 +238,8 @@ def save_settings(): # merge forwarded data settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] - all_exported_functions = set(settings['EXPORTED_FUNCTIONS']) # both asm.js and otherwise + all_exported_functions = set(shared.expand_response(settings['EXPORTED_FUNCTIONS'])) # both asm.js and otherwise + for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented all_exported_functions.add('_' + additional_export) if settings['EXPORT_FUNCTION_TABLES']: @@ -614,10 +615,15 @@ def keyfunc(other): if settings.get('RELOCATABLE'): params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) - coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' - mini_coerced_params = make_coerced_params(sig) + coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, unfloat(sig[p+1]), settings)) for p in range(len(sig)-1)]) + ';' + mini_coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)]) maybe_return = '' if sig[0] == 'v' else 'return' - funcs_js.append(make_func('mftCall_' + sig, 'if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + mini_coerced_params + ')', sig[0], settings) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', sig[0], settings) + ';', params, coercions) + '\n') + final_return = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', unfloat(sig[0]), settings) + ';' + if settings['EMULATED_FUNCTION_POINTERS'] == 1: + body = final_return + else: + body = 'if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + mini_coerced_params + ')', sig[0], settings, ffi_arg=True) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + final_return + funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n') def quote(prop): if settings['USE_CLOSURE_COMPILER'] == 2: @@ -976,17 +982,20 @@ def main(args, compiler_engine, cache, temp_files, DEBUG, DEBUG_CACHE): emscript(args.infile, settings, args.outfile, libraries, compiler_engine=compiler_engine, temp_files=temp_files, DEBUG=DEBUG, DEBUG_CACHE=DEBUG_CACHE) -def _main(environ): +def _main(args=None): + if args is None: + args = sys.argv[1:] + response_file = True while response_file: response_file = None - for index in range(1, len(sys.argv)): - if sys.argv[index][0] == '@': + for index in range(len(args)): + if args[index][0] == '@': # found one, loop again next time response_file = True - response_file_args = read_response_file(sys.argv[index]) + response_file_args = read_response_file(args[index]) # slice in extra_args in place of the response file arg - sys.argv[index:index+1] = response_file_args + args[index:index+1] = response_file_args break parser = optparse.OptionParser( @@ -1028,11 +1037,11 @@ def _main(environ): help='Hides debug output') parser.add_option('--suppressUsageWarning', action='store_true', - default=environ.get('EMSCRIPTEN_SUPPRESS_USAGE_WARNING'), + default=os.environ.get('EMSCRIPTEN_SUPPRESS_USAGE_WARNING'), help=('Suppress usage warning')) # Convert to the same format that argparse would have produced. - keywords, positional = parser.parse_args() + keywords, positional = parser.parse_args(args) if not keywords.suppressUsageWarning: logging.warning(''' @@ -1077,4 +1086,5 @@ def _main(environ): )) if __name__ == '__main__': - _main(environ=os.environ) + _main() + diff --git a/site/source/docs/api_reference/Filesystem-API.rst b/site/source/docs/api_reference/Filesystem-API.rst index bd00e8386cb57..1af6773ce195d 100644 --- a/site/source/docs/api_reference/Filesystem-API.rst +++ b/site/source/docs/api_reference/Filesystem-API.rst @@ -51,7 +51,6 @@ This file system lets a program in *node* directly access files on the local fil See `this test `_ for an example. - .. _filesystem-api-idbfs: IDBFS @@ -63,6 +62,14 @@ The *IDBFS* file system implements the :js:func:`FS.syncfs` interface, which whe This is provided to overcome the limitation that browsers do not offer synchronous APIs for persistent storage, and so (by default) all writes exist only temporarily in-memory. +.. _filesystem-api-workerfs: + +WORKERFS +----- + +.. note:: This file system is only for use when running code inside a worker. + +This file system provides read-only access to ``File`` and ``Blob`` objects inside a worker without copying the entire data into memory and can potentially be used for huge files. Devices ======= @@ -134,6 +141,17 @@ File system API FS.mkdir('/working'); FS.mount(NODEFS, { root: '.' }, '/working'); + ``WORKERFS`` accepts `files` and `blobs` parameters to map provided flat list of files into the ``mountpoint`` directory: + + :: + + var blob = new Blob(['blob data']); + FS.mkdir('/working'); + FS.mount(NODEFS, { + blobs: [{ name: 'blob.txt', data: blob }], + files: files, // Array of File objects or FileList + }, '/working'); + :param string mountpoint: A path to an existing local Emscripten directory where the file system is to be mounted. It can be either an absolute path, or something relative to the current directory. diff --git a/src/library_fs.js b/src/library_fs.js index c97829304cf8d..f506bdc2f6a2f 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1,5 +1,5 @@ mergeInto(LibraryManager.library, { - $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr'], + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', '$WORKERFS', 'stdin', 'stdout', 'stderr'], $FS__postset: 'FS.staticInit();' + '__ATINIT__.unshift(function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() });' + '__ATMAIN__.push(function() { FS.ignorePermissions = false });' + diff --git a/src/library_workerfs.js b/src/library_workerfs.js new file mode 100644 index 0000000000000..9a30a14e391fc --- /dev/null +++ b/src/library_workerfs.js @@ -0,0 +1,114 @@ +mergeInto(LibraryManager.library, { + $WORKERFS__deps: ['$FS'], + $WORKERFS: { + DIR_MODE: {{{ cDefine('S_IFDIR') }}} | 511 /* 0777 */, + FILE_MODE: {{{ cDefine('S_IFREG') }}} | 511 /* 0777 */, + reader: null, + mount: function (mount) { + assert(ENVIRONMENT_IS_WORKER); + if (!WORKERFS.reader) WORKERFS.reader = new FileReaderSync(); + var root = WORKERFS.createNode(null, '/', WORKERFS.DIR_MODE, 0); + // We also accept FileList here. + Array.prototype.forEach.call(mount.opts["files"] || [], function(file) { + WORKERFS.createNode(root, file.name, WORKERFS.FILE_MODE, 0, file, file.lastModifiedDate); + }); + (mount.opts["blobs"] || []).forEach(function(obj) { + WORKERFS.createNode(root, obj["name"], WORKERFS.FILE_MODE, 0, obj["data"]); + }); + return root; + }, + createNode: function (parent, name, mode, dev, contents, mtime) { + var node = FS.createNode(parent, name, mode); + node.mode = mode; + node.node_ops = WORKERFS.node_ops; + node.stream_ops = WORKERFS.stream_ops; + node.timestamp = (mtime || new Date).getTime(); + if (parent) { + node.size = contents.size; + node.contents = contents; + parent.contents[name] = node; + } else { + node.size = 4096; + node.contents = {}; + } + return node; + }, + node_ops: { + getattr: function(node) { + return { + dev: 1, + ino: undefined, + mode: node.mode, + nlink: 1, + uid: 0, + gid: 0, + rdev: undefined, + size: node.size, + atime: new Date(node.timestamp), + mtime: new Date(node.timestamp), + ctime: new Date(node.timestamp), + blksize: 4096, + blocks: Math.ceil(node.size / 4096), + }; + }, + setattr: function(node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + }, + lookup: function(parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + }, + mknod: function (parent, name, mode, dev) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + rename: function (oldNode, newDir, newName) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + unlink: function(parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + rmdir: function(parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + readdir: function(node) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + symlink: function(parent, newName, oldPath) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + readlink: function(node) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }, + }, + stream_ops: { + read: function (stream, buffer, offset, length, position) { + if (position >= stream.node.size) return 0; + var chunk = stream.node.contents.slice(position, position + length); + var ab = WORKERFS.reader.readAsArrayBuffer(chunk); + buffer.set(new Uint8Array(ab), offset); + return chunk.size; + }, + write: function (stream, buffer, offset, length, position) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + }, + llseek: function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + position += stream.node.size; + } + } + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return position; + }, + }, + }, +}); diff --git a/src/modules.js b/src/modules.js index dec6335bf4702..e5c85b9e50850 100644 --- a/src/modules.js +++ b/src/modules.js @@ -109,6 +109,7 @@ var LibraryManager = { 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', + 'library_workerfs.js', 'library_tty.js' ]); } diff --git a/src/settings.js b/src/settings.js index 17192918e5109..bed51c0250884 100644 --- a/src/settings.js +++ b/src/settings.js @@ -176,6 +176,16 @@ var EMULATED_FUNCTION_POINTERS = 0; // By default we implement function pointers // function tables, which is very fast. With this option, // we implement them more flexibly by emulating them: we // call out into JS, which handles the function tables. + // 1: Full emulation. This means you can modify the + // table in JS fully dynamically, not just add to + // the end. + // 2: Optimized emulation. Assumes once something is + // added to the table, it will not change. This allows + // dynamic linking while keeping performance fast, + // as we can do a fast call into the internal table + // if the fp is in the right range. Shared modules + // (MAIN_MODULE, SIDE_MODULE) do this by default. + // This requires RELOCATABLE to be set. var EMULATE_FUNCTION_POINTER_CASTS = 0; // Allows function pointers to be cast, wraps each // call of an incorrect type with a runtime correction. // This adds overhead and should not be used normally. @@ -408,6 +418,10 @@ var RELOCATABLE = 0; // If set to 1, we emit relocatable code from the LLVM back var MAIN_MODULE = 0; // A main module is a file compiled in a way that allows us to link it to // a side module using emlink.py. + // 1: Normal main module. + // 2: DCE'd main module. We eliminate dead code normally. If a side + // module needs something from main, it is up to you to make sure + // it is kept alive. var SIDE_MODULE = 0; // Corresponds to MAIN_MODULE var RUNTIME_LINKED_LIBS = []; // If this is a main module (MAIN_MODULE == 1), then diff --git a/src/struct_info.json b/src/struct_info.json index 2928e69274ccf..d4825ce654989 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1432,141 +1432,20 @@ "file": "emscripten/threading.h", "structs": {}, "defines": [ - "EM_PROXIED_FOPEN", - "EM_PROXIED_FGETS", - "EM_PROXIED_FPUTS", - "EM_PROXIED_FCLOSE", - "EM_PROXIED_OPENDIR", - "EM_PROXIED_CLOSEDIR", - "EM_PROXIED_TELLDIR", - "EM_PROXIED_SEEKDIR", - "EM_PROXIED_REWINDDIR", - "EM_PROXIED_READDIR_R", - "EM_PROXIED_READDIR", "EM_PROXIED_UTIME", "EM_PROXIED_UTIMES", - "EM_PROXIED_STAT", - "EM_PROXIED_LSTAT", - "EM_PROXIED_FSTAT", - "EM_PROXIED_MKNOD", - "EM_PROXIED_MKDIR", - "EM_PROXIED_MKFIFO", - "EM_PROXIED_CHMOD", - "EM_PROXIED_FCHMOD", - "EM_PROXIED_LCHMOD", - "EM_PROXIED_UMASK", - "EM_PROXIED_STATVFS", - "EM_PROXIED_FSTATVFS", - "EM_PROXIED_OPEN", - "EM_PROXIED_CREAT", - "EM_PROXIED_MKTEMP", - "EM_PROXIED_MKSTEMP", - "EM_PROXIED_MKDTEMP", - "EM_PROXIED_FCNTL", - "EM_PROXIED_POSIX_FALLOCATE", - "EM_PROXIED_POLL", - "EM_PROXIED_ACCESS", - "EM_PROXIED_CHDIR", - "EM_PROXIED_CHOWN", "EM_PROXIED_CHROOT", - "EM_PROXIED_CLOSE", - "EM_PROXIED_DUP", - "EM_PROXIED_DUP2", - "EM_PROXIED_FCHOWN", - "EM_PROXIED_FCHDIR", - "EM_PROXIED_CTERMID", - "EM_PROXIED_CRYPT", - "EM_PROXIED_ENCRYPT", "EM_PROXIED_FPATHCONF", - "EM_PROXIED_FSYNC", - "EM_PROXIED_TRUNCATE", - "EM_PROXIED_FTRUNCATE", - "EM_PROXIED_GETCWD", - "EM_PROXIED_ISATTY", - "EM_PROXIED_LCHOWN", - "EM_PROXIED_LINK", - "EM_PROXIED_LOCKF", - "EM_PROXIED_LSEEK", - "EM_PROXIED_PIPE", - "EM_PROXIED_PREAD", - "EM_PROXIED_READ", - "EM_PROXIED_RMDIR", - "EM_PROXIED_UNLINK", - "EM_PROXIED_TTYNAME", - "EM_PROXIED_TTYNAME_R", - "EM_PROXIED_SYMLINK", - "EM_PROXIED_READLINK", - "EM_PROXIED_PWRITE", - "EM_PROXIED_WRITE", "EM_PROXIED_CONFSTR", - "EM_PROXIED_GETHOSTNAME", - "EM_PROXIED_GETLOGIN", - "EM_PROXIED_GETLOGIN_R", "EM_PROXIED_SYSCONF", "EM_PROXIED_SBRK", - "EM_PROXIED_CLEARERR", - "EM_PROXIED_FDOPEN", - "EM_PROXIED_FEOF", - "EM_PROXIED_FERROR", - "EM_PROXIED_FFLUSH", - "EM_PROXIED_FGETC", - "EM_PROXIED_GETCHAR", - "EM_PROXIED_FGETPOS", - "EM_PROXIED_GETS", - "EM_PROXIED_FILENO", - "EM_PROXIED_FPUTC", - "EM_PROXIED_PUTCHAR", - "EM_PROXIED_PUTS", - "EM_PROXIED_FREAD", - "EM_PROXIED_FREOPEN", - "EM_PROXIED_FSEEK", - "EM_PROXIED_FSETPOS", - "EM_PROXIED_FTELL", - "EM_PROXIED_FWRITE", - "EM_PROXIED_POPEN", - "EM_PROXIED_PCLOSE", - "EM_PROXIED_PERROR", - "EM_PROXIED_REMOVE", - "EM_PROXIED_RENAME", - "EM_PROXIED_REWIND", - "EM_PROXIED_TMPNAM", - "EM_PROXIED_TEMPNAM", - "EM_PROXIED_TMPFILE", - "EM_PROXIED_UNGETC", - "EM_PROXIED_FSCANF", - "EM_PROXIED_SCANF", - "EM_PROXIED_FPRINTF", - "EM_PROXIED_PRINTF", - "EM_PROXIED_DPRINTF", - "EM_PROXIED_MMAP", - "EM_PROXIED_MUNMAP", "EM_PROXIED_ATEXIT", "EM_PROXIED_GETENV", "EM_PROXIED_CLEARENV", "EM_PROXIED_SETENV", "EM_PROXIED_UNSETENV", "EM_PROXIED_PUTENV", - "EM_PROXIED_REALPATH", - "EM_PROXIED_TCGETATTR", - "EM_PROXIED_TCSETATTR", "EM_PROXIED_TZSET", - "EM_PROXIED_SOCKET", - "EM_PROXIED_BIND", - "EM_PROXIED_SENDMSG", - "EM_PROXIED_RECVMSG", - "EM_PROXIED_SHUTDOWN", - "EM_PROXIED_IOCTL", - "EM_PROXIED_ACCEPT", - "EM_PROXIED_SELECT", - "EM_PROXIED_CONNECT", - "EM_PROXIED_LISTEN", - "EM_PROXIED_GETSOCKNAME", - "EM_PROXIED_GETPEERNAME", - "EM_PROXIED_SEND", - "EM_PROXIED_RECV", - "EM_PROXIED_SENDTO", - "EM_PROXIED_RECVFROM", - "EM_PROXIED_GETSOCKOPT", "EM_PROXIED_PTHREAD_CREATE", "EM_PROXIED_SYSCALL" ] diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index cadb8dd51beb1..4251d46482e37 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -203,6 +203,8 @@ int emscripten_async_prepare(const char* file, em_str_callback_func onload, em_s typedef void (*em_async_prepare_data_onload_func)(void*, const char*); void emscripten_async_prepare_data(char* data, int size, const char *suffix, void *arg, em_async_prepare_data_onload_func onload, em_arg_callback_func onerror); +// worker APIs + typedef int worker_handle; worker_handle emscripten_create_worker(const char *url); @@ -215,6 +217,8 @@ void emscripten_worker_respond_provisionally(char *data, int size); int emscripten_get_worker_queue_size(worker_handle worker); +// misc. + int emscripten_get_compiler_setting(const char *name); void emscripten_debugger(); diff --git a/system/include/emscripten/threading.h b/system/include/emscripten/threading.h index 5b34903f1ccd1..1964789fd3368 100644 --- a/system/include/emscripten/threading.h +++ b/system/include/emscripten/threading.h @@ -112,141 +112,20 @@ void emscripten_main_thread_process_queued_calls(); // Direct syscall access, second argument is a varargs pointer. used in proxying int emscripten_syscall(int, void*); -#define EM_PROXIED_FOPEN 1 -#define EM_PROXIED_FGETS 2 -#define EM_PROXIED_FPUTS 3 -#define EM_PROXIED_FCLOSE 4 -#define EM_PROXIED_OPENDIR 5 -#define EM_PROXIED_CLOSEDIR 6 -#define EM_PROXIED_TELLDIR 7 -#define EM_PROXIED_SEEKDIR 8 -#define EM_PROXIED_REWINDDIR 9 -#define EM_PROXIED_READDIR_R 10 -#define EM_PROXIED_READDIR 11 #define EM_PROXIED_UTIME 12 #define EM_PROXIED_UTIMES 13 -#define EM_PROXIED_STAT 14 -#define EM_PROXIED_LSTAT 15 -#define EM_PROXIED_FSTAT 16 -#define EM_PROXIED_MKNOD 17 -#define EM_PROXIED_MKDIR 18 -#define EM_PROXIED_MKFIFO 19 -#define EM_PROXIED_CHMOD 20 -#define EM_PROXIED_FCHMOD 21 -#define EM_PROXIED_LCHMOD 22 -#define EM_PROXIED_UMASK 23 -#define EM_PROXIED_STATVFS 24 -#define EM_PROXIED_FSTATVFS 25 -#define EM_PROXIED_OPEN 26 -#define EM_PROXIED_CREAT 27 -#define EM_PROXIED_MKTEMP 28 -#define EM_PROXIED_MKSTEMP 29 -#define EM_PROXIED_MKDTEMP 30 -#define EM_PROXIED_FCNTL 31 -#define EM_PROXIED_POSIX_FALLOCATE 32 -#define EM_PROXIED_POLL 33 -#define EM_PROXIED_ACCESS 34 -#define EM_PROXIED_CHDIR 35 -#define EM_PROXIED_CHOWN 36 #define EM_PROXIED_CHROOT 37 -#define EM_PROXIED_CLOSE 38 -#define EM_PROXIED_DUP 39 -#define EM_PROXIED_DUP2 40 -#define EM_PROXIED_FCHOWN 41 -#define EM_PROXIED_FCHDIR 42 -#define EM_PROXIED_CTERMID 43 -#define EM_PROXIED_CRYPT 44 -#define EM_PROXIED_ENCRYPT 45 #define EM_PROXIED_FPATHCONF 46 -#define EM_PROXIED_FSYNC 47 -#define EM_PROXIED_TRUNCATE 48 -#define EM_PROXIED_FTRUNCATE 49 -#define EM_PROXIED_GETCWD 50 -#define EM_PROXIED_ISATTY 52 -#define EM_PROXIED_LCHOWN 53 -#define EM_PROXIED_LINK 54 -#define EM_PROXIED_LOCKF 55 -#define EM_PROXIED_LSEEK 56 -#define EM_PROXIED_PIPE 57 -#define EM_PROXIED_PREAD 58 -#define EM_PROXIED_READ 59 -#define EM_PROXIED_RMDIR 60 -#define EM_PROXIED_UNLINK 61 -#define EM_PROXIED_TTYNAME 62 -#define EM_PROXIED_TTYNAME_R 63 -#define EM_PROXIED_SYMLINK 64 -#define EM_PROXIED_READLINK 65 -#define EM_PROXIED_PWRITE 66 -#define EM_PROXIED_WRITE 67 #define EM_PROXIED_CONFSTR 68 -#define EM_PROXIED_GETHOSTNAME 69 -#define EM_PROXIED_GETLOGIN 70 -#define EM_PROXIED_GETLOGIN_R 71 #define EM_PROXIED_SYSCONF 72 #define EM_PROXIED_SBRK 73 -#define EM_PROXIED_CLEARERR 74 -#define EM_PROXIED_FDOPEN 75 -#define EM_PROXIED_FEOF 76 -#define EM_PROXIED_FERROR 77 -#define EM_PROXIED_FFLUSH 78 -#define EM_PROXIED_FGETC 79 -#define EM_PROXIED_GETCHAR 80 -#define EM_PROXIED_FGETPOS 81 -#define EM_PROXIED_GETS 82 -#define EM_PROXIED_FILENO 83 -#define EM_PROXIED_FPUTC 84 -#define EM_PROXIED_PUTCHAR 85 -#define EM_PROXIED_PUTS 86 -#define EM_PROXIED_FREAD 87 -#define EM_PROXIED_FREOPEN 88 -#define EM_PROXIED_FSEEK 89 -#define EM_PROXIED_FSETPOS 90 -#define EM_PROXIED_FTELL 91 -#define EM_PROXIED_FWRITE 92 -#define EM_PROXIED_POPEN 93 -#define EM_PROXIED_PCLOSE 94 -#define EM_PROXIED_PERROR 95 -#define EM_PROXIED_REMOVE 96 -#define EM_PROXIED_RENAME 97 -#define EM_PROXIED_REWIND 98 -#define EM_PROXIED_TMPNAM 99 -#define EM_PROXIED_TEMPNAM 100 -#define EM_PROXIED_TMPFILE 101 -#define EM_PROXIED_UNGETC 102 -#define EM_PROXIED_FSCANF 103 -#define EM_PROXIED_SCANF 104 -#define EM_PROXIED_FPRINTF 105 -#define EM_PROXIED_PRINTF 106 -#define EM_PROXIED_DPRINTF 107 -#define EM_PROXIED_MMAP 108 -#define EM_PROXIED_MUNMAP 109 #define EM_PROXIED_ATEXIT 110 #define EM_PROXIED_GETENV 111 #define EM_PROXIED_CLEARENV 112 #define EM_PROXIED_SETENV 113 #define EM_PROXIED_UNSETENV 114 #define EM_PROXIED_PUTENV 115 -#define EM_PROXIED_REALPATH 116 -#define EM_PROXIED_TCGETATTR 117 -#define EM_PROXIED_TCSETATTR 118 #define EM_PROXIED_TZSET 119 -#define EM_PROXIED_SOCKET 120 -#define EM_PROXIED_BIND 121 -#define EM_PROXIED_SENDMSG 122 -#define EM_PROXIED_RECVMSG 123 -#define EM_PROXIED_SHUTDOWN 124 -#define EM_PROXIED_IOCTL 125 -#define EM_PROXIED_ACCEPT 126 -#define EM_PROXIED_SELECT 127 -#define EM_PROXIED_CONNECT 128 -#define EM_PROXIED_LISTEN 129 -#define EM_PROXIED_GETSOCKNAME 130 -#define EM_PROXIED_GETPEERNAME 131 -#define EM_PROXIED_SEND 132 -#define EM_PROXIED_RECV 133 -#define EM_PROXIED_SENDTO 134 -#define EM_PROXIED_RECVFROM 135 -#define EM_PROXIED_GETSOCKOPT 136 #define EM_PROXIED_PTHREAD_CREATE 137 #define EM_PROXIED_SYSCALL 138 diff --git a/system/lib/pthread/library_pthread.c b/system/lib/pthread/library_pthread.c index 39c24e1b0b569..98d04ebbdc0db 100644 --- a/system/lib/pthread/library_pthread.c +++ b/system/lib/pthread/library_pthread.c @@ -253,141 +253,20 @@ static void _do_call(em_queued_call *q) { switch(q->function) { - case EM_PROXIED_FOPEN: q->returnValue.vp = (void*)fopen(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_FGETS: q->returnValue.cp = fgets(q->args[0].cp, q->args[1].i, (FILE*)q->args[2].vp); break; - case EM_PROXIED_FPUTS: q->returnValue.i = fputs(q->args[0].cp, (FILE*)q->args[1].vp); break; - case EM_PROXIED_FCLOSE: q->returnValue.i = fclose((FILE*)q->args[0].vp); break; - case EM_PROXIED_OPENDIR: q->returnValue.vp = opendir(q->args[0].cp); break; - case EM_PROXIED_CLOSEDIR: q->returnValue.i = closedir((DIR*)q->args[0].vp); break; - case EM_PROXIED_TELLDIR: q->returnValue.i = telldir((DIR*)q->args[0].vp); break; - case EM_PROXIED_SEEKDIR: seekdir((DIR*)q->args[0].vp, q->args[1].i); break; - case EM_PROXIED_REWINDDIR: rewinddir((DIR*)q->args[0].vp); break; - case EM_PROXIED_READDIR_R: q->returnValue.i = readdir_r((DIR*)q->args[0].vp, (struct dirent*)q->args[1].vp, (struct dirent**)q->args[2].vp); break; - case EM_PROXIED_READDIR: q->returnValue.vp = readdir((DIR*)q->args[0].vp); break; case EM_PROXIED_UTIME: q->returnValue.i = utime(q->args[0].cp, (struct utimbuf*)q->args[1].vp); break; case EM_PROXIED_UTIMES: q->returnValue.i = utimes(q->args[0].cp, (struct timeval*)q->args[1].vp); break; - case EM_PROXIED_STAT: q->returnValue.i = stat(q->args[0].cp, (struct stat*)q->args[1].vp); break; - case EM_PROXIED_LSTAT: q->returnValue.i = lstat(q->args[0].cp, (struct stat*)q->args[1].vp); break; - case EM_PROXIED_FSTAT: q->returnValue.i = fstat(q->args[0].i, (struct stat*)q->args[1].vp); break; - case EM_PROXIED_MKNOD: q->returnValue.i = mknod(q->args[0].cp, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_MKDIR: q->returnValue.i = mkdir(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_MKFIFO: q->returnValue.i = mkfifo(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_CHMOD: q->returnValue.i = chmod(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_FCHMOD: q->returnValue.i = fchmod(q->args[0].i, q->args[1].i); break; - case EM_PROXIED_LCHMOD: q->returnValue.i = lchmod(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_UMASK: q->returnValue.i = umask(q->args[0].i); break; - case EM_PROXIED_STATVFS: q->returnValue.i = statvfs(q->args[0].cp, (struct statvfs*)q->args[1].vp); break; - case EM_PROXIED_FSTATVFS: q->returnValue.i = fstatvfs(q->args[0].i, (struct statvfs*)q->args[1].vp); break; - case EM_PROXIED_OPEN: q->returnValue.i = open(q->args[0].cp, q->args[1].i, q->args[2].vp); break; - case EM_PROXIED_CREAT: q->returnValue.i = creat(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_MKTEMP: q->returnValue.cp = mktemp(q->args[0].cp); break; - case EM_PROXIED_MKSTEMP: q->returnValue.i = mkstemp(q->args[0].cp); break; - case EM_PROXIED_MKDTEMP: q->returnValue.cp = mkdtemp(q->args[0].cp); break; - case EM_PROXIED_FCNTL: q->returnValue.i = fcntl(q->args[0].i, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_POSIX_FALLOCATE: q->returnValue.i = posix_fallocate(q->args[0].i, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_POLL: q->returnValue.i = poll((struct pollfd*)q->args[0].vp, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_ACCESS: q->returnValue.i = access(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_CHDIR: q->returnValue.i = chdir(q->args[0].cp); break; - case EM_PROXIED_CHOWN: q->returnValue.i = chown(q->args[0].cp, q->args[1].i, q->args[2].i); break; case EM_PROXIED_CHROOT: q->returnValue.i = chroot(q->args[0].cp); break; - case EM_PROXIED_CLOSE: q->returnValue.i = close(q->args[0].i); break; - case EM_PROXIED_DUP: q->returnValue.i = dup(q->args[0].i); break; - case EM_PROXIED_DUP2: q->returnValue.i = dup2(q->args[0].i, q->args[1].i); break; - case EM_PROXIED_FCHOWN: q->returnValue.i = fchown(q->args[0].i, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_FCHDIR: q->returnValue.i = fchdir(q->args[0].i); break; - case EM_PROXIED_CTERMID: q->returnValue.cp = ctermid(q->args[0].cp); break; - case EM_PROXIED_CRYPT: q->returnValue.cp = crypt(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_ENCRYPT: encrypt(q->args[0].cp, q->args[1].i); break; case EM_PROXIED_FPATHCONF: q->returnValue.i = fpathconf(q->args[0].i, q->args[1].i); break; - case EM_PROXIED_FSYNC: q->returnValue.i = fsync(q->args[0].i); break; - case EM_PROXIED_TRUNCATE: q->returnValue.i = truncate(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_FTRUNCATE: q->returnValue.i = ftruncate(q->args[0].i, q->args[1].i); break; - case EM_PROXIED_GETCWD: q->returnValue.cp = getcwd(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_ISATTY: q->returnValue.i = isatty(q->args[0].i); break; - case EM_PROXIED_LCHOWN: q->returnValue.i = lchown(q->args[0].cp, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_LINK: q->returnValue.i = link(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_LOCKF: q->returnValue.i = lockf(q->args[0].i, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_LSEEK: q->returnValue.i = lseek(q->args[0].i, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_PIPE: q->returnValue.i = pipe((int*)q->args[0].vp); break; - case EM_PROXIED_PREAD: q->returnValue.i = pread(q->args[0].i, q->args[1].vp, q->args[2].i, q->args[2].i); break; - case EM_PROXIED_READ: q->returnValue.i = read(q->args[0].i, q->args[1].vp, q->args[2].i); break; - case EM_PROXIED_RMDIR: q->returnValue.i = rmdir(q->args[0].cp); break; - case EM_PROXIED_UNLINK: q->returnValue.i = unlink(q->args[0].cp); break; - case EM_PROXIED_TTYNAME: q->returnValue.cp = ttyname(q->args[0].i); break; - case EM_PROXIED_TTYNAME_R: q->returnValue.i = ttyname_r(q->args[0].i, q->args[1].cp, q->args[2].i); break; - case EM_PROXIED_SYMLINK: q->returnValue.i = symlink(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_READLINK: q->returnValue.i = readlink(q->args[0].cp, q->args[1].cp, q->args[2].i); break; - case EM_PROXIED_PWRITE: q->returnValue.i = pwrite(q->args[0].i, q->args[1].vp, q->args[2].i, q->args[3].i); break; - case EM_PROXIED_WRITE: q->returnValue.i = write(q->args[0].i, q->args[1].vp, q->args[2].i); break; case EM_PROXIED_CONFSTR: q->returnValue.i = confstr(q->args[0].i, q->args[1].cp, q->args[2].i); break; - case EM_PROXIED_GETHOSTNAME: q->returnValue.i = gethostname(q->args[0].cp, q->args[1].i); break; - case EM_PROXIED_GETLOGIN: q->returnValue.cp = getlogin(); break; - case EM_PROXIED_GETLOGIN_R: q->returnValue.i = getlogin_r(q->args[0].cp, q->args[1].i); break; case EM_PROXIED_SYSCONF: q->returnValue.i = sysconf(q->args[0].i); break; case EM_PROXIED_SBRK: q->returnValue.vp = sbrk(q->args[0].i); break; - case EM_PROXIED_CLEARERR: clearerr(q->args[0].vp); break; - case EM_PROXIED_FDOPEN: q->returnValue.vp = fdopen(q->args[0].i, q->args[1].vp); break; - case EM_PROXIED_FEOF: q->returnValue.i = feof(q->args[0].vp); break; - case EM_PROXIED_FERROR: q->returnValue.i = ferror(q->args[0].vp); break; - case EM_PROXIED_FFLUSH: q->returnValue.i = fflush(q->args[0].vp); break; - case EM_PROXIED_FGETC: q->returnValue.i = fgetc(q->args[0].vp); break; - case EM_PROXIED_GETCHAR: q->returnValue.i = getchar(); break; - case EM_PROXIED_FGETPOS: q->returnValue.i = fgetpos(q->args[0].vp, q->args[1].vp); break; - case EM_PROXIED_GETS: q->returnValue.cp = gets(q->args[0].cp); break; - case EM_PROXIED_FILENO: q->returnValue.i = fileno(q->args[0].vp); break; - case EM_PROXIED_FPUTC: q->returnValue.i = fputc(q->args[0].i, q->args[1].vp); break; - case EM_PROXIED_PUTCHAR: q->returnValue.i = putchar(q->args[0].i); break; - case EM_PROXIED_PUTS: q->returnValue.i = puts(q->args[0].cp); break; - case EM_PROXIED_FREAD: q->returnValue.i = fread(q->args[0].vp, q->args[1].i, q->args[2].i, q->args[3].vp); break; - case EM_PROXIED_FREOPEN: q->returnValue.vp = freopen(q->args[0].cp, q->args[1].cp, q->args[2].vp); break; - case EM_PROXIED_FSEEK: q->returnValue.i = fseek(q->args[0].vp, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_FSETPOS: q->returnValue.i = fsetpos(q->args[0].vp, q->args[1].vp); break; - case EM_PROXIED_FTELL: q->returnValue.i = ftell(q->args[0].vp); break; - case EM_PROXIED_FWRITE: q->returnValue.i = fwrite(q->args[0].vp, q->args[1].i, q->args[2].i, q->args[3].vp); break; - case EM_PROXIED_POPEN: q->returnValue.vp = popen(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_PCLOSE: q->returnValue.i = pclose(q->args[0].vp); break; - case EM_PROXIED_PERROR: perror(q->args[0].cp); break; - case EM_PROXIED_REMOVE: q->returnValue.i = remove(q->args[0].cp); break; - case EM_PROXIED_RENAME: q->returnValue.i = rename(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_REWIND: rewind(q->args[0].vp); break; - case EM_PROXIED_TMPNAM: q->returnValue.cp = tmpnam(q->args[0].cp); break; - case EM_PROXIED_TEMPNAM: q->returnValue.cp = tempnam(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_TMPFILE: q->returnValue.vp = tmpfile(); break; - case EM_PROXIED_UNGETC: q->returnValue.i = ungetc(q->args[0].i, q->args[1].vp); break; - case EM_PROXIED_FSCANF: q->returnValue.i = fscanf(q->args[0].vp, q->args[1].cp, q->args[2].vp); break; - case EM_PROXIED_SCANF: q->returnValue.i = scanf(q->args[0].cp, q->args[1].vp); break; - case EM_PROXIED_FPRINTF: q->returnValue.i = fprintf(q->args[0].vp, q->args[1].cp); break; - case EM_PROXIED_PRINTF: q->returnValue.i = printf(q->args[0].cp); break; - case EM_PROXIED_DPRINTF: q->returnValue.i = dprintf(q->args[0].i, q->args[1].cp); break; - case EM_PROXIED_MMAP: q->returnValue.vp = mmap(q->args[0].vp, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i); break; - case EM_PROXIED_MUNMAP: q->returnValue.i = munmap(q->args[0].vp, q->args[1].i); break; case EM_PROXIED_ATEXIT: q->returnValue.i = atexit(q->args[0].vp); break; case EM_PROXIED_GETENV: q->returnValue.cp = getenv(q->args[0].cp); break; case EM_PROXIED_CLEARENV: q->returnValue.i = clearenv(); break; case EM_PROXIED_SETENV: q->returnValue.i = setenv(q->args[0].cp, q->args[1].cp, q->args[2].i); break; case EM_PROXIED_UNSETENV: q->returnValue.i = unsetenv(q->args[0].cp); break; case EM_PROXIED_PUTENV: q->returnValue.i = putenv(q->args[0].cp); break; - case EM_PROXIED_REALPATH: q->returnValue.cp = realpath(q->args[0].cp, q->args[1].cp); break; - case EM_PROXIED_TCGETATTR: q->returnValue.i = tcgetattr(q->args[0].i, q->args[1].vp); break; - case EM_PROXIED_TCSETATTR: q->returnValue.i = tcsetattr(q->args[0].i, q->args[1].i, q->args[2].vp); break; case EM_PROXIED_TZSET: tzset(); break; - case EM_PROXIED_SOCKET: q->returnValue.i = socket(q->args[0].i, q->args[1].i, q->args[2].i); break; - case EM_PROXIED_BIND: q->returnValue.i = bind(q->args[0].i, q->args[1].vp, q->args[2].i); break; - case EM_PROXIED_SENDMSG: q->returnValue.i = sendmsg(q->args[0].i, q->args[1].vp, q->args[2].i); break; - case EM_PROXIED_RECVMSG: q->returnValue.i = recvmsg(q->args[0].i, q->args[1].vp, q->args[2].i); break; - case EM_PROXIED_SHUTDOWN: q->returnValue.i = shutdown(q->args[0].i, q->args[1].i); break; - case EM_PROXIED_IOCTL: q->returnValue.i = ioctl(q->args[0].i, q->args[1].i, q->args[2].vp); break; - case EM_PROXIED_ACCEPT: q->returnValue.i = accept(q->args[0].i, q->args[1].vp, q->args[2].vp); break; - case EM_PROXIED_SELECT: q->returnValue.i = select(q->args[0].i, q->args[1].vp, q->args[2].vp, q->args[3].vp, q->args[4].vp); break; - case EM_PROXIED_CONNECT: q->returnValue.i = connect(q->args[0].i, q->args[1].vp, q->args[1].i); break; - case EM_PROXIED_LISTEN: q->returnValue.i = listen(q->args[0].i, q->args[1].i); break; - case EM_PROXIED_GETSOCKNAME: q->returnValue.i = getsockname(q->args[0].i, q->args[1].vp, q->args[2].vp); break; - case EM_PROXIED_GETPEERNAME: q->returnValue.i = getpeername(q->args[0].i, q->args[1].vp, q->args[2].vp); break; - case EM_PROXIED_SEND: q->returnValue.i = send(q->args[0].i, q->args[1].vp, q->args[2].i, q->args[3].i); break; - case EM_PROXIED_RECV: q->returnValue.i = recv(q->args[0].i, q->args[1].vp, q->args[2].i, q->args[3].i); break; - case EM_PROXIED_SENDTO: q->returnValue.i = sendto(q->args[0].i, q->args[1].vp, q->args[2].i, q->args[3].i, q->args[4].vp, q->args[5].i); break; - case EM_PROXIED_RECVFROM: q->returnValue.i = recvfrom(q->args[0].i, q->args[1].vp, q->args[2].i, q->args[3].i, q->args[4].vp, q->args[5].vp); break; - case EM_PROXIED_GETSOCKOPT: q->returnValue.i = getsockopt(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].vp, q->args[4].vp); break; case EM_PROXIED_PTHREAD_CREATE: q->returnValue.i = pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp); break; case EM_PROXIED_SYSCALL: q->returnValue.i = emscripten_syscall(q->args[0].i, q->args[1].vp); break; default: assert(0 && "Invalid Emscripten pthread _do_call opcode!"); @@ -459,15 +338,8 @@ void * EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_xprintf_varargs(i len = vsnprintf(s, len+1, format, args); } em_queued_call q = { function, 0 }; - if (function == EM_PROXIED_PRINTF) - { - q.args[0].vp = s; - } - else - { - q.args[0].vp = param0; - q.args[1].vp = s; - } + q.args[0].vp = param0; + q.args[1].vp = s; q.returnValue.vp = 0; emscripten_sync_run_in_main_thread(&q); if (s != str) free(s); diff --git a/tests/fs/test_workerfs_read.c b/tests/fs/test_workerfs_read.c new file mode 100644 index 0000000000000..f97b44a282f87 --- /dev/null +++ b/tests/fs/test_workerfs_read.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SECRET_LEN 10 + +int result = 1; + +int main() { + int fd; + int fd2; + struct stat st; + struct stat st2; + char buf[100]; + char secret2[] = SECRET2; + int len2 = SECRET_LEN / 2; + + if (stat("/work/notexist.txt", &st) != -1 || errno != ENOENT) { + result = -1000 - errno; + goto exit; + } + + if (stat("/work/blob.txt", &st) != 0) { + result = -2000 - errno; + goto exit; + } + + fd = open("/work/blob.txt", O_RDWR, 0666); + if (fd == -1) { + result = -3000 - errno; + goto exit; + } + + if (read(fd, buf, 1000) != SECRET_LEN || + strncmp(buf, SECRET, SECRET_LEN) != 0) { + result = -4000 - errno; + goto exit; + } + + fd2 = open("/work/file.txt", O_RDONLY, 0666); + if (fd == -1) { + result = -5000 - errno; + goto exit; + } + + if (lseek(fd2, len2, SEEK_SET) != len2) { + result = -6000 - errno; + goto exit; + } + + if (read(fd2, buf, len2) != len2 || + strncmp(buf, secret2 + len2, len2) != 0) { + result = -7000 - errno; + goto exit; + } + + stat("/work/file.txt", &st); + chmod("/work/file.txt", 0640); + stat("/work/file.txt", &st2); + if (st.st_mode != (0777 | S_IFREG) || st2.st_mode != (0640 | S_IFREG)) { + result = -8000 - errno; + goto exit; + } + +exit: + REPORT_RESULT(); +} diff --git a/tests/runner.py b/tests/runner.py index 94a277ed0561c..543ffb08ef5ee 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -912,22 +912,28 @@ def get_bullet_library(runner_core, use_cmake): ''' time.sleep(2) - # If we asked to run random tests, do that + # If we were asked to run random tests, do that first = sys.argv[1] if first.startswith('random'): num = 1 first = first[6:] + base_module = 'default' + relevant_modes = test_modes if len(first) > 0: + if first.startswith('other'): + base_module = 'other' + relevant_modes = ['other'] + first = first.replace('other', '') num = int(first) for m in modules: - if hasattr(m, 'default'): + if hasattr(m, base_module): sys.argv = [sys.argv[0]] - tests = filter(lambda t: t.startswith('test_'), dir(getattr(m, 'default'))) + tests = filter(lambda t: t.startswith('test_'), dir(getattr(m, base_module))) print chosen = set() while len(chosen) < num: test = random.choice(tests) - mode = random.choice(test_modes) + mode = random.choice(relevant_modes) print '* ' + mode + '.' + test chosen.add(mode + '.' + test) sys.argv += list(chosen) diff --git a/tests/sdl2_key.c b/tests/sdl2_key.c index 616a73b9f9ef3..d2d1e1c961368 100644 --- a/tests/sdl2_key.c +++ b/tests/sdl2_key.c @@ -28,6 +28,11 @@ int EventHandler(void *userdata, SDL_Event *event) { } } break; + case SDL_TEXTINPUT: + if (event->text.text[0] == 'A') { + printf("a\n");result *= 5; + } + break; default: /* Report an unhandled event */ printf("I don't know what this event is (type=%d)!\n", event->type); } diff --git a/tests/test_browser.py b/tests/test_browser.py index 6def2aa7c7dd3..262845433ec63 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1007,6 +1007,23 @@ def test_fs_memfs_fsync(self): secret = str(time.time()) self.btest(path_from_root('tests', 'fs', 'test_memfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main']''']) + def test_fs_workerfs_read(self): + secret = 'a' * 10; + secret2 = 'b' * 10; + open(self.in_dir('pre.js'), 'w').write(''' + var Module = {}; + Module.preRun = function() { + var blob = new Blob(['%s']); + var file = new File(['%s'], 'file.txt'); + FS.mkdir('/work'); + FS.mount(WORKERFS, { + blobs: [{ name: 'blob.txt', data: blob }], + files: [file], + }, '/work'); + }; + ''' % (secret, secret2)) + self.btest(path_from_root('tests', 'fs', 'test_workerfs_read.c'), '1', force_c=True, args=['--pre-js', 'pre.js', '-DSECRET=\"' + secret + '\"', '-DSECRET2=\"' + secret2 + '\"', '--proxy-to-worker']) + def test_idbstore(self): secret = str(time.time()) for stage in [0, 1, 2, 3, 0, 1, 2, 0, 0, 1, 4, 2, 5]: @@ -2081,7 +2098,15 @@ def test_sdl2_key(self): event.initKeyEvent("keydown", true, true, window, 0, 0, 0, 0, c, c); - document.dispatchEvent(event); + var prevented = !document.dispatchEvent(event); + + //send keypress if not prevented + if (!prevented) { + event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keypress", true, true, window, + 0, 0, 0, 0, 0, c); + document.dispatchEvent(event); + } } function keyup(c) { @@ -2095,7 +2120,7 @@ def test_sdl2_key(self): open(os.path.join(self.get_dir(), 'sdl2_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl2_key.c')).read())) Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl2_key.c'), '-o', 'page.html'] + defines + ['-s', 'USE_SDL=2','--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate() - self.run_browser('page.html', '', '/report_result?7436429') + self.run_browser('page.html', '', '/report_result?37182145') def test_sdl2_text(self): open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' diff --git a/tests/test_core.py b/tests/test_core.py index f12c787fb51ba..c619d0fa1b1db 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,6 +1,6 @@ # coding=utf-8 -import glob, hashlib, os, re, shutil, subprocess, sys +import glob, hashlib, os, re, shutil, subprocess, sys, json from textwrap import dedent import tools.shared from tools.shared import * @@ -465,7 +465,7 @@ def test_bswap64(self): # extra coverages for emulate_casts in [0, 1]: - for emulate_fps in [0, 1]: + for emulate_fps in [0, 1, 2]: print emulate_casts, emulate_fps Settings.EMULATE_FUNCTION_POINTER_CASTS = emulate_casts Settings.EMULATED_FUNCTION_POINTERS = emulate_fps @@ -2055,6 +2055,7 @@ def process(filename): # test EXPORT_ALL Settings.EXPORTED_FUNCTIONS = [] Settings.EXPORT_ALL = 1 + Settings.LINKABLE = 1 self.do_run_from_file(src, output, post_build=check) def test_emscripten_get_now(self): @@ -3855,6 +3856,24 @@ def test_dylink_funcpointers_wrapper(self): extern charfunc get(); ''') + def test_dylink_funcpointers_float(self): + self.dylink_test(r''' + #include + #include "header.h" + int sidey(floatfunc f); + float areturn0(float f) { printf("hello 0: %f\n", f); return 0; } + float areturn1(float f) { printf("hello 1: %f\n", f); return 1; } + float areturn2(float f) { printf("hello 2: %f\n", f); return 2; } + int main(int argc, char **argv) { + volatile floatfunc table[3] = { areturn0, areturn1, areturn2 }; + printf("got: %d\n", (int)table[sidey(NULL)](12.34)); + return 0; + } + ''', ''' + #include "header.h" + int sidey(floatfunc f) { if (f) f(56.78); return 1; } + ''', 'hello 1: 12.340000\ngot: 1\n', header='typedef float (*floatfunc)(float);') + def test_dylink_global_init(self): self.dylink_test(r''' #include @@ -6468,6 +6487,42 @@ def test_exported_response(self): self.do_run(src, '''waka 5!''') assert 'other_function' in open('src.cpp.o.js').read() + def test_large_exported_response(self): + src = r''' + #include + #include + #include + + extern "C" { + ''' + + js_funcs = [] + num_exports = 5000 + count = 0 + while count < num_exports: + src += 'int exported_func_from_response_file_%d () { return %d;}\n' % (count, count) + js_funcs.append('_exported_func_from_response_file_%d' % count) + count += 1 + + src += r''' + } + + int main() { + int x = EM_ASM_INT_V({ return Module._exported_func_from_response_file_4999() }); + emscripten_run_script_string(""); // Add a reference to a symbol that exists in src/deps_info.json to uncover issue #2836 in the test suite. + printf("waka %d!\n", x); + return 0; + } + ''' + + js_funcs.append('_main') + exported_func_json_file = os.path.join(self.get_dir(), 'large_exported_response.json') + open(exported_func_json_file, 'wb').write(json.dumps(js_funcs)) + + self.emcc_args += ['-s', 'EXPORTED_FUNCTIONS=@' + exported_func_json_file] + self.do_run(src, '''waka 4999!''') + assert '_exported_func_from_response_file_1' in open('src.cpp.o.js').read() + def test_add_function(self): Settings.INVOKE_RUN = 0 Settings.RESERVED_FUNCTION_POINTERS = 1 diff --git a/tests/test_other.py b/tests/test_other.py index 34aaf1a40996d..82c5c053f1db3 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -954,7 +954,7 @@ def test_export_all(self): _libf2(); ''') - Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '--post-js', 'main.js'], output_filename='a.out.js') + Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '-s', 'LINKABLE=1', '--post-js', 'main.js'], output_filename='a.out.js') self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -4933,6 +4933,105 @@ def test(args, expected): test(['-O' + str(opts)], 'no visible function tables') test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=1'], 'function table: ') + def test_emulated_function_pointers_2(self): + src = r''' + #include + typedef void (*fp)(); + void one() { EM_ASM( Module.print('one') ); } + void two() { EM_ASM( Module.print('two') ); } + void test() { + volatile fp f = one; + f(); + f = two; + f(); + } + int main(int argc, char **argv) { + test(); + // swap them! + EM_ASM_INT({ + var one = $0; + var two = $1; + if (typeof FUNCTION_TABLE_v === 'undefined') { + Module.print('no'); + return; + } + var temp = FUNCTION_TABLE_v[one]; + FUNCTION_TABLE_v[one] = FUNCTION_TABLE_v[two]; + FUNCTION_TABLE_v[two] = temp; + }, (int)&one, (int)&two); + test(); + return 0; + } + ''' + open('src.c', 'w').write(src) + + flipped = 'one\ntwo\ntwo\none\n' + unchanged = 'one\ntwo\none\ntwo\n' + no_table = 'one\ntwo\nno\none\ntwo\n' + + def test(args, expected): + print args, expected.replace('\n', ' ') + Popen([PYTHON, EMCC, 'src.c'] + args).communicate() + self.assertContained(expected, run_js(self.in_dir('a.out.js'))) + + for opts in [0, 1, 2]: + test(['-O' + str(opts)], no_table) + test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=1'], flipped) + test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=2'], flipped) + test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=1', '-s', 'RELOCATABLE=1'], flipped) + test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=2', '-s', 'RELOCATABLE=1'], unchanged) # with both of those, we optimize and you cannot flip them + test(['-O' + str(opts), '-s', 'MAIN_MODULE=1'], unchanged) # default for modules is optimized + test(['-O' + str(opts), '-s', 'MAIN_MODULE=1', '-s', 'EMULATED_FUNCTION_POINTERS=2'], unchanged) + test(['-O' + str(opts), '-s', 'MAIN_MODULE=1', '-s', 'EMULATED_FUNCTION_POINTERS=1'], flipped) # but you can disable that + + def test_minimal_dynamic(self): + def test(main_args=[], library_args=[], expected='hello from main\nhello from library'): + print 'testing', main_args, library_args + self.clear() + open('library.c', 'w').write(r''' + #include + void library_func() { + #ifdef USE_PRINTF + printf("hello from library: %p", (int)&library_func); + #else + puts("hello from library"); + #endif + } + ''') + check_execute([PYTHON, EMCC, 'library.c', '-s', 'SIDE_MODULE=1', '-O2', '-o', 'library.js'] + library_args) + open('main.c', 'w').write(r''' + #include + #include + int main() { + puts("hello from main"); + void *lib_handle = dlopen("library.js", 0); + typedef void (*voidfunc)(); + voidfunc x = (voidfunc)dlsym(lib_handle, "library_func"); + x(); + } + ''') + check_execute([PYTHON, EMCC, 'main.c', '-s', 'MAIN_MODULE=1', '--embed-file', 'library.js', '-O2'] + main_args) + self.assertContained(expected, run_js('a.out.js', assert_returncode=None, stderr=subprocess.STDOUT)) + size = os.stat('a.out.js').st_size + print ' size:', size + return size + full = test() + printf = test( library_args=['-DUSE_PRINTF']) # printf is not used in main, but libc was linked in, so it's there + dce = test(main_args=['-s', 'MAIN_MODULE=2']) # dce in main, and side happens to be ok since it uses puts as well + dce_fail = test(main_args=['-s', 'MAIN_MODULE=2'], library_args=['-DUSE_PRINTF'], expected='undefined') # printf is not used in main, and we dce, so we failz + dce_save = test(main_args=['-s', 'MAIN_MODULE=2', '-s', 'EXPORTED_FUNCTIONS=["_main", "_printf"]'], + library_args=['-DUSE_PRINTF']) # exporting printf in main keeps it alive for the library + + def percent_diff(x, y): + small = min(x, y) + large = max(x, y) + return float(100*large)/small - 100 + + assert percent_diff(full, printf) < 4 + assert percent_diff(dce, dce_fail) < 4 + assert dce < 0.2*full # big effect, 80%+ is gone + assert dce_save > 1.1*dce # save exported all of printf + def test_file_packager_eval(self): BAD = 'Module = eval(' src = path_from_root('tests', 'hello_world.c') diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 9257050916631..bbcaa1f18f832 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -27,7 +27,7 @@ def path_from_root(*pathelems): NATIVE_OPTIMIZER = os.environ.get('EMCC_NATIVE_OPTIMIZER') or '1' # use native optimizer by default, unless disabled by EMCC_NATIVE_OPTIMIZER=0 in the env -def split_funcs(js, just_split=False, know_generated=True): +def split_funcs(js, just_split=False): if just_split: return map(lambda line: ('(json)', line), js.split('\n')) parts = map(lambda part: part, js.split('\n}\n')) funcs = [] @@ -38,8 +38,7 @@ def split_funcs(js, just_split=False, know_generated=True): if m: ident = m.group(1) else: - if know_generated: continue # ignore whitespace - ident = 'anon_%d' % i + continue assert ident funcs.append((ident, func)) return funcs @@ -312,7 +311,9 @@ def run_on_js(filename, passes, js_engine, source_map=False, extra_info=None, ju start_funcs = js.find(start_funcs_marker) end_funcs = js.rfind(end_funcs_marker) - know_generated = suffix or start_funcs >= 0 + if start_funcs < 0 or end_funcs < start_funcs or not suffix: + logging.critical('Invalid input file. Did not contain appropriate markers. (start_funcs: %s, end_funcs: %s, suffix_start: %s' % (start_funcs, end_funcs, suffix_start)) + sys.exit(1) minify_globals = 'minifyNames' in passes and 'asm' in passes if minify_globals: @@ -329,61 +330,56 @@ def run_on_js(filename, passes, js_engine, source_map=False, extra_info=None, ju if cleanup: passes = filter(lambda p: p != 'cleanup', passes) # we will do it manually - if know_generated: - if not minify_globals: - pre = js[:start_funcs + len(start_funcs_marker)] - post = js[end_funcs + len(end_funcs_marker):] - js = js[start_funcs + len(start_funcs_marker):end_funcs] - if 'asm' not in passes: # can have Module[..] and inlining prevention code, push those to post - class Finals: - buf = [] - def process(line): - if len(line) > 0 and (line.startswith(('Module[', 'if (globalScope)')) or line.endswith('["X"]=1;')): - Finals.buf.append(line) - return False - return True - js = '\n'.join(filter(process, js.split('\n'))) - post = '\n'.join(Finals.buf) + '\n' + post - post = end_funcs_marker + post - else: - # We need to split out the asm shell as well, for minification - pre = js[:start_asm + len(start_asm_marker)] - post = js[end_asm:] - asm_shell = js[start_asm + len(start_asm_marker):start_funcs + len(start_funcs_marker)] + ''' -EMSCRIPTEN_FUNCS(); -''' + js[end_funcs + len(end_funcs_marker):end_asm + len(end_asm_marker)] - js = js[start_funcs + len(start_funcs_marker):end_funcs] - - # we assume there is a maximum of one new name per line - minifier = Minifier(js, js_engine) - def check_symbol_mapping(p): - if p.startswith('symbolMap='): - minifier.symbols_file = p.split('=')[1] - return False - if p == 'profilingFuncs': - minifier.profiling_funcs = True + if not minify_globals: + pre = js[:start_funcs + len(start_funcs_marker)] + post = js[end_funcs + len(end_funcs_marker):] + js = js[start_funcs + len(start_funcs_marker):end_funcs] + if 'asm' not in passes: # can have Module[..] and inlining prevention code, push those to post + class Finals: + buf = [] + def process(line): + if len(line) > 0 and (line.startswith(('Module[', 'if (globalScope)')) or line.endswith('["X"]=1;')): + Finals.buf.append(line) return False return True - passes = filter(check_symbol_mapping, passes) - asm_shell_pre, asm_shell_post = minifier.minify_shell(asm_shell, 'minifyWhitespace' in passes, source_map).split('EMSCRIPTEN_FUNCS();'); - asm_shell_post = asm_shell_post.replace('});', '})'); - pre += asm_shell_pre + '\n' + start_funcs_marker - post = end_funcs_marker + asm_shell_post + post - - minify_info = minifier.serialize() - #if DEBUG: print >> sys.stderr, 'minify info:', minify_info - # remove suffix if no longer needed - if suffix and 'last' in passes: - suffix_start = post.find(suffix_marker) - suffix_end = post.find('\n', suffix_start) - post = post[:suffix_start] + post[suffix_end:] - + js = '\n'.join(filter(process, js.split('\n'))) + post = '\n'.join(Finals.buf) + '\n' + post + post = end_funcs_marker + post else: - pre = '' - post = '' + # We need to split out the asm shell as well, for minification + pre = js[:start_asm + len(start_asm_marker)] + post = js[end_asm:] + asm_shell = js[start_asm + len(start_asm_marker):start_funcs + len(start_funcs_marker)] + ''' +EMSCRIPTEN_FUNCS(); +''' + js[end_funcs + len(end_funcs_marker):end_asm + len(end_asm_marker)] + js = js[start_funcs + len(start_funcs_marker):end_funcs] + + # we assume there is a maximum of one new name per line + minifier = Minifier(js, js_engine) + def check_symbol_mapping(p): + if p.startswith('symbolMap='): + minifier.symbols_file = p.split('=')[1] + return False + if p == 'profilingFuncs': + minifier.profiling_funcs = True + return False + return True + passes = filter(check_symbol_mapping, passes) + asm_shell_pre, asm_shell_post = minifier.minify_shell(asm_shell, 'minifyWhitespace' in passes, source_map).split('EMSCRIPTEN_FUNCS();'); + asm_shell_post = asm_shell_post.replace('});', '})'); + pre += asm_shell_pre + '\n' + start_funcs_marker + post = end_funcs_marker + asm_shell_post + post + + minify_info = minifier.serialize() + #if DEBUG: print >> sys.stderr, 'minify info:', minify_info + # remove suffix if no longer needed + if suffix and 'last' in passes: + suffix_start = post.find(suffix_marker) + suffix_end = post.find('\n', suffix_start) + post = post[:suffix_start] + post[suffix_end:] total_size = len(js) - funcs = split_funcs(js, just_split, know_generated) + funcs = split_funcs(js, just_split) js = None # if we are making source maps, we want our debug numbering to start from the @@ -409,13 +405,12 @@ def write_chunk(chunk, i): f.write(chunk) f.write(suffix_marker) if minify_globals: - if know_generated: - if extra_info: - for key, value in extra_info.iteritems(): - assert key not in minify_info or value == minify_info[key], [key, value, minify_info[key]] - minify_info[key] = value - f.write('\n') - f.write('// EXTRA_INFO:' + json.dumps(minify_info)) + if extra_info: + for key, value in extra_info.iteritems(): + assert key not in minify_info or value == minify_info[key], [key, value, minify_info[key]] + minify_info[key] = value + f.write('\n') + f.write('// EXTRA_INFO:' + json.dumps(minify_info)) elif extra_info: f.write('\n') f.write('// EXTRA_INFO:' + json.dumps(extra_info)) @@ -491,7 +486,7 @@ def write_chunk(chunk, i): # sort functions by size, to make diffing easier and to improve aot times funcses = [] for out_file in filenames: - funcses.append(split_funcs(open(out_file).read(), False, know_generated)) + funcses.append(split_funcs(open(out_file).read(), False)) funcs = [item for sublist in funcses for item in sublist] funcses = None def sorter(x, y): diff --git a/tools/ports/sdl.py b/tools/ports/sdl.py index 4a4adec5a6f51..f369e6b3f92cf 100644 --- a/tools/ports/sdl.py +++ b/tools/ports/sdl.py @@ -1,6 +1,6 @@ import os, shutil, logging -TAG = 'version_8' +TAG = 'version_9' def get_with_configure(ports, settings, shared): # not currently used; no real need for configure on emscripten users' machines! if settings.USE_SDL == 2: diff --git a/tools/shared.py b/tools/shared.py index b6076b82edea8..0b557e7761cad 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1500,20 +1500,16 @@ def emscripten(filename, append_ext=True, extra_args=[]): # Allow usage of emscripten.py without warning os.environ['EMSCRIPTEN_SUPPRESS_USAGE_WARNING'] = '1' + if path_from_root() not in sys.path: + sys.path += [path_from_root()] + from emscripten import _main as call_emscripten # Run Emscripten settings = Settings.serialize() args = settings + extra_args - if WINDOWS: - rsp_file = response_file.create_response_file(args, TEMP_DIR) - args = ['@' + rsp_file] - cmdline = [PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args + cmdline = [filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args if jsrun.TRACK_PROCESS_SPAWNS: logging.info('Executing emscripten.py compiler with cmdline "' + ' '.join(cmdline) + '"') - jsrun.timeout_run(Popen(cmdline, stdout=PIPE), None, 'Compiling') - - # Clean up .rsp file the compiler used after we are finished. - if WINDOWS: - try_delete(rsp_file) + call_emscripten(cmdline) # Detect compilation crashes and errors assert os.path.exists(filename + '.o.js'), 'Emscripten failed to generate .js' @@ -1522,7 +1518,7 @@ def emscripten(filename, append_ext=True, extra_args=[]): @staticmethod def can_build_standalone(): - return not Settings.BUILD_AS_SHARED_LIB and not Settings.LINKABLE and not Settings.EXPORT_ALL and not Settings.MAIN_MODULE and not Settings.SIDE_MODULE + return not Settings.BUILD_AS_SHARED_LIB and not Settings.LINKABLE @staticmethod def can_inline(): @@ -1531,10 +1527,27 @@ def can_inline(): @staticmethod def get_safe_internalize(): if not Building.can_build_standalone(): return [] # do not internalize anything + exps = expand_response(Settings.EXPORTED_FUNCTIONS) - exports = ','.join(map(lambda exp: exp[1:], exps)) + internalize_public_api = '-internalize-public-api-' + internalize_list = ','.join(map(lambda exp: exp[1:], exps)) + + # EXPORTED_FUNCTIONS can potentially be very large. + # 8k is a bit of an arbitrary limit, but a reasonable one + # for max command line size before we use a response file + if len(internalize_list) > 8192: + logging.debug('using response file for EXPORTED_FUNCTIONS in internalize') + finalized_exports = '\n'.join(map(lambda exp: exp[1:], exps)) + internalize_list_file = configuration.get_temp_files().get(suffix='.response').name + internalize_list_fh = open(internalize_list_file, 'w') + internalize_list_fh.write(finalized_exports) + internalize_list_fh.close() + internalize_public_api += 'file=' + internalize_list_file + else: + internalize_public_api += 'list=' + internalize_list + # internalize carefully, llvm 3.2 will remove even main if not told not to - return ['-internalize', '-internalize-public-api-list=' + exports] + return ['-internalize', internalize_public_api] @staticmethod def pick_llvm_opts(optimization_level):