From 423a29a12e1006476dc9101bcc8cae97f1be2933 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 13 Nov 2023 10:17:16 -0800 Subject: [PATCH] Remove limits of ES6 features in standard library code When transpiling to ES5 use closure with `SIMPLE_OPTIMIZATIONS` rather then `WHITESPACE_ONLY`. This means that polyfills cant be included as needed and removes the limits on what ES6 features we can use. The downside of this is that is slows down builds for ES5 users but this seems like a reasonable tradeoff. Fixes: #11984 --- ChangeLog.md | 4 ++++ emcc.py | 2 +- test/test_other.py | 28 +++++++++++++++++++++------- tools/building.py | 3 ++- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 72e2e6f26412b..11a316200d249 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,6 +27,10 @@ See docs/process.md for more on how version tagging works. `warning: treating 'c' input as 'c++' when in C++ mode`. This also means that the `DEFAULT_TO_CXX` setting now only applies when linking and not when compiling. (#20712) +- JavaScript library code can now use the full range ES6 features and we rely + on closure compiler to transpile for ES5 when targetting older browsers. + For those that want to would rather perform transpilation seperately outside + of emscripten you can use the `-sPOLYFILL=0` setting. (#20700) 3.1.49 - 11/14/23 ----------------- diff --git a/emcc.py b/emcc.py index 3bd697fbe960d..6cde43e4af669 100644 --- a/emcc.py +++ b/emcc.py @@ -2318,7 +2318,7 @@ def phase_linker_setup(options, state, newargs): setup_environment_settings() - if options.use_closure_compiler != 0: + if options.use_closure_compiler != 0 and settings.POLYFILL: # Emscripten requires certain ES6 constructs by default in library code # - https://caniuse.com/let : EDGE:12 FF:44 CHROME:49 SAFARI:11 # - https://caniuse.com/const : EDGE:12 FF:36 CHROME:49 SAFARI:11 diff --git a/test/test_other.py b/test/test_other.py index f8756509eaf9f..92edb29a6b778 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -12990,7 +12990,7 @@ def test_hello_function(self): }) @crossplatform def test_es5_transpile(self, args): - self.emcc_args += args + self.emcc_args += ['-Wno-transpile'] + args # Create a library file that uses the following ES6 features # - let/const @@ -13006,6 +13006,11 @@ def test_es5_transpile(self, args): let obj = Object.assign({}, {prop:1}); err('prop: ' + obj.prop); + // for .. of + for (var elem of [42, 43]) { + err('array elem: ' + elem); + } + // arrow funcs + const const bar = () => 2; err('bar: ' + bar()); @@ -13015,14 +13020,14 @@ def test_es5_transpile(self, args): var obj2 = { [key]: 42, }; - err('value: ' + obj2[key]); + err('computed prop: ' + obj2[key]); // Method syntax var obj3 = { myMethod() { return 43 }, }; global['foo'] = obj3; - err('value2: ' + obj3.myMethod()); + err('myMethod: ' + obj3.myMethod()); // Nullish coalescing var definitely = global['maybe'] ?? {}; @@ -13042,6 +13047,15 @@ def test_es5_transpile(self, args): } }); ''') + expected = '''\ +prop: 1 +array elem: 42 +array elem: 43 +bar: 2 +computed prop: 42 +myMethod: 43 +''' + create_file('test.c', 'extern void foo(); int main() { foo(); }') self.emcc_args += ['--js-library', 'es6_library.js'] self.uses_es6 = True @@ -13073,7 +13087,7 @@ def check_for_es6(filename, expect): # Check that under normal circumstances none of these features get # removed / transpiled. print('base case') - self.do_runf('test.c', 'prop: 1\nbar: 2\n') + self.do_runf('test.c', expected) check_for_es6('test.js', True) # If we select and older browser than closure will kick in by default @@ -13081,18 +13095,18 @@ def check_for_es6(filename, expect): print('with old browser') self.emcc_args.remove('-Werror') self.set_setting('MIN_CHROME_VERSION', '10') - self.do_runf('test.c', 'prop: 1\nbar: 2\n', output_basename='test_old') + self.do_runf('test.c', expected, output_basename='test_old') check_for_es6('test_old.js', False) # If we add `--closure=0` that transpiler (closure) is not run at all print('with old browser + --closure=0') - self.do_runf('test.c', 'prop: 1\nbar: 2\n', emcc_args=['--closure=0'], output_basename='test_no_closure') + self.do_runf('test.c', expected, emcc_args=['--closure=0'], output_basename='test_no_closure') check_for_es6('test_no_closure.js', True) # If we use `--closure=1` closure will run in full optimization mode # and also transpile to ES5 print('with old browser + --closure=1') - self.do_runf('test.c', 'prop: 1\nbar: 2\n', emcc_args=['--closure=1'], output_basename='test_closure') + self.do_runf('test.c', expected, emcc_args=['--closure=1'], output_basename='test_closure') check_for_es6('test_closure.js', False) def test_gmtime_noleak(self): diff --git a/tools/building.py b/tools/building.py index dfc357f9300e6..3d1abbb6a25c3 100644 --- a/tools/building.py +++ b/tools/building.py @@ -513,7 +513,8 @@ def closure_transpile(filename): user_args = [] closure_cmd, env = get_closure_compiler_and_env(user_args) closure_cmd += ['--language_out', 'ES5'] - closure_cmd += ['--compilation_level', 'WHITESPACE_ONLY'] + closure_cmd += ['--compilation_level', 'SIMPLE_OPTIMIZATIONS'] + closure_cmd += ['--formatting', 'PRETTY_PRINT'] return run_closure_cmd(closure_cmd, filename, env)