summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/commands.nim6
-rw-r--r--compiler/evalffi.nim43
-rw-r--r--compiler/nimrod.cfg3
-rw-r--r--compiler/vm.nim58
-rw-r--r--compiler/vmdef.nim1
-rw-r--r--compiler/vmgen.nim42
-rw-r--r--config/nimrod.cfg1
-rw-r--r--lib/pure/strutils.nim4
-rw-r--r--lib/wrappers/libffi/libffi.nim (renamed from lib/wrappers/libffi.nim)84
9 files changed, 179 insertions, 63 deletions
diff --git a/compiler/commands.nim b/compiler/commands.nim
index d3266930b..a02728dac 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 21a131996..92b79f9b6 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 ac8f732f1..0affe86d9 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 1596e4220..019397bbc 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) .. <prc.position:
-        newFrame.slots[i] = newNode(nkEmpty)
-      tos = newFrame
-      move(regs, newFrame.slots)
-      # -1 for the following 'inc pc'
-      pc = newPc-1
+      if sfImportc in prc.flags:
+        if allowFFI notin c.features:
+          globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
+        # we pass 'tos.slots' instead of 'regs' so that the compiler can keep
+        # 'regs' in a register:
+        when hasFFI:
+          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
+        else:
+          globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
+      else:
+        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.offset)
+        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) .. <prc.offset:
+          newFrame.slots[i] = newNode(nkEmpty)
+        tos = newFrame
+        move(regs, newFrame.slots)
+        # -1 for the following 'inc pc'
+        pc = newPc-1
     of opcTJmp:
       # jump Bx if A != 0
       let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
@@ -1054,7 +1072,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
   let start = genProc(c, sym)
 
   var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
-  let maxSlots = sym.position
+  let maxSlots = sym.offset
   newSeq(tos.slots, maxSlots)
   # setup arguments:
   var L = n.safeLen
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 16ba1ed8e..15ea9767b 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -175,6 +175,7 @@ type
     module*: PSym
     callsite*: PNode
     mode*: TEvalMode
+    features*: TSandboxFlags
 
   TPosition* = distinct int
 
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 332184e0d..46edad2cd 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -11,7 +11,10 @@
 
 import
   unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, 
-  trees, intsets, rodread, magicsys
+  trees, intsets, rodread, magicsys, options
+
+when hasFFI:
+  import evalffi
 
 proc codeListing(c: PCtx, result: var string) =
   # first iteration: compute all necessary labels:
@@ -395,9 +398,14 @@ proc genReturn(c: PCtx; n: PNode) =
 proc genCall(c: PCtx; n: PNode; dest: var TDest) =
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
   let x = c.getTempRange(n.len, slotTempUnknown)
-  for i in 0.. <n.len: 
+  # varargs need 'opcSetType' for the FFI support:
+  let fntyp = n.sons[0].typ
+  for i in 0.. <n.len:
     var r: TRegister = x+i
     c.gen(n.sons[i], r)
+    if i >= 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 77cc742b2..fbbf8efbb 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 98d1f16d2..79525aa18 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: "<stdio.h>", 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
index 514ce024f..5ce9cc2e2 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".}
+    #<ClInclude: "..\Modules\_ctypes\libffi_msvc\ffi_common.h".}
+    #<ClInclude: "..\Modules\_ctypes\libffi_msvc\fficonfig.h".}
+    #<ClInclude: "..\Modules\_ctypes\libffi_msvc\ffitarget.h".}
+    {.compile: r"msvc\ffi.c".}
+    {.compile: r"msvc\prep_cif.c".}
+    {.compile: r"msvc\win32.c".}
+    {.compile: r"msvc\types.c".}
+    when defined(cpu64):
+      {.compile: r"msvc\win64.asm".}
+  else:
+    {.compile: r"gcc\ffi.c".}
+    {.compile: r"gcc\prep_cif.c".}
+    {.compile: r"gcc\win32.c".}
+    {.compile: r"gcc\types.c".}
+    {.compile: r"gcc\closures.c".}
+    when defined(cpu64):
+      {.compile: r"gcc\ffi64.c".}
+      {.compile: r"gcc\win64.S".}
+    else:
+      {.compile: r"gcc\win32.S".}
+
+elif defined(macosx):
+  {.pragma: mylib, dynlib: "libffi.dylib".}
+else:
+  {.pragma: mylib, dynlib: "libffi.so".}
 
 type
   TArg* = int
@@ -88,19 +116,19 @@ type
     elements*: ptr ptr TType
 
 var
-  type_void* {.importc: "ffi_type_void", dynlib: libffidll.}: TType
-  type_uint8* {.importc: "ffi_type_uint8", dynlib: libffidll.}: TType
-  type_sint8* {.importc: "ffi_type_sint8", dynlib: libffidll.}: TType
-  type_uint16* {.importc: "ffi_type_uint16", dynlib: libffidll.}: TType
-  type_sint16* {.importc: "ffi_type_sint16", dynlib: libffidll.}: TType
-  type_uint32* {.importc: "ffi_type_uint32", dynlib: libffidll.}: TType
-  type_sint32* {.importc: "ffi_type_sint32", dynlib: libffidll.}: TType
-  type_uint64* {.importc: "ffi_type_uint64", dynlib: libffidll.}: TType
-  type_sint64* {.importc: "ffi_type_sint64", dynlib: libffidll.}: TType
-  type_float* {.importc: "ffi_type_float", dynlib: libffidll.}: TType
-  type_double* {.importc: "ffi_type_double", dynlib: libffidll.}: TType
-  type_pointer* {.importc: "ffi_type_pointer", dynlib: libffidll.}: TType
-  type_longdouble* {.importc: "ffi_type_longdouble", dynlib: libffidll.}: TType
+  type_void* {.importc: "ffi_type_void", mylib.}: TType
+  type_uint8* {.importc: "ffi_type_uint8", mylib.}: TType
+  type_sint8* {.importc: "ffi_type_sint8", mylib.}: TType
+  type_uint16* {.importc: "ffi_type_uint16", mylib.}: TType
+  type_sint16* {.importc: "ffi_type_sint16", mylib.}: TType
+  type_uint32* {.importc: "ffi_type_uint32", mylib.}: TType
+  type_sint32* {.importc: "ffi_type_sint32", mylib.}: TType
+  type_uint64* {.importc: "ffi_type_uint64", mylib.}: TType
+  type_sint64* {.importc: "ffi_type_sint64", mylib.}: TType
+  type_float* {.importc: "ffi_type_float", mylib.}: TType
+  type_double* {.importc: "ffi_type_double", mylib.}: TType
+  type_pointer* {.importc: "ffi_type_pointer", mylib.}: TType
+  type_longdouble* {.importc: "ffi_type_longdouble", mylib.}: TType
 
 type 
   Tstatus* {.size: sizeof(cint).} = enum 
@@ -119,20 +147,18 @@ type
     sint*: TSArg
 
 proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer; 
-               avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", 
-                                   dynlib: libffidll.}
+               avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", mylib.}
 proc ptrarray_to_raw*(cif: var Tcif; args: ptr pointer; raw: ptr TRaw) {.cdecl, 
-    importc: "ffi_ptrarray_to_raw", dynlib: libffidll.}
+    importc: "ffi_ptrarray_to_raw", mylib.}
 proc raw_to_ptrarray*(cif: var Tcif; raw: ptr TRaw; args: ptr pointer) {.cdecl, 
-    importc: "ffi_raw_to_ptrarray", dynlib: libffidll.}
-proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", 
-                                     dynlib: libffidll.}
+    importc: "ffi_raw_to_ptrarray", mylib.}
+proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", mylib.}
 
 proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType; 
                atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif", 
-    dynlib: libffidll.}
+    mylib.}
 proc call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer; 
-           avalue: ptr pointer) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
+           avalue: ptr pointer) {.cdecl, importc: "ffi_call", mylib.}
 
 # the same with an easier interface:
 type
@@ -141,9 +167,9 @@ type
 
 proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType; 
                atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif",
-    dynlib: libffidll.}
+    mylib.}
 proc call*(cif: var Tcif; fn, rvalue: pointer;
-           avalue: TArgList) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
+           avalue: TArgList) {.cdecl, importc: "ffi_call", mylib.}
 
 # Useful for eliminating compiler warnings 
 ##define FFI_FN(f) ((void (*)(void))f)