From cad88b317c1ffbbc397b86ca4b65cb837c9c9ea7 Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Tue, 27 May 2014 11:36:57 -0700 Subject: [PATCH 01/12] proper error message for linkable modules not supported in fastcomp --- emcc | 1 + 1 file changed, 1 insertion(+) diff --git a/emcc b/emcc index ae9423cd944e9..342af478564d4 100755 --- a/emcc +++ b/emcc @@ -1272,6 +1272,7 @@ try: 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 From a9798715cf9414f4656b349c7f7f9ad2db1b2bd2 Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Tue, 27 May 2014 13:50:45 -0700 Subject: [PATCH 02/12] testcase for #2314 --- tests/cases/floatundefinvoke_fastcomp.ll | 30 +++++++++++++++++++++++ tests/cases/floatundefinvoke_fastcomp.txt | 3 +++ 2 files changed, 33 insertions(+) create mode 100644 tests/cases/floatundefinvoke_fastcomp.ll create mode 100644 tests/cases/floatundefinvoke_fastcomp.txt diff --git a/tests/cases/floatundefinvoke_fastcomp.ll b/tests/cases/floatundefinvoke_fastcomp.ll new file mode 100644 index 0000000000000..215506ef0f980 --- /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 0000000000000..5e19391eeffe8 --- /dev/null +++ b/tests/cases/floatundefinvoke_fastcomp.txt @@ -0,0 +1,3 @@ +float: 1.000000 +float: 0.000000 +float: 0.000000 From deaf15da8488537086332cc5c64c4bf0bbf49460 Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Tue, 27 May 2014 14:58:31 -0700 Subject: [PATCH 03/12] clean up shell code using uglify, in optimized builds, when not using closure --- emcc | 7 +++++-- tests/test_other.py | 6 +++++- tools/js_optimizer.py | 28 +++++++++++++++++++--------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/emcc b/emcc index 342af478564d4..be9879804ce8d 100755 --- a/emcc +++ b/emcc @@ -1794,8 +1794,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/tests/test_other.py b/tests/test_other.py index 12dd7872ec5e0..03859a4e75e9f 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -203,6 +203,10 @@ def test_emcc(self): (['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), + ([], lambda generated: '// The Module object' in generated, 'without opts, comments in shell code'), + (['-O2'], lambda generated: '// The Module object' not in generated, 'with opts, no comments in shell code'), + (['-O2', '-g2'], lambda generated: '// The Module object' not in generated, 'with -g2, no comments in shell code'), + (['-O2', '-g3'], lambda generated: '// The Module object' in generated, 'with -g3, yes comments in shell code'), ]: print params, text self.clear() @@ -2218,7 +2222,7 @@ def test_module_exports_with_closure(self): test_js_closure_0 = open(path_from_root('tests', 'Module-exports', 'test.js')).read() # Check that test.js compiled with --closure 0 contains "module['exports'] = Module;" - assert "module['exports'] = Module;" in test_js_closure_0 + assert ("module['exports'] = Module;" in test_js_closure_0) or ('module["exports"]=Module' in test_js_closure_0) # Check that main.js (which requires test.js) completes successfully when run in node.js # in order to check that the exports are indeed functioning correctly. diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index d02845281efa2..e06c2d2f57fe3 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -137,6 +137,10 @@ def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info= if closure: passes = filter(lambda p: p != 'closure', passes) # we will do it manually + cleanup = 'cleanup' in passes + if cleanup: + passes = filter(lambda p: p != 'cleanup', passes) # we will do it manually + if not know_generated and jcache: # JCache cannot be used without metadata, since it might reorder stuff, and that's dangerous since only generated can be reordered # This means jcache does not work after closure compiler runs, for example. But you won't get much benefit from jcache with closure @@ -291,23 +295,29 @@ def write_chunk(chunk, i): for filename in filenames: temp_files.note(filename) - if closure: - # run closure on the shell code, everything but what we js-optimize + if closure or cleanup: + # run on the shell code, everything but what we js-optimize start_asm = '// EMSCRIPTEN_START_ASM\n' end_asm = '// EMSCRIPTEN_END_ASM\n' - closure_sep = 'wakaUnknownBefore(); var asm=wakaUnknownAfter(global,env,buffer)\n' + cl_sep = 'wakaUnknownBefore(); var asm=wakaUnknownAfter(global,env,buffer)\n' - closuree = temp_files.get('.closure.js').name - c = open(closuree, 'w') + cle = temp_files.get('.cl.js').name + c = open(cle, 'w') pre_1, pre_2 = pre.split(start_asm) post_1, post_2 = post.split(end_asm) c.write(pre_1) - c.write(closure_sep) + c.write(cl_sep) c.write(post_2) c.close() - closured = shared.Building.closure_compiler(closuree, pretty='minifyWhitespace' not in passes) - temp_files.note(closured) - coutput = open(closured).read() + if closure: + if DEBUG: print >> sys.stderr, 'running closure on shell code' + cld = shared.Building.closure_compiler(cle, pretty='minifyWhitespace' not in passes) + else: + if DEBUG: print >> sys.stderr, 'running cleanup on shell code' + cld = cle + '.js' + subprocess.Popen(js_engine + [JS_OPTIMIZER, cle, 'noPrintMetadata'] + (['minifyWhitespace'] if 'minifyWhitespace' in passes else []), stdout=open(cld, 'w')).communicate() + temp_files.note(cld) + coutput = open(cld).read() coutput = coutput.replace('wakaUnknownBefore();', '') after = 'wakaUnknownAfter' start = coutput.find(after) From afd5a42acfb5885bcb48aae1a5114396f5e54481 Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Tue, 27 May 2014 16:22:37 -0700 Subject: [PATCH 04/12] fix crash in audio code in bananabread --- src/library_sdl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_sdl.js b/src/library_sdl.js index fd8c686063156..a01b3c6ca1163 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. From e792dc4e6aea8bd4b95eada40f85e8c0598dfc3d Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Tue, 27 May 2014 17:20:12 -0700 Subject: [PATCH 05/12] fix ccall regex for minified code, and add testing --- src/preamble.js | 2 +- tests/test_core.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/preamble.js b/src/preamble.js index fbce6b6b65b1d..58b442abffd5a 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -396,7 +396,7 @@ var cwrap, ccall; return ret; } - var sourceRegex = /^function\s\((.*)\)\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); diff --git a/tests/test_core.py b/tests/test_core.py index 46d3964c6d05f..7e15f1b8a6d0a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5453,9 +5453,6 @@ def test_corruption_3(self): ### Integration tests def test_ccall(self): - if self.emcc_args is not None and '-O2' in self.emcc_args: - self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right - post = ''' def process(filename): src = \'\'\' @@ -5499,6 +5496,11 @@ def process(filename): self.do_run_from_file(src, output, post_build=post) + if self.emcc_args is not None and '-O2' in self.emcc_args: + print 'with closure' + self.emcc_args += ['--closure', '1'] + self.do_run_from_file(src, output, post_build=post) + def test_pgo(self): if Settings.ASM_JS: return self.skip('PGO does not work in asm mode') From a2aef0f9212b0bc147aeb850c398b52ae98eaa3e Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Wed, 28 May 2014 10:05:08 -0700 Subject: [PATCH 06/12] fix asm2.test_memorygrowth --- tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 7e15f1b8a6d0a..1d04ebcb32650 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2068,10 +2068,10 @@ def test_memorygrowth(self): if self.emcc_args and '-O2' in self.emcc_args: # Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized) - code_start = 'var TOTAL_MEMORY = ' + code_start = 'var TOTAL_MEMORY' fail = fail[fail.find(code_start):] win = win[win.find(code_start):] - assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + str([len(fail), len(win)]) def test_ssr(self): # struct self-ref src = ''' From 079a9b3e1f317d8e96602a44be4569d670905930 Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Wed, 28 May 2014 10:09:07 -0700 Subject: [PATCH 07/12] fix slow2asm.test_dlfcn_self --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 1d04ebcb32650..281a3a0bead7b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3244,7 +3244,7 @@ def post(filename): break else: raise Exception('Could not find symbol table!') - table = table[table.find('{'):table.rfind('}')+1] + table = table[table.find('{'):table.find('}')+1] # ensure there aren't too many globals; we don't want unnamed_addr assert table.count(',') <= 4 From a0bb46803b6d84f8105dbc254599be8cb6b9f98d Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Wed, 28 May 2014 13:16:58 -0700 Subject: [PATCH 08/12] fix case of exceptions whitelist being empty --- emcc | 4 +-- .../core/test_exceptions_white_list_empty.out | 0 tests/test_core.py | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/core/test_exceptions_white_list_empty.out diff --git a/emcc b/emcc index be9879804ce8d..e55990dfbcabb 100755 --- a/emcc +++ b/emcc @@ -1289,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' 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 0000000000000..e69de29bb2d1d diff --git a/tests/test_core.py b/tests/test_core.py index 281a3a0bead7b..62a061e27eeff 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1347,6 +1347,33 @@ def test_exceptions_white_list(self): test_path = path_from_root('tests', 'core', 'test_exceptions_white_list') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) + size = len(open('src.cpp.o.js').read()) + shutil.copyfile('src.cpp.o.js', 'orig.js') + + if os.environ.get('EMCC_FAST_COMPILER') != '0': + # check that an empty whitelist works properly (as in, same as exceptions disabled) + empty_output = path_from_root('tests', 'core', 'test_exceptions_white_list_empty.out') + + Settings.EXCEPTION_CATCHING_WHITELIST = [] + self.do_run_from_file(src, empty_output) + empty_size = len(open('src.cpp.o.js').read()) + shutil.copyfile('src.cpp.o.js', 'empty.js') + + Settings.EXCEPTION_CATCHING_WHITELIST = ['fake'] + self.do_run_from_file(src, empty_output) + fake_size = len(open('src.cpp.o.js').read()) + shutil.copyfile('src.cpp.o.js', 'fake.js') + + Settings.DISABLE_EXCEPTION_CATCHING = 1 + self.do_run_from_file(src, empty_output) + disabled_size = len(open('src.cpp.o.js').read()) + shutil.copyfile('src.cpp.o.js', 'disabled.js') + + assert size - empty_size > 2000, [empty_size, size] # big change when we disable entirely + assert size - fake_size > 2000, [fake_size, size] + assert empty_size == fake_size, [empty_size, fake_size] + assert empty_size - disabled_size < 100, [empty_size, disabled_size] # full disable removes a tiny bit more + assert fake_size - disabled_size < 100, [disabled_size, fake_size] def test_exceptions_white_list_2(self): Settings.DISABLE_EXCEPTION_CATCHING = 2 From 2bafe8167cf4af7318b0b7183bad993d4ae45fd0 Mon Sep 17 00:00:00 2001 From: Jason Green <jason@transgaming.com> Date: Tue, 27 May 2014 15:51:54 -0500 Subject: [PATCH 09/12] glTex[Sub]Image* should not throw an exception, but should cause a GL_INVALID_ENUM error on unrecognized formats or types --- AUTHORS | 1 + src/library_gl.js | 47 ++++++++++++++--- tests/gl_teximage.c | 120 ++++++++++++++++++++++++++++++++++++++++++ tests/test_browser.py | 3 ++ 4 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 tests/gl_teximage.c diff --git a/AUTHORS b/AUTHORS index 7994f80e26ba4..65354d07e7891 100644 --- a/AUTHORS +++ b/AUTHORS @@ -138,3 +138,4 @@ a license to everyone to use it as detailed in LICENSE.) * Guillaume Blanc <guillaumeblanc.sc@gmail.com> * Usagi Ito <usagi@WonderRabbitProject.net> * Camilo Polymeris <cpolymeris@gmail.com> +* Jason Green <jason@transgaming.com> (copyright owned by TransGaming, Inc.) diff --git a/src/library_gl.js b/src/library_gl.js index 851b01b182880..2659a9d985f10 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/tests/gl_teximage.c b/tests/gl_teximage.c new file mode 100644 index 0000000000000..9cafce9c7076e --- /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() + SDL_Quit(); + + exit_with_status(passed); +} diff --git a/tests/test_browser.py b/tests/test_browser.py index aedc926a66631..c8e07b252a96a 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1425,6 +1425,9 @@ def test_glgetattachedshaders(self): def test_sdlglshader(self): self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1', '-s', 'LEGACY_GL_EMULATION=1']) + def test_gl_glteximage(self): + self.btest('gl_teximage.c', '1') + def test_gl_ps(self): # pointers and a shader shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) From 504b62384aba8da8258ed6f8ba0a455a1db7736a Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Wed, 28 May 2014 14:50:35 -0700 Subject: [PATCH 10/12] add emscripten_align typedefs for #2378 --- system/include/emscripten/emscripten.h | 26 ++++++++++++++ tests/core/test_set_align.c | 50 ++++++++++++++++++++++++++ tests/core/test_set_align.out | 8 +++++ tests/test_core.py | 9 +++++ 4 files changed, 93 insertions(+) create mode 100644 tests/core/test_set_align.c create mode 100644 tests/core/test_set_align.out diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 66017a8ddf5b0..8a08aabb87695 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/core/test_set_align.c b/tests/core/test_set_align.c new file mode 100644 index 0000000000000..26158ef428eba --- /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 0000000000000..55e377b028ab2 --- /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/test_core.py b/tests/test_core.py index 62a061e27eeff..505a051bdb310 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1980,6 +1980,15 @@ def test_llvm_used(self): self.do_run_from_file(src, output) + def test_set_align(self): + if self.run_name == 'slow2asm': return self.skip('FIXME in slow2asm') + + Settings.SAFE_HEAP = 1 + + test_path = path_from_root('tests', 'core', 'test_set_align') + src, output = (test_path + s for s in ('.c', '.out')) + self.do_run_from_file(src, output) + def test_emscripten_api(self): #if Settings.MICRO_OPTS or Settings.RELOOP or Building.LLVM_OPTS: return self.skip('FIXME') From 34599322eea3c9d2240c1c09d0f148f95612ed5a Mon Sep 17 00:00:00 2001 From: Chad Austin <chad@imvu.com> Date: Wed, 28 May 2014 11:20:50 -0700 Subject: [PATCH 11/12] Fix a bug where a returned handle to a derived JS object would not always correctly increment the reference count of the underlying smart pointer. --- src/embind/embind.js | 15 ++++++++++++--- tests/embind/embind.test.js | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 124ea5698ef16..8c8d73ad52e52 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/tests/embind/embind.test.js b/tests/embind/embind.test.js index a6b2e98ccae5f..432202ff3c28e 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(); }); }); From 4ae305542c80f31b06c5e8325c63ade2bb4a3f33 Mon Sep 17 00:00:00 2001 From: Alon Zakai <alonzakai@gmail.com> Date: Thu, 29 May 2014 13:44:16 -0700 Subject: [PATCH 12/12] 1.19.0 --- emscripten-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten-version.txt b/emscripten-version.txt index ca3ca78e64f82..c6dc663de916f 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.18.4 +1.19.0