From a7f53ff157b873eeb93784923c595ba12d22dfce Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 07:27:03 -0700 Subject: [PATCH 1/8] support new Module() in MODULARIZE mode, including saving the scriptDirectory --- emcc.py | 21 +++++++++-------- src/shell.js | 4 ++-- tests/test_browser.py | 55 ++++++++++++++++++++----------------------- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/emcc.py b/emcc.py index 49a0a288135e1..6dd76f1baeeab 100755 --- a/emcc.py +++ b/emcc.py @@ -2604,19 +2604,20 @@ def modularize(): final = final + '.modular.js' f = open(final, 'w') - # Included code may refer to Module (e.g. from file packager), so alias it - f.write('''var %(EXPORT_NAME)s = function(%(EXPORT_NAME)s) { - %(EXPORT_NAME)s = %(EXPORT_NAME)s || {}; + # Included code may refer to Module (e.g. from file packager), so alias it. + # _scriptDir is used to since when MODULARIZE this JS may be executed later, + # after document.currentScript is gone, so we save it for later + f.write(''' +var %(EXPORT_NAME)s = (function() { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + return (function(%(EXPORT_NAME)s) { + %(EXPORT_NAME)s = %(EXPORT_NAME)s || {}; %(src)s - return %(EXPORT_NAME)s; -}; -// When MODULARIZE, this JS may be executed later, after document.currentScript -// is gone, so we save it. -%(EXPORT_NAME)s = %(EXPORT_NAME)s.bind({ - _scriptDir: typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined -})%(instantiate)s; + return %(EXPORT_NAME)s; + })%(instantiate)s; +})(); ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src, diff --git a/src/shell.js b/src/shell.js index adcb3c7aad6b0..c696f91a542d4 100644 --- a/src/shell.js +++ b/src/shell.js @@ -239,8 +239,8 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { #if MODULARIZE // When MODULARIZE, this JS may be executed later, after document.currentScript // is gone, so we saved it, and we use it here instead of any other info. - if (this['_scriptDir']) { - scriptDirectory = this['_scriptDir']; + if (_scriptDir) { + scriptDirectory = _scriptDir; } #endif // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. diff --git a/tests/test_browser.py b/tests/test_browser.py index b0c619333c4ef..4dcaebda4ffb3 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3950,7 +3950,6 @@ def test_browser_run_from_different_directory(self): src = open('test.html').read() # Make sure JS is loaded from subdirectory open('test-subdir.html', 'w').write(src.replace('test.js', 'subdir/test.js')) - self.run_browser('test-subdir.html', None, '/report_result?0') # Similar to `test_browser_run_from_different_directory`, but asynchronous because of `-s MODULARIZE=1` @@ -3963,43 +3962,39 @@ def test_browser_run_from_different_directory_async(self): os.mkdir('subdir') shutil.move('test.js', os.path.join('subdir', 'test.js')) shutil.move('test.wasm', os.path.join('subdir', 'test.wasm')) - # Make sure JS is loaded from subdirectory - open('test-subdir.html', 'w').write(''' - - - ''') - - self.run_browser('test-subdir.html', None, '/report_result?0') + for creation in ( + 'Module();', + 'new Module();' # not documented as working, but we support it + ): + print(creation) + # Make sure JS is loaded from subdirectory + open('test-subdir.html', 'w').write(''' + + + ''' % creation) + self.run_browser('test-subdir.html', None, '/report_result?0') # Similar to `test_browser_run_from_different_directory`, but # also also we eval the initial code, so currentScript is not present. That prevents us # from finding the file in a subdir, but here we at least check we do not regress compared to the # normal case of finding in the current dir. - # In addition, check for new Module(), which overrides the bind() and replaces the object - # which saved the _scriptDir. Again, we can't get the script dir that way, but at least we - # should not regress compared to the normal case. def test_browser_modularize_no_current_script(self): src = open(path_from_root('tests', 'browser_test_hello_world.c')).read() open('test.c', 'w').write(self.with_report_result(src)) # compile the code with the modularize feature and the preload-file option enabled Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1']).communicate() - for creation in ( - 'Module();', - 'new Module();' - ): - print(creation) - open('test.html', 'w').write(''' - - ''' % creation) - self.run_browser('test.html', None, '/report_result?0') + open('test.html', 'w').write(''' + + ''') + self.run_browser('test.html', None, '/report_result?0') From 3bd194bc2a8612627bd67cfe11ebb7badce1ee58 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 08:07:18 -0700 Subject: [PATCH 2/8] refactor emcc.py code, do not add _scriptDir when MODULARIZE_INSTANCE --- emcc.py | 47 +++++++++++++++++++++++++++++++++-------------- src/shell.js | 4 +++- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/emcc.py b/emcc.py index 6dd76f1baeeab..47b8fdc002877 100755 --- a/emcc.py +++ b/emcc.py @@ -2604,25 +2604,44 @@ def modularize(): final = final + '.modular.js' f = open(final, 'w') - # Included code may refer to Module (e.g. from file packager), so alias it. - # _scriptDir is used to since when MODULARIZE this JS may be executed later, - # after document.currentScript is gone, so we save it for later - f.write(''' -var %(EXPORT_NAME)s = (function() { - var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; - return (function(%(EXPORT_NAME)s) { - %(EXPORT_NAME)s = %(EXPORT_NAME)s || {}; + src = ''' +function %(EXPORT_NAME)s(%(EXPORT_NAME)s) { + %(EXPORT_NAME)s = %(EXPORT_NAME)s || {}; %(src)s - return %(EXPORT_NAME)s; - })%(instantiate)s; -})(); + return %(EXPORT_NAME)s; +} ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, - 'src': src, - 'instantiate': '()' if shared.Settings.MODULARIZE_INSTANCE else '' - }) + 'src': src + } + + if not shared.Settings.MODULARIZE_INSTANCE: + # Included code may refer to Module (e.g. from file packager), so alias it. + # _scriptDir is used to since when MODULARIZE this JS may be executed later, + # after document.currentScript is gone, so we save it for later (when + # MODULARIZE_INSTANCE, an instance is created immediately anyhow, like in + # non-modularize mode) + src = ''' +var %(EXPORT_NAME)s = (function() { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; +%(src)s + return (%(src)s); +})(); +''' % { + 'EXPORT_NAME': shared.Settings.EXPORT_NAME, + 'src': src + } + else: + # Create the MODULARIZE_INSTANCE instance + src = ''' +var %(EXPORT_NAME)s = (%(src)s)(); +''' % { + 'src': src + } + + f.write(src) # Export using a UMD style export, or ES6 exports if selected if shared.Settings.EXPORT_ES6: diff --git a/src/shell.js b/src/shell.js index c696f91a542d4..019f94bbf2742 100644 --- a/src/shell.js +++ b/src/shell.js @@ -237,11 +237,13 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href; } #if MODULARIZE - // When MODULARIZE, this JS may be executed later, after document.currentScript +#if MODULARIZE_INSTANCE == 0 + // When MODULARIZE (and not _INSTANCE), this JS may be executed later, after document.currentScript // is gone, so we saved it, and we use it here instead of any other info. if (_scriptDir) { scriptDirectory = _scriptDir; } +#endif #endif // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. // otherwise, slice off the final part of the url to find the script directory. From df3bbd2ce6514cd2ed2620b640afb942e77240f0 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 08:14:20 -0700 Subject: [PATCH 3/8] wip [ci skip] --- emcc.py | 1 + src/closure-externs.js | 4 +++ tests/test_browser.py | 74 +++++++++++++++++++++++------------------- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/emcc.py b/emcc.py index 47b8fdc002877..bace3ed289c10 100755 --- a/emcc.py +++ b/emcc.py @@ -2638,6 +2638,7 @@ def modularize(): src = ''' var %(EXPORT_NAME)s = (%(src)s)(); ''' % { + 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src } diff --git a/src/closure-externs.js b/src/closure-externs.js index 24721dcb9575b..dcc8aede9232f 100644 --- a/src/closure-externs.js +++ b/src/closure-externs.js @@ -1073,6 +1073,10 @@ var wakaUnknownAfter; * @suppress {undefinedVars} */ var wakaUnknownBefore; +/** + * @suppress {undefinedVars} + */ +var _scriptDir; /** * @suppress {duplicate} */ diff --git a/tests/test_browser.py b/tests/test_browser.py index 4dcaebda4ffb3..6d273823f7093 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3956,25 +3956,31 @@ def test_browser_run_from_different_directory(self): def test_browser_run_from_different_directory_async(self): src = open(path_from_root('tests', 'browser_test_hello_world.c')).read() open('test.c', 'w').write(self.with_report_result(src)) - # compile the code with the modularize feature and the preload-file option enabled - Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1', '-O3']).communicate() - if not os.path.exists('subdir'): - os.mkdir('subdir') - shutil.move('test.js', os.path.join('subdir', 'test.js')) - shutil.move('test.wasm', os.path.join('subdir', 'test.wasm')) - for creation in ( - 'Module();', - 'new Module();' # not documented as working, but we support it - ): - print(creation) - # Make sure JS is loaded from subdirectory - open('test-subdir.html', 'w').write(''' - - - ''' % creation) - self.run_browser('test-subdir.html', None, '/report_result?0') + for instance in (0, 1): + print(instance) + # compile the code with the modularize feature and the preload-file option enabled + Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1', '-O3', '-s', 'MODULARIZE_INSTANCE=%d' % instance]).communicate() + if not os.path.exists('subdir'): + os.mkdir('subdir') + shutil.move('test.js', os.path.join('subdir', 'test.js')) + shutil.move('test.wasm', os.path.join('subdir', 'test.wasm')) + if instance: + creations = [''] + else: + creations = [ + 'Module();', + 'new Module();' # not documented as working, but we support it + ] + for creation in creations: + print(creation) + # Make sure JS is loaded from subdirectory + open('test-subdir.html', 'w').write(''' + + + ''' % creation) + self.run_browser('test-subdir.html', None, '/report_result?0') # Similar to `test_browser_run_from_different_directory`, but # also also we eval the initial code, so currentScript is not present. That prevents us @@ -3983,18 +3989,20 @@ def test_browser_run_from_different_directory_async(self): def test_browser_modularize_no_current_script(self): src = open(path_from_root('tests', 'browser_test_hello_world.c')).read() open('test.c', 'w').write(self.with_report_result(src)) - # compile the code with the modularize feature and the preload-file option enabled - Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1']).communicate() - open('test.html', 'w').write(''' - - ''') - self.run_browser('test.html', None, '/report_result?0') + for instance in (0, 1): + print(instance) + # compile the code with the modularize feature and the preload-file option enabled + Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1', '-s', 'MODULARIZE_INSTANCE=%d' % instance]).communicate() + open('test.html', 'w').write(''' + + ''' % 'Module();' if not instance else '') + self.run_browser('test.html', None, '/report_result?0') From c7f87e88b3e7e03e52faba566d256951e2098482 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 08:44:25 -0700 Subject: [PATCH 4/8] fix --- tests/test_browser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 6d273823f7093..57d46fda41ef7 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -4003,6 +4003,6 @@ def test_browser_modularize_no_current_script(self): %s }, 1); - ''' % 'Module();' if not instance else '') + ''' % ('Module();' if not instance else '')) self.run_browser('test.html', None, '/report_result?0') From 228ff02c6553c80ab47bfec29919c64ddd6d9f58 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 13:03:09 -0700 Subject: [PATCH 5/8] fix comment --- emcc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/emcc.py b/emcc.py index bace3ed289c10..1caba12832ee3 100755 --- a/emcc.py +++ b/emcc.py @@ -2618,11 +2618,10 @@ def modularize(): } if not shared.Settings.MODULARIZE_INSTANCE: - # Included code may refer to Module (e.g. from file packager), so alias it. - # _scriptDir is used to since when MODULARIZE this JS may be executed later, - # after document.currentScript is gone, so we save it for later (when - # MODULARIZE_INSTANCE, an instance is created immediately anyhow, like in - # non-modularize mode) + # When MODULARIZE this JS may be executed later, + # after document.currentScript is gone, so we save it. + # (when MODULARIZE_INSTANCE, an instance is created + # immediately anyhow, like in non-modularize mode) src = ''' var %(EXPORT_NAME)s = (function() { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; From 364f9cfe022996f31606d5c7ebc8ee5f28a0f4e7 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 13:07:53 -0700 Subject: [PATCH 6/8] avoid mixing function X() with var X --- emcc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/emcc.py b/emcc.py index 1caba12832ee3..a733cf7816ab6 100755 --- a/emcc.py +++ b/emcc.py @@ -2605,13 +2605,13 @@ def modularize(): f = open(final, 'w') src = ''' -function %(EXPORT_NAME)s(%(EXPORT_NAME)s) { +var %(EXPORT_NAME)s = function(%(EXPORT_NAME)s) { %(EXPORT_NAME)s = %(EXPORT_NAME)s || {}; %(src)s return %(EXPORT_NAME)s; -} +}; ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src @@ -2623,7 +2623,7 @@ def modularize(): # (when MODULARIZE_INSTANCE, an instance is created # immediately anyhow, like in non-modularize mode) src = ''' -var %(EXPORT_NAME)s = (function() { +%(EXPORT_NAME)s = (function() { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; %(src)s return (%(src)s); @@ -2635,7 +2635,7 @@ def modularize(): else: # Create the MODULARIZE_INSTANCE instance src = ''' -var %(EXPORT_NAME)s = (%(src)s)(); +%(EXPORT_NAME)s = (%(src)s)(); ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src From 06eff64e44b84de83dff252b46465a02cf267d45 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 26 Jul 2018 15:23:44 -0700 Subject: [PATCH 7/8] fix --- emcc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/emcc.py b/emcc.py index a733cf7816ab6..777fa36385a27 100755 --- a/emcc.py +++ b/emcc.py @@ -2605,13 +2605,13 @@ def modularize(): f = open(final, 'w') src = ''' -var %(EXPORT_NAME)s = function(%(EXPORT_NAME)s) { +function(%(EXPORT_NAME)s) { %(EXPORT_NAME)s = %(EXPORT_NAME)s || {}; %(src)s return %(EXPORT_NAME)s; -}; +} ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src @@ -2623,9 +2623,8 @@ def modularize(): # (when MODULARIZE_INSTANCE, an instance is created # immediately anyhow, like in non-modularize mode) src = ''' -%(EXPORT_NAME)s = (function() { +var %(EXPORT_NAME)s = (function() { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; -%(src)s return (%(src)s); })(); ''' % { @@ -2635,7 +2634,7 @@ def modularize(): else: # Create the MODULARIZE_INSTANCE instance src = ''' -%(EXPORT_NAME)s = (%(src)s)(); +var %(EXPORT_NAME)s = (%(src)s)(); ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src From 92a5dbb7641dfc7674b14b72009f70af494a184d Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Fri, 27 Jul 2018 16:40:26 -0700 Subject: [PATCH 8/8] feedback --- tests/test_browser.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 57d46fda41ef7..f39903dc9a368 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3956,21 +3956,20 @@ def test_browser_run_from_different_directory(self): def test_browser_run_from_different_directory_async(self): src = open(path_from_root('tests', 'browser_test_hello_world.c')).read() open('test.c', 'w').write(self.with_report_result(src)) - for instance in (0, 1): - print(instance) + for args, creations in [ + (['-s', 'MODULARIZE=1'], [ + 'Module();', # documented way for using modularize + 'new Module();' # not documented as working, but we support it + ]), + (['-s', 'MODULARIZE_INSTANCE=1'], ['']) # instance: no need to create anything + ]: + print(args) # compile the code with the modularize feature and the preload-file option enabled - Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1', '-O3', '-s', 'MODULARIZE_INSTANCE=%d' % instance]).communicate() + Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-O3'] + args).communicate() if not os.path.exists('subdir'): os.mkdir('subdir') shutil.move('test.js', os.path.join('subdir', 'test.js')) shutil.move('test.wasm', os.path.join('subdir', 'test.wasm')) - if instance: - creations = [''] - else: - creations = [ - 'Module();', - 'new Module();' # not documented as working, but we support it - ] for creation in creations: print(creation) # Make sure JS is loaded from subdirectory @@ -3989,10 +3988,15 @@ def test_browser_run_from_different_directory_async(self): def test_browser_modularize_no_current_script(self): src = open(path_from_root('tests', 'browser_test_hello_world.c')).read() open('test.c', 'w').write(self.with_report_result(src)) - for instance in (0, 1): - print(instance) + # test both modularize (and creating an instance) and modularize-instance + # (which creates by itself) + for args, creation in [ + (['-s', 'MODULARIZE=1'], 'Module();'), + (['-s', 'MODULARIZE_INSTANCE=1'], '') + ]: + print(args, creation) # compile the code with the modularize feature and the preload-file option enabled - Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js', '-s', 'MODULARIZE=1', '-s', 'MODULARIZE_INSTANCE=%d' % instance]).communicate() + Popen([PYTHON, EMCC, 'test.c', '-o', 'test.js'] + args).communicate() open('test.html', 'w').write(''' - ''' % ('Module();' if not instance else '')) + ''' % creation) self.run_browser('test.html', None, '/report_result?0')