summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/cgmeth.nim87
-rw-r--r--compiler/transf.nim3
-rw-r--r--koch.nim2
-rw-r--r--lib/pure/osproc.nim32
-rw-r--r--tests/method/tmproto.nim25
-rw-r--r--tests/method/trecmeth.nim22
-rw-r--r--tests/stdlib/tosprocterminate.nim21
7 files changed, 158 insertions, 34 deletions
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 6703b1ba5..6c997b983 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -11,7 +11,7 @@
 
 import 
   intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2
+  sempass2, strutils
 
 proc genConv(n: PNode, d: PType, downcast: bool): PNode = 
   var dest = skipTypes(d, abstractPtrs)
@@ -44,7 +44,8 @@ proc methodCall*(n: PNode): PNode =
     result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
 
 # save for incremental compilation:
-var gMethods: seq[TSymSeq] = @[]
+var
+  gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
 
 proc sameMethodBucket(a, b: PSym): bool = 
   result = false
@@ -80,31 +81,70 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
   else:
     s.ast.add(dispatcher)
 
+proc createDispatcher(s: PSym): PSym =
+  var disp = copySym(s)
+  incl(disp.flags, sfDispatcher)
+  excl(disp.flags, sfExported)
+  disp.typ = copyType(disp.typ, disp.typ.owner, false)
+  # we can't inline the dispatcher itself (for now):
+  if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
+  disp.ast = copyTree(s.ast)
+  disp.ast.sons[bodyPos] = ast.emptyNode
+  disp.loc.r = nil
+  if s.typ.sons[0] != nil:
+    if disp.ast.sonsLen > resultPos:
+      disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
+    else:
+      # We've encountered a method prototype without a filled-in
+      # resultPos slot. We put a placeholder in there that will
+      # be updated in fixupDispatcher().
+      disp.ast.addSon(ast.emptyNode)
+  attachDispatcher(s, newSymNode(disp))
+  # attach to itself to prevent bugs:
+  attachDispatcher(disp, newSymNode(disp))
+  return disp
+
+proc fixupDispatcher(meth, disp: PSym) =
+  # We may have constructed the dispatcher from a method prototype
+  # and need to augment the incomplete dispatcher with information
+  # from later definitions, particularly the resultPos slot. Also,
+  # the lock level of the dispatcher needs to be updated/checked
+  # against that of the method.
+  if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
+     disp.ast.sons[resultPos] == ast.emptyNode:
+    disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
+
+  # The following code works only with lock levels, so we disable
+  # it when they're not available.
+  when declared(TLockLevel):
+    proc `<`(a, b: TLockLevel): bool {.borrow.}
+    proc `==`(a, b: TLockLevel): bool {.borrow.}
+    if disp.typ.lockLevel == UnspecifiedLockLevel:
+      disp.typ.lockLevel = meth.typ.lockLevel
+    elif meth.typ.lockLevel != UnspecifiedLockLevel and
+         meth.typ.lockLevel != disp.typ.lockLevel:
+      message(meth.info, warnLockLevel,
+        "method has lock level $1, but another method has $2" %
+        [$meth.typ.lockLevel, $disp.typ.lockLevel])
+      # XXX The following code silences a duplicate warning in
+      # checkMethodeffects() in sempass2.nim for now.
+      if disp.typ.lockLevel < meth.typ.lockLevel:
+        disp.typ.lockLevel = meth.typ.lockLevel
+
 proc methodDef*(s: PSym, fromCache: bool) =
   var L = len(gMethods)
   for i in countup(0, L - 1):
-    let disp = gMethods[i][0]
+    var disp = gMethods[i].dispatcher
     if sameMethodBucket(disp, s):
-      add(gMethods[i], s)
+      add(gMethods[i].methods, s)
       attachDispatcher(s, lastSon(disp.ast))
+      fixupDispatcher(s, disp)
       when useEffectSystem: checkMethodEffects(disp, s)
       return 
-  add(gMethods, @[s])
   # create a new dispatcher:
-  if not fromCache:
-    var disp = copySym(s)
-    incl(disp.flags, sfDispatcher)
-    excl(disp.flags, sfExported)
-    disp.typ = copyType(disp.typ, disp.typ.owner, false)
-    # we can't inline the dispatcher itself (for now):
-    if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
-    disp.ast = copyTree(s.ast)
-    disp.ast.sons[bodyPos] = ast.emptyNode
-    if s.typ.sons[0] != nil: 
-      disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
-    attachDispatcher(s, newSymNode(disp))
-    # attach to itself to prevent bugs:
-    attachDispatcher(disp, newSymNode(disp))
+  add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
+  if fromCache:
+    internalError(s.info, "no method dispatcher found")
 
 proc relevantCol(methods: TSymSeq, col: int): bool =
   # returns true iff the position is relevant
@@ -194,8 +234,9 @@ proc generateMethodDispatchers*(): PNode =
   result = newNode(nkStmtList)
   for bucket in countup(0, len(gMethods) - 1): 
     var relevantCols = initIntSet()
-    for col in countup(1, sonsLen(gMethods[bucket][0].typ) - 1): 
-      if relevantCol(gMethods[bucket], col): incl(relevantCols, col)
-    sortBucket(gMethods[bucket], relevantCols)
-    addSon(result, newSymNode(genDispatcher(gMethods[bucket], relevantCols)))
+    for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): 
+      if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
+    sortBucket(gMethods[bucket].methods, relevantCols)
+    addSon(result,
+           newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols)))
 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index bab719ba1..6196512ba 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -624,6 +624,9 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
     # bugfix: check after 'transformSons' if it's still a method call:
     # use the dispatcher for the call:
     if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
+      let t = lastSon(s.sons[0].sym.ast)
+      if t.kind != nkSym or sfDispatcher notin t.sym.flags:
+        methodDef(s.sons[0].sym, false)
       result = methodCall(s).PTransNode
     else:
       result = s.PTransNode
diff --git a/koch.nim b/koch.nim
index cbc22b2a3..2de93bdea 100644
--- a/koch.nim
+++ b/koch.nim
@@ -92,7 +92,7 @@ const
   compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
 
 proc csource(args: string) = 
-  exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nim.ini $1" %
+  exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=mingw32 csource compiler/nim.ini $1" %
        [args, VersionAsString, compileNimInst, findNim()])
 
 proc zip(args: string) =
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 500ec7fb7..3963497fd 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -167,8 +167,14 @@ proc resume*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
   ## Resumes the process `p`.
 
 proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
-  ## Terminates the process `p`.
-
+  ## Stop the process `p`. On Posix OSes the procedure sends ``SIGTERM``
+  ## to the process. On Windows the Win32 API function ``TerminateProcess()``
+  ## is called to stop the process.
+
+proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
+  ## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to
+  ## the process. On Windows ``kill()`` is simply an alias for ``terminate()``.
+  
 proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].}
   ## Returns true iff the process `p` is still running. Returns immediately.
 
@@ -489,7 +495,10 @@ when defined(Windows) and not defined(useNimRtl):
     if running(p):
       discard terminateProcess(p.fProcessHandle, 0)
 
-  proc waitForExit(p: Process, timeout: int = -1): int =
+  proc kill(p: PProcess) =
+    terminate(p)
+      
+  proc waitForExit(p: PProcess, timeout: int = -1): int =
     discard waitForSingleObject(p.fProcessHandle, timeout.int32)
 
     var res: int32
@@ -829,10 +838,10 @@ elif not defined(useNimRtl):
     discard close(p.errHandle)
 
   proc suspend(p: Process) =
-    if kill(-p.id, SIGSTOP) != 0'i32: raiseOSError(osLastError())
+    if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
 
   proc resume(p: Process) =
-    if kill(-p.id, SIGCONT) != 0'i32: raiseOSError(osLastError())
+    if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError())
 
   proc running(p: Process): bool =
     var ret = waitpid(p.id, p.exitCode, WNOHANG)
@@ -840,11 +849,13 @@ elif not defined(useNimRtl):
     result = ret == int(p.id)
 
   proc terminate(p: Process) =
-    if kill(-p.id, SIGTERM) == 0'i32:
-      if p.running():
-        if kill(-p.id, SIGKILL) != 0'i32: raiseOSError(osLastError())
-    else: raiseOSError(osLastError())
+    if kill(p.id, SIGTERM) != 0'i32:
+      raiseOsError(osLastError())
 
+  proc kill(p: Process) =
+    if kill(p.id, SIGKILL) != 0'i32: 
+      raiseOsError(osLastError())
+    
   proc waitForExit(p: Process, timeout: int = -1): int =
     #if waitPid(p.id, p.exitCode, 0) == int(p.id):
     # ``waitPid`` fails if the process is not running anymore. But then
@@ -885,7 +896,8 @@ elif not defined(useNimRtl):
       createStream(p.errStream, p.errHandle, fmRead)
     return p.errStream
 
-  proc csystem(cmd: cstring): cint {.nodecl, importc: "system", header: "<stdlib.h>".}
+  proc csystem(cmd: cstring): cint {.nodecl, importc: "system", 
+                                     header: "<stdlib.h>".}
 
   proc execCmd(command: string): int =
     when defined(linux):
diff --git a/tests/method/tmproto.nim b/tests/method/tmproto.nim
new file mode 100644
index 000000000..5d75cff1a
--- /dev/null
+++ b/tests/method/tmproto.nim
@@ -0,0 +1,25 @@
+type
+  Obj1 = ref object {.inheritable.}
+  Obj2 = ref object of Obj1
+
+method beta(x: Obj1): int
+
+proc delta(x: Obj2): int =
+  beta(x)
+
+method beta(x: Obj2): int
+
+proc alpha(x: Obj1): int =
+  beta(x)
+
+method beta(x: Obj1): int = 1
+method beta(x: Obj2): int = 2
+
+proc gamma(x: Obj1): int =
+  beta(x)
+
+doAssert alpha(Obj1()) == 1
+doAssert gamma(Obj1()) == 1
+doAssert alpha(Obj2()) == 2
+doAssert gamma(Obj2()) == 2
+doAssert delta(Obj2()) == 2
diff --git a/tests/method/trecmeth.nim b/tests/method/trecmeth.nim
new file mode 100644
index 000000000..32f620f15
--- /dev/null
+++ b/tests/method/trecmeth.nim
@@ -0,0 +1,22 @@
+# Note: We only compile this to verify that code generation
+# for recursive methods works, no code is being executed
+
+type
+  Obj = ref object of TObject
+
+# Mutual recursion
+
+method alpha(x: Obj)
+method beta(x: Obj)
+
+method alpha(x: Obj) =
+  beta(x)
+
+method beta(x: Obj) =
+  alpha(x)
+
+# Simple recursion
+
+method gamma(x: Obj) =
+  gamma(x)
+
diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim
new file mode 100644
index 000000000..fd044414c
--- /dev/null
+++ b/tests/stdlib/tosprocterminate.nim
@@ -0,0 +1,21 @@
+import os, osproc
+
+when defined(Windows):
+  const ProgramWhichDoesNotEnd = "notepad"
+else:
+  const ProgramWhichDoesNotEnd = "/bin/sh"
+
+echo("starting " & ProgramWhichDoesNotEnd)
+var process = startProcess(ProgramWhichDoesNotEnd)
+sleep(500)
+echo("stopping process")
+process.terminate()
+var TimeToWait = 5000
+while process.running() and TimeToWait > 0:
+  sleep(100)
+  TimeToWait = TimeToWait - 100
+  
+if process.running():
+  echo("FAILED")
+else:
+  echo("SUCCESS")