new VM: some progress for the FFI support

This commit is contained in:
Araq 2013-12-23 01:17:48 +01:00
commit 9145bcfbb6
8 changed files with 80 additions and 60 deletions

View file

@ -9,7 +9,7 @@
## This file implements the FFI part of the evaluator for Nimrod code. ## This file implements the FFI part of the evaluator for Nimrod code.
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
when defined(windows): when defined(windows):
const libcDll = "msvcrt.dll" const libcDll = "msvcrt.dll"
@ -20,7 +20,11 @@ type
TDllCache = tables.TTable[string, TLibHandle] TDllCache = tables.TTable[string, TLibHandle]
var var
gDllCache = initTable[string, TLibHandle]() gDllCache = initTable[string, TLibHandle]()
gExeHandle = LoadLib()
when defined(windows):
var gExeHandle = loadLib(os.getAppFilename())
else:
var gExeHandle = loadLib()
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer = proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
result = cache[dll] result = cache[dll]
@ -28,10 +32,10 @@ proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
var libs: seq[string] = @[] var libs: seq[string] = @[]
libCandidates(dll, libs) libCandidates(dll, libs)
for c in libs: for c in libs:
result = LoadLib(c) result = loadLib(c)
if not result.isNil: break if not result.isNil: break
if result.isNil: if result.isNil:
GlobalError(info, "cannot load: " & dll) globalError(info, "cannot load: " & dll)
cache[dll] = result cache[dll] = result
const const
@ -50,7 +54,7 @@ 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}:
GlobalError(sym.info, "dynlib needs to be a string lit for the REPL") globalError(sym.info, "dynlib needs to be a string lit for the REPL")
var theAddr: pointer var theAddr: pointer
if lib.isNil and not gExehandle.isNil: if lib.isNil and not gExehandle.isNil:
# first try this exe itself: # first try this exe itself:
@ -58,10 +62,12 @@ proc importcSymbol*(sym: PSym): PNode =
# then try libc: # then try libc:
if theAddr.isNil: if theAddr.isNil:
let dllhandle = gDllCache.getDll(libcDll, sym.info) let dllhandle = gDllCache.getDll(libcDll, sym.info)
theAddr = dllhandle.checkedSymAddr(name) theAddr = dllhandle.symAddr(name)
else: elif not lib.isNil:
let dllhandle = gDllCache.getDll(lib.path.strVal, sym.info) let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
theAddr = dllhandle.checkedSymAddr(name) else: lib.path.strVal, sym.info)
theAddr = dllhandle.symAddr(name)
if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
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 =
@ -139,7 +145,7 @@ proc getField(n: PNode; position: int): PSym =
else: internalError(n.info, "getField(record case branch)") else: internalError(n.info, "getField(record case branch)")
of nkSym: of nkSym:
if n.sym.position == position: result = n.sym if n.sym.position == position: result = n.sym
else: nil else: discard
proc packObject(x: PNode, typ: PType, res: pointer) = proc packObject(x: PNode, typ: PType, res: pointer) =
InternalAssert x.kind in {nkObjConstr, nkPar} InternalAssert x.kind in {nkObjConstr, nkPar}
@ -192,7 +198,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyPointer, tyProc, tyCString, tyString: of tyPointer, tyProc, tyCString, tyString:
if v.kind == nkNilLit: if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway # nothing to do since the memory is 0 initialized anyway
nil discard
elif v.kind == nkPtrLit: elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal)) awr(pointer, cast[pointer](v.intVal))
elif v.kind in {nkStrLit..nkTripleStrLit}: elif v.kind in {nkStrLit..nkTripleStrLit}:
@ -202,7 +208,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar:
if v.kind == nkNilLit: if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway # nothing to do since the memory is 0 initialized anyway
nil discard
elif v.kind == nkPtrLit: elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal)) awr(pointer, cast[pointer](v.intVal))
else: else:
@ -220,7 +226,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyObject, tyTuple: of tyObject, tyTuple:
packObject(v, typ, res) packObject(v, typ, res)
of tyNil: of tyNil:
nil discard
of tyDistinct, tyGenericInst: of tyDistinct, tyGenericInst:
pack(v, typ.sons[0], res) pack(v, typ.sons[0], res)
else: else:
@ -241,7 +247,7 @@ proc unpackObjectAdd(x: pointer, n, result: PNode) =
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil) pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
#echo "offset: ", n.sym.name.s, " ", n.sym.offset #echo "offset: ", n.sym.name.s, " ", n.sym.offset
result.add pair result.add pair
else: nil else: discard
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
# compute the field's offsets: # compute the field's offsets:

View file

@ -632,12 +632,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep # we pass 'tos.slots' instead of 'regs' so that the compiler can keep
# 'regs' in a register: # 'regs' in a register:
when hasFFI: when hasFFI:
if c.globals.sons[prc.position-1].kind == nkEmpty:
globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s)
let newValue = callForeignFunction(c.globals.sons[prc.position-1], let newValue = callForeignFunction(c.globals.sons[prc.position-1],
prc.typ, tos.slots, prc.typ, tos.slots,
rb+1, rc-1, c.debug[pc]) rb+1, rc-1, c.debug[pc])
if newValue.kind != nkEmpty: if newValue.kind != nkEmpty:
assert instr.opcode == opcIndCallAsgn assert instr.opcode == opcIndCallAsgn
regs[ra] = newValue asgnRef(regs[ra], newValue)
else: else:
globalError(c.debug[pc], errGenerated, "VM not built with FFI support") globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
elif prc.kind != skTemplate: elif prc.kind != skTemplate:
@ -796,7 +798,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
decodeB(nkBracket) decodeB(nkBracket)
let newLen = regs[rb].getOrdValue.int let newLen = regs[rb].getOrdValue.int
setLen(regs[ra].sons, newLen) setLen(regs[ra].sons, newLen)
of opcSwap, opcCast, opcReset: of opcSwap, opcReset:
internalError(c.debug[pc], "too implement") internalError(c.debug[pc], "too implement")
of opcIsNil: of opcIsNil:
decodeB(nkIntLit) decodeB(nkIntLit)
@ -938,6 +940,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
stackTrace(c, tos, pc, errGenerated, stackTrace(c, tos, pc, errGenerated,
msgKindToString(errIllegalConvFromXtoY) % [ msgKindToString(errIllegalConvFromXtoY) % [
"unknown type" , "unknown type"]) "unknown type" , "unknown type"])
of opcCast:
let rb = instr.regB
inc pc
let typ = c.types[c.code[pc].regBx - wordExcess]
when hasFFI:
let dest = fficast(regs[rb], typ)
asgnRef(regs[ra], dest)
else:
globalError(c.debug[pc], "cannot evaluate cast")
of opcNSetIntVal: of opcNSetIntVal:
decodeB(nkMetaNode) decodeB(nkMetaNode)
var dest = regs[ra].uast var dest = regs[ra].uast
@ -1074,6 +1085,8 @@ proc myOpen(module: PSym): PPassContext =
# XXX produce a new 'globals' environment here: # XXX produce a new 'globals' environment here:
setupGlobalCtx(module) setupGlobalCtx(module)
result = globalCtx result = globalCtx
when hasFFI:
globalCtx.features = {allowFFI, allowCast}
var oldErrorCount: int var oldErrorCount: int

View file

@ -945,6 +945,9 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
when hasFFI: when hasFFI:
if allowFFI in c.features: if allowFFI in c.features:
if s.kind == skVar and lfNoDecl in s.loc.flags:
c.globals.add(copyNode(emptyNode))
else:
c.globals.add(importcSymbol(s)) c.globals.add(importcSymbol(s))
s.position = c.globals.len s.position = c.globals.len
else: else:
@ -1303,6 +1306,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
of nkCurly: genSetConstr(c, n, dest) of nkCurly: genSetConstr(c, n, dest)
of nkObjConstr: genObjConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest)
of nkPar, nkClosure: genTupleConstr(c, n, dest) of nkPar, nkClosure: genTupleConstr(c, n, dest)
of nkCast:
if allowCast in c.features:
genConv(c, n, n.sons[1], dest, opcCast)
else:
localError(n.info, errGenerated, "VM is not allowed to 'cast'")
else: else:
InternalError n.info, "too implement " & $n.kind InternalError n.info, "too implement " & $n.kind

View file

@ -341,16 +341,6 @@ when defined(windows):
template FindNextFile(a, b: expr): expr = FindNextFileW(a, b) template FindNextFile(a, b: expr): expr = FindNextFileW(a, b)
template getCommandLine(): expr = getCommandLineW() template getCommandLine(): expr = getCommandLineW()
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
let
nul = 0
dot = ord('.')
result = (f.cFilename[0].int == dot)
if result:
result = (f.cFilename[1].int in {dot, nul})
if result:
result = (f.cFilename[2].int == nul)
template getFilename(f: expr): expr = template getFilename(f: expr): expr =
$cast[WideCString](addr(f.cFilename[0])) $cast[WideCString](addr(f.cFilename[0]))
else: else:
@ -358,18 +348,13 @@ when defined(windows):
template FindNextFile(a, b: expr): expr = FindNextFileA(a, b) template FindNextFile(a, b: expr): expr = FindNextFileA(a, b)
template getCommandLine(): expr = getCommandLineA() template getCommandLine(): expr = getCommandLineA()
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
let
nul = '\0'
dot = '.'
result = (f.cFilename[0] == dot)
if result:
result = (f.cFilename[1] in {dot, nul})
if result:
result = (f.cFilename[2] == nul)
template getFilename(f: expr): expr = $f.cFilename template getFilename(f: expr): expr = $f.cFilename
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
const dot = ord('.')
result = f.cFilename[0].int == dot and(f.cFilename[1].int == 0 or
f.cFilename[1].int == dot and f.cFilename[2].int == 0)
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} = tags: [FReadDir].} =
## Returns true if the file exists, false otherwise. ## Returns true if the file exists, false otherwise.
@ -468,20 +453,21 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
## `newDir` cannot been set. ## `newDir` cannot been set.
when defined(Windows): when defined(Windows):
when useWinUnicode: when useWinUnicode:
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError()) if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
OSError(OSLastError())
else: else:
if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError()) if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError())
else: else:
if chdir(newDir) != 0'i32: OSError(OSLastError()) if chdir(newDir) != 0'i32: OSError(OSLastError())
proc JoinPath*(head, tail: string): string {. proc joinPath*(head, tail: string): string {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Joins two directory names to one. ## Joins two directory names to one.
## ##
## For example on Unix: ## For example on Unix:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## JoinPath("usr", "lib") ## joinPath("usr", "lib")
## ##
## results in: ## results in:
## ##
@ -495,10 +481,10 @@ proc JoinPath*(head, tail: string): string {.
## examples on Unix: ## examples on Unix:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## assert JoinPath("usr", "") == "usr/" ## assert joinPath("usr", "") == "usr/"
## assert JoinPath("", "lib") == "lib" ## assert joinPath("", "lib") == "lib"
## assert JoinPath("", "/lib") == "/lib" ## assert joinPath("", "/lib") == "/lib"
## assert JoinPath("usr/", "/lib") == "usr/lib" ## assert joinPath("usr/", "/lib") == "usr/lib"
if len(head) == 0: if len(head) == 0:
result = tail result = tail
elif head[len(head)-1] in {DirSep, AltSep}: elif head[len(head)-1] in {DirSep, AltSep}:
@ -512,14 +498,14 @@ proc JoinPath*(head, tail: string): string {.
else: else:
result = head & DirSep & tail result = head & DirSep & tail
proc JoinPath*(parts: varargs[string]): string {.noSideEffect, proc joinPath*(parts: varargs[string]): string {.noSideEffect,
rtl, extern: "nos$1OpenArray".} = rtl, extern: "nos$1OpenArray".} =
## The same as `JoinPath(head, tail)`, but works with any number of directory ## The same as `joinPath(head, tail)`, but works with any number of directory
## parts. You need to pass at least one element or the proc will assert in ## parts. You need to pass at least one element or the proc will assert in
## debug builds and crash on release builds. ## debug builds and crash on release builds.
result = parts[0] result = parts[0]
for i in 1..high(parts): for i in 1..high(parts):
result = JoinPath(result, parts[i]) result = joinPath(result, parts[i])
proc `/` * (head, tail: string): string {.noSideEffect.} = proc `/` * (head, tail: string): string {.noSideEffect.} =
## The same as ``JoinPath(head, tail)`` ## The same as ``JoinPath(head, tail)``
@ -533,7 +519,7 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
## assert "usr/" / "/lib" == "usr/lib" ## assert "usr/" / "/lib" == "usr/lib"
return JoinPath(head, tail) return JoinPath(head, tail)
proc SplitPath*(path: string): tuple[head, tail: string] {. proc splitPath*(path: string): tuple[head, tail: string] {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Splits a directory into (head, tail), so that ## Splits a directory into (head, tail), so that
## ``JoinPath(head, tail) == path``. ## ``JoinPath(head, tail) == path``.
@ -699,7 +685,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
if r.isNil: OSError(OSLastError()) if r.isNil: OSError(OSLastError())
setlen(result, c_strlen(result)) setlen(result, c_strlen(result))
proc ChangeFileExt*(filename, ext: string): string {. proc changeFileExt*(filename, ext: string): string {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Changes the file extension to `ext`. ## Changes the file extension to `ext`.
## ##

View file

@ -194,9 +194,9 @@ const
# should not be translated. # should not be translated.
proc Open(f: var TFile, filename: string, proc open(f: var TFile, filename: string,
mode: TFileMode = fmRead, mode: TFileMode = fmRead,
bufSize: int = -1): Bool = bufSize: int = -1): bool =
var p: pointer = fopen(filename, FormatOpen[mode]) var p: pointer = fopen(filename, FormatOpen[mode])
result = (p != nil) result = (p != nil)
f = cast[TFile](p) f = cast[TFile](p)

View file

@ -101,7 +101,8 @@ proc newWideCString*(s: cstring): WideCString =
if s.isNil: return nil if s.isNil: return nil
when not defined(c_strlen): when not defined(c_strlen):
proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".} proc c_strlen(a: cstring): int {.
header: "<string.h>", noSideEffect, importc: "strlen".}
let L = cstrlen(s) let L = cstrlen(s)
result = newWideCString(s, L) result = newWideCString(s, L)

View file

@ -3,7 +3,6 @@ version 0.9.4
- new VM: - new VM:
- implement overflow checking - implement overflow checking
- implement the FFI
- make 'bind' default for templates and introduce 'mixin' - make 'bind' default for templates and introduce 'mixin'
- special rule for ``[]=`` - special rule for ``[]=``

View file

@ -10,7 +10,7 @@
import os, strutils import os, strutils
const const
cc = "gcc -o $1 $1.c" cc = "gcc -o $# $#.c"
cfile = """ cfile = """
/* Generated by detect.nim */ /* Generated by detect.nim */
@ -37,6 +37,7 @@ var
tl = "" tl = ""
proc myExec(cmd: string): bool = proc myExec(cmd: string): bool =
echo "CMD ", cmd
return execShellCmd(cmd) == 0 return execShellCmd(cmd) == 0
proc header(s: string): bool = proc header(s: string): bool =
@ -46,7 +47,7 @@ proc header(s: string): bool =
f.write("#include $1\n" % s) f.write("#include $1\n" % s)
f.write("int main() { return 0; }\n") f.write("int main() { return 0; }\n")
close(f) close(f)
result = myExec(cc % testh) result = myExec(cc % [testh.addFileExt(ExeExt), testh])
removeFile(addFileExt(testh, "c")) removeFile(addFileExt(testh, "c"))
if result: if result:
addf(hd, "#include $1\n", s) addf(hd, "#include $1\n", s)
@ -60,13 +61,16 @@ proc main =
if open(f, addFileExt(gen, "c"), fmWrite): if open(f, addFileExt(gen, "c"), fmWrite):
f.write(cfile % [hd, tl, system.hostOS, system.hostCPU]) f.write(cfile % [hd, tl, system.hostOS, system.hostCPU])
close(f) close(f)
if not myExec(cc % gen): quit(1) if not myExec(cc % [gen.addFileExt(ExeExt), gen]): quit(1)
when defined(windows):
if not myExec(gen.addFileExt(ExeExt)): quit(1)
else:
if not myExec("./" & gen): quit(1) if not myExec("./" & gen): quit(1)
removeFile(addFileExt(gen, "c")) removeFile(addFileExt(gen, "c"))
echo("Success") echo("Success")
proc v(name: string, typ: TTypeKind=cint) = proc v(name: string, typ: TTypeKind=cint) =
var n = if name[0] == '_': copy(name, 1) else: name var n = if name[0] == '_': substr(name, 1) else: name
var t = $typ var t = $typ
case typ case typ
of pointer: of pointer:
@ -369,7 +373,7 @@ if header("<pthread.h>"):
#v("PTHREAD_MUTEX_INITIALIZER") #v("PTHREAD_MUTEX_INITIALIZER")
v("PTHREAD_MUTEX_NORMAL") v("PTHREAD_MUTEX_NORMAL")
v("PTHREAD_MUTEX_RECURSIVE") #{.importc, header: "<pthread.h>".}: cint v("PTHREAD_MUTEX_RECURSIVE") #{.importc, header: "<pthread.h>".}: cint
v("PTHREAD_ONCE_INIT") #{.importc, header: "<pthread.h>".}: cint #v("PTHREAD_ONCE_INIT") #{.importc, header: "<pthread.h>".}: cint
v("PTHREAD_PRIO_INHERIT") #{.importc, header: "<pthread.h>".}: cint v("PTHREAD_PRIO_INHERIT") #{.importc, header: "<pthread.h>".}: cint
v("PTHREAD_PRIO_NONE") #{.importc, header: "<pthread.h>".}: cint v("PTHREAD_PRIO_NONE") #{.importc, header: "<pthread.h>".}: cint
v("PTHREAD_PRIO_PROTECT") #{.importc, header: "<pthread.h>".}: cint v("PTHREAD_PRIO_PROTECT") #{.importc, header: "<pthread.h>".}: cint
@ -820,5 +824,8 @@ if header("<spawn.h>"):
v("POSIX_SPAWN_SETSIGDEF") v("POSIX_SPAWN_SETSIGDEF")
v("POSIX_SPAWN_SETSIGMASK") v("POSIX_SPAWN_SETSIGMASK")
main() if header("<stdio.h>"):
v "_IOFBF"
v "_IONBF"
main()