summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc12
-rw-r--r--src/jsifier.js20
-rw-r--r--src/library.js102
-rw-r--r--src/library_browser.js36
-rw-r--r--src/library_egl.js421
-rw-r--r--src/library_gl.js11
-rw-r--r--src/library_glut.js31
-rw-r--r--src/library_sdl.js36
-rw-r--r--src/parseTools.js8
-rw-r--r--src/preamble.js10
-rw-r--r--src/runtime.js2
-rw-r--r--src/settings.js12
-rw-r--r--src/shell.js26
-rw-r--r--system/include/emscripten/emscripten.h16
-rw-r--r--tests/checksummer.c71
-rw-r--r--tests/cubegeom.c8
-rwxr-xr-xtests/runner.py333
-rw-r--r--tests/sdl_image_prepare_data.c69
-rw-r--r--tests/sdl_resize.c45
-rw-r--r--tests/websockets_bi_bigdata.c137
-rw-r--r--tests/websockets_bi_side_bigdata.c69
-rw-r--r--tests/websockets_bigdata.h20
-rw-r--r--tools/file_packager.py4
-rw-r--r--tools/js-optimizer.js17
-rw-r--r--tools/shared.py84
25 files changed, 1494 insertions, 106 deletions
diff --git a/emcc b/emcc
index 68f5cc35..fa00bb52 100755
--- a/emcc
+++ b/emcc
@@ -74,7 +74,7 @@ emcc can be influenced by a few environment variables:
EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang.
'''
-import os, sys, shutil, tempfile, subprocess, shlex
+import os, sys, shutil, tempfile, subprocess, shlex, time
from subprocess import PIPE, STDOUT
from tools import shared
from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename
@@ -815,7 +815,8 @@ try:
extra_files_to_link = []
- if not LEAVE_INPUTS_RAW and not AUTODEBUG:
+ if not LEAVE_INPUTS_RAW and not AUTODEBUG and \
+ not shared.Settings.BUILD_AS_SHARED_LIB == 2: # shared lib 2 use the library in the parent
# Check if we need to include some libraries that we compile. (We implement libc ourselves in js, but
# compile a malloc implementation and stdlibc++.)
# Note that we assume a single symbol is enough to know if we have/do not have dlmalloc etc. If you
@@ -921,10 +922,15 @@ try:
print >> sys.stderr, 'emcc: saving intermediate processing steps to %s' % shared.EMSCRIPTEN_TEMP_DIR
intermediate_counter = 0
+ intermediate_time = None
def save_intermediate(name=None, suffix='js'):
- global intermediate_counter
+ global intermediate_counter, intermediate_time
shutil.copyfile(final, os.path.join(shared.EMSCRIPTEN_TEMP_DIR, 'emcc-%d%s.%s' % (intermediate_counter, '' if name is None else '-' + name, suffix)))
intermediate_counter += 1
+ now = time.time()
+ if intermediate_time:
+ print >> sys.stderr, 'emcc: step took %.2f seconds' % (now - intermediate_time)
+ intermediate_time = now
if not LEAVE_INPUTS_RAW: save_intermediate('basebc', 'bc')
diff --git a/src/jsifier.js b/src/jsifier.js
index b76579cc..2aa65980 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -358,11 +358,21 @@ function JSify(data, functionsOnly, givenFunctions) {
item.JS = 'var ' + item.ident + ';';
// Set the actual value in a postset, since it may be a global variable. We also order by dependencies there
var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value);
+ var fix = '';
+ if (BUILD_AS_SHARED_LIB == 2 && !item.private_) {
+ var target = item.ident;
+ if (isFunctionType(item.type)) {
+ target = item.value.ident; // the other side does not know this is an alias/function table index. So make it the alias target.
+ var varData = Variables.globals[target];
+ assert(!varData, 'multi-level aliasing does not work yet in shared lib 2 exports');
+ }
+ fix = '\nif (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + target + ' }'
+ }
ret.push({
intertype: 'GlobalVariablePostSet',
ident: item.ident,
dependencies: set([value]),
- JS: item.ident + ' = ' + value + ';'
+ JS: item.ident + ' = ' + value + ';' + fix
});
return ret;
}
@@ -1166,7 +1176,13 @@ function JSify(data, functionsOnly, givenFunctions) {
return makeFunctionCall(item.ident, item.params, item.funcData, item.type) + (item.standalone ? ';' : '');
});
- makeFuncLineActor('unreachable', function(item) { return 'throw "Reached an unreachable!"' }); // Original .ll line: ' + item.lineNum + '";' });
+ makeFuncLineActor('unreachable', function(item) {
+ if (ASSERTIONS) {
+ return 'throw "Reached an unreachable!"';
+ } else {
+ return ';';
+ }
+ });
// Final combiner
diff --git a/src/library.js b/src/library.js
index 1bb58833..2855c33d 100644
--- a/src/library.js
+++ b/src/library.js
@@ -289,7 +289,83 @@ LibraryManager.library = {
// XHR, which is not possible in browsers except in a web worker! Use preloading,
// either --preload-file in emcc or FS.createPreloadedFile
createLazyFile: function(parent, name, url, canRead, canWrite) {
- var properties = {isDevice: false, url: url};
+
+ if (typeof XMLHttpRequest !== 'undefined') {
+ if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
+ // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
+ var LazyUint8Array = function(chunkSize, length) {
+ this.length = length;
+ this.chunkSize = chunkSize;
+ this.chunks = []; // Loaded chunks. Index is the chunk number
+ }
+ LazyUint8Array.prototype.get = function(idx) {
+ if (idx > this.length-1 || idx < 0) {
+ return undefined;
+ }
+ var chunkOffset = idx % chunkSize;
+ var chunkNum = Math.floor(idx / chunkSize);
+ return this.getter(chunkNum)[chunkOffset];
+ }
+ LazyUint8Array.prototype.setDataGetter = function(getter) {
+ this.getter = getter;
+ }
+
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+#if SMALL_XHR_CHUNKS
+ var chunkSize = 1024; // Chunk size in bytes
+#else
+ var chunkSize = 1024*1024; // Chunk size in bytes
+#endif
+ if (!hasByteServing) chunkSize = datalength;
+
+ // Function to get a range from the remote URL.
+ var doXHR = (function(from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
+
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+
+ var lazyArray = new LazyUint8Array(chunkSize, datalength);
+ lazyArray.setDataGetter(function(chunkNum) {
+ var start = chunkNum * lazyArray.chunkSize;
+ var end = (chunkNum+1) * lazyArray.chunkSize - 1; // including this byte
+ end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+ var properties = { isDevice: false, contents: lazyArray };
+ } else {
+ var properties = { isDevice: false, url: url };
+ }
+
return FS.createFile(parent, name, properties, canRead, canWrite);
},
// Preloads a file asynchronously. You can call this before run, for example in
@@ -360,8 +436,7 @@ LibraryManager.library = {
if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
var success = true;
if (typeof XMLHttpRequest !== 'undefined') {
- // Browser.
- throw 'Cannot do synchronous binary XHRs in modern browsers. Use --embed-file or --preload-file in emcc';
+ throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
} else if (Module['read']) {
// Command-line.
try {
@@ -1631,8 +1706,14 @@ LibraryManager.library = {
}
var contents = stream.object.contents;
var size = Math.min(contents.length - offset, nbyte);
- for (var i = 0; i < size; i++) {
- {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}}
+ if (contents.subarray || contents.slice) { // typed array or normal array
+ for (var i = 0; i < size; i++) {
+ {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}}
+ }
+ } else {
+ for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
+ {{{ makeSetValue('buf', 'i', 'contents.get(offset + i)', 'i8') }}}
+ }
}
bytesRead += size;
return bytesRead;
@@ -2417,8 +2498,8 @@ LibraryManager.library = {
while ((curr < max_ || isNaN(max_)) && next > 0) {
if (!(next in __scanString.whiteSpace) && // stop on whitespace
(type == 's' ||
- ((type === 'd' || type == 'u') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) ||
- (first && next == '-'.charCodeAt(0)))) ||
+ ((type === 'd' || type == 'u' || type == 'i') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) ||
+ (first && next == '-'.charCodeAt(0)))) ||
(type === 'x' && (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0) ||
next >= 'a'.charCodeAt(0) && next <= 'f'.charCodeAt(0) ||
next >= 'A'.charCodeAt(0) && next <= 'F'.charCodeAt(0)))) &&
@@ -2437,7 +2518,7 @@ LibraryManager.library = {
var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
argIndex += Runtime.getNativeFieldSize('void*');
switch (type) {
- case 'd': case 'u':
+ case 'd': case 'u': case 'i':
if (half) {
{{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}};
} else {
@@ -4352,6 +4433,9 @@ LibraryManager.library = {
__strtok_state: 0,
strtok__deps: ['__strtok_state', 'strtok_r'],
strtok: function(s, delim) {
+ if (!___strtok_state) {
+ ___strtok_state = _malloc(4);
+ }
return _strtok_r(s, delim, ___strtok_state);
},
@@ -6452,7 +6536,7 @@ LibraryManager.library = {
assert(info.host, 'problem translating fake ip ' + parts);
}
console.log('opening ws://' + info.host + ':' + info.port);
- info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['arraybuffer']);
+ info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['base64']);
info.socket.binaryType = 'arraybuffer';
info.buffer = new Uint8Array(Sockets.BUFFER_SIZE);
info.bufferWrite = info.bufferRead = 0;
diff --git a/src/library_browser.js b/src/library_browser.js
index fc8cb84c..99106fc3 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -347,11 +347,21 @@ mergeInto(LibraryManager.library, {
});
addRunDependency('al ' + url);
},
-
- setCanvasSize: function(width, height) {
+
+ resizeListeners: [],
+
+ updateResizeListeners: function() {
+ var canvas = Module['canvas'];
+ Browser.resizeListeners.forEach(function(listener) {
+ listener(canvas.width, canvas.height);
+ });
+ },
+
+ setCanvasSize: function(width, height, noUpdates) {
var canvas = Module['canvas'];
canvas.width = width;
canvas.height = height;
+ if (!noUpdates) Browser.updateResizeListeners();
}
},
@@ -392,6 +402,28 @@ mergeInto(LibraryManager.library, {
return 0;
},
+ emscripten_async_prepare_data: function(data, size, suffix, arg, onload, onerror) {
+ var _suffix = Pointer_stringify(suffix);
+ if (!Browser.asyncPrepareDataCounter) Browser.asyncPrepareDataCounter = 0;
+ var name = 'prepare_data_' + (Browser.asyncPrepareDataCounter++) + '.' + _suffix;
+ var cname = _malloc(name.length+1);
+ writeStringToMemory(name, cname);
+ FS.createPreloadedFile(
+ '',
+ name,
+ {{{ makeHEAPView('U8', 'data', 'data + size') }}},
+ true, true,
+ function() {
+ if (onload) FUNCTION_TABLE[onload](arg, cname);
+ },
+ function() {
+ if (onerror) FUNCTION_TABLE[onerror](arg);
+ },
+ true // don'tCreateFile - it's already there
+ );
+ return 0;
+ },
+
emscripten_async_run_script__deps: ['emscripten_run_script'],
emscripten_async_run_script: function(script, millis) {
Module['noExitRuntime'] = true;
diff --git a/src/library_egl.js b/src/library_egl.js
index 635e00a7..1c35ddf4 100644
--- a/src/library_egl.js
+++ b/src/library_egl.js
@@ -1,26 +1,423 @@
-
+/*
+ The EGL implementation supports only one EGLNativeDisplayType, the EGL_DEFAULT_DISPLAY.
+ This native display type returns the only supported EGLDisplay handle with the magic value 62000.
+ There is only a single EGLConfig configuration supported, that has the magic value 62002.
+ The implementation only allows a single EGLContext to be created, that has the magic value of 62004. (multiple creations silently return this same context)
+ The implementation only creates a single EGLSurface, a handle with the magic value of 62006. (multiple creations silently return the same surface)
+*/
+
var LibraryEGL = {
- eglGetDisplay: function(x_display) { return 3 },
- eglInitialize: function(display, majorVersion, minorVersion) { return 1 },
- eglGetConfigs: function(display, hmm1, hmm2, numConfigs) { return 1 },
- eglChooseConfig: function(display, attribList, config, hmm, numConfigs) { return 1 },
- eglCreateWindowSurface: function(display, config, hWnd, hmm) { return 4 },
+ $EGL: {
+ // This variable tracks the success status of the most recently invoked EGL function call.
+ eglErrorCode: 0x3000 /* EGL_SUCCESS */,
+
+ setErrorCode: function(code) {
+ EGL.eglErrorCode = code;
+ },
+
+ chooseConfig: function(display, attribList, config, config_size, numConfigs) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ // TODO: read attribList.
+ if ((!config || !config_size) && !numConfigs) {
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0;
+ }
+ if (numConfigs) {
+ {{{ makeSetValue('numConfigs', '0', '1', 'i32') }}}; // Total number of supported configs: 1.
+ }
+ if (config && config_size > 0) {
+ {{{ makeSetValue('config', '0', '62002' /* Magic ID for the only EGLConfig supported by Emscripten */, 'i32') }}};
+ }
+
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+ },
+
+ // EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id);
+ eglGetDisplay: function(nativeDisplayType) {
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ // Note: As a 'conformant' implementation of EGL, we would prefer to init here only if the user
+ // calls this function with EGL_DEFAULT_DISPLAY. Other display IDs would be preferred to be unsupported
+ // and EGL_NO_DISPLAY returned. Uncomment the following code lines to do this.
+ // Instead, an alternative route has been preferred, namely that the Emscripten EGL implementation
+ // "emulates" X11, and eglGetDisplay is expected to accept/receive a pointer to an X11 Display object.
+ // Therefore, be lax and allow anything to be passed in, and return the magic handle to our default EGLDisplay object.
+
+// if (nativeDisplayType == 0 /* EGL_DEFAULT_DISPLAY */) {
+ return 62000; // Magic ID for Emscripten 'default display'
+// }
+// else
+// return 0; // EGL_NO_DISPLAY
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
+ eglInitialize: function(display, majorVersion, minorVersion) {
+ if (display == 62000 /* Magic ID for Emscripten 'default display' */) {
+ if (majorVersion) {
+ {{{ makeSetValue('majorVersion', '0', '1', 'i32') }}}; // Advertise EGL Major version: '1'
+ }
+ if (minorVersion) {
+ {{{ makeSetValue('minorVersion', '0', '4', 'i32') }}}; // Advertise EGL Minor version: '4'
+ }
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ }
+ else {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+ eglGetConfigs: function(display, configs, config_size, numConfigs) {
+ return EGL.chooseConfig(display, 0, configs, config_size, numConfigs);
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+ eglChooseConfig: function(display, attrib_list, configs, config_size, numConfigs) {
+ return EGL.chooseConfig(display, attrib_list, configs, config_size, numConfigs);
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
+ eglGetConfigAttrib: function(display, config, attribute, value) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ if (config != 62002 /* Magic ID for the only EGLConfig supported by Emscripten */) {
+ EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */);
+ return 0;
+ }
+ if (!value) {
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0;
+ }
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ switch(attribute) {
+ case 0x3020: // EGL_BUFFER_SIZE
+ {{{ makeSetValue('value', '0', '32' /* 8 bits for each A,R,G,B. */, 'i32') }}};
+ return 1;
+ case 0x3021: // EGL_ALPHA_SIZE
+ {{{ makeSetValue('value', '0', '8' /* 8 bits for alpha channel. */, 'i32') }}};
+ return 1;
+ case 0x3022: // EGL_BLUE_SIZE
+ {{{ makeSetValue('value', '0', '8' /* 8 bits for blue channel. */, 'i32') }}};
+ return 1;
+ case 0x3023: // EGL_GREEN_SIZE
+ {{{ makeSetValue('value', '0', '8' /* 8 bits for green channel. */, 'i32') }}};
+ return 1;
+ case 0x3024: // EGL_RED_SIZE
+ {{{ makeSetValue('value', '0', '8' /* 8 bits for red channel. */, 'i32') }}};
+ return 1;
+ case 0x3025: // EGL_DEPTH_SIZE
+ {{{ makeSetValue('value', '0', '24' /* 24 bits for depth buffer. TODO: This is hardcoded, add support for this! */, 'i32') }}};
+ return 1;
+ case 0x3026: // EGL_STENCIL_SIZE
+ {{{ makeSetValue('value', '0', '8' /* 8 bits for stencil buffer. TODO: This is hardcoded, add support for this! */, 'i32') }}};
+ return 1;
+ case 0x3027: // EGL_CONFIG_CAVEAT
+ // We can return here one of EGL_NONE (0x3038), EGL_SLOW_CONFIG (0x3050) or EGL_NON_CONFORMANT_CONFIG (0x3051).
+ {{{ makeSetValue('value', '0', '0x3038' /* EGL_NONE */, 'i32') }}};
+ return 1;
+ case 0x3028: // EGL_CONFIG_ID
+ {{{ makeSetValue('value', '0', '62002' /* Magic ID for the only EGLConfig supported by Emscripten */, 'i32') }}};
+ return 1;
+ case 0x3029: // EGL_LEVEL
+ {{{ makeSetValue('value', '0', '0' /* Z order/depth layer for this level. Not applicable for Emscripten. */, 'i32') }}};
+ return 1;
+ case 0x302A: // EGL_MAX_PBUFFER_HEIGHT
+ {{{ makeSetValue('value', '0', '4096', 'i32') }}};
+ return 1;
+ case 0x302B: // EGL_MAX_PBUFFER_PIXELS
+ {{{ makeSetValue('value', '0', '16777216' /* 4096 * 4096 */, 'i32') }}};
+ return 1;
+ case 0x302C: // EGL_MAX_PBUFFER_WIDTH
+ {{{ makeSetValue('value', '0', '4096', 'i32') }}};
+ return 1;
+ case 0x302D: // EGL_NATIVE_RENDERABLE
+ {{{ makeSetValue('value', '0', '0' /* This config does not allow co-rendering with other 'native' rendering APIs. */, 'i32') }}};
+ return 1;
+ case 0x302E: // EGL_NATIVE_VISUAL_ID
+ {{{ makeSetValue('value', '0', '0' /* N/A for Emscripten. */, 'i32') }}};
+ return 1;
+ case 0x302F: // EGL_NATIVE_VISUAL_TYPE
+ {{{ makeSetValue('value', '0', '0x3038' /* EGL_NONE */, 'i32') }}};
+ return 1;
+ case 0x3031: // EGL_SAMPLES
+ {{{ makeSetValue('value', '0', '0' /* No multisampling. */, 'i32') }}};
+ return 1;
+ case 0x3032: // EGL_SAMPLE_BUFFERS
+ {{{ makeSetValue('value', '0', '0' /* No multisampling. */, 'i32') }}};
+ return 1;
+ case 0x3033: // EGL_SURFACE_TYPE
+ {{{ makeSetValue('value', '0', '0x0004' /* EGL_WINDOW_BIT */, 'i32') }}};
+ return 1;
+ case 0x3034: // EGL_TRANSPARENT_TYPE
+ // If this returns EGL_TRANSPARENT_RGB (0x3052), transparency is used through color-keying. No such thing applies to Emscripten canvas.
+ {{{ makeSetValue('value', '0', '0x3038' /* EGL_NONE */, 'i32') }}};
+ return 1;
+ case 0x3035: // EGL_TRANSPARENT_BLUE_VALUE
+ case 0x3036: // EGL_TRANSPARENT_GREEN_VALUE
+ case 0x3037: // EGL_TRANSPARENT_RED_VALUE
+ // "If EGL_TRANSPARENT_TYPE is EGL_NONE, then the values for EGL_TRANSPARENT_RED_VALUE, EGL_TRANSPARENT_GREEN_VALUE, and EGL_TRANSPARENT_BLUE_VALUE are undefined."
+ {{{ makeSetValue('value', '0', '-1' /* Report a "does not apply" value. */, 'i32') }}};
+ return 1;
+ case 0x3039: // EGL_BIND_TO_TEXTURE_RGB
+ case 0x303A: // EGL_BIND_TO_TEXTURE_RGBA
+ {{{ makeSetValue('value', '0', '0' /* Only pbuffers would be bindable, but these are not supported. */, 'i32') }}};
+ return 1;
+ case 0x303B: // EGL_MIN_SWAP_INTERVAL
+ case 0x303C: // EGL_MAX_SWAP_INTERVAL
+ {{{ makeSetValue('value', '0', '1' /* TODO: Currently this is not strictly true, since user can specify custom presentation interval in JS requestAnimationFrame/emscripten_set_main_loop. */, 'i32') }}};
+ return 1;
+ case 0x303D: // EGL_LUMINANCE_SIZE
+ case 0x303E: // EGL_ALPHA_MASK_SIZE
+ {{{ makeSetValue('value', '0', '0' /* N/A in this config. */, 'i32') }}};
+ return 1;
+ case 0x303F: // EGL_COLOR_BUFFER_TYPE
+ // EGL has two types of buffers: EGL_RGB_BUFFER and EGL_LUMINANCE_BUFFER.
+ {{{ makeSetValue('value', '0', '0x308E' /* EGL_RGB_BUFFER */, 'i32') }}};
+ return 1;
+ case 0x3040: // EGL_RENDERABLE_TYPE
+ // A bit combination of EGL_OPENGL_ES_BIT,EGL_OPENVG_BIT,EGL_OPENGL_ES2_BIT and EGL_OPENGL_BIT.
+ {{{ makeSetValue('value', '0', '0x0004' /* EGL_OPENGL_ES2_BIT */, 'i32') }}};
+ return 1;
+ case 0x3042: // EGL_CONFORMANT
+ // "EGL_CONFORMANT is a mask indicating if a client API context created with respect to the corresponding EGLConfig will pass the required conformance tests for that API."
+ {{{ makeSetValue('value', '0', '0' /* EGL_OPENGL_ES2_BIT */, 'i32') }}};
+ return 1;
+ default:
+ EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */);
+ return 0;
+ }
+ },
+
+ // EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
+ eglCreateWindowSurface: function(display, config, win, attrib_list) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ if (config != 62002 /* Magic ID for the only EGLConfig supported by Emscripten */) {
+ EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */);
+ return 0;
+ }
+ // TODO: Examine attrib_list! Parameters that can be present there are:
+ // - EGL_RENDER_BUFFER (must be EGL_BACK_BUFFER)
+ // - EGL_VG_COLORSPACE (can't be set)
+ // - EGL_VG_ALPHA_FORMAT (can't be set)
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 62006; /* Magic ID for Emscripten 'default surface' */
+ },
eglCreateContext__deps: ['glutCreateWindow', '$GL'],
+
+ // EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
eglCreateContext: function(display, config, hmm, contextAttribs) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+
_glutCreateWindow();
- return 1;
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 62004; // Magic ID for Emscripten EGLContext
},
// EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
- eglQuerySurface: function(display, surface, attribute, value) { return 0 },
+ eglQuerySurface: function(display, surface, attribute, value) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ if (surface != 62006 /* Magic ID for Emscripten 'default surface' */) {
+ EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);
+ return 0;
+ }
+ if (!value) {
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0;
+ }
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ switch(attribute) {
+ case 0x3028: // EGL_CONFIG_ID
+ {{{ makeSetValue('value', '0', '62002' /* A magic value for the only EGLConfig configuration ID supported by Emscripten. */, 'i32') }}};
+ return 1;
+ case 0x3058: // EGL_LARGEST_PBUFFER
+ // Odd EGL API: If surface is not a pbuffer surface, 'value' should not be written to. It's not specified as an error, so true should(?) be returned.
+ // Existing Android implementation seems to do so at least.
+ return 1;
+ case 0x3057: // EGL_WIDTH
+ // TODO
+ return 1;
+ case 0x3056: // EGL_HEIGHT
+ // TODO
+ return 1;
+ case 0x3090: // EGL_HORIZONTAL_RESOLUTION
+ {{{ makeSetValue('value', '0', '-1' /* EGL_UNKNOWN */, 'i32') }}};
+ return 1;
+ case 0x3091: // EGL_VERTICAL_RESOLUTION
+ {{{ makeSetValue('value', '0', '-1' /* EGL_UNKNOWN */, 'i32') }}};
+ return 1;
+ case 0x3092: // EGL_PIXEL_ASPECT_RATIO
+ {{{ makeSetValue('value', '0', '-1' /* EGL_UNKNOWN */, 'i32') }}};
+ return 1;
+ case 0x3086: // EGL_RENDER_BUFFER
+ // The main surface is bound to the visible canvas window - it's always backbuffered.
+ // Alternative to EGL_BACK_BUFFER would be EGL_SINGLE_BUFFER.
+ {{{ makeSetValue('value', '0', '0x3084' /* EGL_BACK_BUFFER */, 'i32') }}};
+ return 1;
+ case 0x3099: // EGL_MULTISAMPLE_RESOLVE
+ {{{ makeSetValue('value', '0', '0x309A' /* EGL_MULTISAMPLE_RESOLVE_DEFAULT */, 'i32') }}};
+ return 1;
+ case 0x3093: // EGL_SWAP_BEHAVIOR
+ // The two possibilities are EGL_BUFFER_PRESERVED and EGL_BUFFER_DESTROYED. Slightly unsure which is the
+ // case for browser environment, but advertise the 'weaker' behavior to be sure.
+ {{{ makeSetValue('value', '0', '0x3095' /* EGL_BUFFER_DESTROYED */, 'i32') }}};
+ return 1;
+ case 0x3080: // EGL_TEXTURE_FORMAT
+ case 0x3081: // EGL_TEXTURE_TARGET
+ case 0x3082: // EGL_MIPMAP_TEXTURE
+ case 0x3083: // EGL_MIPMAP_LEVEL
+ // This is a window surface, not a pbuffer surface. Spec:
+ // "Querying EGL_TEXTURE_FORMAT, EGL_TEXTURE_TARGET, EGL_MIPMAP_TEXTURE, or EGL_MIPMAP_LEVEL for a non-pbuffer surface is not an error, but value is not modified."
+ // So pass-through.
+ return 1;
+ default:
+ EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */);
+ return 0;
+ }
+ },
+ // EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
+ eglQueryContext: function(display, context, attribute, value) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.
+ if (context != 62004 /* Magic ID for Emscripten EGLContext */) {
+ EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */);
+ return 0;
+ }
+ if (!value) {
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0;
+ }
+
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ switch(attribute) {
+ case 0x3028: // EGL_CONFIG_ID
+ {{{ makeSetValue('value', '0', '62002' /* A magic value for the only EGLConfig configuration ID supported by Emscripten. */, 'i32') }}};
+ return 1;
+ case 0x3097: // EGL_CONTEXT_CLIENT_TYPE
+ {{{ makeSetValue('value', '0', '0x30A0' /* EGL_OPENGL_ES_API */, 'i32') }}};
+ return 1;
+ case 0x3098: // EGL_CONTEXT_CLIENT_VERSION
+ {{{ makeSetValue('value', '0', '2' /* GLES2 context */, 'i32') }}}; // We always report the context to be a GLES2 context (and not a GLES1 context)
+ return 1;
+ case 0x3086: // EGL_RENDER_BUFFER
+ // The context is bound to the visible canvas window - it's always backbuffered.
+ // Alternative to EGL_BACK_BUFFER would be EGL_SINGLE_BUFFER.
+ {{{ makeSetValue('value', '0', '0x3084' /* EGL_BACK_BUFFER */, 'i32') }}};
+ return 1;
+ default:
+ EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */);
+ return 0;
+ }
+ },
+
// EGLAPI EGLint EGLAPIENTRY eglGetError(void);
- eglGetError: function() { return 0x3000 /* EGL_SUCCESS */ },
+ eglGetError: function() {
+ return EGL.eglErrorCode;
+ },
- eglMakeCurrent: function(display, surface, surface_, context) { return 1 },
- eglSwapBuffers: function() {},
+ eglQueryString: function(display, name) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ switch(name) {
+ case 0x3053 /* EGL_VENDOR */: return allocate(intArrayFromString("Emscripten"), 'i8', ALLOC_NORMAL);
+ case 0x3054 /* EGL_VERSION */: return allocate(intArrayFromString("1.4 Emscripten EGL"), 'i8', ALLOC_NORMAL);
+ case 0x3055 /* EGL_EXTENSIONS */: return allocate(intArrayFromString(""), 'i8', ALLOC_NORMAL); // Currently not supporting any EGL extensions.
+ case 0x308D /* EGL_CLIENT_APIS */: return allocate(intArrayFromString("OpenGL_ES"), 'i8', ALLOC_NORMAL);
+ default:
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0;
+ }
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api);
+ eglBindAPI: function(api) {
+ if (api == 0x30A0 /* EGL_OPENGL_ES_API */) {
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ } else { // if (api == 0x30A1 /* EGL_OPENVG_API */ || api == 0x30A2 /* EGL_OPENGL_API */) {
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0;
+ }
+ },
+
+ // EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void);
+ eglQueryAPI: function() {
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 0x30A0; // EGL_OPENGL_ES_API
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient(void);
+ eglWaitClient: function() {
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative(EGLint engine);
+ eglWaitNative: function(nativeEngineId) {
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval);
+ eglSwapInterval: function(display, interval) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ // \todo Could we use this function to specify the rate for requestAnimationFrame+main loop?
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+ eglMakeCurrent: function(display, draw, read, context) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.
+ if (context != 62004 /* Magic ID for Emscripten EGLContext */) {
+ EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */);
+ return 0;
+ }
+ if (read != 62006 || draw != 62006 /* Magic ID for Emscripten 'default surface' */) {
+ EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);
+ return 0;
+ }
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+
+ eglSwapBuffers: function() {
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ },
};
-mergeInto(LibraryManager.library, LibraryEGL);
+autoAddDeps(LibraryEGL, '$EGL');
+mergeInto(LibraryManager.library, LibraryEGL);
diff --git a/src/library_gl.js b/src/library_gl.js
index 765b3cdb..99198196 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -19,6 +19,8 @@ var LibraryGL = {
uniforms: [],
shaders: [],
+ uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation
+
packAlignment: 4, // default alignment is 4 bytes
unpackAlignment: 4, // default alignment is 4 bytes
@@ -520,10 +522,15 @@ var LibraryGL = {
glGetUniformLocation: function(program, name) {
name = Pointer_stringify(name);
+ var ptable = GL.uniformTable[program];
+ if (!ptable) ptable = GL.uniformTable[program] = {};
+ var id = ptable[name];
+ if (id) return id;
var loc = Module.ctx.getUniformLocation(GL.programs[program], name);
if (!loc) return -1;
- var id = GL.getNewId(GL.uniforms);
+ id = GL.getNewId(GL.uniforms);
GL.uniforms[id] = loc;
+ ptable[name] = id;
return id;
},
@@ -826,6 +833,7 @@ var LibraryGL = {
glDeleteProgram: function(program) {
Module.ctx.deleteProgram(GL.programs[program]);
GL.programs[program] = null;
+ GL.uniformTable[program] = null;
},
glAttachShader: function(program, shader) {
@@ -842,6 +850,7 @@ var LibraryGL = {
glLinkProgram: function(program) {
Module.ctx.linkProgram(GL.programs[program]);
+ GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking
},
glGetProgramInfoLog: function(program, maxLength, length, infoLog) {
diff --git a/src/library_glut.js b/src/library_glut.js
index b146cf47..6069b484 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -270,6 +270,21 @@ var LibraryGLUT = {
glutInit: function(argcp, argv) {
// Ignore arguments
GLUT.initTime = Date.now();
+
+ window.addEventListener("keydown", GLUT.onKeydown, true);
+ window.addEventListener("keyup", GLUT.onKeyup, true);
+ window.addEventListener("mousemove", GLUT.onMousemove, true);
+ window.addEventListener("mousedown", GLUT.onMouseButtonDown, true);
+ window.addEventListener("mouseup", GLUT.onMouseButtonUp, true);
+
+ __ATEXIT__.push({ func: function() {
+ window.removeEventListener("keydown", GLUT.onKeydown, true);
+ window.removeEventListener("keyup", GLUT.onKeyup, true);
+ window.removeEventListener("mousemove", GLUT.onMousemove, true);
+ window.removeEventListener("mousedown", GLUT.onMouseButtonDown, true);
+ window.removeEventListener("mouseup", GLUT.onMouseButtonUp, true);
+ Module["canvas"].width = Module["canvas"].height = 1;
+ } });
},
glutInitWindowSize: function(width, height) {
@@ -408,22 +423,6 @@ var LibraryGLUT = {
glutMainLoop__deps: ['$GLUT', 'glutReshapeWindow', 'glutPostRedisplay'],
glutMainLoop: function() {
-
- window.addEventListener("keydown", GLUT.onKeydown, true);
- window.addEventListener("keyup", GLUT.onKeyup, true);
- window.addEventListener("mousemove", GLUT.onMousemove, true);
- window.addEventListener("mousedown", GLUT.onMouseButtonDown, true);
- window.addEventListener("mouseup", GLUT.onMouseButtonUp, true);
-
- __ATEXIT__.push({ func: function() {
- window.removeEventListener("keydown", GLUT.onKeydown, true);
- window.removeEventListener("keyup", GLUT.onKeyup, true);
- window.removeEventListener("mousemove", GLUT.onMousemove, true);
- window.removeEventListener("mousedown", GLUT.onMouseButtonDown, true);
- window.removeEventListener("mouseup", GLUT.onMouseButtonUp, true);
- Module["canvas"].width = Module["canvas"].height = 1;
- } });
-
_glutReshapeWindow(Module['canvas'].width, Module['canvas'].height);
_glutPostRedisplay();
throw 'GLUT mainloop called, simulating infinite loop by throwing so we get right into the JS event loop';
diff --git a/src/library_sdl.js b/src/library_sdl.js
index a04d3462..a77a4668 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -3,6 +3,12 @@
// See browser tests for examples (tests/runner.py, search for sdl_). Run with
// python tests/runner.py browser
+// Notes:
+// SDL_VIDEORESIZE: This is sent when the canvas is resized. Note that the user
+// cannot manually do so, so this is only sent when the
+// program manually resizes it (emscripten_set_canvas_size
+// or otherwise).
+
var LibrarySDL = {
$SDL__deps: ['$FS', '$Browser'],
$SDL: {
@@ -189,6 +195,11 @@ var LibrarySDL = {
['i32', 'x'],
['i32', 'y']
]),
+ ResizeEvent: Runtime.generateStructInfo([
+ ['i32', 'type'],
+ ['i32', 'w'],
+ ['i32', 'h']
+ ]),
AudioSpec: Runtime.generateStructInfo([
['i32', 'freq'],
['i16', 'format'],
@@ -411,6 +422,9 @@ var LibrarySDL = {
// Force-run a main event loop, since otherwise this event will never be caught!
Browser.mainLoop.runner();
return true;
+ case 'resize':
+ SDL.events.push(event);
+ break;
}
return false;
},
@@ -515,6 +529,12 @@ var LibrarySDL = {
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
break;
}
+ case 'resize': {
+ {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.ResizeEvent.w', 'event.w', 'i32') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.ResizeEvent.h', 'event.h', 'i32') }}};
+ break;
+ }
default: throw 'Unhandled SDL event: ' + event.type;
}
},
@@ -598,6 +618,7 @@ var LibrarySDL = {
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
+ SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
return 0; // success
},
@@ -659,8 +680,19 @@ var LibrarySDL = {
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
- Browser.setCanvasSize(width, height);
- return SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
+ Browser.setCanvasSize(width, height, true);
+ SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
+ if (!SDL.addedResizeListener) {
+ SDL.addedResizeListener = true;
+ Browser.resizeListeners.push(function(w, h) {
+ SDL.receiveEvent({
+ type: 'resize',
+ w: w,
+ h: h
+ });
+ });
+ }
+ return SDL.screen;
},
SDL_GetVideoSurface: function() {
diff --git a/src/parseTools.js b/src/parseTools.js
index 80ba269b..8e919d15 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -905,6 +905,7 @@ function getHeapOffset(offset, type) {
// See makeSetValue
function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe) {
+ if (UNALIGNED_MEMORY) align = 1;
if (isStructType(type)) {
var typeData = Types.types[type];
var ret = [];
@@ -930,7 +931,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
// Special case that we can optimize
ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '+' +
'(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)';
- } else if (bytes <= 4) {
+ } else { // XXX we cannot truly handle > 4...
ret = '';
for (var i = 0; i < bytes; i++) {
ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore) + (i > 0 ? '<<' + (8*i) : '') + ')';
@@ -983,7 +984,8 @@ function indexizeFunctions(value, type) {
//! 'null' means, in the context of SAFE_HEAP, that we should accept all types;
//! which means we should write to all slabs, ignore type differences if any on reads, etc.
//! @param noNeedFirst Whether to ignore the offset in the pointer itself.
-function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep) {
+function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
+ if (UNALIGNED_MEMORY && !forcedAlign) align = 1;
sep = sep || ';';
if (isStructType(type)) {
var typeData = Types.types[type];
@@ -1026,7 +1028,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
}
}
} else {
- ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8) + sep;
+ ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, null, null, true) + sep;
ret += makeCopyValues(getFastValue(ptr, '+', pos), 'tempDoublePtr', Runtime.getNativeTypeSize(type), type, null, align, sep);
}
return ret;
diff --git a/src/preamble.js b/src/preamble.js
index f1b95958..0fbb6fac 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -16,7 +16,7 @@ function SAFE_HEAP_CLEAR(dest) {
#if SAFE_HEAP_LOG
Module.print('SAFE_HEAP clear: ' + dest);
#endif
- HEAP_HISTORY[dest] = [];
+ HEAP_HISTORY[dest] = undefined;
}
var SAFE_HEAP_ERRORS = 0;
var ACCEPTABLE_SAFE_HEAP_ERRORS = 0;
@@ -37,7 +37,7 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore) {
#if USE_TYPED_ARRAYS == 2
return; // It is legitimate to violate the load-store assumption in this case
#endif
- if (type && type[type.length-1] == '*') type = 'i32'; // pointers are ints, for our purposes here
+ if (type && type.charAt(type.length-1) == '*') type = 'i32'; // pointers are ints, for our purposes here
// Note that this will pass even with unions: You can store X, load X, then store Y and load Y.
// You cannot, however, do the nonportable act of store X and load Y!
if (store) {
@@ -391,7 +391,7 @@ Module["cwrap"] = cwrap;
// getValue need LLVM types ('i8', 'i32') - this is a lower-level operation
function setValue(ptr, value, type, noSafe) {
type = type || 'i8';
- if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit
+ if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
#if SAFE_HEAP
if (noSafe) {
switch(type) {
@@ -425,7 +425,7 @@ Module['setValue'] = setValue;
// Parallel to setValue.
function getValue(ptr, type, noSafe) {
type = type || 'i8';
- if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit
+ if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
#if SAFE_HEAP
if (noSafe) {
switch(type) {
@@ -700,6 +700,8 @@ STACK_MAX = tempDoublePtr + 8;
STATICTOP = alignMemoryPage(STACK_MAX);
+assert(STATICTOP < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY
+
var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STATIC);
function callRuntimeCallbacks(callbacks) {
diff --git a/src/runtime.js b/src/runtime.js
index 816d864f..dd14e779 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -187,7 +187,7 @@ var Runtime = {
"%double": 8
}['%'+type]; // add '%' since float and double confuse Closure compiler as keys, and also spidermonkey as a compiler will remove 's from '_i8' etc
if (!size) {
- if (type[type.length-1] == '*') {
+ if (type.charAt(type.length-1) == '*') {
size = Runtime.QUANTUM_SIZE; // A pointer
} else if (type[0] == 'i') {
var bits = parseInt(type.substr(1));
diff --git a/src/settings.js b/src/settings.js
index 9e6f257a..a6c11448 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -72,6 +72,10 @@ var DOUBLE_MODE = 1; // How to load and store 64-bit doubles. Without typed arra
// then load it aligned, and that load-store will make JS engines alter it if it is being
// stored to a typed array for security reasons. That will 'fix' the number from being a
// NaN or an infinite number.
+var UNALIGNED_MEMORY = 0; // If enabled, all memory accesses are assumed to be unaligned. (This only matters in
+ // typed arrays mode 2 where alignment is relevant.) In unaligned memory mode, you
+ // can run nonportable code that typically would break in JS (or on ARM for that
+ // matter, which also cannot do unaligned reads/writes), at the cost of slowness
var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which is slow but precise. If disabled,
// we use the 'double trick' which is fast but incurs rounding at high values.
// Note that we do not catch 32-bit multiplication by default (which must be done in
@@ -198,6 +202,10 @@ var INCLUDE_FULL_LIBRARY = 0; // Whether to include the whole library rather tha
// functions used by the generated code. This is needed when
// dynamically loading modules that make use of runtime
// library functions that are not used in the main module.
+ // Note that this includes js libraries but *not* C. You will
+ // need the main file to include all needed C libraries. For
+ // example, if a library uses malloc or new, you will need
+ // to use those in the main file too to link in dlmalloc.
var SHELL_FILE = 0; // set this to a string to override the shell file used
@@ -247,6 +255,10 @@ var WARN_ON_UNDEFINED_SYMBOLS = 0; // If set to 1, we will warn on any undefined
// the existing buildsystem), and (2) functions might be
// implemented later on, say in --pre-js
+var SMALL_XHR_CHUNKS = 0; // Use small chunk size for binary synchronous XHR's in Web Workers.
+ // Used for testing.
+ // See test_chunked_synchronous_xhr in runner.py and library.js.
+
// Compiler debugging options
var DEBUG_TAGS_SHOWING = [];
// Some useful items:
diff --git a/src/shell.js b/src/shell.js
index 891a6328..d6dc0fb5 100644
--- a/src/shell.js
+++ b/src/shell.js
@@ -44,7 +44,9 @@ if (ENVIRONMENT_IS_NODE) {
if (!Module['arguments']) {
Module['arguments'] = process['argv'].slice(2);
}
-} else if (ENVIRONMENT_IS_SHELL) {
+}
+
+if (ENVIRONMENT_IS_SHELL) {
Module['print'] = print;
if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm
@@ -62,7 +64,9 @@ if (ENVIRONMENT_IS_NODE) {
Module['arguments'] = arguments;
}
}
-} else if (ENVIRONMENT_IS_WEB) {
+}
+
+if (ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER) {
if (!Module['print']) {
Module['print'] = function(x) {
console.log(x);
@@ -74,7 +78,9 @@ if (ENVIRONMENT_IS_NODE) {
console.log(x);
};
}
+}
+if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
Module['read'] = function(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
@@ -87,12 +93,24 @@ if (ENVIRONMENT_IS_NODE) {
Module['arguments'] = arguments;
}
}
-} else if (ENVIRONMENT_IS_WORKER) {
+}
+
+if (ENVIRONMENT_IS_WORKER) {
// We can do very little here...
+ var TRY_USE_DUMP = false;
+ if (!Module['print']) {
+ Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) {
+ dump(x);
+ }) : (function(x) {
+ // self.postMessage(x); // enable this if you want stdout to be sent as messages
+ }));
+ }
Module['load'] = importScripts;
+}
-} else {
+if (!ENVIRONMENT_IS_WORKER && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_SHELL) {
+ // Unreachable because SHELL is dependant on the others
throw 'Unknown runtime environment. Where are we?';
}
diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h
index 078f87d2..ed078acd 100644
--- a/system/include/emscripten/emscripten.h
+++ b/system/include/emscripten/emscripten.h
@@ -197,6 +197,22 @@ void emscripten_async_wget(const char* url, const char* file, void (*onload)(con
int emscripten_async_prepare(const char* file, void (*onload)(const char*), void (*onerror)(const char*));
/*
+ * Data version of emscripten_async_prepare, which receives
+ * raw data as input instead of a filename (this can prevent
+ * the need to write data to a file first). onload and
+ * onerror are called back with the given arg pointer as the
+ * first parameter. onload also receives a second
+ * parameter, which is a 'fake' filename which you can
+ * then pass into IMG_Load (it is not an actual file,
+ * but it identifies this image for IMG_Load to be able
+ * to process it). Note that the user of this API is
+ * responsible for free()ing the memory allocated for
+ * the fake filename.
+ * @suffix The file suffix, e.g. 'png' or 'jpg'.
+ */
+void emscripten_async_prepare_data(char* data, int size, const char *suffix, void *arg, void (*onload)(void*, const char*), void (*onerror)(void*));
+
+/*
* Profiling tools.
* INIT must be called first, with the maximum identifier that
* will be used. BEGIN will add some code that marks
diff --git a/tests/checksummer.c b/tests/checksummer.c
new file mode 100644
index 00000000..c3eb1eea
--- /dev/null
+++ b/tests/checksummer.c
@@ -0,0 +1,71 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const int MOD_ADLER = 65521;
+
+uint64_t adler32(unsigned char *data, int32_t len) /* where data is the location of the data in physical memory and
+ len is the length of the data in bytes */
+{
+ uint64_t a = 1, b = 0;
+ int32_t index;
+
+ /* Process each byte of the data in order */
+ for (index = 0; index < len; ++index)
+ {
+ a = (a + data[index]) % MOD_ADLER;
+ b = (b + a) % MOD_ADLER;
+ }
+
+ return (b << 16) | a;
+}
+
+int main(int argc, char* argv[]) {
+ long bufsize;
+
+ if (argc != 2) {
+ fputs("Need 1 argument\n", stderr);
+ return (EXIT_FAILURE);
+ }
+
+ unsigned char *source = NULL;
+ FILE *fp = fopen(argv[1], "rb");
+ if (fp != NULL) {
+ /* Go to the end of the file. */
+ if (fseek(fp, 0L, SEEK_END) == 0) {
+ /* Get the size of the file. */
+ bufsize = ftell(fp);
+ if (bufsize == -1) { fputs("Couldn't get size\n", stderr); return (EXIT_FAILURE); }
+
+ /* Allocate our buffer to that size. */
+ source = malloc(sizeof(char) * (bufsize + 1));
+ if (source == NULL) { fputs("Couldn't allocate\n", stderr); return (EXIT_FAILURE); }
+
+ /* Go back to the start of the file. */
+ if (fseek(fp, 0L, SEEK_SET) == -1) { fputs("Couldn't seek\n", stderr); return (EXIT_FAILURE); }
+
+ /* Read the entire file into memory. */
+ size_t newLen = fread(source, sizeof(char), bufsize, fp);
+ if (newLen == 0) {
+ fputs("Error reading file\n", stderr);
+ //return (EXIT_FAILURE);
+ } else {
+ source[++newLen] = '\0'; /* Just to be safe. */
+ }
+ } else {
+ fputs("Couldn't seek to end\n", stderr);
+ return (EXIT_FAILURE);
+ }
+ fclose(fp);
+ } else {
+ fputs("Couldn't open\n", stderr);
+ return (EXIT_FAILURE);
+ }
+
+ printf("%d\n", (uint32_t) adler32(source, bufsize));
+
+ free(source); /* Don't forget to call free() later! */
+
+ return (EXIT_SUCCESS);
+
+}
diff --git a/tests/cubegeom.c b/tests/cubegeom.c
index ecefb24a..c137ad80 100644
--- a/tests/cubegeom.c
+++ b/tests/cubegeom.c
@@ -256,6 +256,14 @@ int main(int argc, char *argv[])
GLint lightmapLocation = glGetUniformLocation(program, "lightmap");
assert(lightmapLocation >= 0);
+ assert(lightmapLocation == glGetUniformLocation(program, "lightmap")); // must get identical ids
+ glLinkProgram(program);
+ glGetProgramiv(program, GL_LINK_STATUS, &ok);
+ assert(ok);
+ assert(lightmapLocation != glGetUniformLocation(program, "lightmap")); // must NOT get identical ids, we re-linked!
+ lightmapLocation = glGetUniformLocation(program, "lightmap");
+ assert(lightmapLocation == glGetUniformLocation(program, "lightmap")); // must get identical ids
+
glUniform1i(lightmapLocation, 1); // sampler2D? Is it the texture unit?
GLint diffusemapLocation = glGetUniformLocation(program, "diffusemap");
diff --git a/tests/runner.py b/tests/runner.py
index a8af375c..41b9ee19 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -41,6 +41,12 @@ To run a specific set of tests, you can do things like
Combinations work too, for example
python tests/runner.py browser.test_sdl_image
+
+In the main test suite, you can run all variations (O0, O1, O2, etc.) of
+an individual test with
+
+ python tests/runner.py ALL.test_hello_world
+
==============================================================================
'''
@@ -394,6 +400,11 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows
print "Running Emscripten tests..."
+ if len(sys.argv) == 2 and 'ALL.' in sys.argv[1]:
+ ignore, test = sys.argv[1].split('.')
+ print 'Running all test modes on test "%s"' % test
+ sys.argv = [sys.argv[0], 'default.'+test, 'o1.'+test, 'o2.'+test, 's_0_0.'+test, 's_0_1.'+test, 's_0_1_q1.'+test, 's_1_0.'+test, 's_1_1.'+test, 's_1_1_q1.'+test]
+
class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline
## Does a complete test - builds, runs, checks output, etc.
def do_run(self, src, expected_output, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[]):
@@ -1183,16 +1194,11 @@ c5,de,15,8a
}
'''
- Settings.EMULATE_UNALIGNED_ACCESSES = 0
-
try:
self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n')
except Exception, e:
assert 'must be aligned' in str(e), e # expected to fail without emulation
- # XXX TODO Settings.EMULATE_UNALIGNED_ACCESSES = 1
- #self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n') # but succeeds with it
-
def test_unsigned(self):
Settings.CORRECT_SIGNS = 1 # We test for exactly this sort of thing here
Settings.CHECK_SIGNS = 0
@@ -4264,13 +4270,20 @@ at function.:blag
printf("%d\n", sscanf("-123 -765 -34-6", "%d %u %d", &neg, &neg2, &neg3));
printf("%d,%u,%d\n", neg, neg2, neg3);
+ {
+ int a = 0;
+ sscanf("1", "%i", &a);
+ printf("%i\n", a);
+ }
+
return 0;
}
'''
self.do_run(src, 'en-us : 2\nen-r : 99\nen : 3\n1.234567, 0.000000\n2.8208\n-3.0300\n|some|\n|something|\n|somethingmoar|\n' +
'1\n1499\n' +
'5\n87,0.481565,0.059481,0,1\n' +
- '3\n-123,4294966531,-34\n')
+ '3\n-123,4294966531,-34\n' +
+ '1\n')
def test_sscanf_2(self):
# doubles
@@ -7272,6 +7285,26 @@ f.close()
Popen(['python', EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-fcatch-undefined-behavior']).communicate()
self.assertContained('hello, world!', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+ def test_unaligned_memory(self):
+ open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
+ #include <stdio.h>
+
+ typedef unsigned char Bit8u;
+ typedef unsigned short Bit16u;
+ typedef unsigned int Bit32u;
+
+ int main()
+ {
+ Bit8u data[4] = {0x01,0x23,0x45,0x67};
+
+ printf("data: %x\n", *(Bit32u*)data);
+ printf("data[0,1] 16bit: %x\n", *(Bit16u*)data);
+ printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1));
+ }
+ ''')
+ Popen(['python', EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate()
+ self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+
def test_l_link(self):
# Linking with -lLIBNAME and -L/DIRNAME should work
@@ -7343,6 +7376,70 @@ f.close()
Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'subdir', 'libfile.so'), '-L.']).communicate()
self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+ def test_runtimelink_multi(self):
+ open('testa.h', 'w').write(r'''
+ #ifndef _TESTA_H_
+ #define _TESTA_H_
+
+ class TestA {
+ public:
+ TestA();
+ };
+
+ #endif
+ ''')
+ open('testb.h', 'w').write(r'''
+ #ifndef _TESTB_H_
+ #define _TESTB_H_
+
+ class TestB {
+ public:
+ TestB();
+ };
+
+ #endif
+ ''')
+ open('testa.cpp', 'w').write(r'''
+ #include <stdio.h>
+ #include <testa.h>
+
+ TestA::TestA() {
+ printf("TestA\n");
+ }
+ ''')
+ open('testb.cpp', 'w').write(r'''
+ #include <stdio.h>
+ #include <testb.h>
+ #include <testa.h>
+ /*
+ */
+ TestB::TestB() {
+ printf("TestB\n");
+ TestA* testa = new TestA();
+ }
+ ''')
+ open('main.cpp', 'w').write(r'''
+ #include <stdio.h>
+ #include <testa.h>
+ #include <testb.h>
+
+ /*
+ */
+ int main(int argc, char** argv) {
+ printf("Main\n");
+ TestA* testa = new TestA();
+ TestB* testb = new TestB();
+ }
+ ''')
+
+ Popen(['python', EMCC, 'testa.cpp', '-o', 'liba.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-I.']).communicate()
+ Popen(['python', EMCC, 'testb.cpp', '-o', 'libb.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-I.']).communicate()
+ Popen(['python', EMCC, 'main.cpp', '-o', 'main.js', '-s', 'RUNTIME_LINKED_LIBS=["liba.js", "libb.js"]', '-I.']).communicate()
+
+ Popen(['python', EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate()
+
+ self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE))
+
def test_js_libraries(self):
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
#include <stdio.h>
@@ -8506,6 +8603,11 @@ elif 'browser' in str(sys.argv):
shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not'))
self.btest('sdl_image_prepare.c', reference='screenshot.jpg', args=['--preload-file', 'screenshot.not'])
+ def test_sdl_image_prepare_data(self):
+ # load an image file, get pixel data. Also O2 coverage for --preload-file
+ shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not'))
+ self.btest('sdl_image_prepare_data.c', reference='screenshot.jpg', args=['--preload-file', 'screenshot.not'])
+
def test_sdl_canvas(self):
open(os.path.join(self.get_dir(), 'sdl_canvas.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas.c')).read()))
@@ -8678,6 +8780,114 @@ elif 'browser' in str(sys.argv):
html_file.close()
self.run_browser('main.html', 'You should see that the worker was called, and said "hello from worker!"', '/report_result?hello%20from%20worker!')
+ def test_chunked_synchronous_xhr(self):
+ main = 'chunked_sync_xhr.html'
+ worker_filename = "download_and_checksum_worker.js"
+
+ html_file = open(main, 'w')
+ html_file.write(r"""
+ <!doctype html>
+ <html>
+ <head><meta charset="utf-8"><title>Chunked XHR</title></head>
+ <html>
+ <body>
+ Chunked XHR Web Worker Test
+ <script>
+ var worker = new Worker(""" + json.dumps(worker_filename) + r""");
+ var buffer = [];
+ worker.onmessage = function(event) {
+ if (event.data.channel === "stdout") {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'http://localhost:8888/report_result?' + event.data.line);
+ xhr.send();
+ setTimeout(function() { window.close() }, 1000);
+ } else {
+ if (event.data.trace) event.data.trace.split("\n").map(function(v) { console.error(v); });
+ if (event.data.line) {
+ console.error(event.data.line);
+ } else {
+ var v = event.data.char;
+ if (v == 10) {
+ var line = buffer.splice(0);
+ console.error(line = line.map(function(charCode){return String.fromCharCode(charCode);}).join(''));
+ } else {
+ buffer.push(v);
+ }
+ }
+ }
+ };
+ </script>
+ </body>
+ </html>
+ """)
+ html_file.close()
+
+ c_source_filename = "checksummer.c"
+
+ prejs_filename = "worker_prejs.js"
+ prejs_file = open(prejs_filename, 'w')
+ prejs_file.write(r"""
+ if (typeof(Module) === "undefined") Module = {};
+ Module["arguments"] = ["/bigfile"];
+ Module["preInit"] = function() {
+ FS.createLazyFile('/', "bigfile", "http://localhost:11111/bogus_file_path", true, false);
+ };
+ var doTrace = true;
+ Module["print"] = function(s) { self.postMessage({channel: "stdout", line: s}); };
+ Module["stderr"] = function(s) { self.postMessage({channel: "stderr", char: s, trace: ((doTrace && s === 10) ? new Error().stack : null)}); doTrace = false; };
+ """)
+ prejs_file.close()
+ # vs. os.path.join(self.get_dir(), filename)
+ # vs. path_from_root('tests', 'hello_world_gles.c')
+ Popen(['python', EMCC, path_from_root('tests', c_source_filename), '-g', '-s', 'SMALL_CHUNKS=1', '-o', worker_filename,
+ '--pre-js', prejs_filename]).communicate()
+
+ chunkSize = 1024
+ data = os.urandom(10*chunkSize+1) # 10 full chunks and one 1 byte chunk
+ expectedConns = 11
+ import zlib
+ checksum = zlib.adler32(data)
+
+ def chunked_server(support_byte_ranges):
+ class ChunkedServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ @staticmethod
+ def sendheaders(s, extra=[], length=len(data)):
+ s.send_response(200)
+ s.send_header("Content-Length", str(length))
+ s.send_header("Access-Control-Allow-Origin", "http://localhost:8888")
+ s.send_header("Access-Control-Expose-Headers", "Content-Length, Accept-Ranges")
+ s.send_header("Content-type", "application/octet-stream")
+ if support_byte_ranges:
+ s.send_header("Accept-Ranges", "bytes")
+ for i in extra:
+ s.send_header(i[0], i[1])
+ s.end_headers()
+
+ def do_HEAD(s):
+ ChunkedServerHandler.sendheaders(s)
+
+ def do_GET(s):
+ if not support_byte_ranges:
+ ChunkedServerHandler.sendheaders(s)
+ s.wfile.write(data)
+ else:
+ (start, end) = s.headers.get("range").split("=")[1].split("-")
+ start = int(start)
+ end = int(end)
+ end = min(len(data)-1, end)
+ length = end-start+1
+ ChunkedServerHandler.sendheaders(s,[],length)
+ s.wfile.write(data[start:end+1])
+ s.wfile.close()
+ httpd = BaseHTTPServer.HTTPServer(('localhost', 11111), ChunkedServerHandler)
+ for i in range(expectedConns+1):
+ httpd.handle_request()
+
+ server = multiprocessing.Process(target=chunked_server, args=(True,))
+ server.start()
+ self.run_browser(main, 'Chunked binary synchronous XHR in Web Workers!', '/report_result?' + str(checksum))
+ server.terminate()
+
def test_glgears(self):
self.reftest(path_from_root('tests', 'gears.png'))
Popen(['python', EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html',
@@ -8764,6 +8974,9 @@ elif 'browser' in str(sys.argv):
def test_sdl_quit(self):
self.btest('sdl_quit.c', '1')
+ def test_sdl_resize(self):
+ self.btest('sdl_resize.c', '1')
+
def test_gc(self):
self.btest('browser_gc.cpp', '1')
@@ -8782,52 +8995,52 @@ elif 'browser' in str(sys.argv):
self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'])
def test_cubegeom_pre(self):
- self.btest('cubegeom_pre.c', expected=['-1472804742', '-1626058463'])
+ self.btest('cubegeom_pre.c', expected=['-1472804742', '-1626058463', '-2046234971'])
def test_cubegeom_pre2(self):
- self.btest('cubegeom_pre2.c', expected=['-1472804742', '-1626058463'], args=['-s', 'GL_DEBUG=1']) # some coverage for GL_DEBUG not breaking the build
+ self.btest('cubegeom_pre2.c', expected=['-1472804742', '-1626058463', '-2046234971'], args=['-s', 'GL_DEBUG=1']) # some coverage for GL_DEBUG not breaking the build
def test_cubegeom_pre3(self):
- self.btest('cubegeom_pre3.c', expected=['-1472804742', '-1626058463'])
+ self.btest('cubegeom_pre3.c', expected=['-1472804742', '-1626058463', '-2046234971'])
def test_cubegeom(self):
- self.btest('cubegeom.c', expected=['188641320', '1522377227', '-1054007155'])
+ self.btest('cubegeom.c', expected=['188641320', '1522377227', '-1054007155', '-1111866053'])
def test_cubegeom_color(self):
- self.btest('cubegeom_color.c', expected=['588472350', '-687660609'])
+ self.btest('cubegeom_color.c', expected=['588472350', '-687660609', '-818120875'])
def test_cubegeom_normal(self):
- self.btest('cubegeom_normal.c', expected=['752917084', '-251570256'])
+ self.btest('cubegeom_normal.c', expected=['752917084', '-251570256', '-291655550'])
def test_cubegeom_normal_dap(self): # draw is given a direct pointer to clientside memory, no element array buffer
- self.btest('cubegeom_normal_dap.c', expected=['752917084', '-251570256'])
+ self.btest('cubegeom_normal_dap.c', expected=['752917084', '-251570256', '-291655550'])
def test_cubegeom_normal_dap_far(self): # indices do nto start from 0
- self.btest('cubegeom_normal_dap_far.c', expected=['752917084', '-251570256'])
+ self.btest('cubegeom_normal_dap_far.c', expected=['752917084', '-251570256', '-291655550'])
def test_cubegeom_normal_dap_far_range(self): # glDrawRangeElements
- self.btest('cubegeom_normal_dap_far_range.c', expected=['752917084', '-251570256'])
+ self.btest('cubegeom_normal_dap_far_range.c', expected=['752917084', '-251570256', '-291655550'])
def test_cubegeom_normal_dap_far_glda(self): # use glDrawArrays
- self.btest('cubegeom_normal_dap_far_glda.c', expected=['-218745386', '-263951846'])
+ self.btest('cubegeom_normal_dap_far_glda.c', expected=['-218745386', '-263951846', '-375182658'])
def test_cubegeom_normal_dap_far_glda_quad(self): # with quad
- self.btest('cubegeom_normal_dap_far_glda_quad.c', expected=['1757386625', '-677777235'])
+ self.btest('cubegeom_normal_dap_far_glda_quad.c', expected=['1757386625', '-677777235', '-690699597'])
def test_cubegeom_mt(self):
- self.btest('cubegeom_mt.c', expected=['-457159152', '910983047']) # multitexture
+ self.btest('cubegeom_mt.c', expected=['-457159152', '910983047', '870576921']) # multitexture
def test_cubegeom_color2(self):
- self.btest('cubegeom_color2.c', expected=['1121999515', '-391668088'])
+ self.btest('cubegeom_color2.c', expected=['1121999515', '-391668088', '-522128354'])
def test_cubegeom_texturematrix(self):
- self.btest('cubegeom_texturematrix.c', expected=['1297500583', '-791216738'])
+ self.btest('cubegeom_texturematrix.c', expected=['1297500583', '-791216738', '-783804685'])
def test_cubegeom_fog(self):
- self.btest('cubegeom_fog.c', expected=['1617140399', '-898782526'])
+ self.btest('cubegeom_fog.c', expected=['1617140399', '-898782526', '-946179526'])
def test_cube_explosion(self):
- self.btest('cube_explosion.c', expected=['667220544', '-1543354600'])
+ self.btest('cube_explosion.c', expected=['667220544', '-1543354600', '-1485258415'])
def test_sdl_canvas_palette(self):
self.btest('sdl_canvas_palette.c', reference='sdl_canvas_palette.png')
@@ -8944,6 +9157,7 @@ elif 'browser' in str(sys.argv):
def websockify_func(q):
print >> sys.stderr, 'running websockify on %d, forward to tcp %d' % (self.port+1, self.port)
proc = Popen([path_from_root('third_party', 'websockify', 'other', 'websockify'), '-vvv', str(self.port+1), '127.0.0.1:' + str(self.port)])
+ #proc = Popen([path_from_root('third_party', 'websockify', 'websockify.py'), '-vvv', str(self.port+1), '127.0.0.1:' + str(self.port)])
q.put(proc.pid)
proc.communicate()
@@ -9003,6 +9217,15 @@ elif 'browser' in str(sys.argv):
finally:
self.clean_pids()
+ def zzztest_zz_websockets_bi_bigdata(self):
+ try:
+ with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)):
+ with self.WebsockHarness(3994, no_server=True):
+ Popen(['python', EMCC, path_from_root('tests', 'websockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')]).communicate()
+ self.btest('websockets_bi_bigdata.c', expected='0', args=['-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')])
+ finally:
+ self.clean_pids()
+
def zzztest_zz_enet(self):
try_delete(self.in_dir('enet'))
shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet'))
@@ -9106,13 +9329,13 @@ elif 'benchmark' in str(sys.argv):
print ' JavaScript: mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) (%d runs)' % (mean, std, median, min(times), max(times), 100*std/mean, TEST_REPS)
print ' Native : mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) JS is %.2f X slower' % (mean_native, std_native, median_native, min(native_times), max(native_times), 100*std_native/mean_native, final)
- def do_benchmark(self, src, args=[], expected_output='FAIL', emcc_args=[]):
+ def do_benchmark(self, name, src, args=[], expected_output='FAIL', emcc_args=[]):
dirname = self.get_dir()
- filename = os.path.join(dirname, 'src.cpp')
+ filename = os.path.join(dirname, name + '.cpp')
f = open(filename, 'w')
f.write(src)
f.close()
- final_filename = os.path.join(dirname, 'src.js')
+ final_filename = os.path.join(dirname, name + '.js')
try_delete(final_filename)
output = Popen(['python', EMCC, filename, '-O3',
@@ -9175,7 +9398,7 @@ elif 'benchmark' in str(sys.argv):
return 1;
}
'''
- self.do_benchmark(src, [], 'lastprime: 1297001.')
+ self.do_benchmark('primes', src, [], 'lastprime: 1297001.')
def test_memops(self):
src = '''
@@ -9198,7 +9421,7 @@ elif 'benchmark' in str(sys.argv):
return 1;
}
'''
- self.do_benchmark(src, [], 'final: 720.')
+ self.do_benchmark('memops', src, [], 'final: 720.')
def zzztest_files(self):
src = r'''
@@ -9284,11 +9507,11 @@ elif 'benchmark' in str(sys.argv):
return 1;
}
'''
- self.do_benchmark(src, [], 'sum:9928\n', emcc_args=['-s', 'QUANTUM_SIZE=4', '-s', 'USE_TYPED_ARRAYS=2'])
+ self.do_benchmark('copy', src, [], 'sum:9928\n', emcc_args=['-s', 'QUANTUM_SIZE=4', '-s', 'USE_TYPED_ARRAYS=2'])
def test_fannkuch(self):
src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read()
- self.do_benchmark(src, ['10'], 'Pfannkuchen(10) = 38.')
+ self.do_benchmark('fannkuch', src, ['10'], 'Pfannkuchen(10) = 38.')
def test_corrections(self):
src = r'''
@@ -9311,11 +9534,11 @@ elif 'benchmark' in str(sys.argv):
return 1;
}
'''
- self.do_benchmark(src, [], 'final: 826:14324.', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1'])
+ self.do_benchmark('corrections', src, [], 'final: 826:14324.', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1'])
def fasta(self, double_rep):
src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', double_rep)
- self.do_benchmark(src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''')
+ self.do_benchmark('fasta', src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''')
def test_fasta_float(self):
self.fasta('float')
@@ -9325,12 +9548,12 @@ elif 'benchmark' in str(sys.argv):
def test_skinning(self):
src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read()
- self.do_benchmark(src, ['10000', '1000'], 'blah=0.000000')
+ self.do_benchmark('skinning', src, ['10000', '1000'], 'blah=0.000000')
def test_dlmalloc(self):
# XXX This seems to have regressed slightly with emcc. Are -g and the signs lines passed properly?
src = open(path_from_root('system', 'lib', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read()
- self.do_benchmark(src, ['400', '400'], '*400,0*', emcc_args=['-g', '-s', 'CORRECT_SIGNS=2', '-s', 'CORRECT_SIGNS_LINES=[4820, 4195, 4250, 4203, 4209, 4239, 4231]'])
+ self.do_benchmark('dlmalloc', src, ['400', '400'], '*400,0*', emcc_args=['-g', '-s', 'CORRECT_SIGNS=2', '-s', 'CORRECT_SIGNS_LINES=[4820, 4195, 4250, 4203, 4209, 4239, 4231]'])
elif 'sanity' in str(sys.argv):
@@ -9502,6 +9725,50 @@ elif 'sanity' in str(sys.argv):
finally:
del os.environ['EM_IGNORE_SANITY']
+ def test_node(self):
+ NODE_WARNING = 'warning: node version appears too old'
+ NODE_WARNING_2 = 'warning: cannot check node version'
+
+ restore()
+
+ # Clang should report the version number we expect, and emcc should not warn
+ assert check_node_version()
+ output = self.check_working(EMCC)
+ assert NODE_WARNING not in output, output
+
+ # Fake a different node version
+ restore()
+ f = open(CONFIG_FILE, 'a')
+ f.write('NODE_JS = "' + path_from_root('tests', 'fake', 'nodejs') + '"')
+ f.close()
+
+ if not os.path.exists(path_from_root('tests', 'fake')):
+ os.makedirs(path_from_root('tests', 'fake'))
+
+ try:
+ os.environ['EM_IGNORE_SANITY'] = '1'
+ for version, succeed in [(('v0.6.6'), False), (('v0.6.7'), False), (('v0.6.8'), True), (('v0.6.9'), True), (('v0.7.1'), True), (('v0.7.9'), True), (('v0.8.7'), True), (('v0.8.9'), True), ('cheez', False)]:
+ f = open(path_from_root('tests', 'fake', 'nodejs'), 'w')
+ f.write('#!/bin/sh\n')
+ f.write('''if [ $1 = "--version" ]; then
+ echo "%s"
+else
+ %s $@
+fi
+''' % (version, NODE_JS))
+ f.close()
+ os.chmod(path_from_root('tests', 'fake', 'nodejs'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
+ if not succeed:
+ if version[0] == 'v':
+ self.check_working(EMCC, NODE_WARNING)
+ else:
+ self.check_working(EMCC, NODE_WARNING_2)
+ else:
+ output = self.check_working(EMCC)
+ assert NODE_WARNING not in output, output
+ finally:
+ del os.environ['EM_IGNORE_SANITY']
+
def test_emcc(self):
SANITY_MESSAGE = 'Emscripten: Running sanity checks'
SANITY_FAIL_MESSAGE = 'sanity check failed to run'
diff --git a/tests/sdl_image_prepare_data.c b/tests/sdl_image_prepare_data.c
new file mode 100644
index 00000000..87e33399
--- /dev/null
+++ b/tests/sdl_image_prepare_data.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <assert.h>
+#include <emscripten.h>
+
+SDL_Surface* screen;
+
+int testImage(const char* fileName) {
+ printf("IMG_Load: %s\n", fileName);
+ SDL_Surface *image = IMG_Load(fileName);
+ if (!image)
+ {
+ printf("IMG_Load: %s\n", IMG_GetError());
+ return 0;
+ }
+ assert(image->format->BitsPerPixel == 32);
+ assert(image->format->BytesPerPixel == 4);
+ assert(image->pitch == 4*image->w);
+ int result = image->w;
+
+ SDL_BlitSurface (image, NULL, screen, NULL);
+ SDL_FreeSurface (image);
+
+ return result;
+}
+
+void ready(void *arg, const char *fileName) {
+ printf("ready! %s (%d)\n", fileName, (int)arg);
+
+ static int first = 1;
+ static const char *seenName;
+ static void *seenArg;
+ if (first) {
+ first = 0;
+ seenName = fileName;
+ seenArg = arg;
+ } else {
+ printf("%s ? %s == %d\n", fileName, seenName, strcmp(fileName, seenName));
+ assert(strcmp(fileName, seenName)); // different names
+
+ assert(seenArg != arg); // different args
+
+ testImage(seenName);
+
+ free(seenName); // As the API docs say, we are responsible for freeing the 'fake' names we are given
+
+ SDL_Flip(screen);
+ }
+}
+
+int main() {
+ SDL_Init(SDL_INIT_VIDEO);
+ screen = SDL_SetVideoMode(600, 450, 32, SDL_SWSURFACE);
+
+ printf("prepare..\n");
+
+ #define SIZE 203164
+ FILE *f = open("screenshot.not", "rb");
+ char *buffer = malloc(SIZE);
+ fread(buffer, SIZE, 1, f);
+ fclose(f);
+
+ emscripten_async_prepare_data(buffer, SIZE, "jpg", (void*)25, ready, NULL);
+ emscripten_async_prepare_data(buffer, SIZE, "jpg", (void*)33, ready, NULL); // twice to see different filenames
+
+ return 0;
+}
+
diff --git a/tests/sdl_resize.c b/tests/sdl_resize.c
new file mode 100644
index 00000000..dc3b374e
--- /dev/null
+++ b/tests/sdl_resize.c
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+#include <assert.h>
+#include <emscripten.h>
+
+int stage = 0;
+
+void loop() {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch(event.type) {
+ case SDL_VIDEORESIZE: {
+ SDL_ResizeEvent *r = (SDL_ResizeEvent*)&event;
+ printf("resize event! %d:%d\n", r->w, r->h);
+ switch (stage) {
+ case 0:
+ assert(r->w == 100);
+ assert(r->h == 200);
+ emscripten_set_canvas_size(123, 246);
+ stage++;
+ break;
+ case 1:
+ assert(r->w == 123);
+ assert(r->h == 246);
+ int result = 1;
+ REPORT_RESULT();
+ break;
+ }
+ }
+ }
+ }
+}
+
+void main_2();
+
+int main() {
+ SDL_Init(SDL_INIT_VIDEO);
+ SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
+
+ emscripten_set_canvas_size(100, 200);
+
+ emscripten_set_main_loop(loop, 0, 0);
+}
+
diff --git a/tests/websockets_bi_bigdata.c b/tests/websockets_bi_bigdata.c
new file mode 100644
index 00000000..9e8635e3
--- /dev/null
+++ b/tests/websockets_bi_bigdata.c
@@ -0,0 +1,137 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+#include "websockets_bigdata.h"
+
+#define EXPECTED_BYTES DATA_SIZE
+
+int SocketFD;
+
+unsigned int get_all_buf(int sock, char* output, unsigned int maxsize)
+{
+ int bytes;
+ if (ioctl(sock, FIONREAD, &bytes)) return 0;
+ if (bytes == 0) return 0;
+
+ char buffer[EXPECTED_BYTES];
+ int n;
+ unsigned int offset = 0;
+ while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) ||
+ errno == EINTR) {
+ if(n>0)
+ {
+ if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); }
+ memcpy(output+offset, buffer, n);
+ offset += n;
+ }
+ }
+
+ if(n < 0) {
+ fprintf(stderr, "error in get_all_buf!");
+ exit(EXIT_FAILURE);
+ }
+ return offset;
+}
+
+int done = 0;
+
+void iter(void *arg) {
+ /* perform read write operations ... */
+ static char out[EXPECTED_BYTES];
+ static int pos = 0;
+ printf("so far %d, expecting up to %d\n", pos, EXPECTED_BYTES-pos);
+ int n = get_all_buf(SocketFD, out+pos, EXPECTED_BYTES-pos);
+ if (n) printf("read! %d\n", n);
+ pos += n;
+ if (pos >= EXPECTED_BYTES) {
+ shutdown(SocketFD, SHUT_RDWR);
+
+ close(SocketFD);
+
+ done = 1;
+
+ emscripten_cancel_main_loop();
+
+#if EMSCRIPTEN
+ char *comp = generateData();
+ int result = strcmp(comp, out);
+ if (result != 0) {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ printf("%d:%d\n", comp[i], out[i]);
+ }
+ }
+ REPORT_RESULT();
+#endif
+ }
+}
+
+int main(void)
+{
+ printf("hello from main page\n");
+
+ struct sockaddr_in stSockAddr;
+ int Res;
+ SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (-1 == SocketFD)
+ {
+ perror("cannot create socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&stSockAddr, 0, sizeof(stSockAddr));
+
+ stSockAddr.sin_family = AF_INET;
+ stSockAddr.sin_port = htons(
+#if EMSCRIPTEN
+ 3993
+#else
+ 3992
+#endif
+ );
+ Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);
+
+ if (0 > Res) {
+ perror("error: first parameter is not a valid address family");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ } else if (0 == Res) {
+ perror("char string (second parameter does not contain valid ipaddress)");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ }
+
+ if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) {
+ perror("connect failed");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+
+ }
+
+#if EMSCRIPTEN
+ emscripten_run_script("console.log('adding iframe');"
+ "var iframe = document.createElement('iframe');"
+ "iframe.src = 'side.html';"
+ "iframe.width = '100%';"
+ "iframe.width = '40%';"
+ "document.body.appendChild(iframe);"
+ "console.log('added.');");
+ emscripten_set_main_loop(iter, 1, 0);
+#else
+ while (!done) iter(NULL);
+#endif
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/tests/websockets_bi_side_bigdata.c b/tests/websockets_bi_side_bigdata.c
new file mode 100644
index 00000000..9b67fe4c
--- /dev/null
+++ b/tests/websockets_bi_side_bigdata.c
@@ -0,0 +1,69 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+#include "websockets_bigdata.h"
+
+#define EXPECTED_BYTES 5
+
+void stall(void *arg) {
+}
+
+int main(void)
+{
+ struct sockaddr_in stSockAddr;
+ int Res;
+ int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (-1 == SocketFD)
+ {
+ perror("cannot create socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&stSockAddr, 0, sizeof(stSockAddr));
+
+ stSockAddr.sin_family = AF_INET;
+ stSockAddr.sin_port = htons(SOCKK);
+ Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);
+
+ if (0 > Res) {
+ perror("error: first parameter is not a valid address family");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ } else if (0 == Res) {
+ perror("char string (second parameter does not contain valid ipaddress)");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("connect..\n");
+
+ if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) {
+ perror("connect failed");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("send..\n");
+
+ char *data = generateData();
+ send(SocketFD, data, DATA_SIZE, 0);
+
+ printf("stall..\n");
+
+ emscripten_set_main_loop(stall, 1, 0);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/tests/websockets_bigdata.h b/tests/websockets_bigdata.h
new file mode 100644
index 00000000..fa1a6b17
--- /dev/null
+++ b/tests/websockets_bigdata.h
@@ -0,0 +1,20 @@
+
+#include <stdlib.h>
+
+#define DATA_SIZE 1250
+// 1500 fails
+
+char *generateData() {
+ char *ret = malloc(65536*2);
+ char *curr = ret;
+ for (int i = 0; i < 256; i++) {
+ for (int j = 0; j < 256; j++) {
+ *curr = i;
+ curr++;
+ *curr = j;
+ curr++;
+ }
+ }
+ return ret;
+}
+
diff --git a/tools/file_packager.py b/tools/file_packager.py
index 9bf781e4..33082ac2 100644
--- a/tools/file_packager.py
+++ b/tools/file_packager.py
@@ -355,9 +355,9 @@ if has_preloaded:
num++;
}
total = Math.ceil(total * Module.expectedDataFileDownloads/num);
- Module.setStatus('Downloading data... (' + loaded + '/' + total + ')');
+ Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')');
} else if (!Module.dataFileDownloads) {
- Module.setStatus('Downloading data...');
+ Module['setStatus']('Downloading data...');
}
}
dataFile.open('GET', '%s', true);
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 5dac36f0..f1574e2a 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -454,8 +454,25 @@ function simplifyExpressionsPre(ast) {
}
}
+ // if (x == 0) can be if (!x), etc.
+ function simplifyZeroComp(ast) {
+ traverseGenerated(ast, function(node, type) {
+ var binary;
+ if (type == 'if' && (binary = node[1])[0] == 'binary') {
+ if ((binary[1] == '!=' || binary[1] == '!==') && binary[3][0] == 'num' && binary[3][1] == 0) {
+ node[1] = binary[2];
+ return node;
+ } else if ((binary[1] == '==' || binary[1] == '===') && binary[3][0] == 'num' && binary[3][1] == 0) {
+ node[1] = ['unary-prefix', '!', binary[2]];
+ return node;
+ }
+ }
+ });
+ }
+
simplifyBitops(ast);
joinAdditions(ast);
+ // simplifyZeroComp(ast); TODO: investigate performance
}
// In typed arrays mode 2, we can have
diff --git a/tools/shared.py b/tools/shared.py
index 24adaec8..37552b09 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -77,17 +77,29 @@ def check_clang_version():
actual = Popen([CLANG, '-v'], stderr=PIPE).communicate()[1].split('\n')[0]
if expected in actual:
return True
-
print >> sys.stderr, 'warning: LLVM version appears incorrect (seeing "%s", expected "%s")' % (actual, expected)
return False
-
def check_llvm_version():
try:
check_clang_version();
except Exception, e:
print >> sys.stderr, 'warning: Could not verify LLVM version: %s' % str(e)
+EXPECTED_NODE_VERSION = (0,6,8)
+
+def check_node_version():
+ try:
+ actual = Popen([NODE_JS, '--version'], stdout=PIPE).communicate()[0].strip()
+ version = tuple(map(int, actual.replace('v', '').split('.')))
+ if version >= EXPECTED_NODE_VERSION:
+ return True
+ print >> sys.stderr, 'warning: node version appears too old (seeing "%s", expected "%s")' % (actual, 'v' + ('.'.join(map(str, EXPECTED_NODE_VERSION))))
+ return False
+ except Exception, e:
+ print >> sys.stderr, 'warning: cannot check node version:', e
+ return False
+
# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM)
# exists.
# The test runner always does this check (through |force|). emcc does this less frequently,
@@ -110,7 +122,9 @@ def check_sanity(force=False):
print >> sys.stderr, '(Emscripten: Config file changed, clearing cache)' # LLVM may have changed, etc.
Cache.erase()
- check_llvm_version() # just a warning, not a fatal check - do it even if EM_IGNORE_SANITY is on
+ # some warning, not fatal checks - do them even if EM_IGNORE_SANITY is on
+ check_llvm_version()
+ check_node_version()
if os.environ.get('EM_IGNORE_SANITY'):
print >> sys.stderr, 'EM_IGNORE_SANITY set, ignoring sanity checks'
@@ -912,15 +926,60 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
Building.LLVM_OPT_OPTS = opts
return opts
+ MAX_JS_PROCESS_SIZE = 12*1024*1024
+ BEST_JS_PROCESS_SIZE = 8*1024*1024
+
+ @staticmethod
+ def splitter(filename, addendum, func, args=[]):
+ # Split up huge files into pieces small enough for node/uglify/etc. to handle
+ js = open(filename).read()
+ if len(js) > Building.MAX_JS_PROCESS_SIZE:
+ if os.environ.get('EMCC_DEBUG'): print >> sys.stderr, 'splitting up js file'
+
+ suffix_marker = '// EMSCRIPTEN_GENERATED_FUNCTIONS'
+ suffix_start = js.find(suffix_marker)
+ suffix = ''
+ if suffix_start >= 0:
+ suffix = js[suffix_start:js.find('\n', suffix_start)] + '\n'
+
+ filename += addendum
+ f = open(filename, 'w')
+
+ i = 0
+ f_start = 0
+ while True:
+ f_end = f_start
+ while f_end-f_start < Building.BEST_JS_PROCESS_SIZE and f_end != -1:
+ f_end = js.find('\n}\n', f_end+1)
+ chunk = js[f_start:(-1 if f_end == -1 else f_end+3)] + suffix
+ temp_in_file = filename + '.p%d.js' % i
+ if os.environ.get('EMCC_DEBUG'): print >> sys.stderr, ' split %d, size %d' % (i, len(chunk))
+ i += 1
+ open(temp_in_file, 'w').write(chunk)
+ temp_out_file = func(*([temp_in_file] + args + [False])) # maybe_big is now false
+ f.write(
+ ''.join(filter(lambda line: not line.startswith(suffix_marker), open(temp_out_file).readlines()))
+ )
+ if f_end == -1: break
+ f_start = f_end+3
+ if f_start >= len(js): break
+
+ f.write(suffix)
+ f.close()
+ return filename
+
@staticmethod
- def js_optimizer(filename, passes):
- if not check_engine(NODE_JS):
- raise Exception('Node.js appears to be missing or broken, looked at: ' + str(NODE_JS))
+ def js_optimizer(filename, passes, maybe_big=True):
+ if maybe_big:
+ # When we split up, we cannot do unGlobalize, the only pass which is *not* function-local
+ args = [filter(lambda p: p != 'unGlobalize', passes)]
+ ret = Building.splitter(filename, addendum='.jo.js', func=Building.js_optimizer, args=args)
+ if ret: return ret
if type(passes) == str:
passes = [passes]
- # XXX Disable crankshaft to work around v8 bug 1895
- output = Popen([NODE_JS, '--nocrankshaft', JS_OPTIMIZER, filename] + passes, stdout=PIPE).communicate()[0]
+ # XXX Use '--nocrankshaft' to disable crankshaft to work around v8 bug 1895, needed for older v8/node (node 0.6.8+ should be ok)
+ output = Popen([NODE_JS, JS_OPTIMIZER, filename] + passes, stdout=PIPE).communicate()[0]
assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output
filename += '.jo.js'
f = open(filename, 'w')
@@ -929,9 +988,10 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
return filename
@staticmethod
- def eliminator(filename):
- if not check_engine(NODE_JS):
- raise Exception('Node.js appears to be missing or broken, looked at: ' + str(NODE_JS))
+ def eliminator(filename, maybe_big=True):
+ if maybe_big:
+ ret = Building.splitter(filename, addendum='.el.js', func=Building.eliminator)
+ if ret: return ret
coffee = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee')
eliminator = path_from_root('tools', 'eliminator', 'eliminator.coffee')
@@ -952,7 +1012,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# Something like this (adjust memory as needed):
# java -Xmx1024m -jar CLOSURE_COMPILER --compilation_level ADVANCED_OPTIMIZATIONS --variable_map_output_file src.cpp.o.js.vars --js src.cpp.o.js --js_output_file src.cpp.o.cc.js
args = [JAVA,
- '-Xmx1024m',
+ '-Xmx' + (os.environ.get('JAVA_HEAP_SIZE') or '1024m'), # if you need a larger Java heap, use this environment variable
'-jar', CLOSURE_COMPILER,
'--compilation_level', 'ADVANCED_OPTIMIZATIONS',
'--formatting', 'PRETTY_PRINT',