mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-07 21:43:33 +00:00
small improvements for FFI
This commit is contained in:
@@ -17,11 +17,12 @@ else:
|
||||
const libcDll = "libc.so(.6|.5|)"
|
||||
|
||||
type
|
||||
TDllCache* = tables.TTable[string, TLibHandle]
|
||||
TDllCache = tables.TTable[string, TLibHandle]
|
||||
var
|
||||
gDllCache = initTable[string, TLibHandle]()
|
||||
gExeHandle = LoadLib()
|
||||
|
||||
proc getDll(cache: var TDllCache; dll: string): pointer =
|
||||
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
|
||||
result = cache[dll]
|
||||
if result.isNil:
|
||||
var libs: seq[string] = @[]
|
||||
@@ -30,7 +31,7 @@ proc getDll(cache: var TDllCache; dll: string): pointer =
|
||||
result = LoadLib(c)
|
||||
if not result.isNil: break
|
||||
if result.isNil:
|
||||
InternalError("cannot load: " & dll)
|
||||
GlobalError(info, errGenerated, "cannot load: " & dll)
|
||||
cache[dll] = result
|
||||
|
||||
const
|
||||
@@ -41,7 +42,7 @@ proc importcSymbol*(sym: PSym): PNode =
|
||||
|
||||
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
||||
# that contains the address instead:
|
||||
result = newNodeIT(nkIntLit, sym.info, sym.typ)
|
||||
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
||||
case name
|
||||
of "stdin": result.intVal = cast[TAddress](system.stdin)
|
||||
of "stdout": result.intVal = cast[TAddress](system.stdout)
|
||||
@@ -49,12 +50,19 @@ proc importcSymbol*(sym: PSym): PNode =
|
||||
else:
|
||||
let lib = sym.annex
|
||||
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
||||
InternalError(sym.info, "dynlib needs to be a string literal for the REPL")
|
||||
|
||||
let dllpath = if lib.isNil: libcDll else: lib.path.strVal
|
||||
let dllhandle = gDllCache.getDll(dllpath)
|
||||
let theAddr = dllhandle.checkedSymAddr(name)
|
||||
|
||||
GlobalError(sym.info, errGenerated,
|
||||
"dynlib needs to be a string lit for the REPL")
|
||||
var theAddr: pointer
|
||||
if lib.isNil and not gExehandle.isNil:
|
||||
# first try this exe itself:
|
||||
theAddr = gExehandle.symAddr(name)
|
||||
# then try libc:
|
||||
if theAddr.isNil:
|
||||
let dllhandle = gDllCache.getDll(libcDll)
|
||||
theAddr = dllhandle.checkedSymAddr(name)
|
||||
else:
|
||||
let dllhandle = gDllCache.getDll(lib.path.strVal, sym.info)
|
||||
theAddr = dllhandle.checkedSymAddr(name)
|
||||
result.intVal = cast[TAddress](theAddr)
|
||||
|
||||
proc mapType(t: ast.PType): ptr libffi.TType =
|
||||
@@ -90,11 +98,12 @@ proc mapCallConv(cc: TCallingConvention): TABI =
|
||||
|
||||
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
|
||||
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
|
||||
template `+!`(x, y: expr): expr {.immediate.} =
|
||||
cast[pointer](cast[TAddress](x) + y)
|
||||
|
||||
proc pack(v: PNode, typ: PType): pointer =
|
||||
proc pack(v: PNode, typ: PType, res: pointer) =
|
||||
template awr(T, v: expr) {.immediate, dirty.} =
|
||||
result = alloc0(sizeof(T))
|
||||
wr(T, result, v)
|
||||
wr(T, res, v)
|
||||
|
||||
case typ.kind
|
||||
of tyBool: awr(bool, v.intVal != 0)
|
||||
@@ -121,18 +130,37 @@ proc pack(v: PNode, typ: PType): pointer =
|
||||
of tyFloat32: awr(float32, v.floatVal)
|
||||
of tyFloat64: awr(float64, v.floatVal)
|
||||
|
||||
of tyPointer, tyProc, tyPtr, tyRef:
|
||||
of tyPointer, tyProc:
|
||||
if v.kind == nkNilLit:
|
||||
result = alloc0(sizeof(pointer))
|
||||
else:
|
||||
# nothing to do since the memory is 0 initialized anyway
|
||||
nil
|
||||
elif v.kind == nkPtrLit:
|
||||
awr(pointer, cast[pointer](v.intVal))
|
||||
else:
|
||||
InternalError("cannot map pointer/proc value to FFI")
|
||||
of tyPtr, tyRef, tyVar:
|
||||
if v.kind == nkNilLit:
|
||||
# nothing to do since the memory is 0 initialized anyway
|
||||
nil
|
||||
elif v.kind == nkPtrLit:
|
||||
awr(pointer, cast[pointer](v.intVal))
|
||||
else:
|
||||
# XXX this is pretty hard: we need to allocate a new buffer and store it
|
||||
# somewhere to be freed; we also need to write back any changes to the
|
||||
# data!
|
||||
InternalError("cannot map pointer/proc value to FFI")
|
||||
of tyCString, tyString:
|
||||
if v.kind == nkNilLit:
|
||||
result = alloc0(sizeof(pointer))
|
||||
nil
|
||||
else:
|
||||
awr(cstring, cstring(v.strVal))
|
||||
of tyArray, tyArrayConstr:
|
||||
let baseSize = typ.sons[1].getSize
|
||||
assert(v.len == lengthOrd(typ.sons[0]))
|
||||
for i in 0 .. <v.len:
|
||||
pack(v.sons[i], typ.sons[1], res +! i * baseSize)
|
||||
of tyNil:
|
||||
result = alloc0(sizeof(pointer))
|
||||
nil
|
||||
of tyDistinct, tyGenericInst:
|
||||
result = pack(v, typ.sons[0])
|
||||
else:
|
||||
@@ -205,7 +233,9 @@ proc callForeignFunction*(call: PNode): PNode =
|
||||
var args: TArgList
|
||||
let fn = cast[pointer](call.sons[0].intVal)
|
||||
for i in 1 .. call.len-1:
|
||||
args[i-1] = pack(call.sons[i], call.sons[i].typ)
|
||||
var t = call.sons[i].typ
|
||||
args[i-1] = alloc0(typ.sons[0].getSize.int)
|
||||
pack(call.sons[i], t, args[i-1])
|
||||
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
|
||||
else: alloc(typ.sons[0].getSize.int)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -18,6 +18,10 @@ proc LoadLib*(path: string): TLibHandle
|
||||
## loads a library from `path`. Returns nil if the library could not
|
||||
## be loaded.
|
||||
|
||||
proc LoadLib*(): TLibHandle
|
||||
## gets the handle from the current executable. Returns nil if the
|
||||
## library could not be loaded.
|
||||
|
||||
proc UnloadLib*(lib: TLibHandle)
|
||||
## unloads the library `lib`
|
||||
|
||||
@@ -57,6 +61,7 @@ when defined(posix):
|
||||
importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
|
||||
proc LoadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
|
||||
proc UnloadLib(lib: TLibHandle) = dlclose(lib)
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
return dlsym(lib, name)
|
||||
@@ -78,6 +83,8 @@ elif defined(windows) or defined(dos):
|
||||
|
||||
proc LoadLib(path: string): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(path))
|
||||
proc LoadLib(): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(nil))
|
||||
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
|
||||
@@ -1716,8 +1716,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
|
||||
proc initStackBottom() {.inline, compilerproc.} =
|
||||
# WARNING: This is very fragile! An array size of 8 does not work on my
|
||||
# Linux 64bit system. Very strange, but we are at the will of GCC's
|
||||
# optimizer...
|
||||
# Linux 64bit system. -- That's because the stack direction is the other
|
||||
# way round.
|
||||
when defined(setStackBottom):
|
||||
var locals {.volatile.}: pointer
|
||||
locals = addr(locals)
|
||||
@@ -1754,7 +1754,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
proc endbStep()
|
||||
|
||||
# ----------------- IO Part ------------------------------------------------
|
||||
|
||||
type
|
||||
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
||||
# data hiding
|
||||
@@ -1783,7 +1782,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
## The standard error stream.
|
||||
##
|
||||
## Note: In my opinion, this should not be used -- the concept of a
|
||||
## separate error stream is a design flaw of UNIX. A seperate *message
|
||||
## separate error stream is a design flaw of UNIX. A separate *message
|
||||
## stream* is a good idea, but since it is named ``stderr`` there are few
|
||||
## programs out there that distinguish properly between ``stdout`` and
|
||||
## ``stderr``. So, that's what you get if you don't name your variables
|
||||
@@ -2121,6 +2120,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
|
||||
elif defined(ecmaScript) or defined(NimrodVM):
|
||||
# Stubs:
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
|
||||
|
||||
proc GC_disable() = nil
|
||||
proc GC_enable() = nil
|
||||
proc GC_fullCollect() = nil
|
||||
@@ -2151,6 +2152,10 @@ elif defined(ecmaScript) or defined(NimrodVM):
|
||||
if x == y: return 0
|
||||
if x < y: return -1
|
||||
return 1
|
||||
|
||||
when defined(nimffi):
|
||||
include "system/sysio"
|
||||
|
||||
|
||||
proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
|
||||
## a shorthand for ``echo(errormsg); quit(errorcode)``.
|
||||
|
||||
Reference in New Issue
Block a user