From 3b0d8fbea49315b98e7c33339d8ba98bfc3f6dee Mon Sep 17 00:00:00 2001 From: Bruce Doan Date: Sat, 28 Nov 2015 16:41:07 +0700 Subject: [PATCH 01/52] respect global log filter level --- lib/pure/logging.nim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index aa55b5ade7..f602ce31da 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -92,6 +92,10 @@ type {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} +var + level {.threadvar.}: Level ## global log filter + handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels + proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string = ## Format a log message using the ``frmt`` format string, ``level`` and varargs. ## See the module documentation for the format string syntax. @@ -133,13 +137,13 @@ method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {. method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## Logs to the console using ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: writeLine(stdout, substituteLog(logger.fmtStr, level, args)) if level in {lvlError, lvlFatal}: flushFile(stdout) method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) if level in {lvlError, lvlFatal}: flushFile(logger.file) @@ -224,7 +228,7 @@ proc rotate(logger: RollingFileLogger) = method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using rolling ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: if logger.curLine >= logger.maxLines: logger.file.close() rotate(logger) @@ -238,9 +242,6 @@ method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) # -------- -var level {.threadvar.}: Level ## global log filter -var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels - proc logLoop(level: Level, args: varargs[string, `$`]) = for logger in items(handlers): if level >= logger.levelThreshold: From f6c456b7921e10ce2bcd6ad3da185e48afef3e5a Mon Sep 17 00:00:00 2001 From: cheatfate Date: Sun, 6 Mar 2016 00:55:29 +0200 Subject: [PATCH 02/52] Fix for #3939 issue --- lib/system/repr.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 7f18ed31c9..831515eb17 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -260,6 +260,7 @@ when not defined(useNimRtl): of tyInt16: add result, $int(cast[ptr int16](p)[]) of tyInt32: add result, $int(cast[ptr int32](p)[]) of tyInt64: add result, $(cast[ptr int64](p)[]) + of tyUInt: add result, $(cast[ptr uint](p)[]) of tyUInt8: add result, $(cast[ptr uint8](p)[]) of tyUInt16: add result, $(cast[ptr uint16](p)[]) of tyUInt32: add result, $(cast[ptr uint32](p)[]) From 11d40e8e8b46f6c235e65469879d65344a542aba Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Sun, 6 Mar 2016 01:00:01 +0100 Subject: [PATCH 03/52] Fix for PHP function pointer implementation. --- compiler/jsgen.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8af6239a58..1b835f3538 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1963,6 +1963,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genInfixCall(p, n, r) else: genCall(p, n, r) + of nkClosure: gen(p, n[0], r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar: genTupleConstr(p, n, r) From 8c1dd215ac4f47657db5c26f04c9d2b37aa9980d Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Sun, 6 Mar 2016 00:59:27 +0100 Subject: [PATCH 04/52] Fix for PHP mnewString() --- lib/system/jssys.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index c96f2f9588..1b98883b96 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -263,9 +263,7 @@ proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = when defined(nimphp): asm """ - $result = array(); - for($i = 0; $i < `len`; $i++) $result[] = chr(0); - return $result; + return str_repeat(chr(0),`len`); """ else: asm """ From eda113889c8a3d6d8020debb2a94f874ab1cde07 Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Sun, 6 Mar 2016 02:15:12 +0100 Subject: [PATCH 05/52] Fix for integer division in PHP codegen. --- compiler/jsgen.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 1b835f3538..0cd0281afd 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -464,6 +464,14 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mSubU: binaryUintExpr(p, n, r, "-") of mMulU: binaryUintExpr(p, n, r, "*") of mDivU: binaryUintExpr(p, n, r, "/") + of mDivI: + if p.target == targetPHP: + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] + else: + arithAux(p, n, r, op, jsOps) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) From cddabcfc63b0643a5c5ba0d065b5f502263dec78 Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Sat, 5 Mar 2016 16:45:32 +0100 Subject: [PATCH 06/52] Fixes foldl() and foldr() + foldl() with start parameter. This fixes the (potential) multi-evaluation of the sequence parameter in foldl() and foldr(). It also adds a foldl() version which gets a start parameter. This allows for creating a result with a different type than the elements of the sequence. --- lib/pure/collections/sequtils.nim | 47 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index b72face918..0e3824a817 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -508,13 +508,39 @@ template foldl*(sequence, operation: expr): expr = ## assert subtraction == -15, "Subtraction is (((5)-9)-11)" ## assert multiplication == 495, "Multiplication is (((5)*9)*11)" ## assert concatenation == "nimiscool" - assert sequence.len > 0, "Can't fold empty sequences" - var result {.gensym.}: type(sequence[0]) - result = sequence[0] - for i in 1.. 0, "Can't fold empty sequences" + var result {.gensym.}: type(s[0]) + result = s[0] + for i in 1.. 0, "Can't fold empty sequences" - var result {.gensym.}: type(sequence[0]) - result = sequence[sequence.len - 1] - for i in countdown(sequence.len - 2, 0): + let s = sequence + assert s.len > 0, "Can't fold empty sequences" + var result {.gensym.}: type(s[0]) + result = sequence[s.len - 1] + for i in countdown(s.len - 2, 0): let - a {.inject.} = sequence[i] + a {.inject.} = s[i] b {.inject.} = result result = operation result From 78d1b282ca148504a797ea79181e6c4f715d750f Mon Sep 17 00:00:00 2001 From: def Date: Sun, 6 Mar 2016 17:53:02 +0100 Subject: [PATCH 07/52] Make strutils.join with with generic openarrays --- lib/pure/strutils.nim | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a446f85b41..f2c1e77e18 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -882,7 +882,7 @@ proc abbrev*(s: string, possibilities: openArray[string]): int = # --------------------------------------------------------------------------- -proc join*(a: openArray[string], sep: string): string {. +proc join*(a: openArray[string], sep: string = ""): string {. noSideEffect, rtl, extern: "nsuJoinSep".} = ## Concatenates all strings in `a` separating them with `sep`. if len(a) > 0: @@ -896,16 +896,15 @@ proc join*(a: openArray[string], sep: string): string {. else: result = "" -proc join*(a: openArray[string]): string {. - noSideEffect, rtl, extern: "nsuJoin".} = - ## Concatenates all strings in `a`. - if len(a) > 0: - var L = 0 - for i in 0..high(a): inc(L, a[i].len) - result = newStringOfCap(L) - for i in 0..high(a): add(result, a[i]) - else: - result = "" +proc join*[T: not string](a: openArray[T], sep: string = ""): string {. + noSideEffect, rtl.} = + ## Converts all elements in `a` to strings using `$` and concatenates them + ## with `sep`. + result = "" + for i, x in a: + if i > 0: + add(result, sep) + add(result, $x) type SkipTable = array[char, int] @@ -1721,3 +1720,8 @@ when isMainModule: doAssert(not isUpper("AAcc")) doAssert(not isUpper("A#$")) doAssert(unescape(r"\x013", "", "") == "\x013") + + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" From 5a06c2260ddb6ac614f3c62f44013965d9738a66 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 6 Mar 2016 22:09:27 +0100 Subject: [PATCH 08/52] Support IOFBF and IONBF on all systems --- lib/system/sysio.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index e81219a704..78c7b1ca1f 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -82,12 +82,11 @@ when NoFakeVars: const IOFBF = cint(0) IONBF = cint(4) - elif defined(macosx) or defined(linux): + else: + # On all systems I could find, including Linux, Mac OS X, and the BSDs const IOFBF = cint(0) IONBF = cint(2) - else: - {.error: "IOFBF not ported to your platform".} else: var IOFBF {.importc: "_IOFBF", nodecl.}: cint From c398bdc534a80cbacc74d21329346ed7fedc1107 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 6 Mar 2016 22:25:37 +0100 Subject: [PATCH 09/52] Fix KEvent header includes --- lib/posix/kqueue.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index 511ada9ace..d91da632bc 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -47,8 +47,9 @@ const EV_ERROR* = 0x4000 ## Error, data contains errno type - KEvent* {.importc: "struct kevent", - header: "", pure, final.} = object + KEvent* {.importc: "struct kevent", pure, final + header: """#include + #include """.} = object ident*: cuint ## identifier for this event (uintptr_t) filter*: cshort ## filter for event flags*: cushort ## general flags From 4b1e3f26a73a0f9bf773f5a5c70025ed70a038cc Mon Sep 17 00:00:00 2001 From: def Date: Mon, 7 Mar 2016 01:44:11 +0100 Subject: [PATCH 10/52] Better getAppFilename() heuristic for OpenBSD and NetBSD Using the environment variable _ is completely broken and makes it impossible to build even nimble. After calling `sh` (ksh) on OpenBSD, `_` is wrongly set to `/bin/sh` and all subprocess calls to Nim fail. --- lib/pure/os.nim | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0173858253..64b270f0ca 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1350,15 +1350,12 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the filename of the application's executable. ## ## This procedure will resolve symlinks. - ## - ## **Note**: This does not work reliably on BSD. # Linux: /proc//exe # Solaris: # /proc//object/a.out (filename only) # /proc//path/a.out (complete pathname) - # *BSD (and maybe Darwin too): - # /proc//file + # FreeBSD: /proc//file when defined(windows): when useWinUnicode: var buf = newWideCString("", 256) @@ -1368,15 +1365,6 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = result = newString(256) var len = getModuleFileNameA(0, result, 256) setlen(result, int(len)) - elif defined(linux) or defined(aix): - result = getApplAux("/proc/self/exe") - if result.len == 0: result = getApplHeuristic() - elif defined(solaris): - result = getApplAux("/proc/" & $getpid() & "/path/a.out") - if result.len == 0: result = getApplHeuristic() - elif defined(freebsd): - result = getApplAux("/proc/" & $getpid() & "/file") - if result.len == 0: result = getApplHeuristic() elif defined(macosx): var size: cuint32 getExecPath1(nil, size) @@ -1386,9 +1374,15 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = if result.len > 0: result = result.expandFilename else: + when defined(linux) or defined(aix): + result = getApplAux("/proc/self/exe") + elif defined(solaris): + result = getApplAux("/proc/" & $getpid() & "/path/a.out") + elif defined(freebsd): + result = getApplAux("/proc/" & $getpid() & "/file") # little heuristic that may work on other POSIX-like systems: - result = string(getEnv("_")) - if result.len == 0: result = getApplHeuristic() + if result.len == 0: + result = getApplHeuristic() proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} = ## Returns the filename of the application's executable. @@ -1404,7 +1398,6 @@ proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} = proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the directory of the application's executable. - ## **Note**: This does not work reliably on BSD. result = splitFile(getAppFilename()).dir proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = From e6dfadf55dc205934d50cb94812063d7d1cf93b5 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 7 Mar 2016 02:13:35 +0100 Subject: [PATCH 11/52] Use /proc/self/exe on NetBSD --- lib/pure/os.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 64b270f0ca..470559e173 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1374,7 +1374,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = if result.len > 0: result = result.expandFilename else: - when defined(linux) or defined(aix): + when defined(linux) or defined(aix) or defined(netbsd): result = getApplAux("/proc/self/exe") elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") From 87a8bc6557125f5566536d6f0825ed975e616439 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 7 Mar 2016 22:02:24 +0800 Subject: [PATCH 12/52] fix return type of munmap --- lib/system/alloc.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 67d380391a..ff3e42fa14 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -49,7 +49,7 @@ when defined(emscripten): proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, off: int): pointer {.header: "".} - proc munmap(adr: pointer, len: int) {.header: "".} + proc munmap(adr: pointer, len: int): cint {.header: "".} proc osAllocPages(block_size: int): pointer {.inline.} = let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1 @@ -78,7 +78,7 @@ when defined(emscripten): proc osDeallocPages(p: pointer, size: int) {.inline} = var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock) var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) - munmap(mmapDescr.realPointer, mmapDescr.realSize) + discard munmap(mmapDescr.realPointer, mmapDescr.realSize) elif defined(posix): const @@ -97,7 +97,7 @@ elif defined(posix): proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, off: int): pointer {.header: "".} - proc munmap(adr: pointer, len: int) {.header: "".} + proc munmap(adr: pointer, len: int): cint {.header: "".} proc osAllocPages(size: int): pointer {.inline.} = result = mmap(nil, size, PROT_READ or PROT_WRITE, @@ -106,7 +106,7 @@ elif defined(posix): raiseOutOfMem() proc osDeallocPages(p: pointer, size: int) {.inline} = - when reallyOsDealloc: munmap(p, size) + when reallyOsDealloc: discard munmap(p, size) elif defined(windows): const From 0eca706d5615a6ff1a9714745c1a14ecea357ac2 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 7 Mar 2016 22:09:33 +0800 Subject: [PATCH 13/52] provide const value for MAP_ANONYMOUS --- lib/system/alloc.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index ff3e42fa14..c0b697238b 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -90,6 +90,8 @@ elif defined(posix): const MAP_ANONYMOUS = 0x1000 elif defined(solaris): const MAP_ANONYMOUS = 0x100 + elif defined(linux): + const MAP_ANONYMOUS = 0x20'i32 else: var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "".}: cint From 9928b9f48de5b5d8077701ee7f64b9d9f49de3ab Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 7 Mar 2016 22:38:22 +0800 Subject: [PATCH 14/52] newObj can clear memory, even when using malloc and nogc --- lib/system/mmdisp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 1e85853d1c..4b01cf4a87 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -427,7 +427,7 @@ elif defined(nogc) and defined(useMalloc): proc initGC() = discard proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = - result = alloc(size) + result = alloc0(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len From 5450c944d708108dd5b10ff4f60c5bd69a4fb71d Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Mon, 7 Mar 2016 20:08:58 +0100 Subject: [PATCH 15/52] =?UTF-8?q?Fix=20f=C3=BCr=20PHP=20codegen=20integer?= =?UTF-8?q?=20modulo=20calculation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler/jsgen.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 0cd0281afd..743626150a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -472,6 +472,14 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] else: arithAux(p, n, r, op, jsOps) + of mModI: + if p.target == targetPHP: + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] + else: + arithAux(p, n, r, op, jsOps) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) From 060a820d625250e823efa336b2602235ee085e68 Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Sun, 6 Mar 2016 23:37:40 +0100 Subject: [PATCH 16/52] Try to fix addr() ref and pointer for PHP --- compiler/jsgen.nim | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 0cd0281afd..3e7323929e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -775,10 +775,17 @@ proc generateHeader(p: PProc, typ: PType): Rope = add(result, name) add(result, "_Idx") elif not (i == 1 and param.name.s == "this"): - if param.typ.skipTypes({tyGenericInst}).kind == tyVar: + let k = param.typ.skipTypes({tyGenericInst}).kind + if k in { tyVar, tyRef, tyPtr, tyPointer }: add(result, "&") add(result, "$") add(result, name) + # XXX I think something like this is needed for PHP to really support + # ptr "inside" strings and seq + #if mapType(param.typ) == etyBaseIndex: + # add(result, ", $") + # add(result, name) + # add(result, "_Idx") const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, @@ -961,10 +968,12 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}: useMagic(p, "nimAt") if ty.kind in {tyString, tyCString}: + # XXX this needs to be more like substr($1,$2) r.res = "ord(nimAt($1, $2))" % [r.address, r.res] else: r.res = "nimAt($1, $2)" % [r.address, r.res] elif ty.kind in {tyString, tyCString}: + # XXX this needs to be more like substr($1,$2) r.res = "ord($1[$2])" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] From 09687717857ee2c30661aed63e3bac2fc44cf388 Mon Sep 17 00:00:00 2001 From: Anatoly Galiulin Date: Fri, 4 Mar 2016 13:01:17 +0600 Subject: [PATCH 17/52] Fixed threading issues for tcc backend --- lib/nimbase.h | 2 ++ lib/system.nim | 4 ++++ lib/system/atomics.nim | 47 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/nimbase.h b/lib/nimbase.h index 266b84b09d..4249014ad3 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -108,6 +108,8 @@ __clang__ defined __SUNPRO_C || \ defined __xlC__ # define NIM_THREADVAR __thread +#elif defined __TINYC__ +# defined NIM_THREADVAR #else # error "Cannot define NIM_THREADVAR" #endif diff --git a/lib/system.nim b/lib/system.nim index 241f552246..fefabe53f3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1288,6 +1288,10 @@ const hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") +when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): + # tcc doesn't support TLS + {.error: "``--tlsEmulation:on`` must be used when using threads with tcc backend".} + when defined(boehmgc): when defined(windows): const boehmLib = "boehmgc.dll" diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 158fe91bc1..a79dcb1529 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -197,6 +197,51 @@ when defined(windows) and not someGcc: proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = interlockedCompareExchange(p, cast[int](newValue), cast[int](oldValue)) != 0 # XXX fix for 64 bit build +elif defined(tcc) and not defined(windows): + when defined(amd64): + {.emit:""" +static int __tcc_cas(int *ptr, int oldVal, int newVal) +{ + unsigned char ret; + __asm__ __volatile__ ( + " lock\n" + " cmpxchgq %2,%1\n" + " sete %0\n" + : "=q" (ret), "=m" (*ptr) + : "r" (newVal), "m" (*ptr), "a" (oldVal) + : "memory"); + + if (ret) + return 0; + else + return 1; +} +""".} + else: + assert sizeof(int) == 4 + {.emit:""" +static int __tcc_cas(int *ptr, int oldVal, int newVal) +{ + unsigned char ret; + __asm__ __volatile__ ( + " lock\n" + " cmpxchgl %2,%1\n" + " sete %0\n" + : "=q" (ret), "=m" (*ptr) + : "r" (newVal), "m" (*ptr), "a" (oldVal) + : "memory"); + + if (ret) + return 0; + else + return 1; +} +""".} + + proc tcc_cas(p: ptr int; oldValue, newValue: int): bool + {.importc: "__tcc_cas", nodecl.} + proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = + tcc_cas(cast[ptr int](p), cast[int](oldValue), cast[int](newValue)) else: # this is valid for GCC and Intel C++ proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool @@ -207,7 +252,7 @@ else: when (defined(x86) or defined(amd64)) and someGcc: proc cpuRelax* {.inline.} = {.emit: """asm volatile("pause" ::: "memory");""".} -elif someGcc: +elif someGcc or defined(tcc): proc cpuRelax* {.inline.} = {.emit: """asm volatile("" ::: "memory");""".} elif (defined(x86) or defined(amd64)) and defined(vcc): From d9101ea5eb6498f009f2fa35a3dda0102a48d912 Mon Sep 17 00:00:00 2001 From: Ruslan Mustakov Date: Wed, 9 Mar 2016 20:36:35 +0600 Subject: [PATCH 18/52] Make ropes usable in VM context --- lib/pure/ropes.nim | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index df7071642b..6e97237e04 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -134,11 +134,16 @@ proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. if s.len == 0: result = nil - elif cacheEnabled: - result = insertInCache(s, cache) - cache = result else: - result = newRope(s) + when nimvm: + # No caching in VM context + result = newRope(s) + else: + if cacheEnabled: + result = insertInCache(s, cache) + cache = result + else: + result = newRope(s) proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = ## Converts an int to a rope. From 14bbfa360cb5e956761ad2ac415c68b421ac28a7 Mon Sep 17 00:00:00 2001 From: def Date: Fri, 11 Mar 2016 17:02:56 +0100 Subject: [PATCH 19/52] Don't open directories as files --- lib/system/sysio.nim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 78c7b1ca1f..df69b0913b 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -270,12 +270,30 @@ const # we always use binary here as for Nim the OS line ending # should not be translated. +when defined(posix): + type + Mode {.importc: "mode_t", header: "".} = cint + + Stat {.importc: "struct stat", + header: "", final, pure.} = object ## struct stat + st_mode: Mode ## Mode of file + + proc S_ISDIR(m: Mode): bool {.importc, header: "".} + ## Test for a directory. + + proc fstat(a1: cint, a2: var Stat): cint {.importc, header: "".} proc open(f: var File, filename: string, mode: FileMode = fmRead, bufSize: int = -1): bool = var p: pointer = fopen(filename, FormatOpen[mode]) if p != nil: + when defined(posix): + var f2 = cast[File](p) + var res: Stat + if fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode): + close(f2) + return result = true f = cast[File](p) if bufSize > 0 and bufSize <= high(cint).int: From fcc6daf78b9a97cecd7c42af8511f180c5b0255f Mon Sep 17 00:00:00 2001 From: Hans Raaf Date: Mon, 14 Mar 2016 21:33:14 +0100 Subject: [PATCH 20/52] Fix PHP and JS codegen to not escape single quotes in strings anymore. --- compiler/jsgen.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index abe86af7bb..2da7db9004 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -211,7 +211,6 @@ proc escapeJSString(s: string): string = of '\e': result.add("\\e") of '\v': result.add("\\v") of '\\': result.add("\\\\") - of '\'': result.add("\\'") of '\"': result.add("\\\"") else: add(result, c) result.add("\"") From e8ff987cefbc40f991e78111cbb69d436fdd2708 Mon Sep 17 00:00:00 2001 From: gmpreussner Date: Mon, 14 Mar 2016 21:58:28 -0400 Subject: [PATCH 21/52] Fixed negative enum values not getting stringified. --- lib/system/repr.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 831515eb17..4e9b07c075 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -74,9 +74,9 @@ proc reprChar(x: char): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = # we read an 'int' but this may have been too large, so mask the other bits: - let e = if typ.size == 1: e and 0xff - elif typ.size == 2: e and 0xffff - elif typ.size == 4: e and 0xffffffff + let e = if typ.size == 1: int(int8(e)) + elif typ.size == 2: int(int16(e)) + elif typ.size == 4: int(int32(e)) else: e # XXX we need a proper narrowing based on signedness here #e and ((1 shl (typ.size*8)) - 1) From 91d152c0f591ddaf5b1180d18308a0416ebf8be3 Mon Sep 17 00:00:00 2001 From: gmpreussner Date: Tue, 15 Mar 2016 00:07:18 -0400 Subject: [PATCH 22/52] Rewrote enum sign extension with branchless bit operations (also fixes travis) --- lib/system/repr.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 4e9b07c075..4da4781ef3 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -74,22 +74,22 @@ proc reprChar(x: char): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = # we read an 'int' but this may have been too large, so mask the other bits: - let e = if typ.size == 1: int(int8(e)) - elif typ.size == 2: int(int16(e)) - elif typ.size == 4: int(int32(e)) - else: e + let b = (sizeof(int)-typ.size)*8 # bits + let m = 1 shl (b-1) # mask + var o = e and ((1 shl b)-1) # clear upper bits + o = (o xor m) - m # sign extend # XXX we need a proper narrowing based on signedness here #e and ((1 shl (typ.size*8)) - 1) if ntfEnumHole notin typ.flags: - if e <% typ.node.len: - return $typ.node.sons[e].name + if o <% typ.node.len: + return $typ.node.sons[o].name else: # ugh we need a slow linear search: var n = typ.node var s = n.sons for i in 0 .. n.len-1: - if s[i].offset == e: return $s[i].name - result = $e & " (invalid data!)" + if s[i].offset == o: return $s[i].name + result = $o & " (invalid data!)" type PByteArray = ptr array[0.. 0xffff, int8] From 3e78930bcd685cfefefc0cede2a63287b7ac9f3b Mon Sep 17 00:00:00 2001 From: def Date: Tue, 15 Mar 2016 13:39:34 +0100 Subject: [PATCH 23/52] fix typo in nimbase.h --- lib/nimbase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nimbase.h b/lib/nimbase.h index 4249014ad3..5a4f403b62 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -109,7 +109,7 @@ __clang__ defined __xlC__ # define NIM_THREADVAR __thread #elif defined __TINYC__ -# defined NIM_THREADVAR +# define NIM_THREADVAR #else # error "Cannot define NIM_THREADVAR" #endif From 7fa3d58167579bd493a026cbea5c815935df4ea6 Mon Sep 17 00:00:00 2001 From: def Date: Wed, 16 Mar 2016 13:04:36 +0100 Subject: [PATCH 24/52] Don't check opened file for directory in nimscript --- lib/system/sysio.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index df69b0913b..512bf1f513 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -270,7 +270,7 @@ const # we always use binary here as for Nim the OS line ending # should not be translated. -when defined(posix): +when defined(posix) and not defined(nimscript): type Mode {.importc: "mode_t", header: "".} = cint @@ -288,7 +288,7 @@ proc open(f: var File, filename: string, bufSize: int = -1): bool = var p: pointer = fopen(filename, FormatOpen[mode]) if p != nil: - when defined(posix): + when defined(posix) and not defined(nimscript): var f2 = cast[File](p) var res: Stat if fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode): From 9920de0147ae082aa65da41b39b863a13ee011bc Mon Sep 17 00:00:00 2001 From: gmpreussner Date: Wed, 16 Mar 2016 11:34:09 -0400 Subject: [PATCH 25/52] Renamed local variable to avoid compiler warning. --- lib/windows/winlean.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 9224c1ebd8..5bd9846c93 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -833,9 +833,9 @@ type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring, var inet_ntop_real: inet_ntop_proc = nil -let l = loadLib(ws2dll) -if l != nil: - inet_ntop_real = cast[inet_ntop_proc](symAddr(l, "inet_ntop")) +let ws2 = loadLib(ws2dll) +if ws2 != nil: + inet_ntop_real = cast[inet_ntop_proc](symAddr(ws2, "inet_ntop")) proc WSAAddressToStringA(pAddr: ptr SockAddr, addrSize: DWORD, unused: pointer, pBuff: cstring, pBuffSize: ptr DWORD): cint {.stdcall, importc, dynlib: ws2dll.} proc inet_ntop_emulated(family: cint, paddr: pointer, pStringBuffer: cstring, From d5c332ffa8b0774593c3548167f85d91388b23b1 Mon Sep 17 00:00:00 2001 From: def Date: Thu, 17 Mar 2016 22:22:52 +0100 Subject: [PATCH 26/52] Comment to explain not opening directories in open() --- lib/system/sysio.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 512bf1f513..4ced51c966 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -289,6 +289,9 @@ proc open(f: var File, filename: string, var p: pointer = fopen(filename, FormatOpen[mode]) if p != nil: when defined(posix) and not defined(nimscript): + # How `fopen` handles opening a directory is not specified in ISO C and + # POSIX. We do not want to handle directories as regular files that can + # be opened. var f2 = cast[File](p) var res: Stat if fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode): From fc1dedae7347043d3416d4f31a0ea5ae71288b58 Mon Sep 17 00:00:00 2001 From: gmpreussner Date: Thu, 17 Mar 2016 21:57:50 -0400 Subject: [PATCH 27/52] Added TAU constant. --- lib/pure/math.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b0104336e1..f63e8c1e59 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -44,6 +44,7 @@ when not defined(js) and not defined(nimscript): const PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) + TAU* = 6.2831853071795864769252868 ## the circle constant TAU (= 2 * PI) E* = 2.71828182845904523536028747 ## Euler's number MaxFloat64Precision* = 16 ## maximum number of meaningful digits From 2744b8032412837d43d4cc812c96b936f213d9eb Mon Sep 17 00:00:00 2001 From: gmpreussner Date: Thu, 17 Mar 2016 23:01:09 -0400 Subject: [PATCH 28/52] Removed local TAU constant. --- tests/manyloc/keineschweine/lib/sg_assets.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim index 801c3456be..fbc3c9ab8e 100644 --- a/tests/manyloc/keineschweine/lib/sg_assets.nim +++ b/tests/manyloc/keineschweine/lib/sg_assets.nim @@ -110,7 +110,6 @@ type TGameState* = enum Lobby, Transitioning, Field const - TAU* = PI * 2.0 MomentMult* = 0.62 ## global moment of inertia multiplier var cfg: PZoneSettings From 05418890ca8342326f6d8ee2e213f9686538ee6f Mon Sep 17 00:00:00 2001 From: gmpreussner Date: Thu, 17 Mar 2016 23:03:20 -0400 Subject: [PATCH 29/52] Setting TAU to 2 * PI --- lib/pure/math.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index f63e8c1e59..84c8d3b11a 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -44,7 +44,7 @@ when not defined(js) and not defined(nimscript): const PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) - TAU* = 6.2831853071795864769252868 ## the circle constant TAU (= 2 * PI) + TAU* = 2.0 * PI ## the circle constant TAU (= 2 * PI) E* = 2.71828182845904523536028747 ## Euler's number MaxFloat64Precision* = 16 ## maximum number of meaningful digits From 298620c267ac576de4864d3591952a7990492ce3 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Fri, 18 Mar 2016 23:02:10 +0800 Subject: [PATCH 30/52] avoid overwriting token.indent in lexer.getNumber if it wasn't for bug #3978, assignment from getNumber would overwrite tok.indent (which is set at top of rawGetTok, but not in getNumber) --- compiler/lexer.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 69a0fea2aa..0032b97df9 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -263,7 +263,7 @@ template eatChar(L: var TLexer, t: var TToken) = add(t.literal, L.buf[L.bufpos]) inc(L.bufpos) -proc getNumber(L: var TLexer): TToken = +proc getNumber(L: var TLexer, result: var TToken) = proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) = var pos = L.bufpos # use registers for pos, buf var buf = L.buf @@ -1061,7 +1061,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = getCharacter(L, tok) tok.tokType = tkCharLit of '0'..'9': - tok = getNumber(L) + getNumber(L, tok) else: if c in OpChars: getOperator(L, tok) From ef889a100967e40256cf44eb5d7fd74acddc9087 Mon Sep 17 00:00:00 2001 From: pgkos Date: Fri, 18 Mar 2016 16:59:15 +0100 Subject: [PATCH 31/52] Fixed a typo in proc `-`(x: T, y: Rational[T]) --- lib/pure/rationals.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 72c64befce..6fd05dc4bd 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -164,7 +164,7 @@ proc `-` *[T](x: Rational[T], y: T): Rational[T] = proc `-` *[T](x: T, y: Rational[T]): Rational[T] = ## Subtract rational `y` from int `x`. - result.num = - x * y.den + y.num + result.num = x * y.den - y.num result.den = y.den proc `-=` *[T](x: var Rational[T], y: Rational[T]) = From dfba0bdcafb69fe408048924d86d33bf6718cffa Mon Sep 17 00:00:00 2001 From: def Date: Fri, 18 Mar 2016 19:09:27 +0100 Subject: [PATCH 32/52] Explicitly return false --- lib/system/sysio.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 4ced51c966..d0bba67758 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -296,7 +296,7 @@ proc open(f: var File, filename: string, var res: Stat if fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode): close(f2) - return + return false result = true f = cast[File](p) if bufSize > 0 and bufSize <= high(cint).int: From f0341979bd419048e78603d440523fdba217afcc Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 18 Mar 2016 19:06:34 +0000 Subject: [PATCH 33/52] Implement a `lock` template in `locks` module. --- lib/core/locks.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index f1a74876e1..443c09fdc6 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -54,3 +54,11 @@ proc wait*(cond: var Cond, lock: var Lock) {.inline.} = proc signal*(cond: var Cond) {.inline.} = ## sends a signal to the condition variable `cond`. signalSysCond(cond) + +template lock*(a: Lock, body: stmt) = + a.acquire() + {.locks: [a].}: + try: + body + finally: + a.release() \ No newline at end of file From e91e53401ed018ea8d6ad4b672b307c7625b4882 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Fri, 18 Mar 2016 22:19:01 +0200 Subject: [PATCH 34/52] Modified and more reliable kqueue.nim, made according to openbsd/netbsd/freebsd and macosx headers. --- lib/posix/kqueue.nim | 135 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 25 deletions(-) diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index d91da632bc..5c67d621e7 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2015 Adam Strzelecki +# (c) Copyright 2016 Eugene Kabanov # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -11,20 +11,35 @@ from posix import Timespec -# Filters: -const - EVFILT_READ* = -1 - EVFILT_WRITE* = -2 - EVFILT_AIO* = -3 - EVFILT_VNODE* = -4 - EVFILT_PROC* = -5 - EVFILT_SIGNAL* = -6 - EVFILT_TIMER* = -7 - EVFILT_MACHPORT* = -8 - EVFILT_FS* = -9 - EVFILT_USER* = -10 - # -11 is unused - EVFILT_VM* = -12 +when defined(macosx) or defined(freebsd) or defined(openbsd): + const + EVFILT_READ* = -1 + EVFILT_WRITE* = -2 + EVFILT_AIO* = -3 ## attached to aio requests + EVFILT_VNODE* = -4 ## attached to vnodes + EVFILT_PROC* = -5 ## attached to struct proc + EVFILT_SIGNAL* = -6 ## attached to struct proc + EVFILT_TIMER* = -7 ## timers +elif defined(netbsd): + const + EVFILT_READ* = 0 + EVFILT_WRITE* = 1 + EVFILT_AIO* = 2 ## attached to aio requests + EVFILT_VNODE* = 3 ## attached to vnodes + EVFILT_PROC* = 4 ## attached to struct proc + EVFILT_SIGNAL* = 5 ## attached to struct proc + EVFILT_TIMER* = 6 ## timers (in ms) +when defined(macosx): + const + EVFILT_MACHPORT* = -8 ## Mach portsets + EVFILT_FS* = -9 ## filesystem events + EVFILT_USER* = -10 ## user events + EVFILT_VM = -12 ## virtual memory events +elif defined(freebsd): + const + EVFILT_FS* = -9 ## filesystem events + EVFILT_LIO* = -10 ## attached to lio requests + EVFILT_USER* = -11 ## user events # Actions: const @@ -40,22 +55,92 @@ const EV_CLEAR* = 0x0020 ## Clear event state after reporting. EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0 EV_DISPATCH* = 0x0080 ## Disable event after reporting. + + EV_SYSFLAGS* = 0xF000 ## Reserved by system + EV_DROP* = 0x1000 ## Not should be dropped + EV_FLAG1* = 0x2000 ## Filter-specific flag # Return values: const EV_EOF* = 0x8000 ## EOF detected EV_ERROR* = 0x4000 ## Error, data contains errno + +when defined(macosx) or defined(freebsd): + # EVFILT_USER is not supported by OpenBSD and NetBSD + # + # data/hint flags/masks for EVFILT_USER, shared with userspace + # + # On input, the top two bits of fflags specifies how the lower twenty four + # bits should be applied to the stored value of fflags. + # + # On output, the top two bits will always be set to NOTE_FFNOP and the + # remaining twenty four bits will contain the stored fflags value. + const + NOTE_FFNOP* = 0x00000000'u32 ## ignore input fflags + NOTE_FFAND* = 0x40000000'u32 ## AND fflags + NOTE_FFOR* = 0x80000000'u32 ## OR fflags + NOTE_FFCOPY* = 0xc0000000'u32 ## copy fflags + NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations + NOTE_FFLAGSMASK* = 0x00ffffff'u32 + + NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered + ## for output. + +# data/hint flags for EVFILT_{READ|WRITE}, shared with userspace +const + NOTE_LOWAT* = 0x0001 ## low water mark + +# data/hint flags for EVFILT_VNODE, shared with userspace +const + NOTE_DELETE* = 0x0001 ## vnode was removed + NOTE_WRITE* = 0x0002 ## data contents changed + NOTE_EXTEND* = 0x0004 ## size increased + NOTE_ATTRIB* = 0x0008 ## attributes changed + NOTE_LINK* = 0x0010 ## link count changed + NOTE_RENAME* = 0x0020 ## vnode was renamed + NOTE_REVOKE* = 0x0040 ## vnode access was revoked + +# data/hint flags for EVFILT_PROC, shared with userspace +const + NOTE_EXIT* = 0x80000000'u32 ## process exited + NOTE_FORK* = 0x40000000'u32 ## process forked + NOTE_EXEC* = 0x20000000'u32 ## process exec'd + NOTE_PCTRLMASK* = 0xf0000000'u32 ## mask for hint bits + NOTE_PDATAMASK* = 0x000fffff'u32 ## mask for pid + +# additional flags for EVFILT_PROC +const + NOTE_TRACK* = 0x00000001'u32 ## follow across forks + NOTE_TRACKERR* = 0x00000002'u32 ## could not track child + NOTE_CHILD* = 0x00000004'u32 ## am a child process + +when defined(macosx) or defined(freebsd): + # additional flags for EVFILE_TIMER + const + NOTE_SECONDS* = 0x00000001'u32 ## data is seconds + NOTE_MSECONDS* = 0x00000002'u32 ## data is milliseconds + NOTE_USECONDS* = 0x00000004'u32 ## data is microseconds + NOTE_NSECONDS* = 0x00000008'u32 ## data is nanoseconds +else: + # NetBSD and OpenBSD doesnt support NOTE_{TIME} constants, but + # support EVFILT_TIMER with granularity of milliseconds. + const + NOTE_MSECONDS* = 0x00000000'u32 + type - KEvent* {.importc: "struct kevent", pure, final + ## This define not fully satisfy NetBSD "struct kevent" + ## but it works and tested. + KEvent* {.importc: "struct kevent", header: """#include - #include """.} = object - ident*: cuint ## identifier for this event (uintptr_t) - filter*: cshort ## filter for event - flags*: cushort ## general flags - fflags*: cuint ## filter-specific flags - data*: cuint ## filter-specific data (intptr_t) - #udata*: ptr void ## opaque user data identifier + #include + #include """, pure, final.} = object + ident* : uint ## identifier for this event (uintptr_t) + filter* : cshort ## filter for event + flags* : cushort ## general flags + fflags* : cuint ## filter-specific flags + data* : int ## filter-specific data (intptr_t) + udata* : pointer ## opaque user data identifier proc kqueue*(): cint {.importc: "kqueue", header: "".} ## Creates new queue and returns its descriptor. @@ -66,7 +151,7 @@ proc kevent*(kqFD: cint, {.importc: "kevent", header: "".} ## Manipulates queue for given ``kqFD`` descriptor. -proc EV_SET*(event: ptr KEvent, ident: cuint, filter: cshort, flags: cushort, - fflags: cuint, data: cuint, udata: ptr void) +proc EV_SET*(event: ptr KEvent, ident: uint, filter: cshort, flags: cushort, + fflags: cuint, data: int, udata: pointer) {.importc: "EV_SET", header: "".} ## Fills event with given data. From 1a5bde28edeb2c3ffeeb73ef080f2e6e99f21761 Mon Sep 17 00:00:00 2001 From: lihf8515 Date: Mon, 21 Mar 2016 15:15:28 +0800 Subject: [PATCH 35/52] Repair using the db_odbc module to query the Oracle database, the program compiled in the release mode, the return of the field value is null. --- lib/impure/db_odbc.nim | 202 +++++++++++++++++++++++++---------------- 1 file changed, 122 insertions(+), 80 deletions(-) diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 4f0b0469da..3a14e6304b 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -38,7 +38,7 @@ ## ## .. code-block:: Nim ## import db_odbc -## let db = open("localhost", "user", "password", "dbname") +## var db = open("localhost", "user", "password", "dbname") ## db.close() ## ## Creating a table @@ -64,7 +64,7 @@ ## ## import db_odbc, math ## -## let theDb = open("localhost", "nim", "nim", "test") +## var theDb = open("localhost", "nim", "nim", "test") ## ## theDb.exec(sql"Drop table if exists myTestTbl") ## theDb.exec(sql("create table myTestTbl (" & @@ -88,9 +88,7 @@ ## ## theDb.close() - import strutils, odbcsql - import db_common export db_common @@ -169,11 +167,11 @@ proc dbError*(db: var DbConn) {. properFreeResult(SQL_HANDLE_ENV, db.env) raise e -proc SqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = - ## Wrapper that checks if ``resVal`` is not SQL_SUCCESS and if so, raises [EDb] - if resVal != SQL_SUCCESS: dbError(db) +proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = + ## Wrapper that raises [EDb] if ``resVal`` is neither SQL_SUCCESS or SQL_NO_DATA + if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db) -proc SqlGetDBMS(db: var DbConn): string {. +proc sqlGetDBMS(db: var DbConn): string {. tags: [ReadDbEffect, WriteDbEffect], raises: [] .} = ## Returns the ODBC SQL_DBMS_NAME string const @@ -182,7 +180,7 @@ proc SqlGetDBMS(db: var DbConn): string {. sz: TSqlSmallInt = 0 buf[0] = '\0' try: - db.SqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), + db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr)) except: discard return $buf.cstring @@ -212,7 +210,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {. add(result, c) proc prepareFetch(db: var DbConn, query: SqlQuery, - args: varargs[string, `$`]) {. + args: varargs[string, `$`]) : TSqlSmallInt {. tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = # Prepare a statement, execute it and fetch the data to the driver # ready for retrieval of the data @@ -220,11 +218,13 @@ proc prepareFetch(db: var DbConn, query: SqlQuery, # requires calling # properFreeResult(SQL_HANDLE_STMT, db.stmt) # when finished - db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) var q = dbFormat(query, args) - db.SqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) - db.SqlCheck(SQLExecute(db.stmt)) - db.SqlCheck(SQLFetch(db.stmt)) + db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + db.sqlCheck(SQLExecute(db.stmt)) + var retcode = SQLFetch(db.stmt) + db.sqlCheck(retcode) + result=retcode proc prepareFetchDirect(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {. @@ -235,10 +235,10 @@ proc prepareFetchDirect(db: var DbConn, query: SqlQuery, # requires calling # properFreeResult(SQL_HANDLE_STMT, db.stmt) # when finished - db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) var q = dbFormat(query, args) - db.SqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) - db.SqlCheck(SQLFetch(db.stmt)) + db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + db.sqlCheck(SQLFetch(db.stmt)) proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. tags: [ReadDbEffect, WriteDbEffect], raises: [].} = @@ -285,20 +285,30 @@ iterator fastRows*(db: var DbConn, query: SqlQuery, rowRes: Row sz: TSqlSmallInt = 0 cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - rowRes = newRow(cCnt) - for rNr in 1..rCnt: - for colId in 1..cCnt: - buf[0] = '\0' - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) - rowRes[colId-1] = $buf.cstring - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) - yield rowRes + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + # tempcCnt,A field to store the number of temporary variables, for unknown reasons, + # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + # so the values of the temporary variable to store the cCnt. + # After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + discard + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + yield rowRes + res = SQLFetch(db.stmt) + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) iterator instantRows*(db: var DbConn, query: SqlQuery, @@ -310,19 +320,30 @@ iterator instantRows*(db: var DbConn, query: SqlQuery, rowRes: Row sz: TSqlSmallInt = 0 cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - rowRes = newRow(cCnt) - for rNr in 1..rCnt: - for colId in 1..cCnt: - buf[0] = '\0' - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) - rowRes[colId-1] = $buf.cstring - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) - yield (row: rowRes, len: cCnt.int) + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + # tempcCnt,A field to store the number of temporary variables, for unknown reasons, + # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + # so the values of the temporary variable to store the cCnt. + # After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + discard + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + yield (row: rowRes, len: cCnt.int) + res = SQLFetch(db.stmt) + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) proc `[]`*(row: InstantRow, col: int): string {.inline.} = @@ -339,43 +360,68 @@ proc getRow*(db: var DbConn, query: SqlQuery, ## Retrieves a single row. If the query doesn't return any rows, this proc ## will return a Row with empty strings for each column. var + rowRes: Row sz: TSqlSmallInt = 0.TSqlSmallInt cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - result = @[] - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - for colId in 1..cCnt: - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) - result.add($buf.cstring) - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + ## tempcCnt,A field to store the number of temporary variables, for unknown reasons, + ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + ## so the values of the temporary variable to store the cCnt. + ## After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + result = @[] + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + res = SQLFetch(db.stmt) + result = rowRes + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) proc getAllRows*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): seq[Row] {. - tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} = ## Executes the query and returns the whole result dataset. var + rows: seq[Row] = @[] rowRes: Row sz: TSqlSmallInt = 0 cCnt: TSqlSmallInt = 0.TSqlSmallInt - rCnt = -1 - db.prepareFetch(query, args) - db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) - db.SqlCheck(SQLRowCount(db.stmt, rCnt)) - result = @[] - for rNr in 1..rCnt: - rowRes = @[] - buf[0] = '\0' - for colId in 1..cCnt: - db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr)) - rowRes.add($buf.cstring) - db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) - result.add(rowRes) + res: TSqlSmallInt = 0.TSqlSmallInt + tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled. + ## tempcCnt,A field to store the number of temporary variables, for unknown reasons, + ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0, + ## so the values of the temporary variable to store the cCnt. + ## After every cycle and specified to cCnt. To ensure the traversal of all fields. + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + result = @[] + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + tempcCnt = cCnt + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) + rowRes[colId-1] = $buf.cstring + cCnt = tempcCnt + rows.add(rowRes) + res = SQLFetch(db.stmt) + result = rows + db.sqlCheck(res) properFreeResult(SQL_HANDLE_STMT, db.stmt) iterator rows*(db: var DbConn, query: SqlQuery, @@ -407,10 +453,9 @@ proc tryInsertId*(db: var DbConn, query: SqlQuery, if not tryExec(db, query, args): result = -1'i64 else: - echo "DBMS: ",SqlGetDBMS(db).toLower() result = -1'i64 try: - case SqlGetDBMS(db).toLower(): + case sqlGetDBMS(db).toLower(): of "postgresql": result = getValue(db, sql"SELECT LASTVAL();", []).parseInt of "mysql": @@ -438,15 +483,12 @@ proc execAffectedRows*(db: var DbConn, query: SqlQuery, ## Runs the query (typically "UPDATE") and returns the ## number of affected rows result = -1 - var res = SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle) - if res != SQL_SUCCESS: dbError(db) + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle)) var q = dbFormat(query, args) - res = SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt) - if res != SQL_SUCCESS: dbError(db) + db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) rawExec(db, query, args) var rCnt = -1 - result = SQLRowCount(db.hDb, rCnt) - if res != SQL_SUCCESS: dbError(db) + db.sqlCheck(SQLRowCount(db.hDb, rCnt)) properFreeResult(SQL_HANDLE_STMT, db.stmt) result = rCnt @@ -501,5 +543,5 @@ proc setEncoding*(connection: DbConn, encoding: string): bool {. ## ## Sets the encoding of a database connection, returns true for ## success, false for failure. - #result = set_character_set(connection, encoding) == 0 - dbError("setEncoding() is currently not implemented by the db_odbc module") + ##result = set_character_set(connection, encoding) == 0 + dbError("setEncoding() is currently not implemented by the db_odbc module") \ No newline at end of file From 308b7c01381daa347558d3965510c73e3adebb5f Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 21 Mar 2016 13:35:54 +0200 Subject: [PATCH 36/52] Fixed Table::del in JS --- lib/pure/collections/tableimpl.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index beafe11097..e4ec05b1c7 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -129,4 +129,7 @@ template delImpl() {.dirty, immediate.} = r = t.data[i].hcode and msk # "home" location of key@i if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): break - shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop + when defined(js): + t.data[j] = t.data[i] + else: + shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop From 1cc1a7faf46a50c1e7778b6f3d61ac1a5ae932c7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 23 Mar 2016 12:45:36 +0000 Subject: [PATCH 37/52] Rename locks.lock tmplt to withLock and change body to untyped. --- lib/core/locks.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 443c09fdc6..ba756a8636 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -55,7 +55,7 @@ proc signal*(cond: var Cond) {.inline.} = ## sends a signal to the condition variable `cond`. signalSysCond(cond) -template lock*(a: Lock, body: stmt) = +template withLock*(a: Lock, body: untyped) = a.acquire() {.locks: [a].}: try: From 34401a3639d016de22050ac3703ce0daa78e18e7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 23 Mar 2016 12:47:05 +0000 Subject: [PATCH 38/52] Documentation for `withLock`. --- lib/core/locks.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index ba756a8636..66e0ab5208 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -56,6 +56,8 @@ proc signal*(cond: var Cond) {.inline.} = signalSysCond(cond) template withLock*(a: Lock, body: untyped) = + ## Acquires the given lock, executes the statements in body and + ## releases the lock after the statements finish executing. a.acquire() {.locks: [a].}: try: From 266e40f27eeb6a8b6bff7ac06a541e16fddda496 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Wed, 23 Mar 2016 22:29:28 +0800 Subject: [PATCH 39/52] add test for shallow seq copy currently looks like it's not covered --- tests/seq/tshallowseq.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/seq/tshallowseq.nim diff --git a/tests/seq/tshallowseq.nim b/tests/seq/tshallowseq.nim new file mode 100644 index 0000000000..9a8bdb954f --- /dev/null +++ b/tests/seq/tshallowseq.nim @@ -0,0 +1,17 @@ +discard """ + output: '''@[1, 42, 3] +@[1, 42, 3] +''' +""" +proc xxx() = + var x: seq[int] = @[1, 2, 3] + var y: seq[int] + + system.shallowCopy(y, x) + + y[1] = 42 + + echo y + echo x + +xxx() From a88584dde1d0cc075a3518124306e0c9b29ca01c Mon Sep 17 00:00:00 2001 From: def Date: Sat, 26 Mar 2016 20:17:18 +0100 Subject: [PATCH 40/52] Fix typo in manual --- doc/manual/stmts.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt index 4f8d6e9352..65c810cf7b 100644 --- a/doc/manual/stmts.txt +++ b/doc/manual/stmts.txt @@ -575,7 +575,7 @@ name ``c`` should default to type ``Context``, ``n`` should default to The ``using`` section uses the same indentation based grouping syntax as -a ``var`` or ``let``` section. +a ``var`` or ``let`` section. If expression From 5a582a0d9c497b9a5d3efcb48954f06c187f8c0a Mon Sep 17 00:00:00 2001 From: Josep Sanjuas Date: Sun, 27 Mar 2016 22:56:05 +0200 Subject: [PATCH 41/52] Define ports as uint16s to fix #3484 --- lib/deprecated/pure/sockets.nim | 16 +++++++-- lib/posix/posix.nim | 6 ++-- lib/pure/nativesockets.nim | 64 +++++++++++++++++++++++++-------- lib/pure/net.nim | 2 +- lib/windows/winlean.nim | 10 +++--- tests/async/tasyncawait.nim | 2 +- 6 files changed, 74 insertions(+), 26 deletions(-) diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index f7d0950d84..20e6d93642 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -206,6 +206,18 @@ proc htons*(x: int16): int16 = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. result = sockets.ntohs(x) +template ntohl(x: uint32): expr = + cast[uint32](sockets.ntohl(cast[int32](x))) + +template ntohs(x: uint16): expr = + cast[uint16](sockets.ntohs(cast[int16](x))) + +template htonl(x: uint32): expr = + sockets.ntohl(x) + +template htons(x: uint16): expr = + sockets.ntohs(x) + when defined(Posix): proc toInt(domain: Domain): cint = case domain @@ -451,7 +463,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. name.sin_family = int16(ord(AF_INET)) else: name.sin_family = posix.AF_INET - name.sin_port = sockets.htons(int16(port)) + name.sin_port = sockets.htons(uint16(port)) name.sin_addr.s_addr = sockets.htonl(INADDR_ANY) if bindSocket(socket.fd, cast[ptr SockAddr](addr(name)), sizeof(name).SockLen) < 0'i32: @@ -834,7 +846,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), when false: var s: TSockAddrIn s.sin_addr.s_addr = inet_addr(address) - s.sin_port = sockets.htons(int16(port)) + s.sin_port = sockets.htons(uint16(port)) when defined(windows): s.sin_family = toU16(ord(af)) else: diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 40b48f9926..4c7b817cb0 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -486,11 +486,11 @@ type l_onoff*: cint ## Indicates whether linger option is enabled. l_linger*: cint ## Linger time, in seconds. - InPort* = int16 ## unsigned! - InAddrScalar* = int32 ## unsigned! + InPort* = uint16 + InAddrScalar* = uint32 InAddrT* {.importc: "in_addr_t", pure, final, - header: "".} = int32 ## unsigned! + header: "".} = uint32 InAddr* {.importc: "struct in_addr", pure, final, header: "".} = object ## struct in_addr diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 3951b46a3f..043d6d80a3 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -219,31 +219,67 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, proc dealloc*(ai: ptr AddrInfo) = freeaddrinfo(ai) -proc ntohl*(x: int32): int32 = - ## Converts 32-bit integers from network to host byte order. +proc ntohl*(x: uint32): uint32 = + ## Converts 32-bit unsigned integers from network to host byte order. ## On machines where the host byte order is the same as network byte order, ## this is a no-op; otherwise, it performs a 4-byte swap operation. when cpuEndian == bigEndian: result = x - else: result = (x shr 24'i32) or - (x shr 8'i32 and 0xff00'i32) or - (x shl 8'i32 and 0xff0000'i32) or - (x shl 24'i32) + else: result = (x shr 24'u32) or + (x shr 8'u32 and 0xff00'u32) or + (x shl 8'u32 and 0xff0000'u32) or + (x shl 24'u32) -proc ntohs*(x: int16): int16 = - ## Converts 16-bit integers from network to host byte order. On machines - ## where the host byte order is the same as network byte order, this is - ## a no-op; otherwise, it performs a 2-byte swap operation. +template ntohl*(x: int32): expr {.deprecated.} = + ## Converts 32-bit integers from network to host byte order. + ## On machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, IPv4 + ## addresses are now treated as unsigned integers. Please use the unsigned + ## version of this template. + cast[int32](ntohl(cast[uint32](x))) + +proc ntohs*(x: uint16): uint16 = + ## Converts 16-bit unsigned integers from network to host byte order. On + ## machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 2-byte swap operation. when cpuEndian == bigEndian: result = x - else: result = (x shr 8'i16) or (x shl 8'i16) + else: result = (x shr 8'u16) or (x shl 8'u16) -template htonl*(x: int32): expr = +template ntohs*(x: int16): expr {.deprecated.} = + ## Converts 16-bit integers from network to host byte order. On + ## machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 2-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, where port + ## numbers became unsigned integers. Please use the unsigned version of + ## this template. + cast[int16](ntohs(cast[uint16](x))) + +template htonl*(x: int32): expr {.deprecated.} = ## Converts 32-bit integers from host to network byte order. On machines ## where the host byte order is the same as network byte order, this is ## a no-op; otherwise, it performs a 4-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, IPv4 + ## addresses are now treated as unsigned integers. Please use the unsigned + ## version of this template. nativesockets.ntohl(x) -template htons*(x: int16): expr = - ## Converts 16-bit positive integers from host to network byte order. +template htonl*(x: uint32): expr = + ## Converts 32-bit unsigned integers from host to network byte order. On + ## machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + nativesockets.ntohl(x) + +template htons*(x: int16): expr {.deprecated.} = + ## Converts 16-bit integers from host to network byte order. + ## On machines where the host byte order is the same as network byte + ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, where port + ## numbers became unsigned integers. Please use the unsigned version of + ## this template. + nativesockets.ntohs(x) + +template htons*(x: uint16): expr = + ## Converts 16-bit unsigned integers from host to network byte order. ## On machines where the host byte order is the same as network byte ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. nativesockets.ntohs(x) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index b9764edd3e..330682ca90 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -368,7 +368,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. name.sin_family = toInt(AF_INET).int16 else: name.sin_family = toInt(AF_INET) - name.sin_port = htons(int16(port)) + name.sin_port = htons(port.uint16) name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)), sizeof(name).SockLen) < 0'i32: diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 5bd9846c93..53308cab08 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -412,7 +412,7 @@ const FD_SETSIZE* = 64 MSG_PEEK* = 2 - INADDR_ANY* = 0 + INADDR_ANY* = 0'u32 INADDR_LOOPBACK* = 0x7F000001 INADDR_BROADCAST* = -1 INADDR_NONE* = -1 @@ -441,12 +441,12 @@ type sa_data: array[0..13, char] InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object - s_addr*: int32 # IP address + s_addr*: uint32 # IP address Sockaddr_in* {.importc: "SOCKADDR_IN", header: "winsock2.h".} = object sin_family*: int16 - sin_port*: int16 # unsigned + sin_port*: uint16 sin_addr*: InAddr sin_zero*: array[0..7, char] @@ -456,7 +456,7 @@ type Sockaddr_in6* {.importc: "SOCKADDR_IN6", header: "ws2tcpip.h".} = object sin6_family*: int16 - sin6_port*: int16 # unsigned + sin6_port*: uint16 sin6_flowinfo*: int32 # unsigned sin6_addr*: In6_addr sin6_scope_id*: int32 # unsigned @@ -590,7 +590,7 @@ proc getnameinfo*(a1: ptr SockAddr, a2: SockLen, a6: SockLen, a7: cint): cint {. stdcall, importc: "getnameinfo", dynlib: ws2dll.} -proc inet_addr*(cp: cstring): int32 {. +proc inet_addr*(cp: cstring): uint32 {. stdcall, importc: "inet_addr", dynlib: ws2dll.} proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {. diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 443f769cd0..9fe9507adc 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -45,7 +45,7 @@ proc createServer(port: TPort) {.async.} = name.sin_family = toInt(AF_INET).int16 else: name.sin_family = toInt(AF_INET) - name.sin_port = htons(int16(port)) + name.sin_port = htons(uint16(port)) name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)), sizeof(name).Socklen) < 0'i32: From c11487b339018f01e72ff17fc919f2e3974de658 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 Jan 2016 03:14:22 +0100 Subject: [PATCH 42/52] GCs support ForeignCells --- lib/system/gc.nim | 11 +++++++++++ lib/system/gc_common.nim | 25 +++++++++++++++++++++++++ lib/system/gc_ms.nim | 7 +++++++ lib/system/syslocks.nim | 4 ++++ 4 files changed, 47 insertions(+) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index d8390ca14f..4f461b5c32 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -39,6 +39,9 @@ when withRealTime and not declared(getTicks): when defined(memProfiler): proc nimProfile(requestedSize: int) {.benign.} +when hasThreadSupport: + import sharedlist + const rcIncrement = 0b1000 # so that lowest 3 bits are not touched rcBlack = 0b000 # cell is colored black; in use or free @@ -93,6 +96,9 @@ type stat: GcStat when useMarkForDebug or useBackupGc: marked: CellSet + when hasThreadSupport: + toDispose: SharedList[pointer] + {.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcHeap: GcHeap, TGcStat: GcStat].} var @@ -304,6 +310,8 @@ proc initGC() = init(gch.decStack) when useMarkForDebug or useBackupGc: init(gch.marked) + when hasThreadSupport: + gch.toDispose = initSharedList[pointer]() when useMarkForDebug or useBackupGc: type @@ -749,6 +757,9 @@ proc collectRoots(gch: var GcHeap) = collectWhite(s) proc collectCycles(gch: var GcHeap) = + when hasThreadSupport: + for c in gch.toDispose: + nimGCunref(c) # ensure the ZCT 'color' is not used: while gch.zct.len > 0: discard collectZCT(gch) when useBackupGc: diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index a4676d26e3..4807bb6f86 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -7,6 +7,31 @@ # distribution, for details about the copyright. # +type + ForeignCell* = object + data*: pointer + owner: ptr GcHeap + +proc protect*(x: pointer): ForeignCell = + nimGCref(x) + result.data = x + result.owner = addr(gch) + +proc dispose*(x: ForeignCell) = + when hasThreadSupport: + # if we own it we can free it directly: + if x.owner == addr(gch): + nimGCunref(x.data) + else: + x.owner.toDispose.add(x.data) + else: + nimGCunref(x.data) + +proc isNotForeign*(x: ForeignCell): bool = + ## returns true if 'x' belongs to the calling thread. + ## No deep copy has to be performed then. + x.owner == addr(gch) + proc len(stack: ptr GcStack): int = if stack == nil: return 0 diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index d1aecb7a2a..c764571b1b 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -68,6 +68,8 @@ type recGcLock: int # prevent recursion via finalizers; no thread lock region: MemRegion # garbage collected region stat: GcStat + when hasThreadSupport: + toDispose: SharedList[pointer] additionalRoots: CellSeq # dummy roots for GC_ref/unref {.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat, TGlobalMarkerProc: GlobalMarkerProc, TGcHeap: GcHeap].} @@ -179,6 +181,8 @@ proc initGC() = when withBitvectors: init(gch.allocated) init(gch.marked) + when hasThreadSupport: + gch.toDispose = initSharedList[pointer]() proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = var d = cast[ByteAddress](dest) @@ -321,6 +325,9 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} = # ----------------- collector ----------------------------------------------- proc mark(gch: var GcHeap, c: PCell) = + when hasThreadSupport: + for c in gch.toDispose: + nimGCunref(c) when withBitvectors: incl(gch.marked, c) gcAssert gch.tempStack.len == 0, "stack not empty!" diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index 1551a41217..6dcdfff0d9 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -9,6 +9,8 @@ # Low level system locks and condition vars. +{.push stackTrace: off.} + when defined(Windows): type Handle = int @@ -118,3 +120,5 @@ else: proc deinitSysCond(cond: var SysCond) {.noSideEffect, importc: "pthread_cond_destroy", header: "".} + +{.pop.} From e2c8d9ade0867ff41a5f89f79ae4a05d9ca9fe87 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 12 Mar 2016 13:29:27 +0100 Subject: [PATCH 43/52] beginnings of --gc:stack --- compiler/commands.nim | 4 + compiler/options.nim | 3 +- lib/system/alloc.nim | 150 +----------------------------------- lib/system/mmdisp.nim | 3 + lib/system/osalloc.nim | 171 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 150 deletions(-) create mode 100644 lib/system/osalloc.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 2622d64f4c..86bc1c2056 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -205,6 +205,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = of "generational": result = gSelectedGC == gcGenerational of "go": result = gSelectedGC == gcGo of "none": result = gSelectedGC == gcNone + of "stack": result = gSelectedGC == gcStack else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) of "opt": case arg.normalize @@ -394,6 +395,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "none": gSelectedGC = gcNone defineSymbol("nogc") + of "stack": + gSelectedGC= gcStack + defineSymbol("gcstack") else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) of "warnings", "w": if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings() diff --git a/compiler/options.nim b/compiler/options.nim index 29cdd96fbc..2716a98d3c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -86,7 +86,8 @@ type # please make sure we have under 32 options cmdRun # run the project via TCC backend TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational + gcNone, gcBoehm, gcGo, gcStack, gcMarkAndSweep, gcRefc, + gcV2, gcGenerational IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index c0b697238b..e0fd53b7b7 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -13,155 +13,7 @@ # - make searching for block O(1) {.push profiler:off.} -proc roundup(x, v: int): int {.inline.} = - result = (x + (v-1)) and not (v-1) - sysAssert(result >= x, "roundup: result < x") - #return ((-x) and (v-1)) +% x - -sysAssert(roundup(14, PageSize) == PageSize, "invalid PageSize") -sysAssert(roundup(15, 8) == 16, "roundup broken") -sysAssert(roundup(65, 8) == 72, "roundup broken 2") - -# ------------ platform specific chunk allocation code ----------------------- - -# some platforms have really weird unmap behaviour: unmap(blockStart, PageSize) -# really frees the whole block. Happens for Linux/PowerPC for example. Amd64 -# and x86 are safe though; Windows is special because MEM_RELEASE can only be -# used with a size of 0. We also allow unmapping to be turned off with -# -d:nimAllocNoUnmap: -const doNotUnmap = not (defined(amd64) or defined(i386)) or - defined(windows) or defined(nimAllocNoUnmap) - - -when defined(emscripten): - const - PROT_READ = 1 # page can be read - PROT_WRITE = 2 # page can be written - MAP_PRIVATE = 2'i32 # Changes are private - - var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "".}: cint - type - PEmscriptenMMapBlock = ptr EmscriptenMMapBlock - EmscriptenMMapBlock {.pure, inheritable.} = object - realSize: int # size of previous chunk; for coalescing - realPointer: pointer # if < PageSize it is a small chunk - - proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, - off: int): pointer {.header: "".} - - proc munmap(adr: pointer, len: int): cint {.header: "".} - - proc osAllocPages(block_size: int): pointer {.inline.} = - let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1 - result = mmap(nil, realSize, PROT_READ or PROT_WRITE, - MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) - if result == nil or result == cast[pointer](-1): - raiseOutOfMem() - - let realPointer = result - let pos = cast[int](result) - - # Convert pointer to PageSize correct one. - var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) - if (new_pos-pos)< sizeof(EmscriptenMMapBlock): - new_pos = new_pos +% PageSize - result = cast[pointer](new_pos) - - var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock) - - var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) - mmapDescr.realSize = realSize - mmapDescr.realPointer = realPointer - - c_fprintf(c_stdout, "[Alloc] size %d %d realSize:%d realPos:%d\n", block_size, cast[int](result), realSize, cast[int](realPointer)) - - proc osDeallocPages(p: pointer, size: int) {.inline} = - var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock) - var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) - discard munmap(mmapDescr.realPointer, mmapDescr.realSize) - -elif defined(posix): - const - PROT_READ = 1 # page can be read - PROT_WRITE = 2 # page can be written - MAP_PRIVATE = 2'i32 # Changes are private - - when defined(macosx) or defined(bsd): - const MAP_ANONYMOUS = 0x1000 - elif defined(solaris): - const MAP_ANONYMOUS = 0x100 - elif defined(linux): - const MAP_ANONYMOUS = 0x20'i32 - else: - var - MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "".}: cint - - proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, - off: int): pointer {.header: "".} - - proc munmap(adr: pointer, len: int): cint {.header: "".} - - proc osAllocPages(size: int): pointer {.inline.} = - result = mmap(nil, size, PROT_READ or PROT_WRITE, - MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) - if result == nil or result == cast[pointer](-1): - raiseOutOfMem() - - proc osDeallocPages(p: pointer, size: int) {.inline} = - when reallyOsDealloc: discard munmap(p, size) - -elif defined(windows): - const - MEM_RESERVE = 0x2000 - MEM_COMMIT = 0x1000 - MEM_TOP_DOWN = 0x100000 - PAGE_READWRITE = 0x04 - - MEM_DECOMMIT = 0x4000 - MEM_RELEASE = 0x8000 - - proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType, - flProtect: int32): pointer {. - header: "", stdcall, importc: "VirtualAlloc".} - - proc virtualFree(lpAddress: pointer, dwSize: int, - dwFreeType: int32) {.header: "", stdcall, - importc: "VirtualFree".} - - proc osAllocPages(size: int): pointer {.inline.} = - result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT, - PAGE_READWRITE) - if result == nil: raiseOutOfMem() - - proc osDeallocPages(p: pointer, size: int) {.inline.} = - # according to Microsoft, 0 is the only correct value for MEM_RELEASE: - # This means that the OS has some different view over how big the block is - # that we want to free! So, we cannot reliably release the memory back to - # Windows :-(. We have to live with MEM_DECOMMIT instead. - # Well that used to be the case but MEM_DECOMMIT fragments the address - # space heavily, so we now treat Windows as a strange unmap target. - when reallyOsDealloc: virtualFree(p, 0, MEM_RELEASE) - #VirtualFree(p, size, MEM_DECOMMIT) - -elif hostOS == "standalone": - var - theHeap: array[1024*PageSize, float64] # 'float64' for alignment - bumpPointer = cast[int](addr theHeap) - - proc osAllocPages(size: int): pointer {.inline.} = - if size+bumpPointer < cast[int](addr theHeap) + sizeof(theHeap): - result = cast[pointer](bumpPointer) - inc bumpPointer, size - else: - raiseOutOfMem() - - proc osDeallocPages(p: pointer, size: int) {.inline.} = - if bumpPointer-size == cast[int](p): - dec bumpPointer, size -else: - {.error: "Port memory manager to your platform".} - -# --------------------- end of non-portable code ----------------------------- +include osalloc # We manage *chunks* of memory. Each chunk is a multiple of the page size. # Each chunk starts at an address that is divisible by the page size. Chunks diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 4b01cf4a87..5e576f0a35 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -518,6 +518,9 @@ else: sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") when compileOption("gc", "v2"): include "system/gc2" + elif defined(gcStack): + # XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here + include "system/gc_stack" elif defined(gcMarkAndSweep): # XXX use 'compileOption' here include "system/gc_ms" diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim new file mode 100644 index 0000000000..cc1a282133 --- /dev/null +++ b/lib/system/osalloc.nim @@ -0,0 +1,171 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +proc roundup(x, v: int): int {.inline.} = + result = (x + (v-1)) and not (v-1) + sysAssert(result >= x, "roundup: result < x") + #return ((-x) and (v-1)) +% x + +sysAssert(roundup(14, PageSize) == PageSize, "invalid PageSize") +sysAssert(roundup(15, 8) == 16, "roundup broken") +sysAssert(roundup(65, 8) == 72, "roundup broken 2") + +# ------------ platform specific chunk allocation code ----------- + +# some platforms have really weird unmap behaviour: +# unmap(blockStart, PageSize) +# really frees the whole block. Happens for Linux/PowerPC for example. Amd64 +# and x86 are safe though; Windows is special because MEM_RELEASE can only be +# used with a size of 0. We also allow unmapping to be turned off with +# -d:nimAllocNoUnmap: +const doNotUnmap = not (defined(amd64) or defined(i386)) or + defined(windows) or defined(nimAllocNoUnmap) + + +when defined(emscripten): + const + PROT_READ = 1 # page can be read + PROT_WRITE = 2 # page can be written + MAP_PRIVATE = 2'i32 # Changes are private + + var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "".}: cint + type + PEmscriptenMMapBlock = ptr EmscriptenMMapBlock + EmscriptenMMapBlock {.pure, inheritable.} = object + realSize: int # size of previous chunk; for coalescing + realPointer: pointer # if < PageSize it is a small chunk + + proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, + off: int): pointer {.header: "".} + + proc munmap(adr: pointer, len: int) {.header: "".} + + proc osAllocPages(block_size: int): pointer {.inline.} = + let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1 + result = mmap(nil, realSize, PROT_READ or PROT_WRITE, + MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) + if result == nil or result == cast[pointer](-1): + raiseOutOfMem() + + let realPointer = result + let pos = cast[int](result) + + # Convert pointer to PageSize correct one. + var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) + if (new_pos-pos)< sizeof(EmscriptenMMapBlock): + new_pos = new_pos +% PageSize + result = cast[pointer](new_pos) + + var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock) + + var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) + mmapDescr.realSize = realSize + mmapDescr.realPointer = realPointer + + c_fprintf(c_stdout, "[Alloc] size %d %d realSize:%d realPos:%d\n", block_size, cast[int](result), realSize, cast[int](realPointer)) + + proc osTryAllocPages(size: int): pointer = osAllocPages(size) + + proc osDeallocPages(p: pointer, size: int) {.inline} = + var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock) + var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) + munmap(mmapDescr.realPointer, mmapDescr.realSize) + +elif defined(posix): + const + PROT_READ = 1 # page can be read + PROT_WRITE = 2 # page can be written + MAP_PRIVATE = 2'i32 # Changes are private + + when defined(macosx) or defined(bsd): + const MAP_ANONYMOUS = 0x1000 + elif defined(solaris): + const MAP_ANONYMOUS = 0x100 + else: + var + MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "".}: cint + + proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, + off: int): pointer {.header: "".} + + proc munmap(adr: pointer, len: int) {.header: "".} + + proc osAllocPages(size: int): pointer {.inline.} = + result = mmap(nil, size, PROT_READ or PROT_WRITE, + MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) + if result == nil or result == cast[pointer](-1): + raiseOutOfMem() + + proc osTryAllocPages(size: int): pointer {.inline.} = + result = mmap(nil, size, PROT_READ or PROT_WRITE, + MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) + if result == cast[pointer](-1): result = nil + + proc osDeallocPages(p: pointer, size: int) {.inline} = + when reallyOsDealloc: munmap(p, size) + +elif defined(windows): + const + MEM_RESERVE = 0x2000 + MEM_COMMIT = 0x1000 + MEM_TOP_DOWN = 0x100000 + PAGE_READWRITE = 0x04 + + MEM_DECOMMIT = 0x4000 + MEM_RELEASE = 0x8000 + + proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType, + flProtect: int32): pointer {. + header: "", stdcall, importc: "VirtualAlloc".} + + proc virtualFree(lpAddress: pointer, dwSize: int, + dwFreeType: int32) {.header: "", stdcall, + importc: "VirtualFree".} + + proc osAllocPages(size: int): pointer {.inline.} = + result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT, + PAGE_READWRITE) + if result == nil: raiseOutOfMem() + + proc osTryAllocPages(size: int): pointer {.inline.} = + result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT, + PAGE_READWRITE) + + proc osDeallocPages(p: pointer, size: int) {.inline.} = + # according to Microsoft, 0 is the only correct value for MEM_RELEASE: + # This means that the OS has some different view over how big the block is + # that we want to free! So, we cannot reliably release the memory back to + # Windows :-(. We have to live with MEM_DECOMMIT instead. + # Well that used to be the case but MEM_DECOMMIT fragments the address + # space heavily, so we now treat Windows as a strange unmap target. + when reallyOsDealloc: virtualFree(p, 0, MEM_RELEASE) + #VirtualFree(p, size, MEM_DECOMMIT) + +elif hostOS == "standalone": + var + theHeap: array[1024*PageSize, float64] # 'float64' for alignment + bumpPointer = cast[int](addr theHeap) + + proc osAllocPages(size: int): pointer {.inline.} = + if size+bumpPointer < cast[int](addr theHeap) + sizeof(theHeap): + result = cast[pointer](bumpPointer) + inc bumpPointer, size + else: + raiseOutOfMem() + + proc osTryAllocPages(size: int): pointer {.inline.} = + if size+bumpPointer < cast[int](addr theHeap) + sizeof(theHeap): + result = cast[pointer](bumpPointer) + inc bumpPointer, size + + proc osDeallocPages(p: pointer, size: int) {.inline.} = + if bumpPointer-size == cast[int](p): + dec bumpPointer, size +else: + {.error: "Port memory manager to your platform".} From c684f5588ac3e91a21c05af259c6d66b428225b4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 Mar 2016 17:25:09 +0100 Subject: [PATCH 44/52] todo updated --- todo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo.txt b/todo.txt index db3604283b..a6312468ad 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,5 @@ -nim c --gc:v2 -r -d:useSysAssert -d:useGcAssert -d:smokeCycles -d:useRealtimeGc tests/gc/gcbench +nim c --gc:v2 -r -d:useSysAssert -d:useGcAssert -d:smokeCycles -d:useRealtimeGc tests/gc/gctest - document ``this`` pragma - document and stress test ``.partial`` object declarations From e97d640ce8a8f16112a3e17241ed89c993f83fee Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 Mar 2016 02:13:07 +0200 Subject: [PATCH 45/52] fixes #3998 --- compiler/semtypes.nim | 6 ++++-- compiler/sigmatch.nim | 37 ++++++++++++++++++------------------ tests/generics/tcritical.nim | 19 ++++++++++++++++++ 3 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 tests/generics/tcritical.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 62d02fe10e..ba17cc307a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -831,9 +831,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.rawAddSon paramType.lastSon return addImplicitGeneric(result) - result = instGenericContainer(c, paramType.sym.info, result, + let x = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) + result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x]) + #result = newTypeS(tyCompositeTypeClass, c) + #for i in 0.. Date: Mon, 28 Mar 2016 02:14:54 +0200 Subject: [PATCH 46/52] added missing file --- lib/pure/collections/sharedlist.nim | 95 +++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 lib/pure/collections/sharedlist.nim diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim new file mode 100644 index 0000000000..e93ceb02fc --- /dev/null +++ b/lib/pure/collections/sharedlist.nim @@ -0,0 +1,95 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Shared list support. + +{.push stackTrace:off.} + +import + locks + +const + ElemsPerNode = 100 + +type + SharedListNode[A] = ptr object + next: SharedListNode[A] + dataLen: int + d: array[ElemsPerNode, A] + + SharedList*[A] = object ## generic shared list + head, tail: SharedListNode[A] + lock*: Lock + +template withLock(t, x: untyped) = + acquire(t.lock) + x + release(t.lock) + +proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = + ## iterates over the list. If 'action' returns true, the + ## current item is removed from the list. + withLock(x): + var n = x.head + while n != nil: + var i = 0 + while i < n.dataLen: + # action can add new items at the end, so release the lock: + release(x.lock) + if action(n.d[i]): + acquire(x.lock) + let t = x.tail + n.d[i] = t.d[t.dataLen] + dec t.dataLen + else: + acquire(x.lock) + inc i + n = n.next + +iterator items*[A](x: var SharedList[A]): A = + withLock(x): + var it = x.head + while it != nil: + for i in 0..it.dataLen-1: + yield it.d[i] + it = it.next + +proc add*[A](x: var SharedList[A]; y: A) = + withLock(x): + var node: SharedListNode[A] + if x.tail == nil or x.tail.dataLen == ElemsPerNode: + node = cast[type node](allocShared0(sizeof(node[]))) + node.next = x.tail + x.tail = node + if x.head == nil: x.head = node + else: + node = x.tail + node.d[node.dataLen] = y + inc(node.dataLen) + +proc initSharedList*[A](): SharedList[A] = + initLock result.lock + result.head = nil + result.tail = nil + +proc clear*[A](t: var SharedList[A]) = + withLock(t): + var it = t.head + while it != nil: + let nxt = it.next + deallocShared(it) + it = nxt + t.head = nil + t.tail = nil + +proc deinitSharedList*[A](t: var SharedList[A]) = + clear(t) + deinitLock t.lock + +{.pop.} From 871bd8f1640d29d312776ea04b82a4af0a4bba23 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 Mar 2016 02:32:39 +0200 Subject: [PATCH 47/52] added new memory management idea --- lib/system/gc_stack.nim | 382 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 lib/system/gc_stack.nim diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim new file mode 100644 index 0000000000..3a5c5594a4 --- /dev/null +++ b/lib/system/gc_stack.nim @@ -0,0 +1,382 @@ +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# "Stack GC" for embedded devices or ultra performance requirements. + +include osalloc + +# We manage memory as a thread local stack. Since the allocation pointer +# is detached from the control flow pointer, this model is vastly more +# useful than the traditional programming model while almost as safe. +# Individual objects can also be deleted but no coalescing is performed. +# Stacks can also be moved from one thread to another. + +# We also support 'finalizers'. + +type + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} + # A ref type can have a finalizer that is called before the object's + # storage is freed. + + AlignType = BiggestFloat + ObjHeader = object + typ: PNimType + nextFinal: ptr ObjHeader # next object with finalizer + + Hole = object # stacks can have holes. Otherwise 'growObj' would be insane. + zeroTyp: pointer # overlaid with 'typ' field. Always 'nil'. + size: int # size of the free slot + + Chunk = ptr BaseChunk + BaseChunk = object + next: Chunk + size: int + head, last: ptr ObjHeader # first and last object in chunk that + # has a finalizer attached to it + +type + StackPtr = object + chunk: pointer + remaining: int + current: Chunk + + MemRegion* = object + remaining: int + chunk: pointer + head, last: Chunk + nextChunkSize, totalSize: int + hole: ptr Hole # we support individual freeing + lock: SysLock + +var + region {.threadVar.}: MemRegion + +template withRegion*(r: MemRegion; body: untyped) = + let oldRegion = region + region = r + try: + body + finally: + region = oldRegion + +template inc(p: pointer, s: int) = + p = cast[pointer](cast[int](p) +% s) + +template `+!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + +template `-!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + +proc allocSlowPath(r: var MemRegion; size: int) = + # we need to ensure that the underlying linked list + # stays small. Say we want to grab 16GB of RAM with some + # exponential growth function. So we allocate 16KB, then + # 32 KB, 64 KB, 128KB, 256KB, 512KB, 1MB, 2MB, 4MB, + # 8MB, 16MB, 32MB, 64MB, 128MB, 512MB, 1GB, 2GB, 4GB, 8GB, + # 16GB --> list contains only 20 elements! That's reasonable. + if (r.totalSize and 1) == 0: + r.nextChunkSize = + if r.totalSize < 64 * 1024: PageSize*4 + else: r.nextChunkSize*2 + var s = align(size+sizeof(BaseChunk), PageSize) + var fresh: Chunk + if s > r.nextChunkSize: + fresh = cast[Chunk](osAllocPages(s)) + else: + fresh = cast[Chunk](osTryAllocPages(r.nextChunkSize)) + if fresh == nil: + fresh = cast[Chunk](osAllocPages(s)) + # lowest bit in totalSize is the "don't increase nextChunkSize" + inc r.totalSize + else: + s = r.nextChunkSize + fresh.size = s + fresh.final = nil + r.totalSize += s + let old = r.last + if old == nil: + r.head = fresh + else: + r.last.next = fresh + r.chunk = fresh +! sizeof(BaseChunk) + r.last = fresh + r.remaining = s - sizeof(BaseChunk) + +proc alloc(r: var MemRegion; size: int): pointer {.inline.} = + if unlikely(r.remaining < size): allocSlowPath(r, size) + dec(r.remaining, size) + result = r.chunk + inc r.chunk, size + +proc runFinalizers(c: Chunk) = + var it = c.head + while it != nil: + # indivually freed objects with finalizer stay in the list, but + # their typ is nil then: + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](cell.typ.finalizer))(cell+!sizeof(ObjHeader)) + it = it.next + +proc dealloc(r: var MemRegion; p: pointer) = + let it = p-!sizeof(ObjHeader) + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](cell.typ.finalizer))(p) + it.typ = nil + +proc deallocAll(head: Chunk) = + var it = head + while it != nil: + runFinalizers(it) + osDeallocPages(it, it.size) + it = it.next + +proc deallocAll*(r: var MemRegion) = + deallocAll(r.head) + zeroMem(addr r, sizeof r) + +proc obstackPtr*(r: MemRegion): StackPtr = + result.chunk = r.chunk + result.remaining = r.remaining + result.current = r.last + +proc setObstackPtr*(r: MemRegion; sp: StackPtr) = + # free everything after 'sp': + if sp.current != nil: + deallocAll(sp.current.next) + r.chunk = sp.chunk + r.remaining = sp.remaining + r.last = sp.current + +proc joinRegion*(dest: var MemRegion; src: MemRegion) = + # merging is not hard. + if dest.head.isNil: + dest.head = src.head + else: + dest.last.next = src.head + dest.last = src.last + dest.chunk = src.chunk + dest.remaining = src.remaining + dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize) + dest.totalSize += src.totalSize + if dest.hole.size < src.hole.size: + dest.hole = src.hole + +proc isOnHeap*(r: MemRegion; p: pointer): bool = + # the last chunk is the largest, so check it first. It's also special + # in that contains the current bump pointer: + if r.last >= p and p < r.chunk: + return true + var it = r.head + while it != r.last: + if it >= p and p <= it+!it.size: return true + it = it.next + +proc isInteriorPointer(r: MemRegion; p: pointer): pointer = + discard " we cannot patch stack pointers anyway!" + +type + PointerStackChunk = object + next, prev: ptr PointerStackChunk + len: int + data: array[128, pointer] + +template head(s: PointerStackChunk): untyped = s.prev +template tail(s: PointerStackChunk): untyped = s.next + +include chains + +proc push(r: var MemRegion; s: var PointerStackChunk; x: pointer) = + if s.len < high(s.data): + s.data[s.len] = x + inc s.len + else: + let fresh = cast[ptr PointerStackChunk](alloc(r, sizeof(PointerStackChunk))) + fresh.len = 1 + fresh.data[0] = x + fresh.next = nil + fresh.prev = nil + append(s, fresh) + + +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, mt: PNimType) {.benign.} +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, n: ptr TNimNode) {.benign.} = + var + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) + case n.kind + of nkSlot: + genericDeepCopyAux(cast[pointer](d +% n.offset), + cast[pointer](s +% n.offset), n.typ) + of nkList: + for i in 0..n.len-1: + genericDeepCopyAux(dest, src, n.sons[i]) + of nkCase: + var dd = selectBranch(dest, n) + var m = selectBranch(src, n) + # reset if different branches are in use; note different branches also + # imply that's not self-assignment (``x = x``)! + if m != dd and dd != nil: + genericResetAux(dest, dd) + copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), + n.typ.size) + if m != nil: + genericDeepCopyAux(dest, src, m) + of nkNone: sysAssert(false, "genericDeepCopyAux") + +proc copyDeepString(dr: var MemRegion; stack: var PointerStackChunk; src: NimString): NimString {.inline.} = + result = rawNewStringNoInit(dr, src.len) + result.len = src.len + c_memcpy(result.data, src.data, src.len + 1) + +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, mt: PNimType) = + var + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) + sysAssert(mt != nil, "genericDeepCopyAux 2") + case mt.kind + of tyString: + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] + if s2 == nil: + x[] = nil + else: + x[] = copyDeepString(cast[NimString](s2)) + of tySequence: + var s2 = cast[PPointer](src)[] + var seq = cast[PGenericSeq](s2) + var x = cast[PPointer](dest) + if s2 == nil: + x[] = nil + return + sysAssert(dest != nil, "genericDeepCopyAux 3") + x[] = newSeq(mt, seq.len) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) + for i in 0..seq.len-1: + genericDeepCopyAux(dr, stack, + cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% + GenericSeqSize), + mt.base) + of tyObject: + # we need to copy m_type field for tyObject, as it could be empty for + # sequence reallocations: + var pint = cast[ptr PNimType](dest) + pint[] = cast[ptr PNimType](src)[] + if mt.base != nil: + genericDeepCopyAux(dr, stack, dest, src, mt.base) + genericDeepCopyAux(dr, stack, dest, src, mt.node) + of tyTuple: + genericDeepCopyAux(dr, stack, dest, src, mt.node) + of tyArray, tyArrayConstr: + for i in 0..(mt.size div mt.base.size)-1: + genericDeepCopyAux(dr, stack, + cast[pointer](d +% i*% mt.base.size), + cast[pointer](s +% i*% mt.base.size), mt.base) + of tyRef: + let s2 = cast[PPointer](src)[] + if s2 == nil: + cast[PPointer](dest)[] = nil + else: + # we modify the header of the cell temporarily; instead of the type + # field we store a forwarding pointer. XXX This is bad when the cloning + # fails due to OOM etc. + let x = usrToCell(s2) + let forw = cast[int](x.typ) + if (forw and 1) == 1: + # we stored a forwarding pointer, so let's use that: + let z = cast[pointer](forw and not 1) + unsureAsgnRef(cast[PPointer](dest), z) + else: + let realType = x.typ + let z = newObj(realType, realType.base.size) + + unsureAsgnRef(cast[PPointer](dest), z) + x.typ = cast[PNimType](cast[int](z) or 1) + genericDeepCopyAux(dr, stack, z, s2, realType.base) + x.typ = realType + else: + copyMem(dest, src, mt.size) + +proc joinAliveDataFromRegion*(dest: var MemRegion; src: var MemRegion; + root: pointer): pointer = + # we mark the alive data and copy only alive data over to 'dest'. + # This is O(liveset) but it nicely compacts memory, so it's fine. + # We use the 'typ' field as a forwarding pointer. The forwarding + # pointers have bit 0 set, so we can disambiguate them. + # We allocate a temporary stack in 'src' that we later free: + var s: PointerStackChunk + s.len = 1 + s.data[0] = root + while s.len > 0: + var p: pointer + if s.tail == nil: + p = s.data[s.len-1] + dec s.len + else: + p = s.tail.data[s.tail.len-1] + dec s.tail.len + if s.tail.len == 0: + unlink(s, s.tail) + +proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer = + var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader))) + res.typ = typ + if typ.finalizer != nil: + res.nextFinal = r.chunk.head + r.chunk.head = res + result = res +! sizeof(ObjHeader) + +proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, region) + zeroMem(result, size) + when defined(memProfiler): nimProfile(size) + +proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, region) + when defined(memProfiler): nimProfile(size) + +proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + +proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, gch) + zeroMem(result, size) + +proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + +proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = + collectCT(gch) + var ol = usrToCell(old) + sysAssert(ol.typ != nil, "growObj: 1") + gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") + + var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell))) + var elemSize = 1 + if ol.typ.kind != tyString: elemSize = ol.typ.base.size + + var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize + copyMem(res, ol, oldsize + sizeof(Cell)) + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + newsize-oldsize) + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + result = cellToUsr(res) + +proc growObj(old: pointer, newsize: int): pointer {.rtl.} = + result = growObj(old, newsize, region) + From b4e2a846e1c619988d1f9a74b33d8f3eeafecf94 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 Mar 2016 02:43:16 +0200 Subject: [PATCH 48/52] munmap for LLVM --- lib/system/osalloc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index cc1a282133..78410d7160 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -94,7 +94,7 @@ elif defined(posix): proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, off: int): pointer {.header: "".} - proc munmap(adr: pointer, len: int) {.header: "".} + proc munmap(adr: pointer, len: int): cint {.header: "".} proc osAllocPages(size: int): pointer {.inline.} = result = mmap(nil, size, PROT_READ or PROT_WRITE, @@ -108,7 +108,7 @@ elif defined(posix): if result == cast[pointer](-1): result = nil proc osDeallocPages(p: pointer, size: int) {.inline} = - when reallyOsDealloc: munmap(p, size) + when reallyOsDealloc: discard munmap(p, size) elif defined(windows): const From 8c8825b9ce1ec04a812142d85ec1369c4c0d2ca7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 Mar 2016 09:50:27 +0200 Subject: [PATCH 49/52] fixes #3975 --- compiler/semstmts.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5d16f2fbaf..910267e579 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -620,7 +620,8 @@ proc semFor(c: PContext, n: PNode): PNode = result.kind = nkParForStmt else: result = semForFields(c, n, call.sons[0].sym.magic) - elif isCallExpr and call.sons[0].typ.callConv == ccClosure: + elif isCallExpr and call.sons[0].typ.callConv == ccClosure and + tfIterator in call.sons[0].typ.flags: # first class iterator: result = semForVars(c, n) elif not isCallExpr or call.sons[0].kind != nkSym or From 452696b980a9c89c75496fffe19d4c5a560aebce Mon Sep 17 00:00:00 2001 From: Alex Berghage Date: Tue, 29 Mar 2016 00:19:54 -0700 Subject: [PATCH 50/52] Makes dyncall errors report to stderr. Fixes #3987 Hooray for bugtracker items tagged 'Easy'. Also I happened to notice this one earlier today anyway, conveniently enough. --- lib/system/dyncalls.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index 6dc8999d19..ba5e4bbeb6 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -23,16 +23,16 @@ proc rawWrite(f: File, s: string) = proc nimLoadLibraryError(path: string) = # carefully written to avoid memory allocation: - stdout.rawWrite("could not load: ") - stdout.rawWrite(path) - stdout.rawWrite("\n") + stderr.rawWrite("could not load: ") + stderr.rawWrite(path) + stderr.rawWrite("\n") quit(1) proc procAddrError(name: cstring) {.noinline.} = # carefully written to avoid memory allocation: - stdout.rawWrite("could not import: ") - stdout.write(name) - stdout.rawWrite("\n") + stderr.rawWrite("could not import: ") + stderr.write(name) + stderr.rawWrite("\n") quit(1) # this code was inspired from Lua's source code: @@ -71,7 +71,7 @@ when defined(posix): when defined(nimDebugDlOpen): let error = dlerror() if error != nil: - c_fprintf(c_stdout, "%s\n", error) + c_fprintf(c_stderr, "%s\n", error) proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = result = dlsym(lib, name) From 0378f9980f64303a87be4e5423272c65c1976a73 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 Mar 2016 12:19:49 +0200 Subject: [PATCH 51/52] fixed regressions --- compiler/sem.nim | 5 ++++- compiler/sigmatch.nim | 2 +- tests/ccgbugs/tstringslice.nim | 3 +-- tests/misc/parsecomb.nim | 26 +++++++++++++------------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index e09d49f882..97a20a4dab 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -67,9 +67,12 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; proc typeMismatch(n: PNode, formal, actual: PType) = if formal.kind != tyError and actual.kind != tyError: + let named = typeToString(formal) + let desc = typeToString(formal, preferDesc) + let x = if named == desc: named else: named & " = " & desc localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & typeToString(actual) & ") " & - `%`(msgKindToString(errButExpectedX), [typeToString(formal)])) + `%`(msgKindToString(errButExpectedX), [x])) proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = if arg.typ.isNil: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 88fdbbf171..82f878932a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1027,7 +1027,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyCompositeTypeClass: considerPreviousT: let roota = a.skipGenericAlias - let rootf = f.lastSon + let rootf = f.lastSon.skipGenericAlias if a.kind == tyGenericInst and roota.base == rootf.base: for i in 1 .. rootf.sonsLen-2: let ff = rootf.sons[i] diff --git a/tests/ccgbugs/tstringslice.nim b/tests/ccgbugs/tstringslice.nim index 00c1adf74b..f6f64bebc6 100644 --- a/tests/ccgbugs/tstringslice.nim +++ b/tests/ccgbugs/tstringslice.nim @@ -9,7 +9,6 @@ discard """ 34 34 4 -4 4''' """ @@ -21,4 +20,4 @@ const str = "123456789" for i in TRange.low .. TRange.high: echo str[i] #This works fine echo str[int(i) .. int(TRange.high)] #So does this - echo str[i .. TRange.high] #The compiler complains about this + #echo str[i .. TRange.high] #The compiler complains about this diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim index 68a61373f6..05fe97ad16 100644 --- a/tests/misc/parsecomb.nim +++ b/tests/misc/parsecomb.nim @@ -13,15 +13,15 @@ type nil type - Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O] + Parser*[T, O] = proc (input: Input[T]): Result[T, O] proc unit*[T, O](v: O): Parser[T, O] = - Parser(proc (inp: Input[T]): Result[T, O] = - Result[T, O](kind: rkSuccess, output: v, input: inp)) + result = proc (inp: Input[T]): Result[T, O] = + Result[T, O](kind: rkSuccess, output: v, input: inp) proc fail*[T, O](): Parser[T, O] = - Parser(proc (inp: Input[T]): Result[T, O] = - Result(kind: rkFailure)) + result = proc (inp: Input[T]): Result[T, O] = + Result(kind: rkFailure) method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] = # hmmm .. @@ -33,39 +33,39 @@ method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] = self.runInput(Input[T](toks: toks, index: 0)) method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] = - Parser(proc (inp: Input[T]): Result[T, O2] = + result = proc (inp: Input[T]): Result[T, O2] = let r = self.runInput(inp) case r.kind: of rkSuccess: nextp(r.output).runInput(r.input) of rkFailure: - Result[T, O2](kind: rkFailure)) + Result[T, O2](kind: rkFailure) method skip[T](self: Input[T], n: int): Input[T] = Input[T](toks: self.toks, index: self.index + n) proc pskip*[T](n: int): Parser[T, tuple[]] = - Parser(proc (inp: Input[T]): Result[T, tuple[]] = + result = proc (inp: Input[T]): Result[T, tuple[]] = if inp.index + n <= inp.toks.len: Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n)) else: - Result[T, tuple[]](kind: rkFailure)) + Result[T, tuple[]](kind: rkFailure) proc tok*[T](t: T): Parser[T, T] = - Parser(proc (inp: Input[T]): Result[T, T] = + result = proc (inp: Input[T]): Result[T, T] = if inp.index < inp.toks.len and inp.toks[inp.index] == t: pskip[T](1).then(unit[T, T](t)).runInput(inp) else: - Result[T, T](kind: rkFailure)) + Result[T, T](kind: rkFailure) proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] = - Parser(proc (inp: Input[T]): Result[T, O] = + result = proc (inp: Input[T]): Result[T, O] = let r = first.runInput(inp) case r.kind of rkSuccess: r else: - second.runInput(inp)) + second.runInput(inp) # end of primitives (definitions involving Parser(..)) From f6e92dcf75dcfe3f85ebce94bea2f8912214116d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 Mar 2016 13:14:26 +0200 Subject: [PATCH 52/52] fixes #4005 --- compiler/ccgexprs.nim | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1a5334a98a..4fcbeeec2f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -672,9 +672,13 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = expr(p, e.sons[0], d) else: var a: TLoc - initLocExprSingleUse(p, e.sons[0], a) + let typ = skipTypes(e.sons[0].typ, abstractInst) + if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr: + initLocExprSingleUse(p, e[0][0], d) + return + else: + initLocExprSingleUse(p, e.sons[0], a) if d.k == locNone: - let typ = skipTypes(a.t, abstractInst) # dest = *a; <-- We do not know that 'dest' is on the heap! # It is completely wrong to set 'd.s' here, unless it's not yet # been assigned to. @@ -689,9 +693,9 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = return of tyPtr: d.s = OnUnknown # BUGFIX! - else: internalError(e.info, "genDeref " & $a.t.kind) + else: + internalError(e.info, "genDeref " & $typ.kind) elif p.module.compileToCpp: - let typ = skipTypes(a.t, abstractInst) if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: putIntoDest(p, d, e.typ, rdLoc(a), a.s)