@@ -143,6 +143,14 @@ export class Callback {
143143 result : "pointer" ;
144144 } > ;
145145
146+ // Keep native-facing buffers alive for as long as this Callback exists
147+ /** @private */
148+ _pyMethodDef ?: Uint8Array < ArrayBuffer > ;
149+ /** @private */
150+ _nameBuf ?: Uint8Array < ArrayBuffer > ;
151+ /** @private */
152+ _docBuf ?: Uint8Array < ArrayBuffer > ;
153+
146154 constructor ( public callback : PythonJSCallback ) {
147155 this . unsafe = new Deno . UnsafeCallback (
148156 {
@@ -490,49 +498,56 @@ export class PyObject {
490498 } else if ( v instanceof Callback ) {
491499 // https://docs.python.org/3/c-api/structures.html#c.PyMethodDef
492500 // there are extra 4 bytes of padding after ml_flags field
493- const pyMethodDef = new Uint8Array ( 8 + 8 + 4 + 4 + 8 ) ;
494- const view = new DataView ( pyMethodDef . buffer ) ;
501+ // Build and pin PyMethodDef + string buffers on the Callback instance
502+ const methodDef = new Uint8Array ( 8 + 8 + 4 + 4 + 8 ) ;
503+ v . _pyMethodDef = methodDef ;
504+ const view = new DataView ( methodDef . buffer ) ;
495505 const LE =
496506 new Uint8Array ( new Uint32Array ( [ 0x12345678 ] ) . buffer ) [ 0 ] !== 0x7 ;
497507
498508 const name = "JSCallback:" + ( v . callback . name || "anonymous" ) ;
499- const nameBuf = new TextEncoder ( ) . encode ( `${ name } \0` ) ;
509+ v . _nameBuf = new TextEncoder ( ) . encode ( `${ name } \0` ) ;
500510 view . setBigUint64 (
501511 0 ,
502- BigInt ( Deno . UnsafePointer . value ( Deno . UnsafePointer . of ( nameBuf ) ! ) ) ,
512+ BigInt (
513+ Deno . UnsafePointer . value ( Deno . UnsafePointer . of ( v . _nameBuf ) ! ) ,
514+ ) ,
503515 LE ,
504516 ) ;
505517 view . setBigUint64 (
506518 8 ,
507519 BigInt ( Deno . UnsafePointer . value ( v . unsafe . pointer ) ) ,
508520 LE ,
509521 ) ;
522+ // METH_VARARGS | METH_KEYWORDS
510523 view . setInt32 ( 16 , 0x1 | 0x2 , LE ) ;
524+
511525 // https://github.com/python/cpython/blob/f27593a87c344f3774ca73644a11cbd5614007ef/Objects/typeobject.c#L688
512526 const SIGNATURE_END_MARKER = ")\n--\n\n" ;
513527 // We're not using the correct arguments name, but just using dummy ones (because they're not accessible in js)
514528 const fnArgs = [ ...Array ( v . callback . length ) . keys ( ) ]
515529 . map ( ( _ , i ) => String . fromCharCode ( 97 + i ) ) . join ( "," ) ;
516- const docBuf = `${ name } (${ fnArgs } ${ SIGNATURE_END_MARKER } \0` ;
530+ v . _docBuf = new TextEncoder ( ) . encode (
531+ `${ name } (${ fnArgs } ${ SIGNATURE_END_MARKER } \0` ,
532+ ) ;
517533 view . setBigUint64 (
518534 24 ,
519535 BigInt (
520- Deno . UnsafePointer . value (
521- Deno . UnsafePointer . of ( new TextEncoder ( ) . encode ( docBuf ) ) ! ,
522- ) ,
536+ Deno . UnsafePointer . value ( Deno . UnsafePointer . of ( v . _docBuf ) ! ) ,
523537 ) ,
524538 LE ,
525539 ) ;
526540 const fn = py . PyCFunction_NewEx (
527- pyMethodDef ,
541+ v . _pyMethodDef ,
528542 PyObject . from ( null ) . handle ,
529543 null ,
530544 ) ;
531545
532546 // NOTE: we need to extend `pyMethodDef` lifetime
533547 // Otherwise V8 can release it before the callback is called
548+ // Is this still needed (after the change of pinning fields to the callabck) ? might be
534549 const pyObject = new PyObject ( fn ) ;
535- pyObject . #pyMethodDef = pyMethodDef ;
550+ pyObject . #pyMethodDef = methodDef ;
536551 return pyObject ;
537552 } else if ( v instanceof PyObject ) {
538553 return v ;
0 commit comments