Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default [{
'**/test/',
'src/polyfill/',
'src/lib/',
'src/minimum_runtime_check.js',
'src/runtime_*.js',
'src/shell*.js',
'src/modularize.js',
Expand Down
64 changes: 64 additions & 0 deletions src/minimum_runtime_check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @license
* Copyright 2024 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/

#if ASSERTIONS

(function() {
// "30.0.0" -> 300000
function humanReadableVersionToPacked(str) {
str = str.split('-')[0]; // Remove any trailing part from e.g. "12.53.3-alpha"
var vers = str.split('.').slice(0, 3);
while(vers.length < 3) vers.push('00');
vers = vers.map((n, i, arr) => n.padStart(2, '0'));
return vers.join('');
}
// 300000 -> "30.0.0"
var packedVersionToHumanReadable = n => [n / 10000 | 0, (n / 100 | 0) % 100, n % 100].join('.');

var TARGET_NOT_SUPPORTED = {{{ TARGET_NOT_SUPPORTED }}};

var currentNodeVersion = typeof process !== 'undefined' && process?.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED;
#if MIN_NODE_VERSION == TARGET_NOT_SUPPORTED
if (currentNodeVersion < TARGET_NOT_SUPPORTED) {
throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
}
#endif
if (currentNodeVersion < {{{ MIN_NODE_VERSION }}}) {
throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable({{{ MIN_NODE_VERSION }}}) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`);
}

var currentSafariVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.includes("Safari/") && navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED;
#if MIN_SAFARI_VERSION == TARGET_NOT_SUPPORTED
if (currentSafariVersion < TARGET_NOT_SUPPORTED) {
throw new Error(`This page was compiled without support for Safari browser. Pass -sMIN_SAFARI_VERSION=${currentSafariVersion} or lower to enable support for this browser.`);
}
#endif
if (currentSafariVersion < {{{ MIN_SAFARI_VERSION }}}) {
throw new Error(`This emscripten-generated code requires Safari v${ packedVersionToHumanReadable({{{ MIN_SAFARI_VERSION }}}) } (detected v${currentSafariVersion})`);
}

var currentFirefoxVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Firefox\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
#if MIN_FIREFOX_VERSION == TARGET_NOT_SUPPORTED
if (currentFirefoxVersion < TARGET_NOT_SUPPORTED) {
throw new Error(`This page was compiled without support for Firefox browser. Pass -sMIN_FIREFOX_VERSION=${currentFirefoxVersion} or lower to enable support for this browser.`);
}
#endif
if (currentFirefoxVersion < {{{ MIN_FIREFOX_VERSION }}}) {
throw new Error(`This emscripten-generated code requires Firefox v{{{ MIN_FIREFOX_VERSION }}} (detected v${currentFirefoxVersion})`);
}

var currentChromeVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Chrome\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
#if MIN_CHROME_VERSION == TARGET_NOT_SUPPORTED
if (currentChromeVersion < TARGET_NOT_SUPPORTED) {
throw new Error(`This page was compiled without support for Chrome browser. Pass -sMIN_CHROME_VERSION=${currentChromeVersion} or lower to enable support for this browser.`);
}
#endif
if (currentChromeVersion < {{{ MIN_CHROME_VERSION }}}) {
throw new Error(`This emscripten-generated code requires Chrome v{{{ MIN_CHROME_VERSION }}} (detected v${currentChromeVersion})`);
}
})();

#endif
17 changes: 3 additions & 14 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"use strict";

#endif

#include "minimum_runtime_check.js"

// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
Expand Down Expand Up @@ -192,15 +195,6 @@ if (ENVIRONMENT_IS_NODE) {
if (!isNode) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
#endif

#if ASSERTIONS
var nodeVersion = process.versions.node;
var numericVersion = nodeVersion.split('.').slice(0, 3);
numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1);
if (numericVersion < {{{ MIN_NODE_VERSION }}}) {
throw new Error('This emscripten-generated code requires node {{{ formattedMinNodeVersion() }}} (detected v' + nodeVersion + ')');
}
#endif

// These modules will usually be used on Node.js. Load them eagerly to avoid
// the complexity of lazy-loading.
var fs = require('fs');
Expand Down Expand Up @@ -269,11 +263,6 @@ if (ENVIRONMENT_IS_NODE) {
#if ENVIRONMENT_MAY_BE_SHELL || ASSERTIONS
if (ENVIRONMENT_IS_SHELL) {

#if ENVIRONMENT.length && ASSERTIONS
const isNode = {{{ nodeDetectionCode() }}};
if (isNode || typeof window == 'object' || typeof WorkerGlobalScope != 'undefined') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
#endif

#if ENVIRONMENT_MAY_BE_SHELL
readBinary = (f) => {
if (typeof readbuffer == 'function') {
Expand Down
2 changes: 2 additions & 0 deletions src/shell_minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* SPDX-License-Identifier: MIT
*/

#include "minimum_runtime_check.js"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just add this to runtime_common.js which is already included in both runtime modes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I made a separate file is that this code would be run as the very first thing in the generated .js.

If we put the code somewhere in the middle, we risk already attempting to execute some code that might require a new feature (e.g. ES6 Modules or some language syntax keyword, etc)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes that makes sense.


#if MODULARIZE
var Module = moduleArg;
#elif USE_CLOSURE_COMPILER
Expand Down
2 changes: 1 addition & 1 deletion src/shell_minimal_runtime.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script>

#if !MODULARIZE
var {{{ EXPORT_NAME }}} = {}
var {{{ EXPORT_NAME }}} = {};
#endif

#if WASM == 2
Expand Down
2 changes: 2 additions & 0 deletions test/codesize/test_codesize_file_preload.expected.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// include: shell.js
"use strict";

// include: minimum_runtime_check.js
// end include: minimum_runtime_check.js
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_hello_O0.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 22478,
"a.out.js.gz": 8314,
"a.out.js": 23348,
"a.out.js.gz": 8559,
"a.out.nodebug.wasm": 15127,
"a.out.nodebug.wasm.gz": 7450,
"total": 37605,
"total_gz": 15764,
"total": 38475,
"total_gz": 16009,
"sent": [
"fd_write"
],
Expand Down
47 changes: 37 additions & 10 deletions test/codesize/test_codesize_minimal_O0.expected.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
// include: shell.js
"use strict";

// include: minimum_runtime_check.js
(function() {
// "30.0.0" -> 300000
function humanReadableVersionToPacked(str) {
str = str.split('-')[0]; // Remove any trailing part from e.g. "12.53.3-alpha"
var vers = str.split('.').slice(0, 3);
while(vers.length < 3) vers.push('00');
vers = vers.map((n, i, arr) => n.padStart(2, '0'));
return vers.join('');
}
// 300000 -> "30.0.0"
var packedVersionToHumanReadable = n => [n / 10000 | 0, (n / 100 | 0) % 100, n % 100].join('.');

var TARGET_NOT_SUPPORTED = 2147483647;

var currentNodeVersion = typeof process !== 'undefined' && process?.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED;
if (currentNodeVersion < 160000) {
throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable(160000) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`);
}

var currentSafariVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.includes("Safari/") && navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED;
if (currentSafariVersion < 150000) {
throw new Error(`This emscripten-generated code requires Safari v${ packedVersionToHumanReadable(150000) } (detected v${currentSafariVersion})`);
}

var currentFirefoxVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Firefox\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
if (currentFirefoxVersion < 79) {
throw new Error(`This emscripten-generated code requires Firefox v79 (detected v${currentFirefoxVersion})`);
}

var currentChromeVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Chrome\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
if (currentChromeVersion < 85) {
throw new Error(`This emscripten-generated code requires Chrome v85 (detected v${currentChromeVersion})`);
}
})();

// end include: minimum_runtime_check.js
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
Expand Down Expand Up @@ -61,13 +98,6 @@ if (ENVIRONMENT_IS_NODE) {
const isNode = typeof process == 'object' && process.versions?.node && process.type != 'renderer';
if (!isNode) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');

var nodeVersion = process.versions.node;
var numericVersion = nodeVersion.split('.').slice(0, 3);
numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1);
if (numericVersion < 160000) {
throw new Error('This emscripten-generated code requires node v16.0.0 (detected v' + nodeVersion + ')');
}

// These modules will usually be used on Node.js. Load them eagerly to avoid
// the complexity of lazy-loading.
var fs = require('fs');
Expand Down Expand Up @@ -110,9 +140,6 @@ readAsync = async (filename, binary = true) => {
} else
if (ENVIRONMENT_IS_SHELL) {

const isNode = typeof process == 'object' && process.versions?.node && process.type != 'renderer';
if (isNode || typeof window == 'object' || typeof WorkerGlobalScope != 'undefined') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');

} else

// Note that this includes Node.js workers when relevant (pthreads is enabled).
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_minimal_O0.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 17754,
"a.out.js.gz": 6617,
"a.out.js": 18626,
"a.out.js.gz": 6874,
"a.out.nodebug.wasm": 1136,
"a.out.nodebug.wasm.gz": 659,
"total": 18890,
"total_gz": 7276,
"total": 19762,
"total_gz": 7533,
"sent": [],
"imports": [],
"exports": [
Expand Down
16 changes: 8 additions & 8 deletions test/codesize/test_unoptimized_code_size.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"hello_world.js": 54133,
"hello_world.js.gz": 17091,
"hello_world.js": 55560,
"hello_world.js.gz": 17522,
"hello_world.wasm": 15127,
"hello_world.wasm.gz": 7450,
"no_asserts.js": 26513,
"no_asserts.js.gz": 8864,
"no_asserts.js": 26591,
"no_asserts.js.gz": 8875,
"no_asserts.wasm": 12227,
"no_asserts.wasm.gz": 6010,
"strict.js": 52171,
"strict.js.gz": 16426,
"strict.js": 53598,
"strict.js.gz": 16856,
"strict.wasm": 15127,
"strict.wasm.gz": 7447,
"total": 175298,
"total_gz": 63288
"total": 178230,
"total_gz": 64160
}
1 change: 1 addition & 0 deletions tools/feature_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def enable_feature(feature, reason, override=False):
f'({min_version} or above required)')
else:
# If no conflict, bump the minimum version to accommodate the feature.
logger.debug(f'Enabling {name}={min_version} to accommodate {reason}')
setattr(settings, name, min_version)


Expand Down
2 changes: 1 addition & 1 deletion tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def setup_environment_settings():
if any(x for x in settings.ENVIRONMENT if x not in VALID_ENVIRONMENTS):
exit_with_error(f'Invalid environment specified in "ENVIRONMENT": {settings.ENVIRONMENT}. Should be one of: {",".join(VALID_ENVIRONMENTS)}')

settings.ENVIRONMENT_MAY_BE_WEB = not settings.ENVIRONMENT or 'web' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_WEB = not settings.ENVIRONMENT or 'web' in settings.ENVIRONMENT or 'worker' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_WEBVIEW = not settings.ENVIRONMENT or 'webview' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_NODE = not settings.ENVIRONMENT or 'node' in settings.ENVIRONMENT
settings.ENVIRONMENT_MAY_BE_SHELL = not settings.ENVIRONMENT or 'shell' in settings.ENVIRONMENT
Expand Down