evalffi.nim 17 KB

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