evalffi.nim 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This file implements the FFI part of the evaluator for Nim code.
  10. import ast, types, options, tables, dynlib, msgs, lineinfos
  11. from os import getAppFilename
  12. import pkg/libffi
  13. when defined(windows):
  14. const libcDll = "msvcrt.dll"
  15. elif defined(linux):
  16. const libcDll = "libc.so(.6|.5|)"
  17. elif defined(openbsd):
  18. const libcDll = "/usr/lib/libc.so(.95.1|)"
  19. elif defined(bsd):
  20. const libcDll = "/lib/libc.so.7"
  21. elif defined(osx):
  22. const libcDll = "/usr/lib/libSystem.dylib"
  23. else:
  24. {.error: "`libcDll` not implemented on this platform".}
  25. type
  26. TDllCache = tables.Table[string, LibHandle]
  27. var
  28. gDllCache = initTable[string, LibHandle]()
  29. when defined(windows):
  30. var gExeHandle = loadLib(getAppFilename())
  31. else:
  32. var gExeHandle = loadLib()
  33. proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
  34. if dll in cache:
  35. return cache[dll]
  36. var libs: seq[string]
  37. libCandidates(dll, libs)
  38. for c in libs:
  39. result = loadLib(c)
  40. if not result.isNil: break
  41. if result.isNil:
  42. globalError(conf, info, "cannot load: " & dll)
  43. cache[dll] = result
  44. const
  45. nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
  46. proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
  47. let name = sym.cname # $sym.loc.r would point to internal name
  48. # the AST does not support untyped pointers directly, so we use an nkIntLit
  49. # that contains the address instead:
  50. result = newNodeIT(nkPtrLit, sym.info, sym.typ)
  51. when true:
  52. var libPathMsg = ""
  53. let lib = sym.annex
  54. if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
  55. globalError(conf, sym.info, "dynlib needs to be a string lit")
  56. var theAddr: pointer
  57. if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
  58. libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
  59. # first try this exe itself:
  60. theAddr = gExeHandle.symAddr(name.cstring)
  61. # then try libc:
  62. if theAddr.isNil:
  63. let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
  64. theAddr = dllhandle.symAddr(name.cstring)
  65. elif not lib.isNil:
  66. let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
  67. libPathMsg = dll
  68. let dllhandle = getDll(conf, gDllCache, dll, sym.info)
  69. theAddr = dllhandle.symAddr(name.cstring)
  70. if theAddr.isNil: globalError(conf, sym.info,
  71. "cannot import symbol: " & name & " from " & libPathMsg)
  72. result.intVal = cast[int](theAddr)
  73. proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
  74. if t == nil: return addr libffi.type_void
  75. case t.kind
  76. of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
  77. case getSize(conf, t)
  78. of 1: result = addr libffi.type_uint8
  79. of 2: result = addr libffi.type_sint16
  80. of 4: result = addr libffi.type_sint32
  81. of 8: result = addr libffi.type_sint64
  82. else: result = nil
  83. of tyFloat, tyFloat64: result = addr libffi.type_double
  84. of tyFloat32: result = addr libffi.type_float
  85. of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCstring, tySequence, tyString, tyUntyped,
  86. tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
  87. result = addr libffi.type_pointer
  88. of tyDistinct, tyAlias, tySink:
  89. result = mapType(conf, t[0])
  90. else:
  91. result = nil
  92. # too risky:
  93. #of tyFloat128: result = addr libffi.type_longdouble
  94. proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI =
  95. case cc
  96. of ccNimCall: result = DEFAULT_ABI
  97. of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
  98. of ccCDecl: result = DEFAULT_ABI
  99. else:
  100. globalError(conf, info, "cannot map calling convention to FFI")
  101. template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
  102. template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
  103. template `+!`(x, y: untyped): untyped =
  104. cast[pointer](cast[int](x) + y)
  105. proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
  106. ## computes the size of the blob
  107. case typ.kind
  108. of tyPtr, tyRef, tyVar, tyLent:
  109. if v.kind in {nkNilLit, nkPtrLit}:
  110. result = sizeof(pointer)
  111. else:
  112. result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
  113. of tyDistinct, tyGenericInst, tyAlias, tySink:
  114. result = packSize(conf, v, typ[0])
  115. of tyArray:
  116. # consider: ptr array[0..1000_000, int] which is common for interfacing;
  117. # we use the real length here instead
  118. if v.kind in {nkNilLit, nkPtrLit}:
  119. result = sizeof(pointer)
  120. elif v.len != 0:
  121. result = v.len * packSize(conf, v[0], typ[1])
  122. else:
  123. result = getSize(conf, typ).int
  124. proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
  125. proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
  126. case n.kind
  127. of nkRecList:
  128. for i in 0..<n.len:
  129. result = getField(conf, n[i], position)
  130. if result != nil: return
  131. of nkRecCase:
  132. result = getField(conf, n[0], position)
  133. if result != nil: return
  134. for i in 1..<n.len:
  135. case n[i].kind
  136. of nkOfBranch, nkElse:
  137. result = getField(conf, lastSon(n[i]), position)
  138. if result != nil: return
  139. else: internalError(conf, n.info, "getField(record case branch)")
  140. of nkSym:
  141. if n.sym.position == position: result = n.sym
  142. else: discard
  143. proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
  144. internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
  145. # compute the field's offsets:
  146. discard getSize(conf, typ)
  147. for i in ord(x.kind == nkObjConstr)..<x.len:
  148. var it = x[i]
  149. if it.kind == nkExprColonExpr:
  150. internalAssert conf, it[0].kind == nkSym
  151. let field = it[0].sym
  152. pack(conf, it[1], field.typ, res +! field.offset)
  153. elif typ.n != nil:
  154. let field = getField(conf, typ.n, i)
  155. pack(conf, it, field.typ, res +! field.offset)
  156. else:
  157. # XXX: todo
  158. globalError(conf, x.info, "cannot pack unnamed tuple")
  159. const maxPackDepth = 20
  160. var packRecCheck = 0
  161. proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
  162. template awr(typ, v: untyped): untyped =
  163. wr(typ, res, v)
  164. case typ.kind
  165. of tyBool: awr(bool, v.intVal != 0)
  166. of tyChar: awr(char, v.intVal.chr)
  167. of tyInt: awr(int, v.intVal.int)
  168. of tyInt8: awr(int8, v.intVal.int8)
  169. of tyInt16: awr(int16, v.intVal.int16)
  170. of tyInt32: awr(int32, v.intVal.int32)
  171. of tyInt64: awr(int64, v.intVal.int64)
  172. of tyUInt: awr(uint, v.intVal.uint)
  173. of tyUInt8: awr(uint8, v.intVal.uint8)
  174. of tyUInt16: awr(uint16, v.intVal.uint16)
  175. of tyUInt32: awr(uint32, v.intVal.uint32)
  176. of tyUInt64: awr(uint64, v.intVal.uint64)
  177. of tyEnum, tySet:
  178. case getSize(conf, v.typ)
  179. of 1: awr(uint8, v.intVal.uint8)
  180. of 2: awr(uint16, v.intVal.uint16)
  181. of 4: awr(int32, v.intVal.int32)
  182. of 8: awr(int64, v.intVal.int64)
  183. else:
  184. globalError(conf, v.info, "cannot map value to FFI (tyEnum, tySet)")
  185. of tyFloat: awr(float, v.floatVal)
  186. of tyFloat32: awr(float32, v.floatVal)
  187. of tyFloat64: awr(float64, v.floatVal)
  188. of tyPointer, tyProc, tyCstring, tyString:
  189. if v.kind == nkNilLit:
  190. # nothing to do since the memory is 0 initialized anyway
  191. discard
  192. elif v.kind == nkPtrLit:
  193. awr(pointer, cast[pointer](v.intVal))
  194. elif v.kind in {nkStrLit..nkTripleStrLit}:
  195. awr(cstring, cstring(v.strVal))
  196. else:
  197. globalError(conf, v.info, "cannot map pointer/proc value to FFI")
  198. of tyPtr, tyRef, tyVar, tyLent:
  199. if v.kind == nkNilLit:
  200. # nothing to do since the memory is 0 initialized anyway
  201. discard
  202. elif v.kind == nkPtrLit:
  203. awr(pointer, cast[pointer](v.intVal))
  204. else:
  205. if packRecCheck > maxPackDepth:
  206. packRecCheck = 0
  207. globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
  208. inc packRecCheck
  209. pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
  210. dec packRecCheck
  211. awr(pointer, res +! sizeof(pointer))
  212. of tyArray:
  213. let baseSize = getSize(conf, typ[1])
  214. for i in 0..<v.len:
  215. pack(conf, v[i], typ[1], res +! i * baseSize)
  216. of tyObject, tyTuple:
  217. packObject(conf, v, typ, res)
  218. of tyNil:
  219. discard
  220. of tyDistinct, tyGenericInst, tyAlias, tySink:
  221. pack(conf, v, typ[0], res)
  222. else:
  223. globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
  224. proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode
  225. proc unpackObjectAdd(conf: ConfigRef, x: pointer, n, result: PNode) =
  226. case n.kind
  227. of nkRecList:
  228. for i in 0..<n.len:
  229. unpackObjectAdd(conf, x, n[i], result)
  230. of nkRecCase:
  231. globalError(conf, result.info, "case objects cannot be unpacked")
  232. of nkSym:
  233. var pair = newNodeI(nkExprColonExpr, result.info, 2)
  234. pair[0] = n
  235. pair[1] = unpack(conf, x +! n.sym.offset, n.sym.typ, nil)
  236. #echo "offset: ", n.sym.name.s, " ", n.sym.offset
  237. result.add pair
  238. else: discard
  239. proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
  240. # compute the field's offsets:
  241. discard getSize(conf, typ)
  242. # iterate over any actual field of 'n' ... if n is nil we need to create
  243. # the nkPar node:
  244. if n.isNil:
  245. result = newNode(nkTupleConstr)
  246. result.typ = typ
  247. if typ.n.isNil:
  248. internalError(conf, "cannot unpack unnamed tuple")
  249. unpackObjectAdd(conf, x, typ.n, result)
  250. else:
  251. result = n
  252. if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}:
  253. globalError(conf, n.info, "cannot map value from FFI")
  254. if typ.n.isNil:
  255. globalError(conf, n.info, "cannot unpack unnamed tuple")
  256. for i in ord(n.kind == nkObjConstr)..<n.len:
  257. var it = n[i]
  258. if it.kind == nkExprColonExpr:
  259. internalAssert conf, it[0].kind == nkSym
  260. let field = it[0].sym
  261. it[1] = unpack(conf, x +! field.offset, field.typ, it[1])
  262. else:
  263. let field = getField(conf, typ.n, i)
  264. n[i] = unpack(conf, x +! field.offset, field.typ, it)
  265. proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
  266. if n.isNil:
  267. result = newNode(nkBracket)
  268. result.typ = typ
  269. newSeq(result.sons, lengthOrd(conf, typ).toInt)
  270. else:
  271. result = n
  272. if result.kind != nkBracket:
  273. globalError(conf, n.info, "cannot map value from FFI")
  274. let baseSize = getSize(conf, typ[1])
  275. for i in 0..<result.len:
  276. result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
  277. proc canonNodeKind(k: TNodeKind): TNodeKind =
  278. case k
  279. of nkCharLit..nkUInt64Lit: result = nkIntLit
  280. of nkFloatLit..nkFloat128Lit: result = nkFloatLit
  281. of nkStrLit..nkTripleStrLit: result = nkStrLit
  282. else: result = k
  283. proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
  284. template aw(k, v, field: untyped): untyped =
  285. if n.isNil:
  286. result = newNode(k)
  287. result.typ = typ
  288. else:
  289. # check we have the right field:
  290. result = n
  291. if result.kind.canonNodeKind != k.canonNodeKind:
  292. #echo "expected ", k, " but got ", result.kind
  293. #debug result
  294. return newNodeI(nkExceptBranch, n.info)
  295. #globalError(conf, n.info, "cannot map value from FFI")
  296. result.field = v
  297. template setNil() =
  298. if n.isNil:
  299. result = newNode(nkNilLit)
  300. result.typ = typ
  301. else:
  302. reset n[]
  303. result = n
  304. result[] = TNode(kind: nkNilLit)
  305. result.typ = typ
  306. template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
  307. template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
  308. template aws(kind, v: untyped): untyped = aw(kind, v, strVal)
  309. case typ.kind
  310. of tyBool: awi(nkIntLit, rd(bool, x).ord)
  311. of tyChar: awi(nkCharLit, rd(char, x).ord)
  312. of tyInt: awi(nkIntLit, rd(int, x))
  313. of tyInt8: awi(nkInt8Lit, rd(int8, x))
  314. of tyInt16: awi(nkInt16Lit, rd(int16, x))
  315. of tyInt32: awi(nkInt32Lit, rd(int32, x))
  316. of tyInt64: awi(nkInt64Lit, rd(int64, x))
  317. of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
  318. of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
  319. of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
  320. of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
  321. of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
  322. of tyEnum:
  323. case getSize(conf, typ)
  324. of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
  325. of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
  326. of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
  327. of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
  328. else:
  329. globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
  330. of tyFloat: awf(nkFloatLit, rd(float, x))
  331. of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
  332. of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
  333. of tyPointer, tyProc:
  334. let p = rd(pointer, x)
  335. if p.isNil:
  336. setNil()
  337. elif n != nil and n.kind == nkStrLit:
  338. # we passed a string literal as a pointer; however strings are already
  339. # in their unboxed representation so nothing it to be unpacked:
  340. result = n
  341. else:
  342. awi(nkPtrLit, cast[int](p))
  343. of tyPtr, tyRef, tyVar, tyLent:
  344. let p = rd(pointer, x)
  345. if p.isNil:
  346. setNil()
  347. elif n == nil or n.kind == nkPtrLit:
  348. awi(nkPtrLit, cast[int](p))
  349. elif n != nil and n.len == 1:
  350. internalAssert(conf, n.kind == nkRefTy)
  351. n[0] = unpack(conf, p, typ.lastSon, n[0])
  352. result = n
  353. else:
  354. globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
  355. of tyObject, tyTuple:
  356. result = unpackObject(conf, x, typ, n)
  357. of tyArray:
  358. result = unpackArray(conf, x, typ, n)
  359. of tyCstring, tyString:
  360. let p = rd(cstring, x)
  361. if p.isNil:
  362. setNil()
  363. else:
  364. aws(nkStrLit, $p)
  365. of tyNil:
  366. setNil()
  367. of tyDistinct, tyGenericInst, tyAlias, tySink:
  368. result = unpack(conf, x, typ.lastSon, n)
  369. else:
  370. # XXX what to do with 'array' here?
  371. globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
  372. proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
  373. if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
  374. tyProc, tyCstring, tyString,
  375. tySequence}:
  376. result = newNodeIT(x.kind, x.info, destTyp)
  377. result.intVal = x.intVal
  378. elif x.kind == nkNilLit:
  379. result = newNodeIT(x.kind, x.info, destTyp)
  380. else:
  381. # we play safe here and allocate the max possible size:
  382. let size = max(packSize(conf, x, x.typ), packSize(conf, x, destTyp))
  383. var a = alloc0(size)
  384. pack(conf, x, x.typ, a)
  385. # cast through a pointer needs a new inner object:
  386. let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
  387. else: x.copyTree
  388. y.typ = x.typ
  389. result = unpack(conf, a, destTyp, y)
  390. dealloc a
  391. proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
  392. internalAssert conf, call[0].kind == nkPtrLit
  393. var cif: TCif
  394. var sig: ParamList
  395. # use the arguments' types for varargs support:
  396. for i in 1..<call.len:
  397. sig[i-1] = mapType(conf, call[i].typ)
  398. if sig[i-1].isNil:
  399. globalError(conf, call.info, "cannot map FFI type")
  400. let typ = call[0].typ
  401. if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
  402. mapType(conf, typ[0]), sig) != OK:
  403. globalError(conf, call.info, "error in FFI call")
  404. var args: ArgList
  405. let fn = cast[pointer](call[0].intVal)
  406. for i in 1..<call.len:
  407. var t = call[i].typ
  408. args[i-1] = alloc0(packSize(conf, call[i], t))
  409. pack(conf, call[i], t, args[i-1])
  410. let retVal = if isEmptyType(typ[0]): pointer(nil)
  411. else: alloc(getSize(conf, typ[0]).int)
  412. libffi.call(cif, fn, retVal, args)
  413. if retVal.isNil:
  414. result = newNode(nkEmpty)
  415. else:
  416. result = unpack(conf, retVal, typ[0], nil)
  417. result.info = call.info
  418. if retVal != nil: dealloc retVal
  419. for i in 1..<call.len:
  420. call[i] = unpack(conf, args[i-1], typ[i], call[i])
  421. dealloc args[i-1]
  422. proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
  423. args: var TNodeSeq, start, len: int,
  424. info: TLineInfo): PNode =
  425. internalAssert conf, fn.kind == nkPtrLit
  426. var cif: TCif
  427. var sig: ParamList
  428. for i in 0..len-1:
  429. var aTyp = args[i+start].typ
  430. if aTyp.isNil:
  431. internalAssert conf, i+1 < fntyp.len
  432. aTyp = fntyp[i+1]
  433. args[i+start].typ = aTyp
  434. sig[i] = mapType(conf, aTyp)
  435. if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
  436. if prep_cif(cif, mapCallConv(conf, fntyp.callConv, info), cuint(len),
  437. mapType(conf, fntyp[0]), sig) != OK:
  438. globalError(conf, info, "error in FFI call")
  439. var cargs: ArgList
  440. let fn = cast[pointer](fn.intVal)
  441. for i in 0..len-1:
  442. let t = args[i+start].typ
  443. cargs[i] = alloc0(packSize(conf, args[i+start], t))
  444. pack(conf, args[i+start], t, cargs[i])
  445. let retVal = if isEmptyType(fntyp[0]): pointer(nil)
  446. else: alloc(getSize(conf, fntyp[0]).int)
  447. libffi.call(cif, fn, retVal, cargs)
  448. if retVal.isNil:
  449. result = newNode(nkEmpty)
  450. else:
  451. result = unpack(conf, retVal, fntyp[0], nil)
  452. result.info = info
  453. if retVal != nil: dealloc retVal
  454. for i in 0..len-1:
  455. let t = args[i+start].typ
  456. args[i+start] = unpack(conf, cargs[i], t, args[i+start])
  457. dealloc cargs[i]