* A version of callPyObjectKwargs that supports the JSPI. * * It returns a promise. Inside Python, JS promises can be syncified, which * switches the stack to synchronously wait for them to be resolved. * * Pretty much everything is the same as callPyObjectKwargs except we use the * special JS
( ptrobj: number, jsargs: any, kwargs: any, )
| 574 | * _pyproxy_apply with the same arguments we gave to `promisingApply`. |
| 575 | */ |
| 576 | async function callPyObjectKwargsPromising( |
| 577 | ptrobj: number, |
| 578 | jsargs: any, |
| 579 | kwargs: any, |
| 580 | ) { |
| 581 | if (!Module.jspiSupported) { |
| 582 | throw new Error( |
| 583 | "WebAssembly stack switching not supported in this JavaScript runtime", |
| 584 | ); |
| 585 | } |
| 586 | // We don't do any checking for kwargs, checks are in PyProxy.callKwargs |
| 587 | // which only is used when the keyword arguments come from the user. |
| 588 | const num_pos_args = jsargs.length; |
| 589 | const kwargs_names = Object.keys(kwargs); |
| 590 | const kwargs_values = Object.values(kwargs); |
| 591 | const num_kwargs = kwargs_names.length; |
| 592 | jsargs.push(...kwargs_values); |
| 593 | // We have to take ownership of the pointer before we sleep or else it could |
| 594 | // get freed out from under us. This also fixes a bug that was around before |
| 595 | // we added the sleep. |
| 596 | _Py_IncRef(ptrobj); |
| 597 | // Yield to the event loop before starting the task. We may have been called by |
| 598 | // WebAssembly stack frames which own data on the stack. We can't overwrite that |
| 599 | // data until those stack frames are finished with it. Returning to the event |
| 600 | // loop first unwinds all the caller frames. That way enterTask() is free to |
| 601 | // place the task wherever it likes. |
| 602 | await new Promise<void>((res) => API.scheduleCallback(res, 0)); |
| 603 | let result; |
| 604 | const exc = _malloc(4); |
| 605 | try { |
| 606 | // enterTask() decides where the stackStop of the new task should be. |
| 607 | enterTask(); |
| 608 | Py_ENTER(); |
| 609 | // promisingApply clears the error flag and saves any error into excStatus. |
| 610 | // This ensures that tasks that are run between when promisingApply resolves |
| 611 | // and when this task resumes here won't incorrectly observe the error flag. |
| 612 | // See test_stack_switching.test_throw_from_switcher for a detailed |
| 613 | // explanation. |
| 614 | // |
| 615 | // The result of promisingApply() is wrapped in a one element list |
| 616 | // (regardless of whether there was an error or not) to ensure that we only |
| 617 | // await the stack switches and not a thenable result. |
| 618 | result = await Module.promisingApply( |
| 619 | ptrobj, |
| 620 | jsargs, |
| 621 | num_pos_args, |
| 622 | kwargs_names, |
| 623 | num_kwargs, |
| 624 | exc, |
| 625 | ); |
| 626 | Py_EXIT(); |
| 627 | } catch (e) { |
| 628 | API.fatal_error(e); |
| 629 | } |
| 630 | try { |
| 631 | // Unwrap result |
| 632 | result = result[0]; |
| 633 | if (result === Module.error) { |
no test coverage detected
searching dependent graphs…