small improvements for FFI

This commit is contained in:
Araq
2012-12-26 22:48:34 +01:00
parent b6c8e16b0f
commit 81b718641c
3 changed files with 66 additions and 24 deletions

View File

@@ -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)

View File

@@ -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 =

View File

@@ -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)``.