small examples work with the FFI

This commit is contained in:
Araq 2012-12-19 08:44:20 +01:00
commit c73d9fdd4c
4 changed files with 53 additions and 32 deletions

View file

@ -34,19 +34,25 @@ proc getDll(cache: var TDllCache; dll: string): pointer =
cache[dll] = result cache[dll] = result
proc importcSymbol*(sym: PSym): PNode = proc importcSymbol*(sym: PSym): PNode =
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
InternalError("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 name = ropeToStr(sym.loc.r) let name = ropeToStr(sym.loc.r)
let theAddr = dllhandle.checkedSymAddr(name)
# 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(nkIntLit, sym.info, sym.typ)
result.intVal = cast[TAddress](theAddr) case name
of "stdin": result.intVal = cast[TAddress](system.stdin)
of "stdout": result.intVal = cast[TAddress](system.stdout)
of "stderr": result.intVal = cast[TAddress](system.stderr)
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)
result.intVal = cast[TAddress](theAddr)
proc mapType(t: ast.PType): ptr libffi.TType = proc mapType(t: ast.PType): ptr libffi.TType =
if t == nil: return addr libffi.type_void if t == nil: return addr libffi.type_void
@ -63,8 +69,10 @@ proc mapType(t: ast.PType): ptr libffi.TType =
of tyFloat, tyFloat64: result = addr libffi.type_double of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float of tyFloat32: result = addr libffi.type_float
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr: tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyNil:
result = addr libffi.type_pointer result = addr libffi.type_pointer
of tyDistinct:
result = mapType(t.sons[0])
else: else:
InternalError("cannot map type to FFI") InternalError("cannot map type to FFI")
# too risky: # too risky:
@ -80,12 +88,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
proc pack(v: PNode): pointer = proc pack(v: PNode, typ: PType): pointer =
template awr(T, v: expr) {.immediate, dirty.} = template awr(T, v: expr) {.immediate, dirty.} =
result = alloc0(sizeof(T)) result = alloc0(sizeof(T))
wr(T, result, v) wr(T, result, v)
case v.typ.kind case typ.kind
of tyBool: awr(bool, v.intVal != 0) of tyBool: awr(bool, v.intVal != 0)
of tyChar: awr(char, v.intVal.chr) of tyChar: awr(char, v.intVal.chr)
of tyInt: awr(int, v.intVal.int) of tyInt: awr(int, v.intVal.int)
@ -120,6 +128,10 @@ proc pack(v: PNode): pointer =
result = alloc0(sizeof(pointer)) result = alloc0(sizeof(pointer))
else: else:
awr(cstring, cstring(v.strVal)) awr(cstring, cstring(v.strVal))
of tyNil:
result = alloc0(sizeof(pointer))
of tyDistinct, tyGenericInst:
result = pack(v, typ.sons[0])
else: else:
InternalError("cannot map value to FFI " & typeToString(v.typ)) InternalError("cannot map value to FFI " & typeToString(v.typ))
@ -168,31 +180,36 @@ proc unpack(x: pointer, typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkNilLit, info, typ) result = newNodeIT(nkNilLit, info, typ)
else: else:
aws(nkStrLit, $p) aws(nkStrLit, $p)
of tyNil: result = newNodeIT(nkNilLit, info, typ)
of tyDistinct, tyGenericInst:
result = unpack(x, typ.sons[0], info)
else: else:
InternalError("cannot map value from FFI " & typeToString(typ)) InternalError("cannot map value from FFI " & typeToString(typ))
proc callForeignFunction*(call: PNode): PNode = proc callForeignFunction*(call: PNode): PNode =
InternalAssert call.sons[0].kind == nkIntLit InternalAssert call.sons[0].kind == nkIntLit
let typ = call.sons[0].typ
var cif: TCif var cif: TCif
var sig: TParamList var sig: TParamList
for i in 1..typ.len-1: sig[i-1] = mapType(typ.sons[i]) # use the arguments' types for varargs support:
for i in 1..call.len-1: sig[i-1] = mapType(call.sons[i].typ)
if prep_cif(cif, mapCallConv(typ.callConv), cuint(typ.len-1), let typ = call.sons[0].typ
if prep_cif(cif, mapCallConv(typ.callConv), cuint(call.len-1),
mapType(typ.sons[0]), sig) != OK: mapType(typ.sons[0]), sig) != OK:
InternalError(call.info, "error in FFI call") InternalError(call.info, "error in FFI call")
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 0 .. call.len-1: for i in 1 .. call.len-1:
args[i] = pack(call.sons[i+1]) args[i-1] = pack(call.sons[i], call.sons[i].typ)
let retVal = alloc(typ.sons[0].getSize.int) let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
else: alloc(typ.sons[0].getSize.int)
libffi.call(cif, fn, retVal, args) libffi.call(cif, fn, retVal, args)
if isEmptyType(typ.sons[0]): result = emptyNode if retVal.isNil: result = emptyNode
else: result = unpack(retVal, typ.sons[0], call.info) else: result = unpack(retVal, typ.sons[0], call.info)
dealloc retVal if retVal != nil: dealloc retVal
for i in countdown(call.len-1, 0): dealloc args[i] for i in countdown(call.len-2, 0): dealloc args[i]

View file

@ -339,16 +339,15 @@ proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode =
if not aliasNeeded(result, flags): if not aliasNeeded(result, flags):
result = copyTree(result) result = copyTree(result)
else: else:
when hasFFI:
if sfImportc in s.flags:
result = importcSymbol(s)
IdNodeTablePut(c.globals, s, result)
return result
result = s.ast result = s.ast
if result == nil or result.kind == nkEmpty: if result == nil or result.kind == nkEmpty:
when hasFFI: result = getNullValue(s.typ, s.info)
# for 'stdin' etc. we need to support 'importc' for variables:
if sfImportc in s.flags:
result = importcSymbol(s)
else:
result = getNullValue(s.typ, s.info)
else:
result = getNullValue(s.typ, s.info)
else: else:
result = evalAux(c, result, {}) result = evalAux(c, result, {})
if isSpecial(result): return if isSpecial(result): return
@ -392,7 +391,7 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
var newCall = newNodeI(nkCall, n.info, n.len) var newCall = newNodeI(nkCall, n.info, n.len)
newCall.sons[0] = evalGlobalVar(c, prc.sym, {}) newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
for i in 1 .. <n.len: for i in 1 .. <n.len:
newCall.sons[i] = d.params[i-1] newCall.sons[i] = d.params[i]
return callForeignFunction(newCall) return callForeignFunction(newCall)
pushStackFrame(c, d) pushStackFrame(c, d)

View file

@ -215,7 +215,7 @@ when defined(withUpdate):
if errcode == 0: if errcode == 0:
if output == "": if output == "":
# No changes # No changes
echo("No update. Exiting..") echo("No update. Exiting...")
return return
else: else:
echo("Fetching updates from repo...") echo("Fetching updates from repo...")

View file

@ -1,9 +1,12 @@
version 0.9.2 version 0.9.2
============= =============
- FFI:
* proper byte buffers
* support for arrays
* support for tuples/objects
* make system.nim aware of nimffi
- fix closure bug finally - fix closure bug finally
- test&finish first class iterators:
* nested iterators
- fix marshal bug - fix marshal bug
- investigate nimgame bug - investigate nimgame bug
@ -11,6 +14,8 @@ version 0.9.2
version 0.9.X version 0.9.X
============= =============
- test&finish first class iterators:
* nested iterators
- implement the missing features wrt inheritance - implement the missing features wrt inheritance
- implement generic methods - implement generic methods
- improve the compiler as a service - improve the compiler as a service