small improvements for FFI
This commit is contained in:
parent
b6c8e16b0f
commit
81b718641c
3 changed files with 66 additions and 24 deletions
|
|
@ -17,11 +17,12 @@ else:
|
||||||
const libcDll = "libc.so(.6|.5|)"
|
const libcDll = "libc.so(.6|.5|)"
|
||||||
|
|
||||||
type
|
type
|
||||||
TDllCache* = tables.TTable[string, TLibHandle]
|
TDllCache = tables.TTable[string, TLibHandle]
|
||||||
var
|
var
|
||||||
gDllCache = initTable[string, TLibHandle]()
|
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]
|
result = cache[dll]
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
var libs: seq[string] = @[]
|
var libs: seq[string] = @[]
|
||||||
|
|
@ -30,7 +31,7 @@ proc getDll(cache: var TDllCache; dll: string): pointer =
|
||||||
result = LoadLib(c)
|
result = LoadLib(c)
|
||||||
if not result.isNil: break
|
if not result.isNil: break
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
InternalError("cannot load: " & dll)
|
GlobalError(info, errGenerated, "cannot load: " & dll)
|
||||||
cache[dll] = result
|
cache[dll] = result
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -41,7 +42,7 @@ proc importcSymbol*(sym: PSym): PNode =
|
||||||
|
|
||||||
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
||||||
# that contains the address instead:
|
# that contains the address instead:
|
||||||
result = newNodeIT(nkIntLit, sym.info, sym.typ)
|
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
||||||
case name
|
case name
|
||||||
of "stdin": result.intVal = cast[TAddress](system.stdin)
|
of "stdin": result.intVal = cast[TAddress](system.stdin)
|
||||||
of "stdout": result.intVal = cast[TAddress](system.stdout)
|
of "stdout": result.intVal = cast[TAddress](system.stdout)
|
||||||
|
|
@ -49,12 +50,19 @@ proc importcSymbol*(sym: PSym): PNode =
|
||||||
else:
|
else:
|
||||||
let lib = sym.annex
|
let lib = sym.annex
|
||||||
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
||||||
InternalError(sym.info, "dynlib needs to be a string literal for the REPL")
|
GlobalError(sym.info, errGenerated,
|
||||||
|
"dynlib needs to be a string lit for the REPL")
|
||||||
let dllpath = if lib.isNil: libcDll else: lib.path.strVal
|
var theAddr: pointer
|
||||||
let dllhandle = gDllCache.getDll(dllpath)
|
if lib.isNil and not gExehandle.isNil:
|
||||||
let theAddr = dllhandle.checkedSymAddr(name)
|
# 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)
|
result.intVal = cast[TAddress](theAddr)
|
||||||
|
|
||||||
proc mapType(t: ast.PType): ptr libffi.TType =
|
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 rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
|
||||||
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
|
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.} =
|
template awr(T, v: expr) {.immediate, dirty.} =
|
||||||
result = alloc0(sizeof(T))
|
wr(T, res, v)
|
||||||
wr(T, result, v)
|
|
||||||
|
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyBool: awr(bool, v.intVal != 0)
|
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 tyFloat32: awr(float32, v.floatVal)
|
||||||
of tyFloat64: awr(float64, v.floatVal)
|
of tyFloat64: awr(float64, v.floatVal)
|
||||||
|
|
||||||
of tyPointer, tyProc, tyPtr, tyRef:
|
of tyPointer, tyProc:
|
||||||
if v.kind == nkNilLit:
|
if v.kind == nkNilLit:
|
||||||
result = alloc0(sizeof(pointer))
|
# nothing to do since the memory is 0 initialized anyway
|
||||||
else:
|
nil
|
||||||
|
elif v.kind == nkPtrLit:
|
||||||
awr(pointer, cast[pointer](v.intVal))
|
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:
|
of tyCString, tyString:
|
||||||
if v.kind == nkNilLit:
|
if v.kind == nkNilLit:
|
||||||
result = alloc0(sizeof(pointer))
|
nil
|
||||||
else:
|
else:
|
||||||
awr(cstring, cstring(v.strVal))
|
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:
|
of tyNil:
|
||||||
result = alloc0(sizeof(pointer))
|
nil
|
||||||
of tyDistinct, tyGenericInst:
|
of tyDistinct, tyGenericInst:
|
||||||
result = pack(v, typ.sons[0])
|
result = pack(v, typ.sons[0])
|
||||||
else:
|
else:
|
||||||
|
|
@ -205,7 +233,9 @@ proc callForeignFunction*(call: PNode): PNode =
|
||||||
var args: TArgList
|
var args: TArgList
|
||||||
let fn = cast[pointer](call.sons[0].intVal)
|
let fn = cast[pointer](call.sons[0].intVal)
|
||||||
for i in 1 .. call.len-1:
|
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)
|
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
|
||||||
else: alloc(typ.sons[0].getSize.int)
|
else: alloc(typ.sons[0].getSize.int)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# 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
|
## loads a library from `path`. Returns nil if the library could not
|
||||||
## be loaded.
|
## 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)
|
proc UnloadLib*(lib: TLibHandle)
|
||||||
## unloads the library `lib`
|
## unloads the library `lib`
|
||||||
|
|
||||||
|
|
@ -57,6 +61,7 @@ when defined(posix):
|
||||||
importc, header: "<dlfcn.h>".}
|
importc, header: "<dlfcn.h>".}
|
||||||
|
|
||||||
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
|
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 UnloadLib(lib: TLibHandle) = dlclose(lib)
|
||||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||||
return dlsym(lib, name)
|
return dlsym(lib, name)
|
||||||
|
|
@ -78,6 +83,8 @@ elif defined(windows) or defined(dos):
|
||||||
|
|
||||||
proc LoadLib(path: string): TLibHandle =
|
proc LoadLib(path: string): TLibHandle =
|
||||||
result = cast[TLibHandle](winLoadLibrary(path))
|
result = cast[TLibHandle](winLoadLibrary(path))
|
||||||
|
proc LoadLib(): TLibHandle =
|
||||||
|
result = cast[TLibHandle](winLoadLibrary(nil))
|
||||||
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||||
|
|
||||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||||
|
|
|
||||||
|
|
@ -1716,8 +1716,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
|
|
||||||
proc initStackBottom() {.inline, compilerproc.} =
|
proc initStackBottom() {.inline, compilerproc.} =
|
||||||
# WARNING: This is very fragile! An array size of 8 does not work on my
|
# 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
|
# Linux 64bit system. -- That's because the stack direction is the other
|
||||||
# optimizer...
|
# way round.
|
||||||
when defined(setStackBottom):
|
when defined(setStackBottom):
|
||||||
var locals {.volatile.}: pointer
|
var locals {.volatile.}: pointer
|
||||||
locals = addr(locals)
|
locals = addr(locals)
|
||||||
|
|
@ -1754,7 +1754,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
proc endbStep()
|
proc endbStep()
|
||||||
|
|
||||||
# ----------------- IO Part ------------------------------------------------
|
# ----------------- IO Part ------------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
||||||
# data hiding
|
# data hiding
|
||||||
|
|
@ -1783,7 +1782,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
## The standard error stream.
|
## The standard error stream.
|
||||||
##
|
##
|
||||||
## Note: In my opinion, this should not be used -- the concept of a
|
## 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
|
## stream* is a good idea, but since it is named ``stderr`` there are few
|
||||||
## programs out there that distinguish properly between ``stdout`` and
|
## programs out there that distinguish properly between ``stdout`` and
|
||||||
## ``stderr``. So, that's what you get if you don't name your variables
|
## ``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):
|
elif defined(ecmaScript) or defined(NimrodVM):
|
||||||
# Stubs:
|
# Stubs:
|
||||||
|
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
|
||||||
|
|
||||||
proc GC_disable() = nil
|
proc GC_disable() = nil
|
||||||
proc GC_enable() = nil
|
proc GC_enable() = nil
|
||||||
proc GC_fullCollect() = nil
|
proc GC_fullCollect() = nil
|
||||||
|
|
@ -2151,6 +2152,10 @@ elif defined(ecmaScript) or defined(NimrodVM):
|
||||||
if x == y: return 0
|
if x == y: return 0
|
||||||
if x < y: return -1
|
if x < y: return -1
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
when defined(nimffi):
|
||||||
|
include "system/sysio"
|
||||||
|
|
||||||
|
|
||||||
proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
|
proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
|
||||||
## a shorthand for ``echo(errormsg); quit(errorcode)``.
|
## a shorthand for ``echo(errormsg); quit(errorcode)``.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue