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
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.

3.1.61 (in development)
-----------------------
- The JSPI feature now uses the updated browser API for JSPI (available in
Chrome v126+). To support older versions of Chrome use Emscripten version
3.1.60 or earlier.

3.1.60 - 05/20/24
-----------------
Expand Down
25 changes: 11 additions & 14 deletions src/closure-externs/closure-externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ WebAssembly.Memory.prototype.buffer;
* @type {number}
*/
WebAssembly.Table.prototype.length;
/**
* @param {!Function} func
* @returns {Function}
*/
WebAssembly.promising = function(func) {};
/**
* @constructor
* @param {!Function} func
*/
WebAssembly.Suspending = function(func) {};

/**
* @record
Expand All @@ -125,26 +135,13 @@ FunctionType.prototype.parameters;
* @type {Array<string>}
*/
FunctionType.prototype.results;
/**
* @record
*/
function FunctionUsage() {}
/**
* @type {string|undefined}
*/
FunctionUsage.prototype.promising;
/**
* @type {string|undefined}
*/
FunctionUsage.prototype.suspending;

/**
* @constructor
* @param {!FunctionType} type
* @param {!Function} func
* @param {FunctionUsage=} usage
*/
WebAssembly.Function = function(type, func, usage) {};
WebAssembly.Function = function(type, func) {};
/**
* @param {Function} func
* @return {FunctionType}
Expand Down
6 changes: 6 additions & 0 deletions src/embind/embind.js
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,12 @@ var LibraryEmbind = {
assert(!isAsync, 'Async bindings are only supported with JSPI.');
#endif

#if ASYNCIFY == 2
if (isAsync) {
cppInvokerFunc = Asyncify.makeAsyncFunction(cppInvokerFunc);
}
#endif

var isClassMethodFunc = (argTypes[1] !== null && classType !== null);

// Free functions with signature "void function()" do not need an invoker that marshalls between wire types.
Expand Down
8 changes: 2 additions & 6 deletions src/jsifier.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -629,12 +629,8 @@ function(${args}) {
if ((EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) {
contentText += `\nModule['${mangled}'] = ${mangled};`;
}
// Relocatable code needs signatures to create proper wrappers. Stack
// switching needs signatures so we can create a proper
// WebAssembly.Function with the signature for the Promise API.
// TODO: For asyncify we could only add the signatures we actually need,
// of async imports/exports.
if (sig && (RELOCATABLE || ASYNCIFY == 2)) {
// Relocatable code needs signatures to create proper wrappers.
if (sig && RELOCATABLE) {
if (!WASM_BIGINT) {
sig = sig[0].replace('j', 'i') + sig.slice(1).replace(/j/g, 'ii');
}
Expand Down
30 changes: 3 additions & 27 deletions src/library_async.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ addToLibrary({
dbg('asyncify instrumenting imports');
#endif
#if ASSERTIONS && ASYNCIFY == 2
assert('Suspender' in WebAssembly, 'JSPI not supported by current environment. Perhaps it needs to be enabled via flags?');
assert('Suspending' in WebAssembly, 'JSPI not supported by current environment. Perhaps it needs to be enabled via flags?');
#endif
var importPattern = {{{ new RegExp(`^(${ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS.map(x => x.split('.')[1]).join('|').replace(/\*/g, '.*')})$`) }}};

Expand All @@ -52,21 +52,10 @@ addToLibrary({
#if ASYNCIFY == 2
// Wrap async imports with a suspending WebAssembly function.
if (isAsyncifyImport) {
#if ASSERTIONS
assert(original.sig, `Missing __sig for ${x}`);
#endif
let type = sigToWasmTypes(original.sig);
#if ASYNCIFY_DEBUG
dbg('asyncify: suspendOnReturnedPromise for', x, original);
#endif
// Add space for the suspender promise that will be used in the
// Wasm wrapper function.
type.parameters.unshift('externref');
imports[x] = original = new WebAssembly.Function(
type,
original,
{ suspending: 'first' }
);
imports[x] = original = new WebAssembly.Suspending(original);
}
#endif
#if ASSERTIONS && ASYNCIFY != 2 // We cannot apply assertions with stack switching, as the imports must not be modified from suspender.suspendOnReturnedPromise TODO find a way
Expand Down Expand Up @@ -454,20 +443,7 @@ addToLibrary({
#if ASYNCIFY_DEBUG
dbg('asyncify: returnPromiseOnSuspend for', original);
#endif
// TODO: remove `WebAssembly.Function.type` call when the new API is ready on all the testers.
var type = original.type ? original.type() : WebAssembly.Function.type(original);
var parameters = type.parameters;
var results = type.results;
#if ASSERTIONS
assert(results.length !== 0, 'There must be a return result')
assert(parameters[0] === 'externref', 'First param must be externref.');
#endif
// Remove the extern ref.
parameters.shift();
return new WebAssembly.Function(
{ parameters , results: ['externref'] },
original,
{ promising : 'first' });
return WebAssembly.promising(original);
},
#endif
},
Expand Down
40 changes: 7 additions & 33 deletions system/include/emscripten/bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,25 +454,6 @@ struct Invoker<ReturnPolicy, void, Args...> {
}
};

namespace async {

template<typename F, F f> struct Wrapper;
template<typename ReturnType, typename... Args, ReturnType(*f)(Args...)>
struct Wrapper<ReturnType(*)(Args...), f> {
EMSCRIPTEN_KEEPALIVE static ReturnType invoke(Args... args) {
return f(args...);
}
};

} // end namespace async

template<typename T, typename... Policies>
using maybe_wrap_async = typename std::conditional<
isAsync<Policies...>::value,
async::Wrapper<decltype(&T::invoke), &T::invoke>,
T
>::type;

template<typename ReturnPolicy, typename FunctorType, typename ReturnType, typename... Args>
struct FunctorInvoker {
static typename internal::BindingType<ReturnType>::WireType invoke(
Expand Down Expand Up @@ -601,8 +582,7 @@ void function(const char* name, ReturnType (*fn)(Args...), Policies...) {
using namespace internal;
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = Invoker<ReturnPolicy, ReturnType, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = Invoker<ReturnPolicy, ReturnType, Args...>::invoke;
_embind_register_function(
name,
args.getCount(),
Expand Down Expand Up @@ -1469,8 +1449,7 @@ struct RegisterClassMethod<ReturnType (ClassType::*)(Args...)> {
static void invoke(const char* methodName,
ReturnType (ClassType::*memberFunction)(Args...)) {
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = MethodInvoker<ReturnPolicy, decltype(memberFunction), ReturnType, ClassType*, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = MethodInvoker<ReturnPolicy, decltype(memberFunction), ReturnType, ClassType*, Args...>::invoke;

typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<ClassType>, Args...> args;
_embind_register_class_function(
Expand Down Expand Up @@ -1499,8 +1478,7 @@ struct RegisterClassMethod<ReturnType (ClassType::*)(Args...) const> {
static void invoke(const char* methodName,
ReturnType (ClassType::*memberFunction)(Args...) const) {
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = MethodInvoker<ReturnPolicy, decltype(memberFunction), ReturnType, const ClassType*, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = MethodInvoker<ReturnPolicy, decltype(memberFunction), ReturnType, const ClassType*, Args...>::invoke;

typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<const ClassType>, Args...> args;
_embind_register_class_function(
Expand Down Expand Up @@ -1530,8 +1508,7 @@ struct RegisterClassMethod<ReturnType (*)(ThisType, Args...)> {
ReturnType (*function)(ThisType, Args...)) {
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, ThisType, Args...> args;
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = FunctionInvoker<ReturnPolicy, decltype(function), ReturnType, ThisType, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = FunctionInvoker<ReturnPolicy, decltype(function), ReturnType, ThisType, Args...>::invoke;
_embind_register_class_function(
TypeID<ClassType>::get(),
methodName,
Expand Down Expand Up @@ -1559,8 +1536,7 @@ struct RegisterClassMethod<std::function<ReturnType (ThisType, Args...)>> {
std::function<ReturnType (ThisType, Args...)> function) {
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, ThisType, Args...> args;
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = FunctorInvoker<ReturnPolicy, decltype(function), ReturnType, ThisType, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = FunctorInvoker<ReturnPolicy, decltype(function), ReturnType, ThisType, Args...>::invoke;
_embind_register_class_function(
TypeID<ClassType>::get(),
methodName,
Expand All @@ -1582,8 +1558,7 @@ struct RegisterClassMethod<ReturnType (ThisType, Args...)> {
Callable& callable) {
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, ThisType, Args...> args;
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = FunctorInvoker<ReturnPolicy, decltype(callable), ReturnType, ThisType, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = FunctorInvoker<ReturnPolicy, decltype(callable), ReturnType, ThisType, Args...>::invoke;
_embind_register_class_function(
TypeID<ClassType>::get(),
methodName,
Expand Down Expand Up @@ -1866,8 +1841,7 @@ class class_ {

typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
using ReturnPolicy = GetReturnValuePolicy<ReturnType, Policies...>::tag;
using OriginalInvoker = internal::Invoker<ReturnPolicy, ReturnType, Args...>;
auto invoke = &maybe_wrap_async<OriginalInvoker, Policies...>::invoke;
auto invoke = internal::Invoker<ReturnPolicy, ReturnType, Args...>::invoke;
_embind_register_class_class_function(
TypeID<ClassType>::get(),
methodName,
Expand Down
1 change: 0 additions & 1 deletion test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -4942,7 +4942,6 @@ def test_valid_abspath_2(self):
else:
abs_include_path = '/nowhere/at/all'
cmd = [EMCC, test_file('hello_world.c'), '--valid-abspath', abs_include_path, '-I%s' % abs_include_path]
print(' '.join(cmd))
self.run_process(cmd)
self.assertContained('hello, world!', self.run_js('a.out.js'))

Expand Down
10 changes: 0 additions & 10 deletions tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@
DEFAULT_ASYNCIFY_EXPORTS = [
'main',
'__main_argc_argv',
# Embind's async template wrapper functions. These functions are usually in
# the function pointer table and not called from exports, but we need to name
# them so the JSPI pass can find and convert them.
'_ZN10emscripten8internal5async*'
]

VALID_ENVIRONMENTS = ('web', 'webview', 'worker', 'node', 'shell')
Expand Down Expand Up @@ -395,12 +391,6 @@ def check_human_readable_list(items):
if settings.ASYNCIFY_ONLY:
check_human_readable_list(settings.ASYNCIFY_ONLY)
passes += ['--pass-arg=asyncify-onlylist@%s' % ','.join(settings.ASYNCIFY_ONLY)]
elif settings.ASYNCIFY == 2:
passes += ['--jspi']
passes += ['--pass-arg=jspi-imports@%s' % ','.join(settings.ASYNCIFY_IMPORTS)]
passes += ['--pass-arg=jspi-exports@%s' % ','.join(settings.ASYNCIFY_EXPORTS)]
if settings.SPLIT_MODULE:
passes += ['--pass-arg=jspi-split-module']

if settings.MEMORY64 == 2:
passes += ['--memory64-lowering']
Expand Down