@@ -632,9 +632,6 @@ proc registerCAbiCtor*(
632632 )
633633 )
634634
635- proc isStringResp (t: NimNode ): bool =
636- t.kind == nnkIdent and ($ t == " string" or $ t == " cstring" )
637-
638635proc cdeclReplyPragma (): NimNode =
639636 nnkPragma.newTree (
640637 ident (" cdecl" ),
@@ -683,7 +680,8 @@ proc objectTrampBody(boxName, respType, respWire: NimNode): NimNode =
683680 # # Reply trampoline for an object return: recover the box, deliver a transport
684681 # # error as a copied NUL-terminated string, else CBOR-decode the reply,
685682 # # `cwirePack` it into the flat wire struct, hand a pointer to the caller, and
686- # # release the wire.
683+ # # release the wire. `err_msg` is always a non-nil string; the `reply` struct
684+ # # pointer is nil only on error, gated by a non-`RET_OK` `err_code`.
687685 quote:
688686 let box = cast [ptr `boxName`](ud)
689687 if box.isNil ():
@@ -706,15 +704,17 @@ proc objectTrampBody(boxName, respType, respWire: NimNode): NimNode =
706704 else :
707705 var wire: `respWire`
708706 cwirePack (wire, decoded.get ())
709- box.fn (RET_OK , addr wire, nil , box.ud)
707+ box.fn (RET_OK , addr wire, " " . cstring , box.ud)
710708 cwireFree (wire)
711709 except CatchableError as e:
712710 box.fn (RET_ERR , nil , e.msg.cstring , box.ud)
713711
714712proc stringTrampBody (boxName: NimNode ): NimNode =
715713 # # Reply trampoline for a `string` return (and the ctor's address string):
716714 # # CBOR-decode the reply into a Nim string and hand its (NUL-terminated)
717- # # `cstring` to the caller for the duration of the call.
715+ # # `cstring` to the caller for the duration of the call. Reply and error
716+ # # strings are always non-nil empty strings on the paths they don't apply to,
717+ # # so a consumer can `strlen`/print either unconditionally without a nil deref.
718718 quote:
719719 let box = cast [ptr `boxName`](ud)
720720 if box.isNil ():
@@ -728,16 +728,16 @@ proc stringTrampBody(boxName: NimNode): NimNode =
728728 var em = newString (int (len))
729729 if int (len) > 0 :
730730 copyMem (addr em[0 ], msg, int (len))
731- box.fn (ret, nil , em.cstring , box.ud)
731+ box.fn (ret, " " . cstring , em.cstring , box.ud)
732732 return
733733 let decoded = cborDecodePtr (cast [ptr UncheckedArray [byte ]](msg), int (len), string )
734734 if decoded.isErr ():
735- box.fn (RET_ERR , nil , decoded.error.cstring , box.ud)
735+ box.fn (RET_ERR , " " . cstring , decoded.error.cstring , box.ud)
736736 else :
737737 let replyStr = decoded.get ()
738- box.fn (RET_OK , replyStr.cstring , nil , box.ud)
738+ box.fn (RET_OK , replyStr.cstring , " " . cstring , box.ud)
739739 except CatchableError as e:
740- box.fn (RET_ERR , nil , e.msg.cstring , box.ud)
740+ box.fn (RET_ERR , " " . cstring , e.msg.cstring , box.ud)
741741
742742proc exportedMethodProc (
743743 spec: CAbiSpec , boxName, envWire, trampName, poolIdent, cbType: NimNode
@@ -751,11 +751,18 @@ proc exportedMethodProc(
751751 let envName = spec.envelope
752752 let libFFICtx =
753753 nnkPtrTy.newTree (nnkBracketExpr.newTree (ident (" FFIContext" ), spec.libType))
754+ # A string reply is an empty (non-nil) cstring on the error path, matching the
755+ # trampoline; an object reply is a nil struct pointer gated by `err_code`.
756+ let emptyReply =
757+ if isStringType (spec.respType):
758+ newDotExpr (newLit (" " ), ident (" cstring" ))
759+ else :
760+ newNilLit ()
754761 let body = quote:
755762 if onReply.isNil ():
756763 return RET_MISSING_CALLBACK
757764 if not `poolIdent`.isValidCtx (cast [pointer ](ctx)):
758- onReply (RET_ERR , nil , " ctx is not a valid FFI context" .cstring , userData)
765+ onReply (RET_ERR , `emptyReply` , " ctx is not a valid FFI context" .cstring , userData)
759766 return RET_ERR
760767 var reqObj: `envName` = cwireUnpack (req[])
761768 let enc = cborEncodeShared (reqObj)
@@ -771,10 +778,10 @@ proc exportedMethodProc(
771778 let sendRes =
772779 try :
773780 ffi_context.sendRequestToFFIThread (ctx, reqPtr)
774- except Exception as exc :
775- Result [void , string ].err (" sendRequestToFFIThread exception: " & exc .msg)
781+ except Exception as e :
782+ Result [void , string ].err (" sendRequestToFFIThread exception: " & e .msg)
776783 if sendRes.isErr ():
777- onReply (RET_ERR , nil , sendRes.error.cstring , userData)
784+ onReply (RET_ERR , `emptyReply` , sendRes.error.cstring , userData)
778785 return RET_ERR
779786 return RET_OK
780787 newProc (
@@ -815,7 +822,7 @@ proc exportedCtorProc(
815822 if not onCreated.isNil ():
816823 onCreated (
817824 RET_ERR ,
818- nil ,
825+ " " . cstring ,
819826 (" ffiCtor: failed to create FFIContext: " & $ ctxRes.error).cstring ,
820827 userData,
821828 )
@@ -835,11 +842,11 @@ proc exportedCtorProc(
835842 let sendRes =
836843 try :
837844 ctx.sendRequestToFFIThread (reqPtr)
838- except Exception as exc :
839- Result [void , string ].err (" sendRequestToFFIThread exception: " & exc .msg)
845+ except Exception as e :
846+ Result [void , string ].err (" sendRequestToFFIThread exception: " & e .msg)
840847 if sendRes.isErr ():
841848 if not onCreated.isNil ():
842- onCreated (RET_ERR , nil , sendRes.error.cstring , userData)
849+ onCreated (RET_ERR , " " . cstring , sendRes.error.cstring , userData)
843850 return nil
844851 return cast [pointer ](ctx)
845852 body.insert (0 , initGuard)
@@ -899,7 +906,7 @@ proc flushCAbiDispatch*(): NimNode {.compileTime.} =
899906 sink .add (exportedCtorProc (spec, boxName, envWire, trampName, poolIdent, cbType))
900907 of cakMethod:
901908 let rt = spec.respType
902- if isStringResp (rt):
909+ if isStringType (rt):
903910 let cbType = cAbiCbType (ident (" cstring" ))
904911 sink .add (boxTypeDef (boxName, cbType))
905912 sink .add (replyTrampProc (trampName, stringTrampBody (boxName)))
0 commit comments