aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc1
-rwxr-xr-xemscripten.py4
-rw-r--r--src/jsifier.js5
-rw-r--r--src/library.js6
-rw-r--r--src/library_gl.js107
-rw-r--r--src/library_sdl.js27
-rw-r--r--src/preamble.js19
-rw-r--r--src/settings.js21
-rwxr-xr-xtests/runner.py53
-rwxr-xr-xtools/bindings_generator.py5
-rw-r--r--tools/shared.py1
11 files changed, 196 insertions, 53 deletions
diff --git a/emcc b/emcc
index 488f0788..677431b5 100755
--- a/emcc
+++ b/emcc
@@ -191,6 +191,7 @@ Options that are modified or new in %s include:
-s DOUBLE_MODE=0
-s PRECISE_I64_MATH=0
+ -s UTF_STRING_SUPPORT=0
--closure 1
--llvm-lto 1
diff --git a/emscripten.py b/emscripten.py
index 0698c783..c9d8505d 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -269,8 +269,10 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
for key in curr_forwarded_json['Functions']['indexedFunctions'].iterkeys():
indexed_functions.add(key)
if settings.get('ASM_JS'):
+ export_bindings = settings['EXPORT_BINDINGS']
for key in curr_forwarded_json['Functions']['implementedFunctions'].iterkeys():
- if key in all_exported_functions: exported_implemented_functions.add(key)
+ if key in all_exported_functions or (export_bindings and key.startswith('_emscripten_bind')):
+ exported_implemented_functions.add(key)
for key, value in curr_forwarded_json['Functions']['unimplementedFunctions'].iteritems():
forwarded_json['Functions']['unimplementedFunctions'][key] = value
diff --git a/src/jsifier.js b/src/jsifier.js
index 4263618a..bc89b0c4 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -513,7 +513,9 @@ function JSify(data, functionsOnly, givenFunctions) {
item.JS = addFromLibrary(shortident);
} else if (!LibraryManager.library.hasOwnProperty(shortident + '__inline')) {
item.JS = 'var ' + item.ident + '; // stub for ' + item.ident;
- if (WARN_ON_UNDEFINED_SYMBOLS || ASM_JS) { // always warn on undefs in asm, since it breaks validation
+ if (ASM_JS) {
+ throw 'Unresolved symbol: ' + item.ident + ', this must be corrected for asm.js validation to succeed. Consider adding it to DEAD_FUNCTIONS.';
+ } else if (WARN_ON_UNDEFINED_SYMBOLS) {
warn('Unresolved symbol: ' + item.ident);
}
}
@@ -722,6 +724,7 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
} // otherwise, should have been set before!
if (func.setjmpTable) {
+ assert(!ASM_JS, 'asm.js mode does not support setjmp yet');
var setjmpTable = {};
ret += indent + 'var mySetjmpIds = {};\n';
ret += indent + 'var setjmpTable = {';
diff --git a/src/library.js b/src/library.js
index 45187d8d..51921541 100644
--- a/src/library.js
+++ b/src/library.js
@@ -2638,7 +2638,7 @@ LibraryManager.library = {
}
return fields;
},
- // Performs prtinf-style formatting.
+ // Performs printf-style formatting.
// format: A pointer to the format string.
// varargs: A pointer to the start of the arguments list.
// Returns the resulting string string as a character array.
@@ -2821,7 +2821,9 @@ LibraryManager.library = {
} else if (next == {{{ charCode('x') }}} || next == {{{ charCode('X') }}}) {
prefix = flagAlternative ? '0x' : '';
#if PRECISE_I64_MATH
- if (argSize == 8 && i64Math) argText = (origArg[1]>>>0).toString(16) + (origArg[0]>>>0).toString(16); else
+ if (argSize == 8 && i64Math) {
+ argText = (origArg[1] ? (origArg[1]>>>0).toString(16) : '') + (origArg[0]>>>0).toString(16);
+ } else
#endif
if (currArg < 0) {
// Represent negative numbers in hex as 2's complement.
diff --git a/src/library_gl.js b/src/library_gl.js
index 9e12e4ee..031f4560 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -58,7 +58,12 @@ var LibraryGL = {
return ret;
},
- // Temporary buffers
+ // Mini temp buffer
+ MINI_TEMP_BUFFER_SIZE: 16,
+ miniTempBuffer: null,
+ miniTempBufferViews: [0], // index i has the view of size i+1
+
+ // Large temporary buffers
MAX_TEMP_BUFFER_SIZE: {{{ GL_MAX_TEMP_BUFFER_SIZE }}},
tempBufferIndexLookup: null,
tempVertexBuffers: null,
@@ -311,6 +316,11 @@ var LibraryGL = {
if (!Module.useWebGL) return; // an app might link both gl and 2d backends
+ GL.miniTempBuffer = new Float32Array(GL.MINI_TEMP_BUFFER_SIZE);
+ for (var i = 0; i < GL.MINI_TEMP_BUFFER_SIZE; i++) {
+ GL.miniTempBufferViews[i] = GL.miniTempBuffer.subarray(0, i+1);
+ }
+
GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS);
#if FULL_ES2
for (var i = 0; i < GL.maxVertexAttribs; i++) {
@@ -832,53 +842,108 @@ var LibraryGL = {
glUniform1fv__sig: 'viii',
glUniform1fv: function(location, count, value) {
location = GL.uniforms[location];
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniform1fv(location, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform
+ view = GL.miniTempBufferViews[0];
+ view[0] = {{{ makeGetValue('value', '0', 'float') }}};
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
+ }
+ Module.ctx.uniform1fv(location, view);
},
glUniform2fv__sig: 'viii',
glUniform2fv: function(location, count, value) {
location = GL.uniforms[location];
- count *= 2;
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniform2fv(location, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform
+ view = GL.miniTempBufferViews[1];
+ view[0] = {{{ makeGetValue('value', '0', 'float') }}};
+ view[1] = {{{ makeGetValue('value', '4', 'float') }}};
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*8') }}};
+ }
+ Module.ctx.uniform2fv(location, view);
},
glUniform3fv__sig: 'viii',
glUniform3fv: function(location, count, value) {
location = GL.uniforms[location];
- count *= 3;
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniform3fv(location, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform
+ view = GL.miniTempBufferViews[2];
+ view[0] = {{{ makeGetValue('value', '0', 'float') }}};
+ view[1] = {{{ makeGetValue('value', '4', 'float') }}};
+ view[2] = {{{ makeGetValue('value', '8', 'float') }}};
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*12') }}};
+ }
+ Module.ctx.uniform3fv(location, view);
},
glUniform4fv__sig: 'viii',
glUniform4fv: function(location, count, value) {
location = GL.uniforms[location];
- count *= 4;
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniform4fv(location, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform
+ view = GL.miniTempBufferViews[3];
+ view[0] = {{{ makeGetValue('value', '0', 'float') }}};
+ view[1] = {{{ makeGetValue('value', '4', 'float') }}};
+ view[2] = {{{ makeGetValue('value', '8', 'float') }}};
+ view[3] = {{{ makeGetValue('value', '12', 'float') }}};
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*16') }}};
+ }
+ Module.ctx.uniform4fv(location, view);
},
glUniformMatrix2fv: function(location, count, transpose, value) {
location = GL.uniforms[location];
- count *= 4;
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniformMatrix2fv(location, transpose, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform matrix
+ view = GL.miniTempBufferViews[3];
+ for (var i = 0; i < 4; i++) {
+ view[i] = {{{ makeGetValue('value', 'i*4', 'float') }}};
+ }
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*16') }}};
+ }
+ Module.ctx.uniformMatrix2fv(location, transpose, view);
},
glUniformMatrix3fv: function(location, count, transpose, value) {
location = GL.uniforms[location];
- count *= 9;
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniformMatrix3fv(location, transpose, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform matrix
+ view = GL.miniTempBufferViews[8];
+ for (var i = 0; i < 9; i++) {
+ view[i] = {{{ makeGetValue('value', 'i*4', 'float') }}};
+ }
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*36') }}};
+ }
+ Module.ctx.uniformMatrix3fv(location, transpose, view);
},
glUniformMatrix4fv: function(location, count, transpose, value) {
location = GL.uniforms[location];
- count *= 16;
- value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
- Module.ctx.uniformMatrix4fv(location, transpose, value);
+ var view;
+ if (count == 1) {
+ // avoid allocation for the common case of uploading one uniform matrix
+ view = GL.miniTempBufferViews[15];
+ for (var i = 0; i < 16; i++) {
+ view[i] = {{{ makeGetValue('value', 'i*4', 'float') }}};
+ }
+ } else {
+ view = {{{ makeHEAPView('F32', 'value', 'value+count*64') }}};
+ }
+ Module.ctx.uniformMatrix4fv(location, transpose, view);
},
glBindBuffer__sig: 'vii',
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 9fc979d2..77305609 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -443,6 +443,23 @@ var LibrarySDL = {
return false;
},
+ offsetsTemp: { left: 0, top: 0 }, // temporary object to avoid generating garbage in offsets(). assumes the object is not captured
+
+ offsets: function(element) {
+ var left = 0;
+ var top = 0;
+
+ do {
+ left += element.offsetLeft;
+ top += element.offsetTop;
+ } while (element = element.offsetParent)
+
+ var ret = SDL.offsetsTemp;
+ ret.left = left;
+ ret.top = top;
+ return ret;
+ },
+
makeCEvent: function(event, ptr) {
if (typeof event === 'number') {
// This is a pointer to a native C event that was SDL_PushEvent'ed
@@ -524,8 +541,9 @@ var LibrarySDL = {
} else {
// Otherwise, calculate the movement based on the changes
// in the coordinates.
- var x = event.pageX - Module["canvas"].offsetLeft;
- var y = event.pageY - Module["canvas"].offsetTop;
+ var offsets = SDL.offsets(Module["canvas"]);
+ var x = event.pageX - offsets.left;
+ var y = event.pageY - offsets.top;
var movementX = x - SDL.mouseX;
var movementY = y - SDL.mouseY;
}
@@ -912,10 +930,11 @@ var LibrarySDL = {
SDL_WarpMouse: function(x, y) {
return; // TODO: implement this in a non-buggy way. Need to keep relative mouse movements correct after calling this
+ var offsets = SDL.offsets(Module["canvas"]);
SDL.events.push({
type: 'mousemove',
- pageX: x + Module['canvas'].offsetLeft,
- pageY: y + Module['canvas'].offsetTop
+ pageX: x + offsets.left,
+ pageY: y + offsets.top
});
},
diff --git a/src/preamble.js b/src/preamble.js
index 7538b19c..a1755798 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -503,6 +503,7 @@ function allocate(slab, types, allocator, ptr) {
Module['allocate'] = allocate;
function Pointer_stringify(ptr, /* optional */ length) {
+#if UTF_STRING_SUPPORT
var utf8 = new Runtime.UTF8Processor();
var nullTerminated = typeof(length) == "undefined";
var ret = "";
@@ -519,18 +520,16 @@ function Pointer_stringify(ptr, /* optional */ length) {
if (!nullTerminated && i == length) break;
}
return ret;
+#else
+#if USE_TYPED_ARRAYS == 2
+ return String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + (length || _strlen(ptr))));
+#else
+ throw 'unsupported combination';
+#endif
+#endif
}
Module['Pointer_stringify'] = Pointer_stringify;
-function Array_stringify(array) {
- var ret = "";
- for (var i = 0; i < array.length; i++) {
- ret += String.fromCharCode(array[i]);
- }
- return ret;
-}
-Module['Array_stringify'] = Array_stringify;
-
// Memory management
var PAGE_SIZE = 4096;
@@ -557,7 +556,7 @@ function enlargeMemory() {
#if ASM_JS == 0
abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
#else
- abort('Cannot enlarge memory arrays in asm.js. Compile with -s TOTAL_MEMORY=X with X higher than the current value.');
+ abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, or (2) set Module.TOTAL_MEMORY before the program runs.');
#endif
#else
// TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top.
diff --git a/src/settings.js b/src/settings.js
index 101c403c..37c594ac 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -171,6 +171,8 @@ var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL e
var FULL_ES2 = 0; // Forces support for all GLES2 features, not just the WebGL-friendly subset.
var FORCE_GL_EMULATION = 0; // Forces inclusion of full GL emulation code.
+var UTF_STRING_SUPPORT = 1; // Perform utf-8 conversion between C and JS strings (adds overhead in such conversions)
+
var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you
// are compiling does not actually rely on catching exceptions (but the
// compiler generates code for it, maybe because of stdlibc++ stuff),
@@ -224,19 +226,22 @@ var NAMED_GLOBALS = 0; // If 1, we use global variables for globals. Otherwise
// they are referred to by a base plus an offset (called an indexed global),
// saving global variables but adding runtime overhead.
-var EXPORT_ALL = 0; // If true, we export all the symbols
var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported. These functions are kept alive
// through LLVM dead code elimination, and also made accessible outside of
// the generated code even after running closure compiler (on "Module").
// Note the necessary prefix of "_".
+var EXPORT_ALL = 0; // If true, we export all the symbols
+var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This
+ // is necessary to use the bindings generator with asm.js
-var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', '$Browser']; // JS library functions (C functions implemented in JS)
- // that we include by default. If you want to make sure
- // something is included by the JS compiler, add it here.
- // For example, if you do not use some emscripten_*
- // C API call from C, but you want to call it from JS,
- // add it here (and in EXPORTED FUNCTIONS with prefix
- // "_", for closure).
+// JS library functions (C functions implemented in JS)
+// that we include by default. If you want to make sure
+// something is included by the JS compiler, add it here.
+// For example, if you do not use some emscripten_*
+// C API call from C, but you want to call it from JS,
+// add it here (and in EXPORTED FUNCTIONS with prefix
+// "_", for closure).
+var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', 'strlen', '$Browser'];
var LIBRARY_DEPS_TO_AUTOEXPORT = ['memcpy']; // This list is also used to determine
// auto-exporting of library dependencies (i.e., functions that
diff --git a/tests/runner.py b/tests/runner.py
index 2ffc55ec..9ca444fb 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -5253,6 +5253,41 @@ at function.:blag
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
+ def test_vsnprintf(self):
+ src = r'''
+ #include <stdio.h>
+ #include <stdarg.h>
+ #include <stdint.h>
+
+ void printy(const char *f, ...)
+ {
+ char buffer[256];
+ va_list args;
+ va_start(args, f);
+ vsnprintf(buffer, 256, f, args);
+ puts(buffer);
+ va_end(args);
+ }
+
+ int main(int argc, char **argv) {
+ int64_t x = argc - 1;
+ int64_t y = argc - 1 + 0x400000;
+ if (x % 3 == 2) y *= 2;
+
+ printy("0x%llx_0x%llx", x, y);
+ printy("0x%llx_0x%llx", x, x);
+ printy("0x%llx_0x%llx", y, x);
+ printy("0x%llx_0x%llx", y, y);
+
+ return 0;
+ }
+ '''
+ self.do_run(src, '''0x0_0x400000
+0x0_0x0
+0x400000_0x0
+0x400000_0x400000
+''')
+
def test_printf_more(self):
src = r'''
#include <stdio.h>
@@ -6077,6 +6112,8 @@ def process(filename):
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run_and_checks)
def test_utf(self):
+ if self.emcc_args and 'UTF_STRING_SUPPORT=0' in self.emcc_args: return self.skip('need utf support')
+
self.banned_js_engines = [SPIDERMONKEY_ENGINE] # only node handles utf well
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
@@ -7887,7 +7924,8 @@ def process(filename):
def test_scriptaclass(self):
if self.emcc_args is None: return self.skip('requires emcc')
- if Settings.ASM_JS: return self.skip('asm does not bindings generator yet')
+
+ Settings.EXPORT_BINDINGS = 1
header_filename = os.path.join(self.get_dir(), 'header.h')
header = '''
@@ -8062,6 +8100,7 @@ def process(filename):
Module.Child2.prototype.runVirtualFunc(c2);
c2.virtualFunc2();
+''' + ('' if Settings.ASM_JS else '''
// extend the class from JS
var c3 = new Module.Child2;
Module.customizeVTable(c3, [{
@@ -8082,6 +8121,7 @@ def process(filename):
c2.virtualFunc(); // original should remain the same
Module.Child2.prototype.runVirtualFunc(c2);
c2.virtualFunc2();
+''') + '''
Module.print('*ok*');
\'\'\'
@@ -8120,7 +8160,7 @@ Child2:9
*static*
*virtualf*
*virtualf*
-*virtualf2*
+*virtualf2*''' + ('' if Settings.ASM_JS else '''
Parent:9
Child2:9
*js virtualf replacement*
@@ -8128,13 +8168,14 @@ Child2:9
*js virtualf2 replacement*
*virtualf*
*virtualf*
-*virtualf2*
+*virtualf2*''') + '''
*ok*
''', post_build=[post2, post3])
def test_scriptaclass_2(self):
if self.emcc_args is None: return self.skip('requires emcc')
- if Settings.ASM_JS: return self.skip('asm does not bindings generator yet')
+
+ Settings.EXPORT_BINDINGS = 1
header_filename = os.path.join(self.get_dir(), 'header.h')
header = '''
@@ -8751,7 +8792,7 @@ TT = %s
# asm.js
exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1"])')
- exec('asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1", "-g", "-s", "ASSERTIONS=1"])')
+ exec('asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1", "-g", "-s", "ASSERTIONS=1", "-s", "UTF_STRING_SUPPORT=0"])')
# Make custom runs with various options
for compiler, quantum, embetter, typed_arrays, llvm_opts in [
@@ -9288,7 +9329,7 @@ f.close()
filename = self.in_dir('src.cpp')
open(filename, 'w').write(src)
out, err = Popen([PYTHON, EMCC, filename, '-s', 'ASM_JS=1', '-O2'], stderr=PIPE).communicate()
- assert 'Warning: Unresolved symbol' in err, 'always warn on undefs in asm, since it breaks validation'
+ assert 'Unresolved symbol' in err, 'always warn on undefs in asm, since it breaks validation: ' + err
def test_redundant_link(self):
lib = "int mult() { return 1; }"
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py
index d2c165a2..d610ab54 100755
--- a/tools/bindings_generator.py
+++ b/tools/bindings_generator.py
@@ -62,6 +62,10 @@ Notes:
string on the *stack*. The C string will live for the current
function call. If you need it for longer, you need to create a copy
in your C++ code.
+
+ * You should compile with -s EXPORT_BINDINGS=1 in order for the
+ autogenerated bindings functions to be exported. This is necessary
+ when compiling in asm.js mode.
'''
import os, sys, glob, re
@@ -464,6 +468,7 @@ Module['getClass'] = getClass;
function customizeVTable(object, replacementPairs) {
// Does not handle multiple inheritance
+ // Does not work with asm.js
// Find out vtable size
var vTable = getValue(object.ptr, 'void*');
diff --git a/tools/shared.py b/tools/shared.py
index 81a8e053..7fc0421a 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -555,6 +555,7 @@ class Settings:
# Aside from these, -O3 also runs closure compiler and llvm lto
Settings.DOUBLE_MODE = 0
Settings.PRECISE_I64_MATH = 0
+ Settings.UTF_STRING_SUPPORT = 0
if noisy: print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)'
global Settings