diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-08-30 11:21:48 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-08-30 11:21:48 -0700 |
commit | b5b49215d4a40566380a769f47a9c1cce74a28b0 (patch) | |
tree | 68308b6059798a81f24f6a8a1ac28a0091c5d066 | |
parent | 1cc28b8e9e94267041bc71afebfbbe3059db4a3f (diff) | |
parent | b895cdc7df2085d324003c9df582a3dcc1927697 (diff) |
Merge branch 'incoming'
48 files changed, 769 insertions, 155 deletions
@@ -95,4 +95,5 @@ a license to everyone to use it as detailed in LICENSE.) * Nick Bray <ncbray@chromium.org> (copyright owned by Google, Inc.) * Aidan Hobson Sayers <aidanhs@cantab.net> * Charlie Birks <admin@daftgames.net> +* Ranger Harke <ranger.harke@autodesk.com> (copyright owned by Autodesk, Inc.) diff --git a/src/analyzer.js b/src/analyzer.js index 2a7d64f5..b20dedff 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -281,6 +281,14 @@ function analyzer(data, sidePass) { Array.prototype.splice.apply(params, [i, 1].concat(toAdd)); i += toAdd.length; continue; + } else if (param.intertype == 'structvalue') { + // 'flatten' out the struct into scalars + var toAdd = param.params; + toAdd.forEach(function(param) { + param.byval = 0; + }); + Array.prototype.splice.apply(params, [i, 1].concat(toAdd)); + continue; // do not increment i; proceed to process the new params } i++; } diff --git a/src/jsifier.js b/src/jsifier.js index 179a910a..7273f54c 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -23,7 +23,7 @@ function JSify(data, functionsOnly, givenFunctions) { var mainPass = !functionsOnly; if (mainPass) { - var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB ? 'shell_sharedlib.js' : 'shell.js'); + var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js'); if (phase == 'pre') { // We will start to print out the data, but must do so carefully - we are @@ -47,10 +47,11 @@ function JSify(data, functionsOnly, givenFunctions) { var shellParts = read(shellFile).split('{{BODY}}'); print(processMacros(preprocess(shellParts[0]))); - var preFile = BUILD_AS_SHARED_LIB ? 'preamble_sharedlib.js' : 'preamble.js'; + var preFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'preamble_sharedlib.js' : 'preamble.js'; var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()))); print(pre); + // Populate implementedFunctions. Note that this is before types, and will be updated later. data.unparsedFunctions.forEach(function(func) { Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type })); }); @@ -80,7 +81,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (phase == 'pre') { var libFuncsToInclude; if (INCLUDE_FULL_LIBRARY) { - assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.') + assert(!(BUILD_AS_SHARED_LIB || SIDE_MODULE), 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB/SIDE_MODULE set.') libFuncsToInclude = []; for (var key in LibraryManager.library) { if (!key.match(/__(deps|postset|inline|asm|sig)$/)) { @@ -454,7 +455,7 @@ function JSify(data, functionsOnly, givenFunctions) { var postsetId = ident + '__postset'; var postset = LibraryManager.library[postsetId]; - if (postset && !addedLibraryItems[postsetId]) { + if (postset && !addedLibraryItems[postsetId] && !SIDE_MODULE) { addedLibraryItems[postsetId] = true; ret.push({ intertype: 'GlobalVariablePostSet', @@ -497,6 +498,7 @@ function JSify(data, functionsOnly, givenFunctions) { Functions.libraryFunctions[ident.substr(1)] = 2; } } + if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent if ((!ASM_JS || phase == 'pre') && (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) { contentText += '\nModule["' + ident + '"] = ' + ident + ';'; @@ -1835,7 +1837,7 @@ function JSify(data, functionsOnly, givenFunctions) { print('Runtime.typeInfo = ' + JSON.stringify(Types.types)); print('Runtime.structMetadata = ' + JSON.stringify(Types.structMetadata)); } - var postFile = BUILD_AS_SHARED_LIB ? 'postamble_sharedlib.js' : 'postamble.js'; + var postFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'postamble_sharedlib.js' : 'postamble.js'; var postParts = processMacros(preprocess(read(postFile))).split('{{GLOBAL_VARS}}'); print(postParts[0]); @@ -1871,6 +1873,12 @@ function JSify(data, functionsOnly, givenFunctions) { // Data if (mainPass) { + if (phase == 'pre') { + // types have been parsed, so we can figure out function signatures (which can use types) + data.unparsedFunctions.forEach(function(func) { + Functions.implementedFunctions[func.ident] = Functions.getSignature(func.returnType, func.params.map(function(param) { return param.type })); + }); + } substrate.addItems(data.functionStubs, 'FunctionStub'); assert(data.functions.length == 0); } else { diff --git a/src/library.js b/src/library.js index 3ba2f56b..f6b3d5ef 100644 --- a/src/library.js +++ b/src/library.js @@ -2261,7 +2261,11 @@ LibraryManager.library = { // void clearerr(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html stream = FS.getStream(stream); - if (stream) stream.error = false; + if (!stream) { + return; + } + stream.eof = false; + stream.error = false; }, fclose__deps: ['close', 'fsync'], fclose: function(stream) { @@ -2322,7 +2326,6 @@ LibraryManager.library = { if (streamObj.eof || streamObj.error) return -1; var ret = _fread(_fgetc.ret, 1, 1, stream); if (ret == 0) { - streamObj.eof = true; return -1; } else if (ret == -1) { streamObj.error = true; @@ -5154,6 +5157,37 @@ LibraryManager.library = { }, // ========================================================================== + // termios.h + // ========================================================================== + tcgetattr: function(fildes, termios_p) { + // http://pubs.opengroup.org/onlinepubs/009695399/functions/tcgetattr.html + var stream = FS.getStream(fildes); + if (!stream) { + ___setErrNo(ERRNO_CODES.EBADF); + return -1; + } + if (!stream.tty) { + ___setErrNo(ERRNO_CODES.ENOTTY); + return -1; + } + return 0; + }, + + tcsetattr: function(fildes, optional_actions, termios_p) { + // http://pubs.opengroup.org/onlinepubs/7908799/xsh/tcsetattr.html + var stream = FS.getStream(fildes); + if (!stream) { + ___setErrNo(ERRNO_CODES.EBADF); + return -1; + } + if (!stream.tty) { + ___setErrNo(ERRNO_CODES.ENOTTY); + return -1; + } + return 0; + }, + + // ========================================================================== // time.h // ========================================================================== diff --git a/src/library_browser.js b/src/library_browser.js index 591a3c11..235ccc78 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -830,15 +830,21 @@ mergeInto(LibraryManager.library, { }, emscripten_get_now: function() { - if (ENVIRONMENT_IS_NODE) { - var t = process['hrtime'](); - return t[0] * 1e3 + t[1] / 1e6; - } - else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) { - return window['performance']['now'](); - } else { - return Date.now(); + if (!_emscripten_get_now.actual) { + if (ENVIRONMENT_IS_NODE) { + _emscripten_get_now.actual = function() { + var t = process['hrtime'](); + return t[0] * 1e3 + t[1] / 1e6; + } + } else if (typeof dateNow !== 'undefined') { + _emscripten_get_now.actual = dateNow; + } else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) { + _emscripten_get_now.actual = function() { return window['performance']['now'](); }; + } else { + _emscripten_get_now.actual = Date.now; + } } + return _emscripten_get_now.actual(); }, emscripten_create_worker: function(url) { diff --git a/src/library_fs.js b/src/library_fs.js index e1397356..1d9748d3 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -510,7 +510,7 @@ mergeInto(LibraryManager.library, { return FS.create(path, mode); }, createDataFile: function(parent, name, data, canRead, canWrite, canOwn) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; var mode = FS.getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { @@ -530,7 +530,7 @@ mergeInto(LibraryManager.library, { }, createDevice: function(parent, name, input, output) { var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = input && output ? 0777 : (input ? 0333 : 0555); + var mode = FS.getMode(!!input, !!output); if (!FS.createDevice.major) FS.createDevice.major = 64; var dev = FS.makedev(FS.createDevice.major++, 0); // Create a fake device that a set of stream ops to emulate @@ -770,7 +770,7 @@ mergeInto(LibraryManager.library, { Browser.init(); // TODO we should allow people to just pass in a complete filename instead // of parent and name being that we just join them anyways - var fullname = PATH.resolve(PATH.join(parent, name)); + var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent; function processData(byteArray) { function finish(byteArray) { if (!dontCreateFile) { @@ -802,6 +802,90 @@ mergeInto(LibraryManager.library, { } }, + indexedDB: function() { + return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + }, + + DB_NAME: function() { + return 'EM_FS_' + window.location.pathname; + }, + DB_VERSION: 20, + DB_STORE_NAME: 'FILE_DATA', + + // asynchronously saves a list of files to an IndexedDB. The DB will be created if not already existing. + saveFilesToDB: function(paths, onload, onerror) { + onload = onload || function(){}; + onerror = onerror || function(){}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = function() { + console.log('creating db'); + var db = openRequest.result; + db.createObjectStore(FS.DB_STORE_NAME); + }; + openRequest.onsuccess = function() { + var db = openRequest.result; + var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite'); + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, fail = 0, total = paths.length; + function finish() { + if (fail == 0) onload(); else onerror(); + } + paths.forEach(function(path) { + var putRequest = files.put(FS.analyzePath(path).object.contents, path); + putRequest.onsuccess = function() { ok++; if (ok + fail == total) finish() }; + putRequest.onerror = function() { fail++; if (ok + fail == total) finish() }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + }, + + // asychronously loads a file from IndexedDB. + loadFilesFromDB: function(paths, onload, onerror) { + onload = onload || function(){}; + onerror = onerror || function(){}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = onerror; // no database to load from + openRequest.onsuccess = function() { + var db = openRequest.result; + try { + var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly'); + } catch(e) { + onerror(e); + return; + } + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, fail = 0, total = paths.length; + function finish() { + if (fail == 0) onload(); else onerror(); + } + paths.forEach(function(path) { + var getRequest = files.get(path); + getRequest.onsuccess = function() { + if (FS.analyzePath(path).exists) { + FS.unlink(path); + } + FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true); + ok++; + if (ok + fail == total) finish(); + }; + getRequest.onerror = function() { fail++; if (ok + fail == total) finish() }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + }, + // // general // diff --git a/src/library_memfs.js b/src/library_memfs.js index 63326c42..354f5e95 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -2,17 +2,13 @@ mergeInto(LibraryManager.library, { $MEMFS__deps: ['$FS'], $MEMFS: { // content modes - CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it - need to free() when no longer needed + CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary) CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array ensureFlexible: function(node) { if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) { var contents = node.contents; node.contents = Array.prototype.slice.call(contents); - if (node.contentMode === MEMFS.CONTENT_OWNING) { - assert(contents.byteOffset); - Module['_free'](contents.byteOffset); - } node.contentMode = MEMFS.CONTENT_FLEXIBLE; } }, diff --git a/src/library_path.js b/src/library_path.js index 2c2c016a..09808acd 100644 --- a/src/library_path.js +++ b/src/library_path.js @@ -68,6 +68,9 @@ mergeInto(LibraryManager.library, { } return f; }, + extname: function(path) { + return PATH.splitPath(path)[3]; + }, join: function() { var paths = Array.prototype.slice.call(arguments, 0); return PATH.normalize(paths.filter(function(p, index) { diff --git a/src/library_sdl.js b/src/library_sdl.js index 1fb75724..d6cb6d18 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -46,6 +46,9 @@ var LibrarySDL = { keyboardState: null, keyboardMap: {}, + canRequestFullscreen: false, + isRequestingFullscreen: false, + textInput: false, startTime: null, @@ -467,6 +470,23 @@ var LibrarySDL = { SDL.DOMButtons[event.button] = 0; } + // We can only request fullscreen as the result of user input. + // Due to this limitation, we toggle a boolean on keydown which + // SDL_WM_ToggleFullScreen will check and subsequently set another + // flag indicating for us to request fullscreen on the following + // keyup. This isn't perfect, but it enables SDL_WM_ToggleFullScreen + // to work as the result of a keypress (which is an extremely + // common use case). + if (event.type === 'keydown') { + SDL.canRequestFullscreen = true; + } else if (event.type === 'keyup') { + if (SDL.isRequestingFullscreen) { + Module['requestFullScreen'](true, true); + SDL.isRequestingFullscreen = false; + } + SDL.canRequestFullscreen = false; + } + // SDL expects a unicode character to be passed to its keydown events. // Unfortunately, the browser APIs only provide a charCode property on // keypress events, so we must backfill in keydown events with their @@ -1282,7 +1302,11 @@ var LibrarySDL = { Module['canvas'].cancelFullScreen(); return 1; } else { - return 0; + if (!SDL.canRequestFullscreen) { + return 0; + } + SDL.isRequestingFullscreen = true; + return 1; } }, diff --git a/src/library_tty.js b/src/library_tty.js index 8f44cd07..53239989 100644 --- a/src/library_tty.js +++ b/src/library_tty.js @@ -1,17 +1,35 @@ mergeInto(LibraryManager.library, { $TTY__deps: ['$FS'], + $TTY__postset: '__ATINIT__.unshift({ func: function() { TTY.init() } });' + + '__ATEXIT__.push({ func: function() { TTY.shutdown() } });' + + 'TTY.utf8 = new Runtime.UTF8Processor();', $TTY: { ttys: [], + init: function () { + if (ENVIRONMENT_IS_NODE) { + // currently, FS.init does not distinguish if process.stdin is a file or TTY + // device, it always assumes it's a TTY device. because of this, we're forcing + // process.stdin to UTF8 encoding to at least make stdin reading compatible + // with text files until FS.init can be refactored. + process['stdin']['setEncoding']('utf8'); + } + }, + shutdown: function() { + if (ENVIRONMENT_IS_NODE) { + // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? + // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation + // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? + // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle + // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call + process['stdin']['pause'](); + } + }, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops }; FS.registerDevice(dev, TTY.stream_ops); }, stream_ops: { open: function(stream) { - // this wouldn't be required if the library wasn't eval'd at first... - if (!TTY.utf8) { - TTY.utf8 = new Runtime.UTF8Processor(); - } var tty = TTY.ttys[stream.node.rdev]; if (!tty) { throw new FS.ErrnoError(ERRNO_CODES.ENODEV); @@ -66,17 +84,22 @@ mergeInto(LibraryManager.library, { return i; } }, - // NOTE: This is weird to support stdout and stderr - // overrides in addition to print and printErr overrides. default_tty_ops: { + // get_char has 3 particular return values: + // a.) the next character represented as an integer + // b.) undefined to signal that no data is currently available + // c.) null to signal an EOF get_char: function(tty) { if (!tty.input.length) { var result = null; if (ENVIRONMENT_IS_NODE) { - if (process.stdin.destroyed) { - return undefined; + result = process['stdin']['read'](); + if (!result) { + if (process['stdin']['_readableState'] && process['stdin']['_readableState']['ended']) { + return null; // EOF + } + return undefined; // no data available } - result = process.stdin.read(); } else if (typeof window != 'undefined' && typeof window.prompt == 'function') { // Browser. diff --git a/src/parseTools.js b/src/parseTools.js index 046dac1b..66354dca 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -430,6 +430,8 @@ function parseParamTokens(params) { ret.push(parseLLVMFunctionCall(segment)); } else if (segment[1].text === 'blockaddress') { ret.push(parseBlockAddress(segment)); + } else if (segment[1].type && segment[1].type == '{') { + ret.push(parseLLVMSegment(segment)); } else { if (segment[2] && segment[2].text == 'to') { // part of bitcast params segment = segment.slice(0, 2); diff --git a/src/settings.js b/src/settings.js index 03b4ed64..02423ba5 100644 --- a/src/settings.js +++ b/src/settings.js @@ -874,9 +874,9 @@ var C_DEFINES = { 'SOCK_DGRAM': '2', 'SOCK_STREAM': '1', 'STDC_HEADERS': '1', - 'STDERR_FILENO': '2', - 'STDIN_FILENO': '0', - 'STDOUT_FILENO': '1', + 'STDERR_FILENO': '3', + 'STDIN_FILENO': '1', + 'STDOUT_FILENO': '2', 'S_BLKSIZE': '1024', 'S_ENFMT': '0002000', 'S_IEXEC': '0000100', diff --git a/tests/cases/callwithstructural64_ta2.ll b/tests/cases/callwithstructural64_ta2.ll new file mode 100644 index 00000000..d16b0e87 --- /dev/null +++ b/tests/cases/callwithstructural64_ta2.ll @@ -0,0 +1,29 @@ +; ModuleID = 'foo.bc' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + +%ac = type { i8*, i64 } + +@0 = constant [9 x i8] c"func %s\0A\00" +@1 = constant [4 x i8] c"foo\00" + +declare void @llvm.trap() noreturn nounwind + +define void @direct(%ac) { +entry: + %str = alloca %ac + store %ac %0, %ac* %str + %1 = getelementptr inbounds %ac* %str, i32 0, i32 0 + %2 = load i8** %1 + call void (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @0, i32 0, i32 0), i8* %2) + ret void +} + +declare void @printf(i8*, ...) + +define i32 @main() { +entry: + call void @direct(%ac { i8* getelementptr inbounds ([4 x i8]* @1, i32 0, i32 0), i64 3 }) + ret i32 0 +} + diff --git a/tests/cases/callwithstructural64_ta2.txt b/tests/cases/callwithstructural64_ta2.txt new file mode 100644 index 00000000..51a6ac7c --- /dev/null +++ b/tests/cases/callwithstructural64_ta2.txt @@ -0,0 +1 @@ +func foo diff --git a/tests/cases/callwithstructural_ta2.ll b/tests/cases/callwithstructural_ta2.ll new file mode 100644 index 00000000..bc6f852a --- /dev/null +++ b/tests/cases/callwithstructural_ta2.ll @@ -0,0 +1,29 @@ +; ModuleID = 'foo.bc' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + +%ac = type { i8*, i32 } + +@0 = constant [9 x i8] c"func %s\0A\00" +@1 = constant [4 x i8] c"foo\00" + +declare void @llvm.trap() noreturn nounwind + +define void @direct(%ac) { +entry: + %str = alloca %ac + store %ac %0, %ac* %str + %1 = getelementptr inbounds %ac* %str, i32 0, i32 0 + %2 = load i8** %1 + call void (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @0, i32 0, i32 0), i8* %2) + ret void +} + +declare void @printf(i8*, ...) + +define i32 @main() { +entry: + call void @direct(%ac { i8* getelementptr inbounds ([4 x i8]* @1, i32 0, i32 0), i32 3 }) + ret i32 0 +} + diff --git a/tests/cases/callwithstructural_ta2.txt b/tests/cases/callwithstructural_ta2.txt new file mode 100644 index 00000000..51a6ac7c --- /dev/null +++ b/tests/cases/callwithstructural_ta2.txt @@ -0,0 +1 @@ +func foo diff --git a/tests/cases/structinparam.ll b/tests/cases/structinparam.ll new file mode 100644 index 00000000..d81f5e67 --- /dev/null +++ b/tests/cases/structinparam.ll @@ -0,0 +1,36 @@ +; ModuleID = 'min.bc' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + +%ac = type { i8*, i32 } + +@0 = constant [9 x i8] c"func %s\0A\00" +@1 = constant [4 x i8] c"foo\00" +@2 = constant [9 x i8] c"main %s\0A\00" + +declare void @llvm.trap() noreturn nounwind + +define void @direct(%ac) { +entry: + %str = alloca %ac + store %ac %0, %ac* %str + %1 = getelementptr inbounds %ac* %str, i32 0, i32 0 + %2 = load i8** %1 + call void (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @0, i32 0, i32 0), i8* %2) + ret void +} + +declare void @printf(i8*, ...) + +define i32 @main() { +entry: + %str = alloca %ac + store %ac { i8* getelementptr inbounds ([4 x i8]* @1, i32 0, i32 0), i32 3 }, %ac* %str + %0 = getelementptr inbounds %ac* %str, i32 0, i32 0 + %1 = load i8** %0 + call void (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @2, i32 0, i32 0), i8* %1) + %2 = load %ac* %str + call void @direct(%ac %2) + ret i32 0 +} + diff --git a/tests/cases/structinparam.txt b/tests/cases/structinparam.txt new file mode 100644 index 00000000..785191e7 --- /dev/null +++ b/tests/cases/structinparam.txt @@ -0,0 +1,2 @@ +main foo +func foo diff --git a/tests/emscripten_get_now.cpp b/tests/emscripten_get_now.cpp new file mode 100644 index 00000000..17aa7d32 --- /dev/null +++ b/tests/emscripten_get_now.cpp @@ -0,0 +1,46 @@ +#include <stdio.h> +#include "emscripten.h" + +#ifndef REPORT_RESULT +// To be able to run this test outside the browser harness in node.js/spidermonkey: +#define REPORT_RESULT int dummy +#endif + +int result = 0; + +int main() { + // This code tests three things: + // a) Calling emscripten_get_now(), time actually proceeds. + // b) Values returned by emscripten_get_now() are strictly nondecreasing. + // c) emscripten_get_now() is able to return sub-millisecond precision timer values. + bool detected_good_timer_precision = false; + float smallest_delta = 0.f; + for(int x = 0; x < 1000; ++x) { // Have several attempts to find a good small delta, i.e. give time to JS engine to warm up the code and so on. + float t = emscripten_get_now(); + float t2 = emscripten_get_now(); + for(int i = 0; i < 100 && t == t2; ++i) { + t2 = emscripten_get_now(); + } + + if (t2 < t && t2 - t < 1000.f) { // Timer must be monotonous. + printf("Timer is not monotonous!\\n"); + smallest_delta = t2 - t; + break; + } + if (t2 > t && t2 - t < 0.7f) { // Must pass less than a millisecond between two calls. + detected_good_timer_precision = true; + smallest_delta = t2 - t; + break; + } + } + + if (detected_good_timer_precision) { + printf("Timer resolution is good. (%f msecs)\\n", smallest_delta); + result = 1; + } else { + printf("Error: Bad timer precision: Smallest timer delta: %f msecs\\n", smallest_delta); + result = 0; + } + REPORT_RESULT(); + return 0; +} diff --git a/tests/file_db.cpp b/tests/file_db.cpp new file mode 100644 index 00000000..ebb3bb30 --- /dev/null +++ b/tests/file_db.cpp @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <emscripten.h> + +void later(void *) {} + +int main() { +#if FIRST + FILE *f = fopen("waka.txt", "w"); + fputc('a', f); + fputc('z', f); + fclose(f); + + EM_ASM( + FS.saveFilesToDB(['waka.txt', 'moar.txt'], function() { + Module.print('save ok'); + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://localhost:8888/report_result?1'); + xhr.send(); + setTimeout(function() { window.close() }, 1000); + }, function(e) { + abort('saving should succeed ' + e); + }); + ); +#else + EM_ASM( + FS.loadFilesFromDB(['waka.txt', 'moar.txt'], function() { + function stringy(arr) { + return Array.prototype.map.call(arr, function(x) { return String.fromCharCode(x) }).join(''); + } + assert(stringy(FS.analyzePath('waka.txt').object.contents) == 'az'); + var secret = stringy(FS.analyzePath('moar.txt').object.contents); + Module.print('load: ' + secret); + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://localhost:8888/report_result?' + secret); + xhr.send(); + setTimeout(function() { window.close() }, 1000); + }, function() { + abort('loading should succeed'); + }); + ); +#endif + + emscripten_async_call(later, NULL, 100); // keep runtime alive + + return 0; +} + diff --git a/tests/filesystem/src.js b/tests/filesystem/src.js index ceea348d..dbdd4bed 100644 --- a/tests/filesystem/src.js +++ b/tests/filesystem/src.js @@ -1,10 +1,13 @@ +var dummy_device = FS.makedev(64, 0); +FS.registerDevice(dummy_device, {}); + FS.createFolder('/', 'forbidden', false, false); FS.createFolder('/forbidden', 'test', true, true); FS.createPath('/', 'abc/123', true, true); FS.createPath('/', 'abc/456', true, true); FS.createPath('/', 'def/789', true, true); -FS.createDevice('/abc', 'deviceA', function() {}, function() {}); -FS.createDevice('/def', 'deviceB', function() {}, function() {}); +FS.mkdev('/abc/deviceA', 0666, dummy_device); +FS.mkdev('/def/deviceB', 0666, dummy_device); FS.createLink('/abc', 'localLink', '123', true, true); FS.createLink('/abc', 'rootLink', '/', true, true); FS.createLink('/abc', 'relativeLink', '../def', true, true); diff --git a/tests/module/test_stdin.c b/tests/module/test_stdin.c new file mode 100644 index 00000000..4838d466 --- /dev/null +++ b/tests/module/test_stdin.c @@ -0,0 +1,57 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +int line = 0; + +void main_loop(void *arg) +{ + char str[10] = {0}; + int ret; + + errno = 0; + while (errno != EAGAIN) { + if (line == 0) { + ret = fgetc(stdin); + if (ret != EOF) putc(ret, stdout); + if (ret == '\n') line++; + } else if (line > 0) { + ret = scanf("%10s", str); + if (ret > 0) puts(str); + } + + if (ferror(stdin) && errno != EAGAIN) { + puts("error"); + exit(EXIT_FAILURE); + } + + if (feof(stdin)) { + puts("eof"); + exit(EXIT_SUCCESS); + } + + clearerr(stdin); + } +} + +int main(int argc, char const *argv[]) +{ + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); + + // SM shell doesn't implement an event loop and therefor doesn't support + // emscripten_set_main_loop. However, its stdin reads are sync so it + // should exit out after calling main_loop once. + main_loop(NULL); + +#if EMSCRIPTEN + emscripten_set_main_loop(main_loop, 60, 0); +#else + while (1) main_loop(NULL); sleep(1); +#endif + return 0; +}
\ No newline at end of file diff --git a/tests/msvc10/glbook_10_MultiTexture.vcxproj b/tests/msvc10/glbook_10_MultiTexture.vcxproj index b59fda0a..a831f351 100644 --- a/tests/msvc10/glbook_10_MultiTexture.vcxproj +++ b/tests/msvc10/glbook_10_MultiTexture.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_11_Multisample.vcxproj b/tests/msvc10/glbook_11_Multisample.vcxproj index ae5170be..47d5fb4a 100644 --- a/tests/msvc10/glbook_11_Multisample.vcxproj +++ b/tests/msvc10/glbook_11_Multisample.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_11_Stencil_Test.vcxproj b/tests/msvc10/glbook_11_Stencil_Test.vcxproj index d72d7fa6..3a541128 100644 --- a/tests/msvc10/glbook_11_Stencil_Test.vcxproj +++ b/tests/msvc10/glbook_11_Stencil_Test.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_13_ParticleSystem.vcxproj b/tests/msvc10/glbook_13_ParticleSystem.vcxproj index 458d76fa..c18e17ff 100644 --- a/tests/msvc10/glbook_13_ParticleSystem.vcxproj +++ b/tests/msvc10/glbook_13_ParticleSystem.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj b/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj index 654164b3..489628f5 100644 --- a/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj +++ b/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_2_Hello_Triangle.vcxproj b/tests/msvc10/glbook_2_Hello_Triangle.vcxproj index b992294d..34de0780 100644 --- a/tests/msvc10/glbook_2_Hello_Triangle.vcxproj +++ b/tests/msvc10/glbook_2_Hello_Triangle.vcxproj @@ -92,12 +92,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> diff --git a/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj b/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj index fb01c1da..19442df9 100644 --- a/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj +++ b/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_9_MipMap2D.vcxproj b/tests/msvc10/glbook_9_MipMap2D.vcxproj index 1d747d1d..af0e072b 100644 --- a/tests/msvc10/glbook_9_MipMap2D.vcxproj +++ b/tests/msvc10/glbook_9_MipMap2D.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj b/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj index d9232e5d..09ff3e1e 100644 --- a/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj +++ b/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj b/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj index c5c6ee1b..d89da1c7 100644 --- a/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj +++ b/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/msvc10/glbook_9_TextureWrap.vcxproj b/tests/msvc10/glbook_9_TextureWrap.vcxproj index 6cfbb0c8..54f51e26 100644 --- a/tests/msvc10/glbook_9_TextureWrap.vcxproj +++ b/tests/msvc10/glbook_9_TextureWrap.vcxproj @@ -82,12 +82,12 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'"> <ClCompile> - <AdditionalIncludeDirectories>$(SolutionDir)../glbook/Common</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/tests/termios/test_tcgetattr.c b/tests/termios/test_tcgetattr.c new file mode 100644 index 00000000..2b3780ee --- /dev/null +++ b/tests/termios/test_tcgetattr.c @@ -0,0 +1,61 @@ +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +static void create_file(const char *path, const char *buffer, int mode) { + int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); + assert(fd >= 0); + + int err = write(fd, buffer, sizeof(char) * strlen(buffer)); + assert(err == (sizeof(char) * strlen(buffer))); + + close(fd); +} + +void setup() { + create_file("test.txt", "abcdefg", 0666); +} + +void cleanup() { + unlink("test.txt"); +} + +void test() { + struct termios tc; + int ret; + int fd; + + fd = open("test.txt", O_RDONLY); + + ret = tcgetattr(fd, &tc); + assert(ret == -1); + assert(errno = ENOTTY); + + ret = tcgetattr(STDIN_FILENO, &tc); + assert(!ret); + + ret = tcsetattr(fd, 0, &tc); + assert(ret == -1); + assert(errno = ENOTTY); + + ret = tcsetattr(STDIN_FILENO, 0, &tc); + assert(!ret); + + close(fd); + + puts("success"); +} + +int main() { + atexit(cleanup); + signal(SIGABRT, cleanup); + setup(); + test(); + return EXIT_SUCCESS; +} diff --git a/tests/test_browser.py b/tests/test_browser.py index 1c9cbfad..a0c4dceb 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -794,6 +794,20 @@ If manually bisecting: def test_glut_touchevents(self): self.btest('glut_touchevents.c', '1') + def test_emscripten_get_now(self): + self.btest('emscripten_get_now.cpp', '1') + + def test_file_db(self): + secret = str(time.time()) + open('moar.txt', 'w').write(secret) + self.btest('file_db.cpp', '1', args=['--preload-file', 'moar.txt', '-DFIRST']) + shutil.copyfile('test.html', 'first.html') + self.btest('file_db.cpp', secret) + shutil.copyfile('test.html', 'second.html') + open('moar.txt', 'w').write('aliantha') + self.btest('file_db.cpp', secret, args=['--preload-file', 'moar.txt']) # even with a file there, we load over it + shutil.move('test.html', 'third.html') + def test_sdl_pumpevents(self): # key events should be detected using SDL_PumpEvents open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' diff --git a/tests/test_core.py b/tests/test_core.py index 7c5b651f..6d15da1e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3734,6 +3734,13 @@ def process(filename): Settings.EXPORT_ALL = 1 self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) + def test_emscripten_get_now(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') + + if self.run_name == 'o2': + self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage + self.do_run(open(path_from_root('tests', 'emscripten_get_now.cpp')).read(), 'Timer resolution is good.') + def test_inlinejs(self): if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm') src = r''' @@ -4670,6 +4677,10 @@ The current type of b is: 9 expected = open(path_from_root('tests', 'pthread', 'specific.c.txt'), 'r').read() self.do_run(src, expected, force_c=True) + def test_tcgetattr(self): + src = open(path_from_root('tests', 'termios', 'test_tcgetattr.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + def test_time(self): # XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again. src = open(path_from_root('tests', 'time', 'src.c'), 'r').read() @@ -6977,8 +6988,11 @@ def process(filename): src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', \'\'\' + var dummy_device = FS.makedev(64, 0); + FS.registerDevice(dummy_device, {}); + FS.createDataFile('/', 'file', 'abcdef', true, true); - FS.createDevice('/', 'device', function() {}, function() {}); + FS.mkdev('/device', 0666, dummy_device); \'\'\' ) open(filename, 'w').write(src) @@ -8594,7 +8608,7 @@ def process(filename): do_test() # some test coverage for EMCC_DEBUG 1 and 2 - if self.emcc_args and '-O2' in self.emcc_args and 'EMCC_DEBUG' not in os.environ: + if self.emcc_args and '-O2' in self.emcc_args and 'EMCC_DEBUG' not in os.environ and '-g' in self.emcc_args: shutil.copyfile('src.c.o.js', 'release.js') try: os.environ['EMCC_DEBUG'] = '1' @@ -8609,7 +8623,8 @@ def process(filename): del os.environ['EMCC_DEBUG'] for debug in [1,2]: def clean(text): - return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}') + text = text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}') + return '\n'.join(sorted(text.split('\n'))) self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code! print >> sys.stderr, 'debug check %d passed too' % debug @@ -9681,7 +9696,8 @@ def process(filename): # optimizer can deal with both types. out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE) def clean(code): - return code.replace('{\n}', '{}') + code = code.replace('{\n}', '{}') + return '\n'.join(sorted(code.split('\n'))) self.assertIdentical(clean(no_maps_file), clean(out_file)) map_filename = out_filename + '.map' data = json.load(open(map_filename, 'r')) diff --git a/tests/test_other.py b/tests/test_other.py index a6813b07..fd1a6245 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -929,20 +929,14 @@ f.close() self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_stdin(self): - open('main.cpp', 'w').write(r''' -#include <stdio.h> -int main(int argc, char const *argv[]) -{ - char str[10] = {0}; - scanf("%10s", str); - printf("%s\n", str); - return 0; -} -''') - Building.emcc('main.cpp', output_filename='a.out.js') - open('in.txt', 'w').write('abc') - # node's stdin support is broken - self.assertContained('abc', Popen(listify(SPIDERMONKEY_ENGINE) + ['a.out.js'], stdin=open('in.txt'), stdout=PIPE, stderr=PIPE).communicate()[0]) + Building.emcc(path_from_root('tests', 'module', 'test_stdin.c'), output_filename='a.out.js') + open('in.txt', 'w').write('abcdef\nghijkl') + + for engine in JS_ENGINES: + print >> sys.stderr, engine + if engine == NODE_JS: continue # FIXME + if engine == V8_ENGINE: continue # no stdin support in v8 shell + self.assertContained('abcdef\nghijkl\neof', run_js(os.path.join(self.get_dir(), 'a.out.js'), engine=engine, stdin=open('in.txt'))) def test_ungetc_fscanf(self): open('main.cpp', 'w').write(r''' diff --git a/tests/unistd/curdir.js b/tests/unistd/curdir.js index e271b9da..75a1d2ce 100644 --- a/tests/unistd/curdir.js +++ b/tests/unistd/curdir.js @@ -1,4 +1,7 @@ +var dummy_device = FS.makedev(64, 0); +FS.registerDevice(dummy_device, {}); + FS.createDataFile('/', 'file', '', true, true); FS.createFolder('/', 'folder', true, true); -FS.createDevice('/', 'device', function() {}, function() {}); +FS.mkdev('/device', 0666, dummy_device); FS.createLink('/', 'link', 'folder', true, true); diff --git a/tests/unistd/io.c b/tests/unistd/io.c index eeb80373..a96290ef 100644 --- a/tests/unistd/io.c +++ b/tests/unistd/io.c @@ -34,6 +34,19 @@ int main() { printf("errno: %d\n\n", errno); errno = 0; + int cd_ro_r = open("/createDevice-read-only", O_RDONLY); + printf("open read-only device from createDevice for read, errno: %d\n", errno); + errno = 0; + int cd_ro_w = open("/createDevice-read-only", O_WRONLY); + printf("open read-only device from createDevice for write, errno: %d\n", errno); + errno = 0; + int cd_wo_r = open("/createDevice-write-only", O_RDONLY); + printf("open write-only device from createDevice for read, errno: %d\n", errno); + errno = 0; + int cd_wo_w = open("/createDevice-write-only", O_WRONLY); + printf("open write-only device from createDevice for write, errno: %d\n\n", errno); + errno = 0; + int f = open("/file", O_RDWR); printf("read from file: %d\n", read(f, readBuffer, sizeof readBuffer)); printf("data: %s\n", readBuffer); diff --git a/tests/unistd/io.js b/tests/unistd/io.js index e2e442ec..11c0da79 100644 --- a/tests/unistd/io.js +++ b/tests/unistd/io.js @@ -1,19 +1,52 @@ (function() { - var devicePayload = [65, 66, 67, 68]; - FS.createDevice('/', 'device', function() { - if (devicePayload.length) { - return devicePayload.shift(); - } else { - return null; + var major = 80; + + var device = FS.makedev(major++, 0); + var device_ops = { + open: function(stream) { + stream.payload = [65, 66, 67, 68]; + }, + read: function(stream, buffer, offset, length, pos) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + if (stream.payload.length) { + bytesRead++; + buffer[offset+i] = stream.payload.shift(); + } else { + break; + } + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + Module.print("TO DEVICE: " + buffer[offset+i]); + } + return i; } - }, function(arg) { - Module.print("TO DEVICE: " + arg); - }); - FS.createDevice('/', 'broken-device', function() { - throw new Error('Broken device input.'); - }, function(arg) { - throw new Error('Broken device output.'); - }); + }; + FS.registerDevice(device, device_ops); + + FS.mkdev('/device', 0666, device); + + var broken_device = FS.makedev(major++, 0); + var broken_device_ops = { + read: function(stream, buffer, offset, length, pos) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + }, + write: function(stream, buffer, offset, length, pos) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + }; + FS.registerDevice(broken_device, broken_device_ops); + + FS.mkdev('/broken-device', 0666, broken_device); + + // NB: These are meant to test FS.createDevice specifically, + // and as such do not use registerDevice/mkdev + FS.createDevice('/', 'createDevice-read-only', function() {}); + FS.createDevice('/', 'createDevice-write-only', null, function() {}); + FS.createDataFile('/', 'file', '1234567890', true, true); FS.createFolder('/', 'folder', true, true); })(); diff --git a/tests/unistd/io.out b/tests/unistd/io.out index 3061b94e..037d0c34 100644 --- a/tests/unistd/io.out +++ b/tests/unistd/io.out @@ -22,6 +22,11 @@ TO DEVICE: 0 write to device: 8 errno: 0 +open read-only device from createDevice for read, errno: 0 +open read-only device from createDevice for write, errno: 13 +open write-only device from createDevice for read, errno: 13 +open write-only device from createDevice for write, errno: 0 + read from file: 10 data: 1234567890 errno: 0 diff --git a/tools/file_packager.py b/tools/file_packager.py index bb62e905..8f0f8be8 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -344,14 +344,7 @@ if has_preloaded: }, send: function() {}, onload: function() { - var data = this.byteArray.subarray(this.start, this.end); - var size = this.end - this.start; - var ptr = Module['_malloc'](size); // XXX leaked if a preload plugin replaces with new data - Module['HEAPU8'].set(data, ptr); - var arrayBuffer = Module['HEAPU8'].subarray(ptr, ptr + size); - assert(arrayBuffer, 'Loading file ' + name + ' failed'); - var byteArray = !arrayBuffer.subarray ? new Uint8Array(arrayBuffer) : arrayBuffer; - + var byteArray = this.byteArray.subarray(this.start, this.end); if (this.crunched) { var ddsHeader = byteArray.subarray(0, 128); var that = this; @@ -367,7 +360,7 @@ if has_preloaded: }, finish: function(byteArray) { var that = this; - Module['FS_createPreloadedFile'](PATH.dirname(this.name), PATH.basename(this.name), byteArray, true, true, function() { + Module['FS_createPreloadedFile'](this.name, null, byteArray, true, true, function() { Module['removeRunDependency']('fp ' + that.name); }, function() { if (that.audio) { @@ -375,7 +368,7 @@ if has_preloaded: } else { Runtime.warn('Preloading file ' + that.name + ' failed'); } - }); + }, false, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change this.requests[this.name] = null; }, }; @@ -419,7 +412,12 @@ for file_ in data_files: if has_preloaded: # Get the big archive and split it up - use_data = ' DataRequest.prototype.byteArray = byteArray;\n' + use_data = ''' + // copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though. + var ptr = Module['_malloc'](byteArray.length); + Module['HEAPU8'].set(byteArray, ptr); + DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length); +''' for file_ in data_files: if file_['mode'] == 'preload': use_data += ' DataRequest.prototype.requests["%s"].onload();\n' % (file_['dstpath']) diff --git a/tools/find_bigfuncs.py b/tools/find_bigfuncs.py index 79136343..c8e29833 100644 --- a/tools/find_bigfuncs.py +++ b/tools/find_bigfuncs.py @@ -18,6 +18,6 @@ for line in open(filename): size = i - start data.append([curr, size]) curr = None -data.sort(lambda x, y: x[1] - y[1]) +#data.sort(lambda x, y: x[1] - y[1]) print ''.join(['%6d : %s' % (x[1], x[0]) for x in data]) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 9a5104bf..b42164f9 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -135,6 +135,7 @@ var ASSIGN_OR_ALTER = set('assign', 'unary-postfix', 'unary-prefix'); var CONTROL_FLOW = set('do', 'while', 'for', 'if', 'switch'); var NAME_OR_NUM = set('name', 'num'); var ASSOCIATIVE_BINARIES = set('+', '*', '|', '&', '^'); +var ALTER_FLOW = set('break', 'continue', 'return'); var BREAK_CAPTURERS = set('do', 'while', 'for', 'switch'); var CONTINUE_CAPTURERS = LOOP; @@ -3070,10 +3071,10 @@ function outline(ast) { currSize += size; if (!isIf) { var last = part.body; - last = last[stats.length-1]; + last = last[last.length-1]; if (last && last[0] === 'block') last = last[1][last[1].length-1]; if (last && last[0] === 'stat') last = last[1]; - force = !last || last[0] !== 'break'; + force = !last || !(last[0] in ALTER_FLOW); } }); assert(currSize); @@ -3487,7 +3488,7 @@ function outline(ast) { } } outliningParents[newIdent] = func[1]; - printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned)), ' owned: ', setSize(owned), ' left: ', setSize(asmData.vars), setSize(asmData.params), ' loopsDepth: ', loops]); + printErr('performed outline ' + [func[1], newIdent, 'pre size', originalCodeSize, 'resulting size', measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned)), ' owned: ', setSize(owned), ' left: ', setSize(asmData.vars), setSize(asmData.params), ' loopsDepth: ', loops]); calculateThreshold(func, asmData); return [newFunc]; } @@ -3633,6 +3634,8 @@ function outline(ast) { var maxTotalFunctions = Infinity; // debugging tool + printErr('\n'); + var more = true; while (more) { more = false; @@ -3691,7 +3694,8 @@ function outline(ast) { } } } - printErr('... resulting size of ' + func[1] + ' is ' + measureSize(func)); + ret.push(func); + printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n'); } denormalizeAsm(func, asmData); }); diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 5d7dc562..a11da7f0 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -205,23 +205,26 @@ EMSCRIPTEN_FUNCS(); pre = '' post = '' - # Pick where to split into chunks, so that (1) they do not oom in node/uglify, and (2) we can run them in parallel - # If we have metadata, we split only the generated code, and save the pre and post on the side (and do not optimize them) - parts = map(lambda part: part, js.split('\n}\n')) - funcs = [] - for i in range(len(parts)): - func = parts[i] - if i < len(parts)-1: func += '\n}\n' # last part needs no } - m = func_sig.search(func) - if m: - ident = m.group(2) - else: - if know_generated: continue # ignore whitespace - ident = 'anon_%d' % i - assert ident - funcs.append((ident, func)) - parts = None + def split_funcs(js): + # Pick where to split into chunks, so that (1) they do not oom in node/uglify, and (2) we can run them in parallel + # If we have metadata, we split only the generated code, and save the pre and post on the side (and do not optimize them) + parts = map(lambda part: part, js.split('\n}\n')) + funcs = [] + for i in range(len(parts)): + func = parts[i] + if i < len(parts)-1: func += '\n}\n' # last part needs no } + m = func_sig.search(func) + if m: + ident = m.group(2) + else: + if know_generated: continue # ignore whitespace + ident = 'anon_%d' % i + assert ident + funcs.append((ident, func)) + return funcs + total_size = len(js) + funcs = split_funcs(js) js = None if 'last' in passes and len(funcs) > 0: @@ -235,6 +238,7 @@ EMSCRIPTEN_FUNCS(); chunk_size = min(MAX_CHUNK_SIZE, max(MIN_CHUNK_SIZE, total_size / intended_num_chunks)) chunks = shared.chunkify(funcs, chunk_size, jcache.get_cachename('jsopt') if jcache else None) + funcs = None if jcache: # load chunks from cache where we can # TODO: ignore small chunks @@ -324,9 +328,25 @@ EMSCRIPTEN_FUNCS(); filename += '.jo.js' f = open(filename, 'w') f.write(pre); + pre = None + + # sort functions by size, to make diffing easier and to improve aot times + funcses = [] for out_file in filenames: - f.write(open(out_file).read()) - f.write('\n') + funcses.append(split_funcs(open(out_file).read())) + funcs = [item for sublist in funcses for item in sublist] + funcses = None + def sorter(x, y): + diff = len(y[1]) - len(x[1]) + if diff != 0: return diff + if x[0] < y[0]: return 1 + elif x[0] > y[0]: return -1 + return 0 + funcs.sort(sorter) + for func in funcs: + f.write(func[1]) + funcs = None + f.write('\n') if jcache: for cached in cached_outputs: f.write(cached); # TODO: preserve order diff --git a/tools/jsrun.py b/tools/jsrun.py index 571e9cee..91038f6e 100644 --- a/tools/jsrun.py +++ b/tools/jsrun.py @@ -12,13 +12,14 @@ def timeout_run(proc, timeout, note='unnamed process', full_output=False): out = proc.communicate() return '\n'.join(out) if full_output else out[0] -def run_js(filename, engine=None, args=[], check_timeout=False, stdout=PIPE, stderr=None, cwd=None, full_output=False): +def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False): if type(engine) is not list: engine = [engine] command = engine + [filename] + (['--'] if 'd8' in engine[0] or 'jsc' in engine[0] else []) + args return timeout_run( Popen( command, + stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd), diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js index 895004d8..612da16a 100644 --- a/tools/test-js-optimizer-asm-outline1-output.js +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -348,6 +348,15 @@ function switchh() { HEAP32[sp + 44 >> 2] = 0; switchh$2(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + tempValue = HEAP32[sp + 40 >> 2] | 0; + tempInt = HEAP32[sp + 44 >> 2] | 0; + tempDouble = +HEAPF32[sp + 44 >> 2]; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + if ((tempValue | 0) == 5) { + STACKTOP = sp; + return; + } HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 16 >> 2] = helper$1; HEAP32[sp + 32 >> 2] = 0; @@ -749,36 +758,39 @@ function switchh$2(sp) { var helper$0 = 0, helper$1 = 0; helper$0 = HEAP32[sp + 8 >> 2] | 0; helper$1 = HEAP32[sp + 16 >> 2] | 0; - if (helper$0) { - helper$0 = 0; - switch (helper$1 | 0) { - case 1: - { - f(1); - g(); - break; - } - default: - { - helper$0 = 1; + OL : do { + if (helper$0) { + helper$0 = 0; + switch (helper$1 | 0) { + case 1: + { + f(1); + g(); + HEAP32[sp + 40 >> 2] = 5; + break OL; + } + default: + { + helper$0 = 1; + } } } - } - if (helper$0) { - helper$0 = 0; - switch (helper$1 | 0) { - case 2: - { - f(2); - g(); - break; - } - default: - { - helper$0 = 1; + if (helper$0) { + helper$0 = 0; + switch (helper$1 | 0) { + case 2: + { + f(2); + g(); + break; + } + default: + { + helper$0 = 1; + } } } - } + } while (0); HEAP32[sp + 8 >> 2] = helper$0; } diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js index 3c454182..4282ec8e 100644 --- a/tools/test-js-optimizer-asm-outline1.js +++ b/tools/test-js-optimizer-asm-outline1.js @@ -269,7 +269,7 @@ function switchh() { case 1: { f(1); g(); - break; + return; } case 2: { f(2); |