diff --git a/compiler/commands.nim b/compiler/commands.nim index d3266930b1..a02728dacd 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -398,13 +398,13 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) of "cincludes": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cIncludes.add arg + if pass in {passCmd2, passPP}: cIncludes.add arg.processPath of "clibdir": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLibs.add arg + if pass in {passCmd2, passPP}: cLibs.add arg.processPath of "clib": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLinkedLibs.add arg + if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath of "header": headerFile = arg incl(gGlobalOptions, optGenIndex) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 21a131996a..92b79f9b65 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -441,3 +441,46 @@ proc callForeignFunction*(call: PNode): PNode = for i in 1 .. call.len-1: call.sons[i] = unpack(args[i-1], typ.sons[i], call[i]) dealloc args[i-1] + +proc callForeignFunction*(fn: PNode, fntyp: PType, + args: var TNodeSeq, start, len: int, + info: TLineInfo): PNode = + internalAssert fn.kind == nkPtrLit + + var cif: TCif + var sig: TParamList + for i in 0..len-1: + var aTyp = args[i+start].typ + if aTyp.isNil: + internalAssert i+1 < fntyp.len + aTyp = fntyp.sons[i+1] + args[i+start].typ = aTyp + sig[i] = mapType(aTyp) + if sig[i].isNil: globalError(info, "cannot map FFI type") + + if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len), + mapType(fntyp.sons[0]), sig) != OK: + globalError(info, "error in FFI call") + + var cargs: TArgList + let fn = cast[pointer](fn.intVal) + for i in 0 .. len-1: + let t = args[i+start].typ + cargs[i] = alloc0(packSize(args[i+start], t)) + pack(args[i+start], t, cargs[i]) + let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil) + else: alloc(fntyp.sons[0].getSize.int) + + libffi.call(cif, fn, retVal, cargs) + + if retVal.isNil: + result = emptyNode + else: + result = unpack(retVal, fntyp.sons[0], nil) + result.info = info + + if retVal != nil: dealloc retVal + for i in 0 .. len-1: + let t = args[i+start].typ + args[i+start] = unpack(cargs[i], t, args[i+start]) + dealloc cargs[i] diff --git a/compiler/nimrod.cfg b/compiler/nimrod.cfg index ac8f732f1f..0affe86d95 100644 --- a/compiler/nimrod.cfg +++ b/compiler/nimrod.cfg @@ -12,3 +12,6 @@ path:"$lib/packages/docutils" define:booting +@if windows: + cincludes: "$lib/wrappers/libffi/common" +@end diff --git a/compiler/vm.nim b/compiler/vm.nim index 1596e4220f..019397bbcf 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -14,10 +14,13 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, - parser, vmdeps, idents, trees, renderer + parser, vmdeps, idents, trees, renderer, options from semfold import leValueConv, ordinalValToString +when hasFFI: + import evalffi + type PStackFrame* = ref TStackFrame TStackFrame* = object @@ -608,24 +611,39 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = let rc = instr.regC let isClosure = regs[rb].kind == nkPar let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym - let newPc = compile(c, prc) - #echo "new pc ", newPc, " calling: ", prc.name.s - var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) - newSeq(newFrame.slots, prc.position) - if not isEmptyType(prc.typ.sons[0]): - newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) - # pass every parameter by var (the language definition allows this): - for i in 1 .. rc-1: - newFrame.slots[i] = regs[rb+i] - if isClosure: - newFrame.slots[rc] = regs[rb].sons[1] - # allocate the temporaries: - for i in rc+ord(isClosure) .. = fntyp.len: + internalAssert tfVarargs in fntyp.flags + c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ)) if dest < 0: c.gABC(n, opcIndCall, 0, x, n.len) else: @@ -884,13 +892,26 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) = let lit = genLiteral(c, n) c.gABx(n, opc, dest, lit) +proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = + when hasFFI: + if allowFFI in c.features: + c.globals.add(importcSymbol(s)) + s.position = c.globals.len + else: + localError(info, errGenerated, "VM is not allowed to 'importc'") + else: + localError(info, errGenerated, + "cannot 'importc' variable at compile time") + proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = let s = n.sym if sfGlobal in s.flags: if dest < 0: dest = c.getTemp(s.typ) if s.position == 0: - c.globals.add(s.ast) - s.position = c.globals.len + if sfImportc in s.flags: c.importcSym(n.info, s) + else: + c.globals.add(s.ast) + s.position = c.globals.len # XXX var g = codeHere() ? c.gABx(n, opcLdGlobal, dest, s.position) else: @@ -1003,9 +1024,11 @@ proc genVarSection(c: PCtx; n: PNode) = let s = a.sons[0].sym if sfGlobal in s.flags: if s.position == 0: - let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast - c.globals.add(sa) - s.position = c.globals.len + if sfImportc in s.flags: c.importcSym(a.info, s) + else: + let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast + c.globals.add(sa) + s.position = c.globals.len if a.sons[2].kind == nkEmpty: when false: withTemp(tmp, s.typ): @@ -1103,6 +1126,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) = of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest) of skProc, skConverter, skMacro, skMethod, skIterator: + if sfImportc in s.flags: c.importcSym(n.info, s) genLit(c, n, dest) of skConst: gen(c, s.ast, dest) @@ -1317,11 +1341,11 @@ proc genProc(c: PCtx; s: PSym): int = c.patch(procStart) c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) - s.position = c.prc.maxSlots + s.offset = c.prc.maxSlots #if s.name.s == "innerProc": # c.echoCode # echo renderTree(body) c.prc = oldPrc else: - c.prc.maxSlots = s.position + c.prc.maxSlots = s.offset result = x.intVal.int diff --git a/config/nimrod.cfg b/config/nimrod.cfg index 77cc742b22..fbbf8efbb0 100644 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -27,6 +27,7 @@ path="$lib/wrappers/readline" path="$lib/wrappers/sdl" path="$lib/wrappers/x11" path="$lib/wrappers/zip" +path="$lib/wrappers/libffi" path="$lib/windows" path="$lib/posix" path="$lib/js" diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 98d1f16d2c..79525aa18a 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1020,8 +1020,8 @@ proc editDistance*(a, b: string): int {.noSideEffect, # floating point formating: -proc c_sprintf(buf, frmt: CString) {.nodecl, importc: "sprintf", varargs, - noSideEffect.} +proc c_sprintf(buf, frmt: CString) {.header: "", importc: "sprintf", + varargs, noSideEffect.} type TFloatFormat* = enum ## the different modes of floating point formating diff --git a/lib/wrappers/libffi.nim b/lib/wrappers/libffi/libffi.nim similarity index 63% rename from lib/wrappers/libffi.nim rename to lib/wrappers/libffi/libffi.nim index 514ce024fd..5ce9cc2e20 100644 --- a/lib/wrappers/libffi.nim +++ b/lib/wrappers/libffi/libffi.nim @@ -26,12 +26,40 @@ {.deadCodeElim: on.} -when defined(windows): - const libffidll* = "libffi.dll" -elif defined(macosx): - const libffidll* = "libffi.dylib" -else: - const libffidll* = "libffi.so" +when defined(windows): + # on Windows we don't use a DLL but instead embed libffi directly: + {.pragma: mylib, header: r"ffi.h".} + + {.compile: r"common\callproc.c".} + {.compile: r"common\malloc_closure.c".} + {.compile: r"common\raw_api.c".} + when defined(vcc): + #{.compile: "libffi_msvc\ffi.h".} + #