Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/feature_matrix.py
6175 views
1
# Copyright 2022 The Emscripten Authors. All rights reserved.
2
# Emscripten is available under two separate licenses, the MIT license and the
3
# University of Illinois/NCSA Open Source License. Both these licenses can be
4
# found in the LICENSE file.
5
6
"""Utilities for mapping browser versions to webassembly features."""
7
8
import logging
9
from enum import IntEnum, auto
10
11
from . import diagnostics
12
from .settings import settings, user_settings
13
14
logger = logging.getLogger('feature_matrix')
15
16
UNSUPPORTED = 0x7FFFFFFF
17
18
# Oldest support browser versions.
19
# Emscripten unconditionally requires support for:
20
# - DedicatedWorkerGlobalScope.name parameter for multithreading support, which
21
# landed first in Chrome 70, Firefox 55 and Safari 12.2.
22
23
# N.b. when modifying these values, update comments in src/settings.js on
24
# MIN_x_VERSION fields to match accordingly.
25
OLDEST_SUPPORTED_CHROME = 74 # Released on 2019-04-23
26
OLDEST_SUPPORTED_FIREFOX = 68 # Released on 2019-07-09
27
OLDEST_SUPPORTED_SAFARI = 120200 # Released on 2019-03-25
28
# 12.22.09 is the oldest version of node that we do any testing with.
29
# Keep this in sync with the test-node-compat in .circleci/config.yml.
30
OLDEST_SUPPORTED_NODE = 122209
31
32
33
class Feature(IntEnum):
34
MUTABLE_GLOBALS = auto()
35
NON_TRAPPING_FPTOINT = auto()
36
SIGN_EXT = auto()
37
BULK_MEMORY = auto()
38
JS_BIGINT_INTEGRATION = auto()
39
THREADS = auto()
40
PROMISE_ANY = auto()
41
MEMORY64 = auto()
42
WORKER_ES6_MODULES = auto()
43
OFFSCREENCANVAS_SUPPORT = auto()
44
WASM_LEGACY_EXCEPTIONS = auto()
45
WASM_EXCEPTIONS = auto()
46
WEBGL2 = auto()
47
WEBGPU = auto()
48
GROWABLE_ARRAYBUFFERS = auto()
49
50
51
disable_override_features = set()
52
enable_override_features = set()
53
54
min_browser_versions = {
55
Feature.MUTABLE_GLOBALS: {
56
'chrome': 74,
57
'firefox': 61,
58
'safari': 130100,
59
'node': 120000,
60
},
61
Feature.NON_TRAPPING_FPTOINT: {
62
'chrome': 75,
63
'firefox': 65,
64
'safari': 150000,
65
'node': 130000,
66
},
67
Feature.SIGN_EXT: {
68
'chrome': 74,
69
'firefox': 62,
70
'safari': 140100,
71
'node': 120000,
72
},
73
Feature.BULK_MEMORY: {
74
'chrome': 75,
75
'firefox': 79,
76
'safari': 150000,
77
'node': 130000,
78
},
79
Feature.JS_BIGINT_INTEGRATION: {
80
'chrome': 67,
81
'firefox': 78,
82
'safari': 150000,
83
'node': 130000,
84
},
85
Feature.THREADS: {
86
'chrome': 74,
87
'firefox': 79,
88
'safari': 140100,
89
'node': 160400,
90
},
91
Feature.PROMISE_ANY: {
92
'chrome': 85,
93
'firefox': 79,
94
'safari': 140000,
95
'node': 150000,
96
},
97
Feature.MEMORY64: {
98
'chrome': 128,
99
'firefox': 129,
100
'safari': UNSUPPORTED,
101
'node': 230000,
102
},
103
# Emscripten itself does not use this feature but we use it in our browser
104
# tests.
105
Feature.WEBGL2: {
106
'chrome': 56,
107
'firefox': 51,
108
'safari': 150000,
109
'node': UNSUPPORTED,
110
},
111
# Emscripten itself does not use this feature but we use it in our browser
112
# tests.
113
Feature.WEBGPU: {
114
'chrome': 113,
115
'firefox': 141,
116
'safari': 260000,
117
'node': UNSUPPORTED,
118
},
119
# Emscripten itself does not use this feature but we use it in our browser
120
# tests.
121
# https://caniuse.com/mdn-api_worker_worker_ecmascript_modules: The ability to
122
# call new Worker(url, { type: 'module' });
123
Feature.WORKER_ES6_MODULES: {
124
'chrome': 80,
125
'firefox': 114,
126
'safari': 150000,
127
'node': 0, # This is a browser only feature, no requirements on Node.js
128
},
129
# OffscreenCanvas feature allows creating canvases that are not connected to
130
# a visible DOM element, e.g. in a Worker.
131
# https://caniuse.com/offscreencanvas
132
Feature.OFFSCREENCANVAS_SUPPORT: {
133
'chrome': 69,
134
'firefox': 105,
135
'safari': 170000,
136
'node': 0, # This is a browser only feature, no requirements on Node.js
137
},
138
# Legacy Wasm exceptions was the first (now legacy) format for native
139
# exception handling in WebAssembly.
140
Feature.WASM_LEGACY_EXCEPTIONS: {
141
'chrome': 95,
142
'firefox': 100,
143
'safari': 150200,
144
'node': 170000,
145
},
146
# Wasm exceptions is a newer format for native exception handling in
147
# WebAssembly.
148
Feature.WASM_EXCEPTIONS: {
149
'chrome': 137,
150
'firefox': 131,
151
'safari': 180400,
152
# Supported with flag --experimental-wasm-exnref (TODO: Change this to
153
# unflagged version of Node.js 260000 that ships Wasm EH enabled, after
154
# Emscripten unit testing has migrated to Node.js 26, and Emsdk ships
155
# Node.js 26)
156
'node': 220000,
157
},
158
# Growable SharedArrayBuffers improves memory growth feature in multithreaded
159
# builds by avoiding need to poll resizes to ArrayBuffer views in Workers.
160
# This feature is not used anywhere else except the test harness to detect
161
# browser version.
162
Feature.GROWABLE_ARRAYBUFFERS: {
163
'chrome': 136,
164
'firefox': 145,
165
'safari': UNSUPPORTED,
166
'node': 240000,
167
},
168
}
169
170
# Static assertion to check that we actually need each of the above feature flags
171
# Once the OLDEST_SUPPORTED_XX versions are high enough they can/should be removed.
172
for feature, reqs in min_browser_versions.items():
173
always_present = (reqs['chrome'] <= OLDEST_SUPPORTED_CHROME and
174
reqs['firefox'] <= OLDEST_SUPPORTED_FIREFOX and
175
reqs['safari'] <= OLDEST_SUPPORTED_SAFARI and
176
reqs['node'] <= OLDEST_SUPPORTED_NODE)
177
assert not always_present, f'{feature.name} is no longer needed'
178
179
180
def caniuse(feature):
181
if feature in disable_override_features:
182
return False
183
if feature in enable_override_features:
184
return True
185
186
min_versions = min_browser_versions[feature]
187
188
def report_missing(setting_name):
189
setting_value = getattr(settings, setting_name)
190
logger.debug(f'cannot use {feature.name} because {setting_name} is too old: {setting_value}')
191
192
if settings.MIN_CHROME_VERSION < min_versions['chrome']:
193
report_missing('MIN_CHROME_VERSION')
194
return False
195
if settings.MIN_FIREFOX_VERSION < min_versions['firefox']:
196
report_missing('MIN_FIREFOX_VERSION')
197
return False
198
if settings.MIN_SAFARI_VERSION < min_versions['safari']:
199
report_missing('MIN_SAFARI_VERSION')
200
return False
201
if 'node' in min_versions and settings.MIN_NODE_VERSION < min_versions['node']:
202
report_missing('MIN_NODE_VERSION')
203
return False
204
return True
205
206
207
def enable_feature(feature, reason, override=False):
208
"""Updates default settings for browser versions such that the given
209
feature is available everywhere.
210
"""
211
if override:
212
enable_override_features.add(feature)
213
for name, min_version in min_browser_versions[feature].items():
214
name = f'MIN_{name.upper()}_VERSION'
215
if settings[name] < min_version:
216
if name in user_settings:
217
# If the user explicitly chose an older version we issue a warning.
218
if name == 'MIN_SAFARI_VERSION' and reason == 'pthreads':
219
# But as a special case, don't warn when forcing on bulk memory on Safari.
220
# This is because Safari implemented part of bulk memory along with threads in 14.1,
221
# but not all of it. So bulk-mem is listed as supported in 15.0. So we want to
222
# continue enabling bulk memory via pthreads without a warning in 14.1, but without
223
# enabling other features requiring 15.0.
224
continue
225
diagnostics.warning(
226
'compatibility',
227
f'{name}={user_settings[name]} is not compatible with {reason} '
228
f'({name}={min_version} or above required)')
229
else:
230
# If no conflict, bump the minimum version to accommodate the feature.
231
logger.debug(f'Enabling {name}={min_version} to accommodate {reason}')
232
setattr(settings, name, min_version)
233
234
235
def disable_feature(feature):
236
"""Allow the user to disable a feature that would otherwise be on by default.
237
"""
238
disable_override_features.add(feature)
239
240
241
# apply minimum browser version defaults based on user settings. if
242
# a user requests a feature that we know is only supported in browsers
243
# from a specific version and above, we can assume that browser version.
244
def apply_min_browser_versions():
245
if settings.WASM_BIGINT and 'WASM_BIGINT' in user_settings:
246
# WASM_BIGINT is enabled by default, don't use it to enable other features
247
# unless the user explicitly enabled it.
248
enable_feature(Feature.JS_BIGINT_INTEGRATION, 'WASM_BIGINT')
249
if settings.PTHREADS:
250
enable_feature(Feature.THREADS, 'pthreads')
251
enable_feature(Feature.BULK_MEMORY, 'pthreads')
252
elif settings.WASM_WORKERS or settings.SHARED_MEMORY:
253
enable_feature(Feature.BULK_MEMORY, 'shared-mem')
254
if settings.RELOCATABLE:
255
enable_feature(Feature.MUTABLE_GLOBALS, 'dynamic linking')
256
if settings.MEMORY64 == 1:
257
enable_feature(Feature.MEMORY64, 'MEMORY64')
258
if settings.EXPORT_ES6 and settings.PTHREADS:
259
enable_feature(Feature.WORKER_ES6_MODULES, 'EXPORT_ES6 with -pthread')
260
if settings.EXPORT_ES6 and settings.WASM_WORKERS:
261
enable_feature(Feature.WORKER_ES6_MODULES, 'EXPORT_ES6 with -sWASM_WORKERS')
262
if settings.OFFSCREENCANVAS_SUPPORT:
263
enable_feature(Feature.OFFSCREENCANVAS_SUPPORT, 'OFFSCREENCANVAS_SUPPORT')
264
if settings.WASM_EXCEPTIONS or settings.SUPPORT_LONGJMP == 'wasm': # Wasm longjmp support will lean on Wasm (Legacy) EH
265
if settings.WASM_LEGACY_EXCEPTIONS:
266
enable_feature(Feature.WASM_LEGACY_EXCEPTIONS, 'Wasm Legacy exceptions (-fwasm-exceptions with -sWASM_LEGACY_EXCEPTIONS=1)')
267
else:
268
enable_feature(Feature.WASM_EXCEPTIONS, 'Wasm exceptions (-fwasm-exceptions with -sWASM_LEGACY_EXCEPTIONS=0)')
269
270