new VM: some progress for the FFI support

This commit is contained in:
Araq
2013-12-23 01:17:48 +01:00
parent e2a4d591e5
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.
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
when defined(windows):
const libcDll = "msvcrt.dll"
@@ -20,7 +20,11 @@ type
TDllCache = tables.TTable[string, TLibHandle]
var
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 =
result = cache[dll]
@@ -28,10 +32,10 @@ proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
var libs: seq[string] = @[]
libCandidates(dll, libs)
for c in libs:
result = LoadLib(c)
result = loadLib(c)
if not result.isNil: break
if result.isNil:
GlobalError(info, "cannot load: " & dll)
globalError(info, "cannot load: " & dll)
cache[dll] = result
const
@@ -50,7 +54,7 @@ proc importcSymbol*(sym: PSym): PNode =
else:
let lib = sym.annex
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
if lib.isNil and not gExehandle.isNil:
# first try this exe itself:
@@ -58,10 +62,12 @@ proc importcSymbol*(sym: PSym): PNode =
# then try libc:
if theAddr.isNil:
let dllhandle = gDllCache.getDll(libcDll, sym.info)
theAddr = dllhandle.checkedSymAddr(name)
else:
let dllhandle = gDllCache.getDll(lib.path.strVal, sym.info)
theAddr = dllhandle.checkedSymAddr(name)
theAddr = dllhandle.symAddr(name)
elif not lib.isNil:
let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
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)
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)")
of nkSym:
if n.sym.position == position: result = n.sym
else: nil
else: discard
proc packObject(x: PNode, typ: PType, res: pointer) =
InternalAssert x.kind in {nkObjConstr, nkPar}
@@ -192,7 +198,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyPointer, tyProc, tyCString, tyString:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
nil
discard
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
elif v.kind in {nkStrLit..nkTripleStrLit}:
@@ -202,7 +208,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyPtr, tyRef, tyVar:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
nil
discard
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
else:
@@ -220,7 +226,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyObject, tyTuple:
packObject(v, typ, res)
of tyNil:
nil
discard
of tyDistinct, tyGenericInst:
pack(v, typ.sons[0], res)
else:
@@ -241,7 +247,7 @@ proc unpackObjectAdd(x: pointer, n, result: PNode) =
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
result.add pair
else: nil
else: discard
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
# 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
# 'regs' in a register:
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],
prc.typ, tos.slots,
rb+1, rc-1, c.debug[pc])
if newValue.kind != nkEmpty:
assert instr.opcode == opcIndCallAsgn
regs[ra] = newValue
asgnRef(regs[ra], newValue)
else:
globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
elif prc.kind != skTemplate:
@@ -796,7 +798,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
decodeB(nkBracket)
let newLen = regs[rb].getOrdValue.int
setLen(regs[ra].sons, newLen)
of opcSwap, opcCast, opcReset:
of opcSwap, opcReset:
internalError(c.debug[pc], "too implement")
of opcIsNil:
decodeB(nkIntLit)
@@ -938,6 +940,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
stackTrace(c, tos, pc, errGenerated,
msgKindToString(errIllegalConvFromXtoY) % [
"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:
decodeB(nkMetaNode)
var dest = regs[ra].uast
@@ -1074,6 +1085,8 @@ proc myOpen(module: PSym): PPassContext =
# XXX produce a new 'globals' environment here:
setupGlobalCtx(module)
result = globalCtx
when hasFFI:
globalCtx.features = {allowFFI, allowCast}
var oldErrorCount: int

View File

@@ -945,7 +945,10 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
when hasFFI:
if allowFFI in c.features:
c.globals.add(importcSymbol(s))
if s.kind == skVar and lfNoDecl in s.loc.flags:
c.globals.add(copyNode(emptyNode))
else:
c.globals.add(importcSymbol(s))
s.position = c.globals.len
else:
localError(info, errGenerated, "VM is not allowed to 'importc'")
@@ -1303,6 +1306,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
of nkCurly: genSetConstr(c, n, dest)
of nkObjConstr: genObjConstr(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:
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 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 =
$cast[WideCString](addr(f.cFilename[0]))
else:
@@ -358,18 +348,13 @@ when defined(windows):
template FindNextFile(a, b: expr): expr = FindNextFileA(a, b)
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
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",
tags: [FReadDir].} =
## Returns true if the file exists, false otherwise.
@@ -468,20 +453,21 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
## `newDir` cannot been set.
when defined(Windows):
when useWinUnicode:
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError())
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
OSError(OSLastError())
else:
if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError())
else:
if chdir(newDir) != 0'i32: OSError(OSLastError())
proc JoinPath*(head, tail: string): string {.
proc joinPath*(head, tail: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Joins two directory names to one.
##
## For example on Unix:
##
## .. code-block:: nimrod
## JoinPath("usr", "lib")
## joinPath("usr", "lib")
##
## results in:
##
@@ -495,10 +481,10 @@ proc JoinPath*(head, tail: string): string {.
## examples on Unix:
##
## .. code-block:: nimrod
## assert JoinPath("usr", "") == "usr/"
## assert JoinPath("", "lib") == "lib"
## assert JoinPath("", "/lib") == "/lib"
## assert JoinPath("usr/", "/lib") == "usr/lib"
## assert joinPath("usr", "") == "usr/"
## assert joinPath("", "lib") == "lib"
## assert joinPath("", "/lib") == "/lib"
## assert joinPath("usr/", "/lib") == "usr/lib"
if len(head) == 0:
result = tail
elif head[len(head)-1] in {DirSep, AltSep}:
@@ -512,14 +498,14 @@ proc JoinPath*(head, tail: string): string {.
else:
result = head & DirSep & tail
proc JoinPath*(parts: varargs[string]): string {.noSideEffect,
proc joinPath*(parts: varargs[string]): string {.noSideEffect,
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
## debug builds and crash on release builds.
result = parts[0]
for i in 1..high(parts):
result = JoinPath(result, parts[i])
result = joinPath(result, parts[i])
proc `/` * (head, tail: string): string {.noSideEffect.} =
## The same as ``JoinPath(head, tail)``
@@ -533,7 +519,7 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
## assert "usr/" / "/lib" == "usr/lib"
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".} =
## Splits a directory into (head, tail), so that
## ``JoinPath(head, tail) == path``.
@@ -699,7 +685,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
if r.isNil: OSError(OSLastError())
setlen(result, c_strlen(result))
proc ChangeFileExt*(filename, ext: string): string {.
proc changeFileExt*(filename, ext: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Changes the file extension to `ext`.
##

View File

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

View File

@@ -101,7 +101,8 @@ proc newWideCString*(s: cstring): WideCString =
if s.isNil: return nil
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)
result = newWideCString(s, L)

View File

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

View File

@@ -10,7 +10,7 @@
import os, strutils
const
cc = "gcc -o $1 $1.c"
cc = "gcc -o $# $#.c"
cfile = """
/* Generated by detect.nim */
@@ -37,6 +37,7 @@ var
tl = ""
proc myExec(cmd: string): bool =
echo "CMD ", cmd
return execShellCmd(cmd) == 0
proc header(s: string): bool =
@@ -46,7 +47,7 @@ proc header(s: string): bool =
f.write("#include $1\n" % s)
f.write("int main() { return 0; }\n")
close(f)
result = myExec(cc % testh)
result = myExec(cc % [testh.addFileExt(ExeExt), testh])
removeFile(addFileExt(testh, "c"))
if result:
addf(hd, "#include $1\n", s)
@@ -60,13 +61,16 @@ proc main =
if open(f, addFileExt(gen, "c"), fmWrite):
f.write(cfile % [hd, tl, system.hostOS, system.hostCPU])
close(f)
if not myExec(cc % gen): quit(1)
if not myExec("./" & 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)
removeFile(addFileExt(gen, "c"))
echo("Success")
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
case typ
of pointer:
@@ -369,7 +373,7 @@ if header("<pthread.h>"):
#v("PTHREAD_MUTEX_INITIALIZER")
v("PTHREAD_MUTEX_NORMAL")
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_NONE") #{.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_SETSIGMASK")
main()
if header("<stdio.h>"):
v "_IOFBF"
v "_IONBF"
main()