1- import { create } from 'gc-hook' ;
2-
3- import { RUNNING_IN_WORKER , createProgress , writeFile } from './_utils.js' ;
1+ import { createProgress , writeFile } from './_utils.js' ;
42import { getFormat , loader , loadProgress , registerJSModule , run , runAsync , runEvent } from './_python.js' ;
53import { stdio } from './_io.js' ;
64import { IDBMapSync , isArray , fixedRelative } from '../utils.js' ;
@@ -10,67 +8,56 @@ const toJsOptions = { dict_converter: Object.fromEntries };
108
119const { stringify } = JSON ;
1210
11+ const { apply } = Reflect ;
12+ const FunctionPrototype = Function . prototype ;
13+
1314// REQUIRES INTEGRATION TEST
1415/* c8 ignore start */
15- let overrideFunction = false ;
16- const overrideMethod = method => ( ...args ) => {
17- try {
18- overrideFunction = true ;
19- return method ( ...args ) ;
20- }
21- finally {
22- overrideFunction = false ;
23- }
16+ const overrideMethod = method => function ( ...args ) {
17+ return apply ( method , this , args ) ;
2418} ;
2519
26- let overridden = false ;
27- const applyOverride = ( ) => {
28- if ( overridden ) return ;
29- overridden = true ;
20+ let pyproxy , to_js ;
21+ const override = intercept => {
3022
3123 const proxies = new WeakMap ;
32- const onGC = value => value . destroy ( ) ;
33- const patchArgs = args => {
34- for ( let i = 0 ; i < args . length ; i ++ ) {
35- const value = args [ i ] ;
36- if (
37- typeof value === 'function' &&
38- 'copy' in value
39- ) {
40- // avoid seppuku / Harakiri + speed up
41- overrideFunction = false ;
42- // reuse copied value if known already
43- let proxy = proxies . get ( value ) ?. deref ( ) ;
44- if ( ! proxy ) {
45- try {
46- // observe the copy and return a Proxy reference
47- proxy = create ( value . copy ( ) , onGC ) ;
48- proxies . set ( value , new WeakRef ( proxy ) ) ;
49- }
50- catch ( error ) {
51- console . error ( error ) ;
24+
25+ const patch = args => {
26+ for ( let arg , i = 0 ; i < args . length ; i ++ ) {
27+ switch ( typeof ( arg = args [ i ] ) ) {
28+ case 'object' :
29+ if ( arg === null ) break ;
30+ // falls through
31+ case 'function' : {
32+ if ( pyproxy in arg && ! arg [ pyproxy ] . shared ?. gcRegistered ) {
33+ intercept = false ;
34+ let proxy = proxies . get ( arg ) ?. deref ( ) ;
35+ if ( ! proxy ) {
36+ proxy = to_js ( arg ) ;
37+ const wr = new WeakRef ( proxy ) ;
38+ proxies . set ( arg , wr ) ;
39+ proxies . set ( proxy , wr ) ;
40+ }
41+ args [ i ] = proxy ;
42+ intercept = true ;
5243 }
44+ break ;
5345 }
54- if ( proxy ) args [ i ] = proxy ;
55- overrideFunction = true ;
5646 }
5747 }
5848 } ;
5949
60- // trap apply to make call possible after the patch
61- const { call } = Function ;
62- const apply = call . bind ( call , call . apply ) ;
6350 // the patch
64- Object . defineProperties ( Function . prototype , {
51+ Object . defineProperties ( FunctionPrototype , {
6552 apply : {
6653 value ( context , args ) {
67- if ( overrideFunction ) patchArgs ( args ) ;
54+ if ( intercept ) patch ( args ) ;
6855 return apply ( this , context , args ) ;
6956 }
7057 } ,
7158 call : {
7259 value ( context , ...args ) {
73- if ( overrideFunction ) patchArgs ( args ) ;
60+ if ( intercept ) patch ( args ) ;
7461 return apply ( this , context , args ) ;
7562 }
7663 }
@@ -82,12 +69,9 @@ const indexURLs = new WeakMap();
8269
8370export default {
8471 type,
85- module : ( version = '0.27.5 ' ) =>
72+ module : ( version = '0.27.6 ' ) =>
8673 `https://cdn.jsdelivr.net/pyodide/v${ version } /full/pyodide.mjs` ,
8774 async engine ( { loadPyodide } , config , url , baseURL ) {
88- // apply override ASAP then load foreign code
89- if ( ! RUNNING_IN_WORKER && config . experimental_create_proxy === 'auto' )
90- applyOverride ( ) ;
9175 progress ( 'Loading Pyodide' ) ;
9276 let { packages, index_urls } = config ;
9377 if ( packages ) packages = packages . map ( fixedRelative , baseURL ) ;
@@ -134,13 +118,30 @@ export default {
134118 await storage . close ( ) ;
135119 if ( options . lockFileURL ) URL . revokeObjectURL ( options . lockFileURL ) ;
136120 progress ( 'Loaded Pyodide' ) ;
121+ if ( config . experimental_create_proxy === 'auto' ) {
122+ interpreter . runPython ( [
123+ 'import js' ,
124+ 'from pyodide.ffi import to_js' ,
125+ 'o=js.Object.fromEntries' ,
126+ 'js.experimental_create_proxy=lambda r:to_js(r,dict_converter=o)'
127+ ] . join ( ';' ) , { globals : interpreter . toPy ( { } ) } ) ;
128+ to_js = globalThis . experimental_create_proxy ;
129+ delete globalThis . experimental_create_proxy ;
130+ [ pyproxy ] = Reflect . ownKeys ( to_js ) . filter (
131+ k => (
132+ typeof k === 'symbol' &&
133+ String ( k ) === 'Symbol(pyproxy.attrs)'
134+ )
135+ ) ;
136+ override ( true ) ;
137+ }
137138 return interpreter ;
138139 } ,
139140 registerJSModule,
140141 run : overrideMethod ( run ) ,
141142 runAsync : overrideMethod ( runAsync ) ,
142143 runEvent : overrideMethod ( runEvent ) ,
143- transform : ( interpreter , value ) => transform . call ( interpreter , value ) ,
144+ transform : ( interpreter , value ) => apply ( transform , interpreter , [ value ] ) ,
144145 writeFile : ( interpreter , path , buffer , url ) => {
145146 const format = getFormat ( path , url ) ;
146147 if ( format ) {
0 commit comments