@@ -22,7 +22,7 @@ proc markCWireEmitted(typeName: string) {.compileTime.} =
2222 if not isCWireEmitted (typeName):
2323 emittedCWireTypes.add (typeName)
2424
25- proc cwireTypeName (userTypeName: string ): string =
25+ proc cwireTypeName * (userTypeName: string ): string =
2626 userTypeName & " _CWire"
2727
2828proc seqItemsField (obj, field: NimNode ): NimNode =
@@ -31,7 +31,7 @@ proc seqItemsField(obj, field: NimNode): NimNode =
3131proc seqLenField (obj, field: NimNode ): NimNode =
3232 newDotExpr (obj, ident ($ field & cwireLenSuffix))
3333
34- proc isStringType (t: NimNode ): bool =
34+ proc isStringType * (t: NimNode ): bool =
3535 t.kind == nnkIdent and ($ t == " string" or $ t == " cstring" )
3636
3737proc isBracketOf (t: NimNode , heads: openArray [string ]): bool =
@@ -294,12 +294,12 @@ proc emitSeqPack(dstObj, srcAccess, fieldNameIdent, userType: NimNode): NimNode
294294 `items` = nil
295295 `count` = 0
296296 else :
297- `items` = cast [`bufType`](allocShared (sizeof (`wireElem`) * `srcAccess`.len ()))
297+ `items` = cast [`bufType`](cwireAllocBuf (sizeof (`wireElem`) * `srcAccess`.len ()))
298298 `forLoop`
299299 `count` = `srcAccess`.len ()
300300
301301proc emitOptionPack (dstAccess, srcAccess, userType: NimNode ): NimNode =
302- # # Pack an Option into a `ptr`: some → `allocShared ` box, none → nil. Payload
302+ # # Pack an Option into a `ptr`: some → `cwireAllocBuf ` box, none → nil. Payload
303303 # # read into a local once so a composite inner type isn't re-`get()` per element.
304304 let innerType = userType[1 ]
305305 let wireInner = wireValueType (innerType)
@@ -308,7 +308,7 @@ proc emitOptionPack(dstAccess, srcAccess, userType: NimNode): NimNode =
308308 let elemPack = emitElemPack (nnkBracketExpr.newTree (dstAccess), innerVal, innerType)
309309 quote:
310310 if `srcAccess`.isSome ():
311- `dstAccess` = cast [`bufType`](allocShared (sizeof (`wireInner`)))
311+ `dstAccess` = cast [`bufType`](cwireAllocBuf (sizeof (`wireInner`)))
312312 let `innerVal` = `srcAccess`.get ()
313313 `elemPack`
314314 else :
@@ -379,7 +379,7 @@ proc emitSeqFree(dstObj, fieldNameIdent, userType: NimNode): NimNode =
379379 quote:
380380 if not `items`.isNil ():
381381 `freeLoop`
382- deallocShared (`items`)
382+ cwireFreeBuf (`items`)
383383 `items` = nil
384384 `count` = 0
385385
@@ -390,7 +390,7 @@ proc emitOptionFree(dstAccess, userType: NimNode): NimNode =
390390 quote:
391391 if not `dstAccess`.isNil ():
392392 `freeInner`
393- deallocShared (`dstAccess`)
393+ cwireFreeBuf (`dstAccess`)
394394 `dstAccess` = nil
395395
396396proc emitFreeStmt (dstObj, fieldNameIdent, userType: NimNode ): seq [NimNode ] =
539539 paramNames: seq [string ] # # envelope field names (the extra params)
540540 paramTypes: seq [NimNode ] # # envelope field types
541541 respType: NimNode # # method result T; empty for a ctor
542+ handler: NimNode
543+ # # FFI-thread handler, deferred here so it lands after the `_CWire`
544+ # # companions it packs/unpacks through.
542545
543546var cAbiSpecs {.compileTime .}: seq [CAbiSpec ]
544547
@@ -553,7 +556,7 @@ proc registerCAbiMethod*(
553556 libType, envelope: NimNode ,
554557 paramNames: seq [string ],
555558 paramTypes: seq [NimNode ],
556- respType: NimNode ,
559+ respType, handler : NimNode ,
557560) {.compileTime .} =
558561 # # Record an `abi = c` method for `flushCAbiDispatch`. Nodes are `copyNimTree`
559562 # # frozen: reusing the Req section's originals (bound to `nnkSym`) would ICE.
@@ -566,6 +569,7 @@ proc registerCAbiMethod*(
566569 paramNames: paramNames,
567570 paramTypes: copyTypes (paramTypes),
568571 respType: respType.copyNimTree (),
572+ handler: handler.copyNimTree (),
569573 )
570574 )
571575
@@ -574,6 +578,7 @@ proc registerCAbiCtor*(
574578 libType, envelope: NimNode ,
575579 paramNames: seq [string ],
576580 paramTypes: seq [NimNode ],
581+ handler: NimNode ,
577582) {.compileTime .} =
578583 # # Record an `abi = c` ctor for `flushCAbiDispatch`; see `registerCAbiMethod`
579584 # # for why nodes are `copyNimTree` frozen.
@@ -586,6 +591,7 @@ proc registerCAbiCtor*(
586591 paramNames: paramNames,
587592 paramTypes: copyTypes (paramTypes),
588593 respType: newEmptyNode (),
594+ handler: handler.copyNimTree (),
589595 )
590596 )
591597
@@ -630,15 +636,16 @@ proc replyTrampProc(trampName, body: NimNode): NimNode =
630636 pragmas = cdeclReplyPragma (),
631637 )
632638
633- proc objectTrampBody (boxName, respType, respWire: NimNode ): NimNode =
634- # # Reply trampoline for an object return: decode, `cwirePack` into `_CWire`,
635- # # hand a pointer to the caller, release. `reply` is nil only on error.
639+ proc objectTrampBody (boxName, respWire: NimNode ): NimNode =
640+ # # Reply trampoline for an object return: the payload is already the packed
641+ # # `_CWire` image, so hand its address straight to the caller and release the
642+ # # buffers it owns. `reply` is nil only on error.
636643 quote:
637644 let box = cast [ptr `boxName`](ud)
638645 if box.isNil ():
639646 return
640647 if ret == RET_STALE_WARN :
641- # Non-terminal progress signal: keep the box, don't decode .
648+ # Non-terminal progress signal: keep the box, don't read the payload .
642649 return
643650 defer :
644651 freeBox (box)
@@ -651,53 +658,46 @@ proc objectTrampBody(boxName, respType, respWire: NimNode): NimNode =
651658 copyMem (addr em[0 ], msg, int (len))
652659 box.fn (ret, nil , em.cstring , box.ud)
653660 return
654- let decoded =
655- cborDecodePtr (cast [ptr UncheckedArray [byte ]](msg), int (len), `respType`)
656- if decoded.isErr ():
657- box.fn (RET_ERR , nil , decoded.error.cstring , box.ud)
658- else :
659- var wire: `respWire`
660- cwirePack (wire, decoded.get ())
661- box.fn (RET_OK , addr wire, " " .cstring , box.ud)
662- cwireFree (wire)
661+ if msg.isNil () or int (len) != sizeof (`respWire`):
662+ box.fn (RET_ERR , nil , " abi = c reply: unexpected payload size" .cstring , box.ud)
663+ return
664+ var wire = cast [ptr `respWire`](msg)[]
665+ box.fn (RET_OK , addr wire, " " .cstring , box.ud)
666+ cwireFree (wire)
663667 except CatchableError as e:
664668 box.fn (RET_ERR , nil , e.msg.cstring , box.ud)
665669
666670proc stringTrampBody (boxName: NimNode ): NimNode =
667- # # Reply trampoline for a `string` return (and the ctor's address string):
668- # # decode and hand the caller a NUL-terminated `cstring`. Reply/error strings
669- # # are always non-nil empty on the paths they don't apply to (no nil deref).
671+ # # Reply trampoline for a `string` return (and the ctor's address string): the
672+ # # payload is raw UTF-8, copied into a NUL-terminated `cstring` since the wire
673+ # # bytes aren't terminated. Reply/error strings are always non-nil empty on the
674+ # # paths they don't apply to (no nil deref).
670675 quote:
671676 let box = cast [ptr `boxName`](ud)
672677 if box.isNil ():
673678 return
674679 if ret == RET_STALE_WARN :
675- # Non-terminal progress signal: keep the box, don't decode .
680+ # Non-terminal progress signal: keep the box, don't read the payload .
676681 return
677682 defer :
678683 freeBox (box)
679684 if box.fn.isNil ():
680685 return
681686 try :
687+ var payload = newString (int (len))
688+ if int (len) > 0 and not msg.isNil ():
689+ copyMem (addr payload[0 ], msg, int (len))
682690 if ret != RET_OK :
683- var em = newString (int (len))
684- if int (len) > 0 :
685- copyMem (addr em[0 ], msg, int (len))
686- box.fn (ret, " " .cstring , em.cstring , box.ud)
691+ box.fn (ret, " " .cstring , payload.cstring , box.ud)
687692 return
688- let decoded = cborDecodePtr (cast [ptr UncheckedArray [byte ]](msg), int (len), string )
689- if decoded.isErr ():
690- box.fn (RET_ERR , " " .cstring , decoded.error.cstring , box.ud)
691- else :
692- let replyStr = decoded.get ()
693- box.fn (RET_OK , replyStr.cstring , " " .cstring , box.ud)
693+ box.fn (RET_OK , payload.cstring , " " .cstring , box.ud)
694694 except CatchableError as e:
695695 box.fn (RET_ERR , " " .cstring , e.msg.cstring , box.ud)
696696
697697proc exportedMethodProc (
698698 spec: CAbiSpec , boxName, envWire, trampName, poolIdent, cbType: NimNode
699699): NimNode =
700- # No `foreignThreadGc`: `cwireUnpack`/`cborEncodeShared ` alloc on the calling thread (already GC-registered); wrapping would free its live ORC heap.
700+ # No `foreignThreadGc`: `cwireUnpack`/`cwirePack ` alloc on the calling thread (already GC-registered); wrapping would free its live ORC heap.
701701 let envName = spec.envelope
702702 let libFFICtx =
703703 nnkPtrTy.newTree (nnkBracketExpr.newTree (ident (" FFIContext" ), spec.libType))
@@ -713,23 +713,31 @@ proc exportedMethodProc(
713713 if not `poolIdent`.isValidCtx (cast [pointer ](ctx)):
714714 onReply (RET_ERR , `emptyReply`, " ctx is not a valid FFI context" .cstring , userData)
715715 return RET_ERR
716- var reqObj: `envName` = cwireUnpack (req[])
717- let enc = cborEncodeShared (reqObj)
718- let reqBuf = enc.data
719- let reqBufLen = enc.len
716+ var ownedWire: `envWire`
717+ cwirePack (ownedWire, cwireUnpack (req[]))
718+ let ownedCopy = cwireOwnedCopy (ownedWire)
719+ if ownedCopy.isNil ():
720+ cwireFree (ownedWire)
721+ onReply (RET_ERR , `emptyReply`, " out of memory" .cstring , userData)
722+ return RET_ERR
723+ let reqBuf = cast [ptr UncheckedArray [byte ]](ownedCopy)
720724 let box = cast [ptr `boxName`](allocBox (sizeof (`boxName`)))
721725 box.fn = onReply
722726 box.ud = userData
723727 let typeStr = $ `envName`
724728 let reqPtr = FFIThreadRequest .initFromOwnedShared (
725- `trampName`, box, typeStr.cstring , reqBuf, reqBufLen
729+ `trampName`, box, typeStr.cstring , reqBuf, sizeof (`envWire`), rawReply = true
726730 )
727731 let sendRes =
728732 try :
729733 ffi_context.sendRequestToFFIThread (ctx, reqPtr)
730734 except Exception as e:
731735 Result [void , string ].err (" sendRequestToFFIThread exception: " & e.msg)
732736 if sendRes.isErr ():
737+ # A rejected send already `deleteRequest`ed the struct copy, which frees only
738+ # the struct itself; `ownedWire` still aliases its field buffers, so free them
739+ # here — on success the FFI thread's unpack does it instead.
740+ cwireFree (ownedWire)
733741 onReply (RET_ERR , `emptyReply`, sendRes.error.cstring , userData)
734742 return RET_ERR
735743 return RET_OK
@@ -774,23 +782,31 @@ proc exportedCtorProc(
774782 )
775783 return nil
776784 let ctx = ctxRes.get ()
777- var reqObj: `envName` = cwireUnpack (req[])
778- let enc = cborEncodeShared (reqObj)
779- let reqBuf = enc.data
780- let reqBufLen = enc.len
785+ var ownedWire: `envWire`
786+ cwirePack (ownedWire, cwireUnpack (req[]))
787+ let ownedCopy = cwireOwnedCopy (ownedWire)
788+ if ownedCopy.isNil ():
789+ cwireFree (ownedWire)
790+ if not onCreated.isNil ():
791+ onCreated (RET_ERR , " " .cstring , " out of memory" .cstring , userData)
792+ return nil
793+ let reqBuf = cast [ptr UncheckedArray [byte ]](ownedCopy)
781794 let box = cast [ptr `boxName`](allocBox (sizeof (`boxName`)))
782795 box.fn = onCreated
783796 box.ud = userData
784797 let typeStr = $ `envName`
785798 let reqPtr = FFIThreadRequest .initFromOwnedShared (
786- `trampName`, box, typeStr.cstring , reqBuf, reqBufLen
799+ `trampName`, box, typeStr.cstring , reqBuf, sizeof (`envWire`), rawReply = true
787800 )
788801 let sendRes =
789802 try :
790803 ctx.sendRequestToFFIThread (reqPtr)
791804 except Exception as e:
792805 Result [void , string ].err (" sendRequestToFFIThread exception: " & e.msg)
793806 if sendRes.isErr ():
807+ # See exportedMethodProc: the rejected send freed the struct copy, not the
808+ # field buffers `ownedWire` still aliases.
809+ cwireFree (ownedWire)
794810 if not onCreated.isNil ():
795811 onCreated (RET_ERR , " " .cstring , sendRes.error.cstring , userData)
796812 return nil
@@ -838,6 +854,7 @@ proc flushCAbiDispatch*(): NimNode {.compileTime.} =
838854 for spec in cAbiSpecs:
839855 let envName = spec.envelope
840856 ensureCWireForFields (sink , $ envName, spec.paramNames, spec.paramTypes)
857+ sink .add (spec.handler)
841858 let envWire = ident (cwireTypeName ($ envName))
842859 let boxName = ident ($ envName & " CBox" )
843860 let trampName = ident ($ envName & " CReply" )
@@ -861,7 +878,7 @@ proc flushCAbiDispatch*(): NimNode {.compileTime.} =
861878 let respWire = ident (cwireTypeName ($ rt))
862879 let cbType = cAbiCbType (nnkPtrTy.newTree (respWire))
863880 sink .add (boxTypeDef (boxName, cbType))
864- sink .add (replyTrampProc (trampName, objectTrampBody (boxName, rt, respWire)))
881+ sink .add (replyTrampProc (trampName, objectTrampBody (boxName, respWire)))
865882 sink .add (
866883 exportedMethodProc (spec, boxName, envWire, trampName, poolIdent, cbType)
867884 )
0 commit comments