@@ -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,45 @@ 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 length-delimited UTF-8, so copy it into a NUL-terminated
673+ # # `cstring`. Whichever of reply/error is unused rides as empty, safe to deref.
670674 quote:
671675 let box = cast [ptr `boxName`](ud)
672676 if box.isNil ():
673677 return
674678 if ret == RET_STALE_WARN :
675- # Non-terminal progress signal: keep the box, don't decode .
679+ # Non-terminal progress signal: keep the box, don't read the payload .
676680 return
677681 defer :
678682 freeBox (box)
679683 if box.fn.isNil ():
680684 return
681685 try :
686+ var payload = newString (int (len))
687+ if int (len) > 0 and not msg.isNil ():
688+ copyMem (addr payload[0 ], msg, int (len))
682689 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)
690+ box.fn (ret, " " .cstring , payload.cstring , box.ud)
687691 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)
692+ box.fn (RET_OK , payload.cstring , " " .cstring , box.ud)
694693 except CatchableError as e:
695694 box.fn (RET_ERR , " " .cstring , e.msg.cstring , box.ud)
696695
697696proc exportedMethodProc (
698697 spec: CAbiSpec , boxName, envWire, trampName, poolIdent, cbType: NimNode
699698): NimNode =
700- # No `foreignThreadGc`: `cwireUnpack`/`cborEncodeShared ` alloc on the calling thread (already GC-registered); wrapping would free its live ORC heap.
699+ # No `foreignThreadGc`: `cwireUnpack`/`cwirePack ` alloc on the calling thread (already GC-registered); wrapping would free its live ORC heap.
701700 let envName = spec.envelope
702701 let libFFICtx =
703702 nnkPtrTy.newTree (nnkBracketExpr.newTree (ident (" FFIContext" ), spec.libType))
@@ -713,23 +712,31 @@ proc exportedMethodProc(
713712 if not `poolIdent`.isValidCtx (cast [pointer ](ctx)):
714713 onReply (RET_ERR , `emptyReply`, " ctx is not a valid FFI context" .cstring , userData)
715714 return RET_ERR
716- var reqObj: `envName` = cwireUnpack (req[])
717- let enc = cborEncodeShared (reqObj)
718- let reqBuf = enc.data
719- let reqBufLen = enc.len
715+ var ownedWire: `envWire`
716+ cwirePack (ownedWire, cwireUnpack (req[]))
717+ let ownedCopy = cwireOwnedCopy (ownedWire)
718+ if ownedCopy.isNil ():
719+ cwireFree (ownedWire)
720+ onReply (RET_ERR , `emptyReply`, " out of memory" .cstring , userData)
721+ return RET_ERR
722+ let reqBuf = cast [ptr UncheckedArray [byte ]](ownedCopy)
720723 let box = cast [ptr `boxName`](allocBox (sizeof (`boxName`)))
721724 box.fn = onReply
722725 box.ud = userData
723726 let typeStr = $ `envName`
724727 let reqPtr = FFIThreadRequest .initFromOwnedShared (
725- `trampName`, box, typeStr.cstring , reqBuf, reqBufLen
728+ `trampName`, box, typeStr.cstring , reqBuf, sizeof (`envWire`), rawReply = true
726729 )
727730 let sendRes =
728731 try :
729732 ffi_context.sendRequestToFFIThread (ctx, reqPtr)
730733 except Exception as e:
731734 Result [void , string ].err (" sendRequestToFFIThread exception: " & e.msg)
732735 if sendRes.isErr ():
736+ # A rejected send already `deleteRequest`ed the struct copy, which frees only
737+ # the struct itself; `ownedWire` still aliases its field buffers, so free them
738+ # here — on success the FFI thread's unpack does it instead.
739+ cwireFree (ownedWire)
733740 onReply (RET_ERR , `emptyReply`, sendRes.error.cstring , userData)
734741 return RET_ERR
735742 return RET_OK
@@ -774,23 +781,31 @@ proc exportedCtorProc(
774781 )
775782 return nil
776783 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
784+ var ownedWire: `envWire`
785+ cwirePack (ownedWire, cwireUnpack (req[]))
786+ let ownedCopy = cwireOwnedCopy (ownedWire)
787+ if ownedCopy.isNil ():
788+ cwireFree (ownedWire)
789+ if not onCreated.isNil ():
790+ onCreated (RET_ERR , " " .cstring , " out of memory" .cstring , userData)
791+ return nil
792+ let reqBuf = cast [ptr UncheckedArray [byte ]](ownedCopy)
781793 let box = cast [ptr `boxName`](allocBox (sizeof (`boxName`)))
782794 box.fn = onCreated
783795 box.ud = userData
784796 let typeStr = $ `envName`
785797 let reqPtr = FFIThreadRequest .initFromOwnedShared (
786- `trampName`, box, typeStr.cstring , reqBuf, reqBufLen
798+ `trampName`, box, typeStr.cstring , reqBuf, sizeof (`envWire`), rawReply = true
787799 )
788800 let sendRes =
789801 try :
790802 ctx.sendRequestToFFIThread (reqPtr)
791803 except Exception as e:
792804 Result [void , string ].err (" sendRequestToFFIThread exception: " & e.msg)
793805 if sendRes.isErr ():
806+ # See exportedMethodProc: the rejected send freed the struct copy, not the
807+ # field buffers `ownedWire` still aliases.
808+ cwireFree (ownedWire)
794809 if not onCreated.isNil ():
795810 onCreated (RET_ERR , " " .cstring , sendRes.error.cstring , userData)
796811 return nil
@@ -838,6 +853,7 @@ proc flushCAbiDispatch*(): NimNode {.compileTime.} =
838853 for spec in cAbiSpecs:
839854 let envName = spec.envelope
840855 ensureCWireForFields (sink , $ envName, spec.paramNames, spec.paramTypes)
856+ sink .add (spec.handler)
841857 let envWire = ident (cwireTypeName ($ envName))
842858 let boxName = ident ($ envName & " CBox" )
843859 let trampName = ident ($ envName & " CReply" )
@@ -861,7 +877,7 @@ proc flushCAbiDispatch*(): NimNode {.compileTime.} =
861877 let respWire = ident (cwireTypeName ($ rt))
862878 let cbType = cAbiCbType (nnkPtrTy.newTree (respWire))
863879 sink .add (boxTypeDef (boxName, cbType))
864- sink .add (replyTrampProc (trampName, objectTrampBody (boxName, rt, respWire)))
880+ sink .add (replyTrampProc (trampName, objectTrampBody (boxName, respWire)))
865881 sink .add (
866882 exportedMethodProc (spec, boxName, envWire, trampName, poolIdent, cbType)
867883 )
0 commit comments