babel / minify

:scissors: An ES6+ aware minifier based on the Babel toolchain (beta)
https://babeljs.io/repl
MIT License
4.39k stars 225 forks source link

asm.js code minified with babel-plugin-minify-dead-code-elimination causes "asm.js type error: initializer of exported object literal must be name of function" error in Firefox #1018

Open cpeterso opened 3 years ago

cpeterso commented 3 years ago

Describe the bug

asm.js code minified with the "babel-plugin-minify-dead-code-elimination plugin causes an asm.js type error: initializer of exported object literal must be name of function error in Firefox.

NOTE: Even those this console message says "error", the code will still run correctly. However, Firefox will use its regular JS JIT instead of its asm.js-optimized JIT since the code's is no longer valid asm.js syntax.

To Reproduce

Minimal code to reproduce the bug

function AsmTest(stdlib, foreign, buffer) {
  "use asm";
  function foo() { return 0; }
  return { foo: foo };
}

You can reproduce the error using this JS Fiddle example:

  1. Load https://jsfiddle.net/1kcf69g3/ in Firefox.
  2. Click the Run button.
  3. Open Firefox's DevTools console using Shift+Ctrl+I on Windows or Linux or Shift+Command+I on macOS.
  4. Filter the console output for "asm.js".

You will see two asm.js messages, the first when Firefox JITs the GoodAsmTest() function and second when Firefox JITs the BadAsmTest() function:

Successfully compiled asm.js code (total compilation time 0ms)
asm.js type error: initializer of exported object literal must be name of function

NOTE: You must keep the DevTools console closed when you run the script! If you open the DevTools console before you run the script, Firefox disables its JIT and you will see a console message like asm.js type error: Disabled by lack of compiler support or asm.js type error: Asm.js optimizer disabled because debugger is active instead of the asm.js type error: initializer of exported object literal must be name of function JIT error.

Actual Output

function AsmTest() {
  "use asm";

  return {
    foo: function () {
      return 0;
    }
  };
}

The problem is that babel-minify is converting a standalone function definition into a function object in the AsmTest() function's return statement. asm.js requires that use asm functions return an object literal whose property values are function identifiers, not function objects:

http://asmjs.org/spec/latest/#validateexport-s

Expected Output

function AsmTest(stdlib, foreign, buffer) {
  "use asm";

  function foo() {
    return 0;
  }

  return {
    foo: foo
  };
}

Configuration

How are you using babel-minify?

Here is a live example of the bug in the Babel REPL with the babel-plugin-minify-dead-code-elimination plugin added:

https://babeljs.io/repl/#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.6&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABAQQM4FsAqBTVUAUeAJgDYwBGANIsHAE7YwDmY15Iww2dAlIgN4AoRIgBEIVNkQBDDKIDcwmuGjwktOPj79EDKCDpIADPMQBfJXoNIdGgFw04cc4rNA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=script&lineWrap=false&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.15.3&externalPlugins=babel-plugin-minify-dead-code-elimination%400.5.1&assumptions=%7B%7D

You must manually add the babel-plugin-minify-dead-code-elimination plugin.

Possible solution

This problem could be avoided if babel-minify ignored use asm functions, as suggested in https://github.com/babel/minify/issues/599, or didn't run the the babel-plugin-minify-dead-code-elimination plugin on use asm functions.

Additional context

Here are multiple bug reports about this asm.js error in webtorrent.js caused by babel-minify:

https://github.com/webtorrent/webtorrent/issues/1830 https://github.com/webtorrent/webtorrent/issues/1252 https://github.com/webtorrent/webtorrent/issues/281

The unminified version of webtorrent.js and a version minified by Terser load in Firefox without problem. But the version minified by babel-minify (including webtorrent's official webtorrent.min.js version) have the error.

Here is the minified output from Terser (which you can test yourself in the Terser REPL, which doesn't cause this asm.js error because it retains the standalone function f:

(function(n,o,u){"use asm";function f(){return 0}return{foo:f}})().foo();