diff options
74 files changed, 16433 insertions, 672 deletions
@@ -16,3 +16,4 @@ under the licensing terms detailed in LICENSE. * Adrian Taylor <adrian@macrobug.com> * Richard Assar <richard.assar@gmail.com> * Nathan Hammond <emscripten@nathanhammond.com> +* Behdad Esfahbod <behdad@behdad.org> @@ -329,9 +329,11 @@ ASSEMBLY_SUFFIXES = ('.ll',) LIB_PREFIXES = ('', 'lib') IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp') +AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3') +AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' } def suffix(name): - return name.split('.')[:-1] + return name.split('.')[-1] def unsuffixed(name): return '.'.join(name.split('.')[:-1]) @@ -357,6 +359,12 @@ for i in range(1, len(sys.argv)): if arg.endswith('.h') and sys.argv[i-1] != '-include': header = True +if '-M' in sys.argv or '-MM' in sys.argv: + # Just output dependencies, do not compile. Warning: clang and gcc behave differently with -MF! (clang seems to not recognize it) + cmd = [CC] + shared.COMPILER_OPTS + sys.argv[1:] + if DEBUG: print >> sys.stderr, 'emcc, just dependencies: ', ' '.join(cmd) + exit(subprocess.call(cmd)) + # Check if a target is specified target = None for i in range(len(sys.argv)-1): @@ -490,12 +498,6 @@ try: Compression.on = True newargs[i] = '' newargs[i+1] = '' - elif newargs[i] == '-MF': # clang cannot handle this, so we fake it - f = open(newargs[i+1], 'w') - f.write('\n') - f.close() - newargs[i] = '' - newargs[i+1] = '' elif newargs[i] == '--ignore-dynamic-linking': ignore_dynamic_linking = True newargs[i] = '' @@ -830,8 +832,22 @@ try: if DEBUG: print >> sys.stderr, 'emcc: setting up files' code = '' - code += 'var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs"));\n' - code += 'var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");\n' + if final_suffix == 'html': + code += ''' + var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs")); + var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs"); + var hasBlobConstructor; + try { + new Blob(); + hasBlobConstructor = true; + } catch(e) { + hasBlobConstructor = false; + console.log("warning: no blob constructor, cannot create blobs with mimetypes"); + } +''' + + code += 'var preloadedImages = {}; // maps url to image data\n' + code += 'var preloadedAudios = {}; // maps url to audio data\n' # Expand directories into individual files def add(mode, dirname, names): @@ -862,23 +878,24 @@ try: code += '''FS.createFolder('/%s', '%s', true, false);\n''' % (os.path.sep.join(parts[:i]), parts[i]) partial_dirs.append(partial) - if Compression.on: - # Compress all datafiles into one archive + if final_suffix == 'html': + # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead. data = open(data_target, 'wb') start = 0 for file_ in data_files: - file_['compression_start'] = start + file_['data_start'] = start curr = open(file_['name']).read() - file_['compression_end'] = start + len(curr) + file_['data_end'] = start + len(curr) start += len(curr) data.write(curr) data.close() - Compression.compress(data_target) + if Compression.on: + Compression.compress(data_target) - # Decompression requests have a similar API to XHRs + # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs code += ''' - function DecompressionRequest() {} - DecompressionRequest.prototype = { + function DataRequest() {} + DataRequest.prototype = { requests: {}, open: function(mode, name) { this.requests[name] = this; @@ -895,9 +912,60 @@ try: code += '''FS.createDataFile('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(filename, 'rb').read()))) elif file_['mode'] == 'preload': # Preload + assert final_suffix == 'html', 'Can only preload files when generating HTML' + varname = 'filePreload%d' % counter counter += 1 image = filename.endswith(IMAGE_SUFFIXES) + audio = filename.endswith(AUDIO_SUFFIXES) + + if image: + finish = ''' + var bb = new BlobBuilder(); + bb.append(byteArray.buffer); + var b = bb.getBlob(); + var url = URLObject.createObjectURL(b); + var img = new Image(); + img.onload = function() { + assert(img.complete, 'Image %(filename)s could not be decoded'); + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + preloadedImages['%(filename)s'] = canvas; + URLObject.revokeObjectURL(url); + removeRunDependency(); + }; + img.onerror = function(event) { + console.log('Image %(filename)s could not be decoded'); + }; + img.src = url; +''' % { 'filename': filename } + elif audio: + # Need actual blob constructor here, to set the mimetype or else audios fail to decode + finish = ''' + if (hasBlobConstructor) { + var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' }); + var url = URLObject.createObjectURL(b); // XXX we never revoke this! + var audio = new Audio(); + audio['oncanplaythrough'] = function() { // XXX string for closure + audio['oncanplaythrough'] = null; + preloadedAudios['%(filename)s'] = audio; + removeRunDependency(); + }; + audio.onerror = function(event) { + console.log('Audio %(filename)s could not be decoded'); + }; + audio.src = url; + } else { + preloadedAudios['%(filename)s'] = new Audio(); // empty shim + removeRunDependency(); + } +''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] } + else: + finish = 'removeRunDependency();\n' + code += ''' var %(varname)s = new %(request)s(); %(varname)s.open('GET', '%(netname)s', true); @@ -912,46 +980,36 @@ try: addRunDependency(); %(varname)s.send(null); ''' % { - 'request': 'XMLHttpRequest' if not Compression.on else 'DecompressionRequest', + 'request': 'DataRequest', # In the past we also supported XHRs here 'varname': varname, 'filename': filename, 'netname': file_['net_name'], 'dirname': os.path.dirname(filename), 'basename': os.path.basename(filename), - 'finish': 'removeRunDependency();' if not image else '''var bb = new BlobBuilder(); - bb.append(byteArray.buffer); - var b = bb.getBlob(); - var url = URLObject.createObjectURL(b); - var img = new Image(); - img.onload = function() { - assert(img.complete, 'Image %(filename)s could not be decoded'); - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - preloadedImages['%(filename)s'] = canvas; - URLObject.revokeObjectURL(url); - removeRunDependency(); - }; - img.onerror = function(event) { - console.log('Image %(filename)s could not be decoded: ' + JSON.stringify(event)); - }; - img.src = url; -''' % { 'filename': filename } + 'finish': finish } else: assert 0 - if Compression.on: - use_decompressed = '' + if final_suffix == 'html': + # Get the big archive and split it up + use_data = '' for file_ in data_files: if file_['mode'] == 'preload': - use_decompressed += ''' - curr = DecompressionRequest.prototype.requests['%s']; + use_data += ''' + curr = DataRequest.prototype.requests['%s']; curr.response = byteArray.subarray(%d,%d); curr.onload(); - ''' % (file_['name'], file_['compression_start'], file_['compression_end']) + ''' % (file_['name'], file_['data_start'], file_['data_end']) + use_data += ' removeRunDependency();\n' + + if Compression.on: + use_data = ''' + Module["decompress"](byteArray, function(decompressed) { + byteArray = new Uint8Array(decompressed); + %s + }); + ''' % use_data code += ''' var dataFile = new XMLHttpRequest(); @@ -961,17 +1019,13 @@ try: var arrayBuffer = dataFile.response; assert(arrayBuffer, 'Loading data file failed.'); var byteArray = new Uint8Array(arrayBuffer); - Module["decompress"](byteArray, function(decompressed) { - byteArray = new Uint8Array(decompressed); - var curr; - %s - removeRunDependency(); - }); + var curr; + %s }; addRunDependency(); dataFile.send(null); if (Module['setStatus']) Module['setStatus']('Downloading...'); - ''' % (Compression.compressed_name(data_target), use_decompressed) + ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data) src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', code) final += '.files.js' diff --git a/src/jsifier.js b/src/jsifier.js index 99176fd2..dcc853f2 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -38,6 +38,7 @@ function JSify(data, functionsOnly, givenFunctions) { var preFile = BUILD_AS_SHARED_LIB ? 'preamble_sharedlib.js' : 'preamble.js'; var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()))); print(pre); + if (PRECISE_I64_MATH) print(read('long.js')); Functions.implementedFunctions = set(data.unparsedFunctions.map(function(func) { return func.ident })); } @@ -166,23 +167,27 @@ function JSify(data, functionsOnly, givenFunctions) { ret[index++] = 0; } // Add current value(s) - var currValue = flatten(values[i]); + var currValue = values[i]; if (USE_TYPED_ARRAYS == 2 && typeData.fields[i] == 'i64') { // 'flatten' out the 64-bit value into two 32-bit halves - ret[index++] = currValue>>>0; + var parts = parseI64Constant(currValue, true); + ret[index++] = parts[0]; ret[index++] = 0; ret[index++] = 0; ret[index++] = 0; - ret[index++] = Math.floor(currValue/4294967296); + ret[index++] = parts[1]; ret[index++] = 0; ret[index++] = 0; ret[index++] = 0; - } else if (typeof currValue == 'object') { - for (var j = 0; j < currValue.length; j++) { - ret[index++] = currValue[j]; - } } else { - ret[index++] = currValue; + currValue = flatten(currValue); + if (typeof currValue == 'object') { + for (var j = 0; j < currValue.length; j++) { + ret[index++] = currValue[j]; + } + } else { + ret[index++] = currValue; + } } i += 1; } diff --git a/src/library.js b/src/library.js index 0574bb8d..f49a8a58 100644 --- a/src/library.js +++ b/src/library.js @@ -86,6 +86,23 @@ LibraryManager.library = { parentPath: null, parentObject: null }; +#if FS_LOG + var inputPath = path; + function log() { + print('FS.analyzePath("' + inputPath + '", ' + + dontResolveLastLink + ', ' + + linksVisited + ') => {' + + 'isRoot: ' + ret.isRoot + ', ' + + 'exists: ' + ret.exists + ', ' + + 'error: ' + ret.error + ', ' + + 'name: "' + ret.name + '", ' + + 'path: "' + ret.path + '", ' + + 'object: ' + ret.object + ', ' + + 'parentExists: ' + ret.parentExists + ', ' + + 'parentPath: "' + ret.parentPath + '", ' + + 'parentObject: ' + ret.parentObject + '}'); + } +#endif path = FS.absolutePath(path); if (path == '/') { ret.isRoot = true; @@ -123,8 +140,12 @@ LibraryManager.library = { break; } var link = FS.absolutePath(current.link, traversed.join('/')); - return FS.analyzePath([link].concat(path).join('/'), - dontResolveLastLink, linksVisited + 1); + ret = FS.analyzePath([link].concat(path).join('/'), + dontResolveLastLink, linksVisited + 1); +#if FS_LOG + log(); +#endif + return ret; } traversed.push(target); if (path.length == 0) { @@ -133,8 +154,10 @@ LibraryManager.library = { ret.object = current; } } - return ret; } +#if FS_LOG + log(); +#endif return ret; }, // Finds the file system object at a given path. If dontResolveLastLink is @@ -152,6 +175,13 @@ LibraryManager.library = { }, // Creates a file system record: file, link, device or folder. createObject: function(parent, name, properties, canRead, canWrite) { +#if FS_LOG + print('FS.createObject("' + parent + '", ' + + '"' + name + '", ' + + JSON.stringify(properties) + ', ' + + canRead + ', ' + + canWrite + ')'); +#endif if (!parent) parent = '/'; if (typeof parent === 'string') parent = FS.findObject(parent); @@ -415,6 +445,14 @@ LibraryManager.library = { standardizePath: function(path) { if (path.substr(0, 2) == './') path = path.substr(2); return path; + }, + + deleteFile: function(path) { + var path = FS.analyzePath(path); + if (!path.parentExists || !path.exists) { + throw 'Invalid path ' + path; + } + delete path.parentObject.contents[path.name]; } }, @@ -2449,6 +2487,10 @@ LibraryManager.library = { var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0); argSize = argSize || 4; var currArg = getNextArg('i' + (argSize * 8)); +#if PRECISE_I64_MATH == 1 + var origArg = currArg; +#endif + var argText; #if USE_TYPED_ARRAYS == 2 // Flatten i64-1 [low, high] into a (slightly rounded) double if (argSize == 8) { @@ -2462,11 +2504,16 @@ LibraryManager.library = { } // Format the number. var currAbsArg = Math.abs(currArg); |