evalffi.nim 17 KB

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