evalffi.nim 16 KB

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