diff options
32 files changed, 515 insertions, 90 deletions
@@ -141,4 +141,5 @@ a license to everyone to use it as detailed in LICENSE.) * Markus Henschel <markus.henschel@yager.de> * Ophir Lojkine <ophir.lojkine@eleves.ec-nantes.fr> * Ryan Sturgell <ryan.sturgell@gmail.com> (copyright owned by Google, Inc.) +* Jason Green <jason@transgaming.com> (copyright owned by TransGaming, Inc.) @@ -64,6 +64,7 @@ DYNAMICLIB_ENDINGS = ('.dylib', '.so', '.dll') STATICLIB_ENDINGS = ('.a',) ASSEMBLY_ENDINGS = ('.ll',) HEADER_ENDINGS = ('.h', '.hxx', '.hpp', '.hh', '.H', '.HXX', '.HPP', '.HH') +SUPPORTED_LINKER_FLAGS = ('--start-group', '-(', '--end-group', '-)') LIB_PREFIXES = ('', 'lib') @@ -1177,7 +1178,12 @@ try: # (4, a), (4.25, b), (4.5, c), (4.75, d) link_flags_to_add = arg.split(',')[1:] for flag_index, flag in enumerate(link_flags_to_add): - link_flags.append((i + float(flag_index) / len(link_flags_to_add), flag)) + # Only keep flags that shared.Building.link knows how to deal with. + # We currently can't handle flags with options (like + # -Wl,-rpath,/bin:/lib, where /bin:/lib is an option for the -rpath + # flag). + if flag in SUPPORTED_LINKER_FLAGS: + link_flags.append((i + float(flag_index) / len(link_flags_to_add), flag)) newargs[i] = '' original_input_files = input_files[:] @@ -1263,10 +1269,10 @@ try: assert shared.Settings.TARGET_ASMJS_UNKNOWN_EMSCRIPTEN == 1, 'fastcomp requires asmjs-unknown-emscripten' assert shared.Settings.USE_TYPED_ARRAYS == 2, 'fastcomp assumes ta2' assert not split_js_file, '--split-js is deprecated and not supported in fastcomp' - assert shared.Settings.MAX_SETJMPS == 20, 'changing MAX_SETJMPS is not supported in fastcomp yet' assert shared.Settings.INIT_HEAP == 0, 'HEAP_INIT is not supported in fastcomp (and should never be needed except for debugging)' assert not shared.Settings.RUNTIME_TYPE_INFO, 'RUNTIME_TYPE_INFO is not supported in fastcomp' assert not shared.Settings.CORRUPTION_CHECK, 'CORRUPTION_CHECK is not supported in asm.js mode, which is what fastcomp can emit (you can use non-asm.js mode in non-fastcomp)' + assert not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE, 'Linking modules is not supported in fastcomp' except Exception, e: logging.error('Compiler settings are incompatible with fastcomp. You can fall back to the older compiler core, although that is not recommended, see https://github.com/kripken/emscripten/wiki/LLVM-Backend') raise e @@ -1283,8 +1289,8 @@ try: fastcomp_opts += ['-pnacl-abi-simplify-preopt', '-pnacl-abi-simplify-postopt'] if shared.Settings.DISABLE_EXCEPTION_CATCHING != 1: fastcomp_opts += ['-enable-emscripten-cxx-exceptions'] - if len(shared.Settings.EXCEPTION_CATCHING_WHITELIST) > 0: - fastcomp_opts += ['-emscripten-cxx-exceptions-whitelist=' + ','.join(shared.Settings.EXCEPTION_CATCHING_WHITELIST)] + if shared.Settings.DISABLE_EXCEPTION_CATCHING == 2: + fastcomp_opts += ['-emscripten-cxx-exceptions-whitelist=' + ','.join(shared.Settings.EXCEPTION_CATCHING_WHITELIST or ['fake'])] if shared.Settings.ASM_JS: assert opt_level >= 1 or fastcomp, 'asm.js requires -O1 or above' @@ -1314,6 +1320,8 @@ try: logging.warning('disabling closure because debug info was requested') closure = False + assert not (shared.Settings.NO_DYNAMIC_EXECUTION and closure), 'cannot have both NO_DYNAMIC_EXECUTION and closure compiler enabled at the same time' + if closure: shared.Settings.CLOSURE_COMPILER = 1 assert os.path.exists(shared.CLOSURE_COMPILER), logging.error('fatal: Closure compiler (%s) does not exist', shared.CLOSURE_COMPILER) @@ -1788,8 +1796,11 @@ try: if emit_symbol_map: js_optimizer_queue += ['symbolMap='+target+'.symbols'] if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] - if closure and shared.Settings.ASM_JS: - js_optimizer_queue += ['closure'] + if shared.Settings.ASM_JS: + if closure: + js_optimizer_queue += ['closure'] + elif debug_level <= 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE: + js_optimizer_queue += ['cleanup'] if not shared.Settings.SIDE_MODULE: js_optimizer_queue += ['last'] # side modules are not finalized until after relocation diff --git a/emscripten-version.txt b/emscripten-version.txt index 18abf253..c6dc663d 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.18.3 +1.19.0 diff --git a/emscripten.py b/emscripten.py index 9abaf60c..98877a8e 100755 --- a/emscripten.py +++ b/emscripten.py @@ -761,6 +761,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, if settings['ALIASING_FUNCTION_POINTERS'] == 0: backend_args += ['-emscripten-no-aliasing-function-pointers'] backend_args += ['-O' + str(settings['OPT_LEVEL'])] + backend_args += ['-emscripten-max-setjmps=%d' % settings['MAX_SETJMPS']] if DEBUG: logging.debug('emscript: llvm backend: ' + ' '.join(backend_args)) t = time.time() diff --git a/src/embind/embind.js b/src/embind/embind.js index 124ea569..8c8d73ad 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1216,9 +1216,18 @@ RegisteredPointer.prototype['fromWireType'] = function fromWireType(ptr) { var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer); if (undefined !== registeredInstance) { - var rv = registeredInstance['clone'](); - this.destructor(ptr); - return rv; + // JS object has been neutered, time to repopulate it + if (0 === registeredInstance.$$.count.value) { + registeredInstance.$$.ptr = rawPointer; + registeredInstance.$$.smartPtr = ptr; + return registeredInstance['clone'](); + } else { + // else, just increment reference count on existing object + // it already has a reference to the smart pointer + var rv = registeredInstance['clone'](); + this.destructor(ptr); + return rv; + } } function makeDefaultHandle() { diff --git a/src/library.js b/src/library.js index 5119a482..120def05 100644 --- a/src/library.js +++ b/src/library.js @@ -5848,15 +5848,15 @@ LibraryManager.library = { var i = 0; setjmpId = (setjmpId+1)|0; {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; - while ((i|0) < {{{ 2*MAX_SETJMPS }}}) { - if ({{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}} == 0) { - {{{ makeSetValueAsm('table', '(i<<2)', 'setjmpId', 'i32') }}}; - {{{ makeSetValueAsm('table', '(i<<2)+4', 'label', 'i32') }}}; + while ((i|0) < {{{ MAX_SETJMPS }}}) { + if ({{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}} == 0) { + {{{ makeSetValueAsm('table', '(i<<3)', 'setjmpId', 'i32') }}}; + {{{ makeSetValueAsm('table', '(i<<3)+4', 'label', 'i32') }}}; // prepare next slot - {{{ makeSetValueAsm('table', '(i<<2)+8', '0', 'i32') }}}; + {{{ makeSetValueAsm('table', '(i<<3)+8', '0', 'i32') }}}; return 0; } - i = (i+2)|0; + i = i+1|0; } {{{ makePrintChars('too many setjmps in a function call, build with a higher value for MAX_SETJMPS') }}}; abort(0); @@ -5870,12 +5870,12 @@ LibraryManager.library = { table = table|0; var i = 0, curr = 0; while ((i|0) < {{{ MAX_SETJMPS }}}) { - curr = {{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}}; + curr = {{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}}; if ((curr|0) == 0) break; if ((curr|0) == (id|0)) { - return {{{ makeGetValueAsm('table', '(i<<2)+4', 'i32') }}}; + return {{{ makeGetValueAsm('table', '(i<<3)+4', 'i32') }}}; } - i = (i+2)|0; + i = i+1|0; } return 0; }, diff --git a/src/library_gl.js b/src/library_gl.js index 851b01b1..2659a9d9 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -431,21 +431,42 @@ var LibraryGL = { sizePerPixel = 2; break; default: - throw 'Invalid format (' + format + ')'; + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format); +#endif + return { + pixels: null, + internalFormat: 0x0 + }; } break; case 0x1403 /* GL_UNSIGNED_SHORT */: if (format == 0x1902 /* GL_DEPTH_COMPONENT */) { sizePerPixel = 2; } else { - throw 'Invalid format (' + format + ')'; + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format); +#endif + return { + pixels: null, + internalFormat: 0x0 + }; } break; case 0x1405 /* GL_UNSIGNED_INT */: if (format == 0x1902 /* GL_DEPTH_COMPONENT */) { sizePerPixel = 4; } else { - throw 'Invalid format (' + format + ')'; + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format); +#endif + return { + pixels: null, + internalFormat: 0x0 + }; } break; case 0x84FA /* UNSIGNED_INT_24_8_WEBGL */: @@ -468,12 +489,26 @@ var LibraryGL = { sizePerPixel = 4*4; break; default: - throw 'Invalid format (' + format + ')'; + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format); +#endif + return { + pixels: null, + internalFormat: 0x0 + }; } internalFormat = GLctx.RGBA; break; default: - throw 'Invalid type (' + type + ')'; + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format); +#endif + return { + pixels: null, + internalFormat: 0x0 + }; } var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); if (type == 0x1401 /* GL_UNSIGNED_BYTE */) { @@ -488,7 +523,7 @@ var LibraryGL = { return { pixels: pixels, internalFormat: internalFormat - } + }; }, #if GL_FFP_ONLY diff --git a/src/library_sdl.js b/src/library_sdl.js index fd8c6860..a01b3c6c 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -2332,7 +2332,7 @@ var LibrarySDL = { return 0; } - var arrayBuffer = bytes.buffer || bytes; + var arrayBuffer = bytes ? bytes.buffer || bytes : bytes; // To allow user code to work around browser bugs with audio playback on <audio> elements an Web Audio, enable // the user code to hook in a callback to decide on a file basis whether each file should use Web Audio or <audio> for decoding and playback. diff --git a/src/preamble.js b/src/preamble.js index c4db3d1e..431a3c27 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -312,10 +312,15 @@ var globalScope = this; // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) function getCFunc(ident) { - try { - var func = Module['_' + ident]; // closure exported function - if (!func) func = eval('_' + ident); // explicit lookup - } catch(e) { + var func = Module['_' + ident]; // closure exported function + if (!func) { +#if NO_DYNAMIC_EXECUTION == 0 + try { + func = eval('_' + ident); // explicit lookup + } catch(e) {} +#else + abort('NO_DYNAMIC_EXECUTION was set, cannot eval - ccall/cwrap are not functional'); +#endif } assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)'); return func; @@ -396,7 +401,7 @@ var cwrap, ccall; return ret; } - var sourceRegex = /^function \((.*)\)\s*{\s*([^]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/; + var sourceRegex = /^function\s\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/; function parseJSFunc(jsfunc) { // Match the body and the return value of a javascript function source var parsed = jsfunc.toString().match(sourceRegex).slice(1); @@ -458,7 +463,11 @@ var cwrap, ccall; funcstr += JSsource['stackRestore'].body + ';'; } funcstr += 'return ret})'; +#if NO_DYNAMIC_EXECUTION == 0 return eval(funcstr); +#else + abort('NO_DYNAMIC_EXECUTION was set, cannot eval - ccall is not functional'); +#endif }; })(); Module["cwrap"] = cwrap; diff --git a/src/runtime.js b/src/runtime.js index 4466a308..96b12294 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -418,12 +418,16 @@ var Runtime = { abort('invalid EM_ASM input |' + source + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)'); } } +#if NO_DYNAMIC_EXECUTION == 0 try { var evalled = eval('(function(' + args.join(',') + '){ ' + source + ' })'); // new Function does not allow upvars in node } catch(e) { Module.printErr('error in executing inline EM_ASM code: ' + e + ' on: \n\n' + source + '\n\nwith args |' + args + '| (make sure to use the right one out of EM_ASM, EM_ASM_ARGS, etc.)'); throw e; } +#else + abort('NO_DYNAMIC_EXECUTION was set, cannot eval, so EM_ASM is not functional'); +#endif return Runtime.asmConstCache[code] = evalled; }, diff --git a/src/settings.js b/src/settings.js index 3289eace..bdb149e3 100644 --- a/src/settings.js +++ b/src/settings.js @@ -502,6 +502,11 @@ var JS_CHUNK_SIZE = 10240; // Used as a maximum size before breaking up expressi var EXPORT_NAME = 'Module'; // Global variable to export the module as for environments without a standardized module // loading system (e.g. the browser and SM shell). +var NO_DYNAMIC_EXECUTION = 0; // When enabled, we do not emit eval() and new Function(), which disables some functionality + // (causing runtime errors if attempted to be used), but allows the emitted code to be + // acceptable in places that disallow dynamic code execution (chrome packaged app, non- + // privileged firefox app, etc.) + var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler var COMPILER_ASSERTIONS = 0; // costly (slow) compile-time assertions diff --git a/src/shell.js b/src/shell.js index e1c0eb54..279a3461 100644 --- a/src/shell.js +++ b/src/shell.js @@ -96,7 +96,9 @@ else if (ENVIRONMENT_IS_SHELL) { this['{{{ EXPORT_NAME }}}'] = Module; +#if CLOSURE_COMPILER eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined"); // wipe out the SpiderMonkey shell 'gc' function, which can confuse closure (uses it as a minified name, and it is then initted to a non-falsey value unexpectedly) +#endif } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { Module['read'] = function read(url) { @@ -139,7 +141,11 @@ else { } function globalEval(x) { +#if NO_DYNAMIC_EXECUTION == 0 eval.call(null, x); +#else + throw 'NO_DYNAMIC_EXECUTION was set, cannot eval'; +#endif } if (!Module['load'] == 'undefined' && Module['read']) { Module['load'] = function load(f) { diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 66017a8d..8a08aabb 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -18,6 +18,32 @@ extern "C" { #include <SDL/SDL.h> /* for SDL_Delay in async_call */ #endif + +/* Typedefs */ + +/* + * Unaligned types, helpful to force LLVM to emit unaligned + * loads/stores in places in your code where SAFE_HEAP found + * an unaligned operation. (It's better to avoid unaligned + * operations, but if you are reading from a packed stream of + * bytes or such, these types may be useful.) + */ + +typedef short __attribute__((aligned(1))) emscripten_align1_short; + +typedef int __attribute__((aligned(2))) emscripten_align2_int; +typedef int __attribute__((aligned(1))) emscripten_align1_int; + +typedef float __attribute__((aligned(2))) emscripten_align2_float; +typedef float __attribute__((aligned(1))) emscripten_align1_float; + +typedef double __attribute__((aligned(4))) emscripten_align4_double; +typedef double __attribute__((aligned(2))) emscripten_align2_double; +typedef double __attribute__((aligned(1))) emscripten_align1_double; + + +/* Functions */ + /* * Convenient syntax for inline assembly/js. Allows stuff like * diff --git a/tests/cases/floatundefinvoke_fastcomp.ll b/tests/cases/floatundefinvoke_fastcomp.ll new file mode 100644 index 00000000..215506ef --- /dev/null +++ b/tests/cases/floatundefinvoke_fastcomp.ll @@ -0,0 +1,30 @@ +; ModuleID = 'a.o' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32-S128" +target triple = "asmjs-unknown-emscripten" + +@.str = private unnamed_addr constant [11 x i8] c"float: %f\0A\00", align 1 + +define void @_Z10printFloatf(float %f) #0 { +entry: + %conv = fpext float %f to double + %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), double %conv) + ret void +} + +define i32 @main() #1 { +entry: + tail call void @_Z10printFloatf(float 1.000000e+00) + call void @emscripten_preinvoke() + call void @_Z10printFloatf(float undef) + %last = call i32 @emscripten_postinvoke() + %lastf = sitofp i32 %last to float + tail call void @_Z10printFloatf(float %lastf) + ret i32 1 +} + +declare void @emscripten_preinvoke() +declare i32 @emscripten_postinvoke() +declare i32 @printf(i8* nocapture, ...) #1 + +attributes #0 = { noinline nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } diff --git a/tests/cases/floatundefinvoke_fastcomp.txt b/tests/cases/floatundefinvoke_fastcomp.txt new file mode 100644 index 00000000..5e19391e --- /dev/null +++ b/tests/cases/floatundefinvoke_fastcomp.txt @@ -0,0 +1,3 @@ +float: 1.000000 +float: 0.000000 +float: 0.000000 diff --git a/tests/core/test_exceptions_white_list_empty.out b/tests/core/test_exceptions_white_list_empty.out new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/test_exceptions_white_list_empty.out diff --git a/tests/core/test_floatvars.in b/tests/core/test_floatvars.in index b6c94c82..d59b6028 100644 --- a/tests/core/test_floatvars.in +++ b/tests/core/test_floatvars.in @@ -15,13 +15,8 @@ int main(int argc, char **argv) { printf("small: %.10f\n", argc * 0.000001); - /* - // Rounding behavior - float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; - double ds[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; - for (int i = 0; i < 6; i++) - printf("*int(%.2f)=%d,%d*\n", fs[i], int(fs[i]), int(ds[i])); - */ + double d = 1.12345678901234567890123e21; + printf("double: %f\n", d); return 0; } diff --git a/tests/core/test_floatvars.out b/tests/core/test_floatvars.out index 57635092..128c04ae 100644 --- a/tests/core/test_floatvars.out +++ b/tests/core/test_floatvars.out @@ -1,3 +1,4 @@ *1,10,10.5,1,1.2340,0.00* 0.50, 3.30, 3.30, 3.30 small: 0.0000010000 +double: 1.1234567890123457e+21 diff --git a/tests/core/test_set_align.c b/tests/core/test_set_align.c new file mode 100644 index 00000000..26158ef4 --- /dev/null +++ b/tests/core/test_set_align.c @@ -0,0 +1,50 @@ + +#include <stdio.h> +#include <emscripten.h> + +volatile char data[16]; + +__attribute__((noinline)) void *get_aligned(int align) +{ + char *ptr = (char*)(((int)(data + 7)) & ~7); // Make 8-byte aligned + ptr += align; // Now 'align' aligned + return (void*)ptr; +} + +int main() +{ + emscripten_align4_double *d4 = (emscripten_align4_double*)get_aligned(4); + *d4 = 17.0; + printf("addr: %d, value: %f\n", ((int)d4) % 8, *d4); + + emscripten_align2_double *d2 = (emscripten_align2_double*)get_aligned(2); + *d2 = 18.0; + printf("addr: %d, value: %f\n", ((int)d2) % 8, *d2); + + emscripten_align1_double *d1 = (emscripten_align1_double*)get_aligned(1); + *d1 = 19.0; + printf("addr: %d, value: %f\n", ((int)d1) % 8, *d1); + + emscripten_align2_float *f2 = (emscripten_align2_float*)get_aligned(2); + *f2 = 20.0; + printf("addr: %d, value: %f\n", ((int)f2) % 4, *f2); + + emscripten_align1_float *f1 = (emscripten_align1_float*)get_aligned(1); + *f1 = 21.0; + printf("addr: %d, value: %f\n", ((int)f1) % 4, *f1); + + emscripten_align2_int *i2 = (emscripten_align2_int*)get_aligned(2); + *i2 = 22; + printf("addr: %d, value: %d\n", ((int)i2) % 4, *i2); + + emscripten_align1_int *i1 = (emscripten_align1_int*)get_aligned(1); + *i1 = 23; + printf("addr: %d, value: %d\n", ((int)i1) % 4, *i1); + + emscripten_align1_short *s1 = (emscripten_align1_short*)get_aligned(1); + *s1 = 24; + printf("addr: %d, value: %d\n", ((int)s1) % 4, (int)*s1); + + return 0; +} + diff --git a/tests/core/test_set_align.out b/tests/core/test_set_align.out new file mode 100644 index 00000000..55e377b0 --- /dev/null +++ b/tests/core/test_set_align.out @@ -0,0 +1,8 @@ +addr: 4, value: 17.000000 +addr: 2, value: 18.000000 +addr: 1, value: 19.000000 +addr: 2, value: 20.000000 +addr: 1, value: 21.000000 +addr: 2, value: 22 +addr: 1, value: 23 +addr: 1, value: 24 diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index a6b2e98c..432202ff 100644 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -2410,6 +2410,7 @@ module({ var back = holder.get(); assert.equal(back, instance); holder.delete(); + back.delete(); }); }); diff --git a/tests/gl_teximage.c b/tests/gl_teximage.c new file mode 100644 index 00000000..9cafce9c --- /dev/null +++ b/tests/gl_teximage.c @@ -0,0 +1,120 @@ +/* + * GLES2 test for glTexImage2D parameters + * + * Original author: Jason Green <jason@transgaming.com> + * + */ +#include "GLES2/gl2.h" +#include "SDL/SDL.h" + +#include <stdio.h> +#include <stdlib.h> +#include <emscripten.h> +#include <unistd.h> + +typedef enum { + TEST_STATUS_SUCCESS = 0, + TEST_STATUS_FAILURE = 1 +} TestStatus; + +/* Report success or failure (1 or 0) to Emscripten's test harness. Also, exit + * with the given error code. */ +static void exit_with_status(TestStatus code) +{ +#ifdef REPORT_RESULT + int result = (code == TEST_STATUS_SUCCESS) ? 1 : 0; + REPORT_RESULT(); +#endif + + exit(code); +} + +/* Loop over all glGetError() results until GL reports GL_NO_ERROR */ +static void clear_gl_errors() +{ + GLenum err; + do { + err = glGetError(); + } while (err != GL_NO_ERROR); +} + +int main(int argc, char *argv[]) +{ + TestStatus passed = TEST_STATUS_SUCCESS; + SDL_Surface *screen; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + printf("SDL_Init failed with %s\n", SDL_GetError()); + exit_with_status(TEST_STATUS_FAILURE); + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + screen = SDL_SetVideoMode(640, 480, 16, SDL_OPENGL); + if (!screen) { + printf("SDL_SetVideoMode failed with %s\n", SDL_GetError()); + exit_with_status(TEST_STATUS_FAILURE); + } + + GLuint texture; + glGenTextures(1, &texture); + + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Allocate space for a 32x32 image with 4 bytes per pixel. + // No need to fill it with any useful information, as these tests are + // only designed to make sure glTexImage2D doesn't crash on unsupported + // formats. + void* pixels = malloc(4 * 32 * 32); + if (pixels == NULL) { + printf("Unable to allocate pixel data\n"); + exit_with_status(TEST_STATUS_FAILURE); + } + + // First, try 0xffff for the internal format - should fail + glTexImage2D(GL_TEXTURE_2D, 0, 0xffff, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GLenum err = glGetError(); + if (err == GL_NO_ERROR) { + printf("internal format == 0xffff succeeded, but should have failed\n"); + passed = TEST_STATUS_FAILURE; + } + clear_gl_errors(); + + // Try 0xffff for the format - should fail + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, 0xffff, GL_UNSIGNED_BYTE, pixels); + err = glGetError(); + if (err == GL_NO_ERROR) { + printf("format == 0xffff succeeded, but should have failed\n"); + passed = TEST_STATUS_FAILURE; + } + clear_gl_errors(); + + // Try 0xffff for the type - should fail + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, 0xffff, pixels); + err = glGetError(); + if (err == GL_NO_ERROR) { + printf("type == 0xffff succeeded, but should have failed\n"); + passed = TEST_STATUS_FAILURE; + } + clear_gl_errors(); + + // Try GL_RGBA/GL_UNSIGNED_BYTE - should succeed + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + err = glGetError(); + if (err != GL_NO_ERROR) { + printf("GL_RGBA/GL_UNSIGNED_BYTE failed with %x, but should have succeeded\n", err); + passed = TEST_STATUS_FAILURE; + } + clear_gl_errors(); + + // Clean up objects + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &texture); + free(pixels); + + // 'screen' is freed implicitly by SDL_Quit() |