Skip to content

Commit 17c306a

Browse files
committed
Add a new way to mark async imports in library files.
Allow library functions to be asyncify'd with either an `async` function or adding `$name_async`.
1 parent b116323 commit 17c306a

File tree

5 files changed

+40
-26
lines changed

5 files changed

+40
-26
lines changed

emcc.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
}
102102

103103
DEFAULT_ASYNCIFY_IMPORTS = [
104-
'emscripten_sleep', 'emscripten_wget', 'emscripten_wget_data', 'emscripten_idb_load',
104+
'emscripten_wget', 'emscripten_wget_data', 'emscripten_idb_load',
105105
'emscripten_idb_store', 'emscripten_idb_delete', 'emscripten_idb_exists',
106106
'emscripten_idb_load_blob', 'emscripten_idb_store_blob', 'SDL_Delay',
107107
'emscripten_scan_registers', 'emscripten_lazy_load_code',
@@ -507,7 +507,7 @@ def ensure_archive_index(archive_file):
507507
run_process([shared.LLVM_RANLIB, archive_file])
508508

509509

510-
def generate_js_symbols():
510+
def generate_js_sym_info():
511511
# Runs the js compiler to generate a list of all symbols available in the JS
512512
# libraries. This must be done separately for each linker invokation since the
513513
# list of symbols depends on what settings are used.
@@ -520,11 +520,11 @@ def generate_js_symbols():
520520

521521

522522
@ToolchainProfiler.profile_block('JS symbol generation')
523-
def get_all_js_syms():
523+
def get_js_sym_info():
524524
# Avoiding using the cache when generating struct info since
525525
# this step is performed while the cache is locked.
526526
if DEBUG or settings.BOOTSTRAPPING_STRUCT_INFO or config.FROZEN_CACHE:
527-
return generate_js_symbols()
527+
return generate_js_sym_info()
528528

529529
# We define a cache hit as when the settings and `--js-library` contents are
530530
# identical.
@@ -545,29 +545,16 @@ def get_all_js_syms():
545545
def build_symbol_list(filename):
546546
"""Only called when there is no existing symbol list for a given content hash.
547547
"""
548-
library_syms = generate_js_symbols()
549-
lines = []
548+
library_syms = generate_js_sym_info()
550549

551-
for name, deps in library_syms.items():
552-
if deps:
553-
lines.append('%s: %s' % (name, ','.join(deps)))
554-
else:
555-
lines.append(name)
556-
write_file(filename, '\n'.join(lines) + '\n')
550+
write_file(filename, json.dumps(library_syms, separators=(',', ':')))
557551

558552
# We need to use a separate lock here for symbol lists because, unlike with system libraries,
559553
# it's normally for these file to get pruned as part of normal operation. This means that it
560554
# can be deleted between the `cache.get()` then the `read_file`.
561555
with filelock.FileLock(cache.get_path(cache.get_path('symbol_lists.lock'))):
562556
filename = cache.get(f'symbol_lists/{content_hash}.txt', build_symbol_list)
563-
lines = read_file(filename).splitlines()
564-
library_syms = {}
565-
for line in lines:
566-
if ':' in line:
567-
name, deps = line.split(':')
568-
library_syms[name] = deps.strip().split(',')
569-
else:
570-
library_syms[line] = []
557+
library_syms = json.loads(read_file(filename))
571558

572559
# Limit of the overall size of the cache to 100 files.
573560
# This code will get test coverage since a full test run of `other` or `core`
@@ -1334,9 +1321,13 @@ def run(args):
13341321
return 0
13351322

13361323
js_syms = {}
1337-
if not settings.SIDE_MODULE:
1338-
js_syms = get_all_js_syms()
1339-
deps_info.append_deps_info(js_syms)
1324+
if not settings.SIDE_MODULE or settings.ASYNCIFY:
1325+
js_info = get_js_sym_info()
1326+
if not settings.SIDE_MODULE:
1327+
js_syms = js_info['deps']
1328+
deps_info.append_deps_info(js_syms)
1329+
if settings.ASYNCIFY:
1330+
settings.ASYNCIFY_IMPORTS += ['env.' + x for x in js_info['asyncFuncs']]
13401331

13411332
phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs)
13421333

src/compiler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ if (symbolsOnly) {
7474
}
7575

7676
// Side modules are pure wasm and have no JS
77-
assert(!SIDE_MODULE, 'JS compiler should not run on side modules');
77+
assert(!SIDE_MODULE || (ASYNCIFY && global.symbolsOnly), 'JS compiler should only run on side modules if asyncify is used.');
7878

7979
// Output some info and warnings based on settings
8080

src/jsifier.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function isDefined(symName) {
6969
function runJSify() {
7070
const libraryItems = [];
7171
const symbolDeps = {};
72+
const asyncFuncs = [];
7273
let postSets = [];
7374

7475
LibraryManager.load();
@@ -247,6 +248,18 @@ function ${name}(${args}) {
247248

248249
const deps = LibraryManager.library[symbol + '__deps'] || [];
249250

251+
let isAsyncFunction = false;
252+
if (ASYNCIFY) {
253+
const original = LibraryManager.library[symbol];
254+
if (typeof original == 'function' ) {
255+
isAsyncFunction = LibraryManager.library[symbol + '__async'] ||
256+
original.constructor.name == 'AsyncFunction'
257+
}
258+
if (isAsyncFunction) {
259+
asyncFuncs.push(symbol);
260+
}
261+
}
262+
250263
if (symbolsOnly) {
251264
if (!isJsOnlySymbol(symbol) && LibraryManager.library.hasOwnProperty(symbol)) {
252265
externalDeps = deps.filter((d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library) && typeof d === 'string');
@@ -423,6 +436,9 @@ function ${name}(${args}) {
423436
if (sig && (RELOCATABLE || ASYNCIFY == 2)) {
424437
contentText += `\n${mangled}.sig = '${sig}';`;
425438
}
439+
if (ASYNCIFY && isAsyncFunction) {
440+
contentText += `\n${mangled}.isAsync = true;`;
441+
}
426442
if (isStub) {
427443
contentText += `\n${mangled}.stub = true;`;
428444
if (ASYNCIFY) {
@@ -529,6 +545,7 @@ function ${name}(${args}) {
529545
print('//FORWARDED_DATA:' + JSON.stringify({
530546
librarySymbols: librarySymbols,
531547
warnings: warnings,
548+
asyncFuncs,
532549
ATINITS: ATINITS.join('\n'),
533550
ATMAINS: ATMAINS.join('\n'),
534551
ATEXITS: ATEXITS.join('\n'),
@@ -540,7 +557,10 @@ function ${name}(${args}) {
540557
}
541558

542559
if (symbolsOnly) {
543-
print(JSON.stringify(symbolDeps));
560+
print(JSON.stringify({
561+
deps: symbolDeps,
562+
asyncFuncs
563+
}));
544564
return;
545565
}
546566

src/library_async.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ mergeInto(LibraryManager.library, {
4040
var original = imports[x];
4141
var sig = original.sig;
4242
if (typeof original == 'function') {
43-
var isAsyncifyImport = ASYNCIFY_IMPORTS.indexOf(x) >= 0 ||
43+
var isAsyncifyImport = original.isAsync ||
44+
ASYNCIFY_IMPORTS.indexOf(x) >= 0 ||
4445
x.startsWith('__asyncjs__');
4546
#if ASYNCIFY == 2
4647
// Wrap async imports with a suspending WebAssembly function.
@@ -452,6 +453,7 @@ mergeInto(LibraryManager.library, {
452453

453454
emscripten_sleep__sig: 'vi',
454455
emscripten_sleep__deps: ['$safeSetTimeout'],
456+
emscripten_sleep__async: true,
455457
emscripten_sleep: function(ms) {
456458
// emscripten_sleep() does not return a value, but we still need a |return|
457459
// here for stack switching support (ASYNCIFY=2). In that mode this function

src/utility.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ function isJsLibraryConfigIdentifier(ident) {
168168
'__noleakcheck',
169169
'__internal',
170170
'__user',
171+
'__async',
171172
];
172173
return suffixes.some((suffix) => ident.endsWith(suffix));
173174
}

0 commit comments

Comments
 (0)