diff options
-rw-r--r-- | compiler/evalffi.nim | 66 | ||||
-rwxr-xr-x | lib/pure/dynlib.nim | 9 | ||||
-rwxr-xr-x | lib/system.nim | 13 |
3 files changed, 65 insertions, 23 deletions
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 464c932f9..0c5301b76 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -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)) + # 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) diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 3ade1cbf9..a64b7f138 100755 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -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 = diff --git a/lib/system.nim b/lib/system.nim index e199d7611..cbca90a3b 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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)``. |