Skip to content

Commit 033faad

Browse files
committed
Fix regression that broke calls to makeDynCall in JS library code. Old syntax is {{{ makeDynCall('sig') }}}(funcPtr, arg1, arg2);. New syntax is {{{ makeDynCall('sig', 'funcPtr') }}}(arg1, arg2). With this change, both old and new syntax work, with a compile time warning issued for old syntax to migrate to new one. Add ChangeLog entry about broken static dynCall invocation outside JS library files.
1 parent e7cf117 commit 033faad

5 files changed

+72
-0
lines changed

ChangeLog.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ Current Trunk
4848
input that perform all the normal post link steps such as finalizing and
4949
optimizing the wasm file and generating the JavaScript and/or html that will
5050
run it.
51+
- Added emulation support and a build time warning for calling Wasm function
52+
pointers from JS library files via the old syntax
53+
{{{ makeDynCall('sig') }}} (ptr, arg1, arg2);
54+
that was broken on Aug 31st 2020 (Emscripten 2.0.2). A build warning will now
55+
be emitted if the old signature is used. Convert to using the new signature
56+
{{{ makeDynCall('sig', 'ptr') }}} (arg1, arg2);
57+
instead.
5158

5259
2.0.8: 10/24/2020
5360
-----------------
@@ -134,6 +141,30 @@ Current Trunk
134141

135142
2.0.3: 09/10/2020
136143
-----------------
144+
- Breaking changes to calling Wasm function pointers from JavaScript:
145+
1. It is no longer possible to directly call dynCall_sig(funcPtr, param) to
146+
call a function pointer from JavaScript code. As a result, JavaScript code
147+
outside all JS libraries (pre-js/post-js/EM_ASM/EM_JS/external JS code) can no
148+
longer call a function pointer via static signature matching dynCall_sig, but
149+
must instead use the dynamic binding function
150+
151+
dynCall(sig, ptr, args);
152+
153+
This carries a significant performance overhead. The function dynCall is not
154+
available in -s MINIMAL_RUNTIME=1 builds.
155+
2. old syntax for calling a Wasm function pointer from a JS library file used
156+
to be
157+
158+
{{{ makeDynCall('sig') }}} (ptr, arg1, arg2);
159+
160+
This syntax will no longer work, and until Emscripten <2.0.9 causes
161+
a runtime error TypeError: WebAssembly.Table.get(): Argument 0 must be
162+
convertible to a valid number.
163+
164+
New syntax for calling Wasm function pointers from JS library files is
165+
166+
{{{ makeDynCall('sig', 'ptr') }}} (arg1, arg2);
167+
137168
- The native optimizer and the corresponding config setting
138169
(`EMSCRIPTEN_NATIVE_OPTIMIZER`) have been removed (it was only relevant to
139170
asmjs/fastcomp backend).

src/parseTools.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// Various tools for parsing LLVM. Utilities of various sorts, that are
88
// specific to Emscripten (and hence not in utility.js).
99

10+
var currentlyParsedFilename = '';
11+
1012
// Does simple 'macro' substitution, using Django-like syntax,
1113
// {{{ code }}} will be replaced with |eval(code)|.
1214
// NOTE: Be careful with that ret check. If ret is |0|, |ret ? ret.toString() : ''| would result in ''!
@@ -29,6 +31,7 @@ function processMacros(text) {
2931
// Param filenameHint can be passed as a description to identify the file that is being processed, used
3032
// to locate errors for reporting and for html files to stop expansion between <style> and </style>.
3133
function preprocess(text, filenameHint) {
34+
currentlyParsedFilename = filenameHint;
3235
var fileExt = (filenameHint) ? filenameHint.split('.').pop().toLowerCase() : "";
3336
var isHtml = (fileExt === 'html' || fileExt === 'htm') ? true : false;
3437
var inStyle = false;
@@ -985,6 +988,21 @@ function asmFFICoercion(value, type) {
985988

986989
function makeDynCall(sig, funcPtr) {
987990
assert(sig.indexOf('j') == -1);
991+
if (funcPtr === undefined) {
992+
printErr(`warning: ${currentlyParsedFilename}: Legacy use of {{{ makeDynCall("${sig}") }}}(funcPtr, arg1, arg2, ...). Starting from Emscripten 2.0.2 (Aug 31st 2020), syntax for makeDynCall has changed. New syntax is {{{ makeDynCall("${sig}", "funcPtr") }}}(arg1, arg2, ...). Please update to new syntax.`);
993+
var ret = (sig[0] == 'v') ? 'return ' : '';
994+
var args = [];
995+
for(var i = 1; i < sig.length; ++i) {
996+
args.push(`a${i}`);
997+
}
998+
args = args.join(', ');
999+
1000+
if (USE_LEGACY_DYNCALLS) {
1001+
return `(function(cb, ${args}) { return getDynCaller("${sig}", cb)(${args}) })`;
1002+
} else {
1003+
return `(function(cb, ${args}) { return wasmTable.get(cb)(${args}) })`;
1004+
}
1005+
}
9881006
if (USE_LEGACY_DYNCALLS) {
9891007
return `getDynCaller("${sig}", ${funcPtr})`;
9901008
} else {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mergeInto(LibraryManager.library, {
2+
callFunc: function(func, param1, param2) {
3+
{{{ makeDynCall('vii') }}} (func, param1, param2);
4+
}
5+
});

tests/test_old_dyncall_format.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdio.h>
2+
3+
void foo(int param1, int param2)
4+
{
5+
if (param1 == 42 && param2 == 100) printf("Test passed!\n");
6+
}
7+
8+
extern void callFunc(void (*foo)(int, int), int param1, int param2);
9+
10+
int main()
11+
{
12+
callFunc(foo, 42, 100);
13+
}

tests/test_other.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9617,6 +9617,11 @@ def test_oformat(self):
96179617
err = self.expect_fail([EMCC, path_from_root('tests', 'hello_world.c'), '--oformat=foo'])
96189618
self.assertContained("error: invalid output format: `foo` (must be one of ['wasm', 'js', 'mjs', 'html', 'bare']", err)
96199619

9620+
# Tests that the old format of {{{ makeDynCall('sig') }}}(func, param1) works
9621+
def test_old_makeDynCall_syntax(self):
9622+
err = self.run_process([EMCC, path_from_root('tests', 'test_old_dyncall_format.c'), '--js-library', path_from_root('tests', 'library_test_old_dyncall_format.js')], stderr=PIPE).stderr
9623+
self.assertContained('syntax for makeDynCall has changed', err)
9624+
96209625
def test_post_link(self):
96219626
err = self.run_process([EMCC, path_from_root('tests', 'hello_world.c'), '--oformat=bare', '-o', 'bare.wasm'], stderr=PIPE).stderr
96229627
self.assertContained('--oformat=base/--post-link are experimental and subject to change', err)

0 commit comments

Comments
 (0)