Skip to content

Commit b2f4961

Browse files
committed
fix: pr comments
1 parent 4a18627 commit b2f4961

3 files changed

Lines changed: 52 additions & 53 deletions

File tree

examples/echo/c_abi_bindings/echo.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
#include <stdlib.h>
77
#include <string.h>
88

9-
#ifndef NIMFFI_RET_OK
109
#define NIMFFI_RET_OK 0
1110
#define NIMFFI_RET_ERR 1
1211
#define NIMFFI_RET_MISSING_CALLBACK 2
13-
#endif
1412

1513
/* Flat wire structs — the C ABI. Strings are borrowed, NUL-terminated
1614
`const char*` valid only for the duration of the call they cross. */
@@ -60,6 +58,7 @@ typedef void (*EchoCreateFn)(int err_code, EchoCtx* ctx, const char* err_msg, vo
6058
typedef struct { EchoCreateFn fn; void* user_data; } EchoCreateBox;
6159
static void echo_create_trampoline(int ret, const char* ctx_addr, const char* err_msg, void* ud) {
6260
EchoCreateBox* box = (EchoCreateBox*)ud;
61+
if (!box) return;
6362
if (!box->fn) { free(box); return; }
6463
if (ret != 0) {
6564
box->fn(ret, NULL, err_msg ? err_msg : "FFI create failed", box->user_data);

ffi/codegen/c_abi.nim

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,28 @@ func paramByValue(nimType: string, ridesAsPtr: bool): bool =
143143
return true
144144
leafCTypeAbi(nimType.strip()).ok
145145

146+
proc reqParamsAndAssigns(
147+
reg: var AbiReg, extraParams: seq[FFIParamMeta]
148+
): tuple[params, assigns: seq[string]] =
149+
## The C parameter list + `ffi_req` field assignments shared by the ctor and
150+
## method wrappers: by-value params copy straight into the request struct,
151+
## by-const-pointer aggregates are dereferenced in.
152+
var params, assigns: seq[string] = @[]
153+
for ep in extraParams:
154+
let rides = ep.ridesAsPtr()
155+
let cType =
156+
if rides:
157+
CPtrType
158+
else:
159+
wireValueCType(reg, ep.typeName)
160+
if paramByValue(ep.typeName, rides):
161+
params.add(cType & " " & ep.name)
162+
assigns.add(" ffi_req." & ep.name & " = " & ep.name & ";")
163+
else:
164+
params.add("const " & cType & "* " & ep.name)
165+
assigns.add(" ffi_req." & ep.name & " = *" & ep.name & ";")
166+
(params, assigns)
167+
146168
proc methodReplyInfo(
147169
reg: var AbiReg, libType: string, m: FFIProcMeta
148170
): tuple[fnType, replyParam: string] =
@@ -248,6 +270,7 @@ proc emitCtxAndCtor(
248270
"(int ret, const char* ctx_addr, const char* err_msg, void* ud) {"
249271
)
250272
lines.add(" " & createBox & "* box = (" & createBox & "*)ud;")
273+
lines.add(" if (!box) return;")
251274
lines.add(" if (!box->fn) { free(box); return; }")
252275
lines.add(" if (ret != 0) {")
253276
lines.add(
@@ -281,21 +304,7 @@ proc emitCtxAndCtor(
281304
lines.add("")
282305
for ctor in ctors:
283306
let reqStruct = reqStructName(ctor)
284-
var params: seq[string] = @[]
285-
var assigns: seq[string] = @[]
286-
for ep in ctor.extraParams:
287-
let rides = ep.ridesAsPtr()
288-
let cType =
289-
if rides:
290-
CPtrType
291-
else:
292-
wireValueCType(reg, ep.typeName)
293-
if paramByValue(ep.typeName, rides):
294-
params.add(cType & " " & ep.name)
295-
assigns.add(" ffi_req." & ep.name & " = " & ep.name & ";")
296-
else:
297-
params.add("const " & cType & "* " & ep.name)
298-
assigns.add(" ffi_req." & ep.name & " = *" & ep.name & ";")
307+
let (params, assigns) = reqParamsAndAssigns(reg, ctor.extraParams)
299308
let head = "static inline int " & libName & "_ctx_create("
300309
let sig =
301310
if params.len > 0:
@@ -342,21 +351,7 @@ proc emitMethod(
342351
let stripped = stripLibPrefix(m.procName, m.libName)
343352
let reqStruct = reqStructName(m)
344353
let info = methodReplyInfo(reg, libType, m)
345-
var params: seq[string] = @[]
346-
var assigns: seq[string] = @[]
347-
for ep in m.extraParams:
348-
let rides = ep.ridesAsPtr()
349-
let cType =
350-
if rides:
351-
CPtrType
352-
else:
353-
wireValueCType(reg, ep.typeName)
354-
if paramByValue(ep.typeName, rides):
355-
params.add(cType & " " & ep.name)
356-
assigns.add(" ffi_req." & ep.name & " = " & ep.name & ";")
357-
else:
358-
params.add("const " & cType & "* " & ep.name)
359-
assigns.add(" ffi_req." & ep.name & " = *" & ep.name & ";")
354+
let (params, assigns) = reqParamsAndAssigns(reg, m.extraParams)
360355
let head =
361356
"static inline int " & libName & "_ctx_" & stripped & "(const " & ctxType & "* ctx, "
362357
let sig =
@@ -404,11 +399,9 @@ proc generateCAbiLibHeader*(
404399
lines.add("#include <stdlib.h>")
405400
lines.add("#include <string.h>")
406401
lines.add("")
407-
lines.add("#ifndef NIMFFI_RET_OK")
408402
lines.add("#define NIMFFI_RET_OK 0")
409403
lines.add("#define NIMFFI_RET_ERR 1")
410404
lines.add("#define NIMFFI_RET_MISSING_CALLBACK 2")
411-
lines.add("#endif")
412405
lines.add("")
413406
lines.add("/* Flat wire structs — the C ABI. Strings are borrowed, NUL-terminated")
414407
lines.add(" `const char*` valid only for the duration of the call they cross. */")

ffi/internal/c_macro_helpers.nim

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
638635
proc 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

714712
proc 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

742742
proc 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

Comments
 (0)