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