From fce4b7cd6216cb1f3f195cfdefc4c16da37958fd Mon Sep 17 00:00:00 2001 From: andri lim Date: Sat, 1 Apr 2017 02:58:09 +0700 Subject: [PATCH 01/14] attempt to fix #5621 #5615 generic ref object typeRel (#5633) --- compiler/semtypinst.nim | 2 +- tests/generics/tobjecttyperel2.nim | 42 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/generics/tobjecttyperel2.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index fddcc7a24c..9ff0b7e78d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -472,7 +472,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = var r = replaceTypeVarsT(cl, result.sons[i]) if result.kind == tyObject: # carefully coded to not skip the precious tyGenericInst: - let r2 = r.skipTypes({tyGenericInst, tyAlias}) + let r2 = r.skipTypes({tyAlias}) if r2.kind in {tyPtr, tyRef}: r = skipTypes(r2, {tyPtr, tyRef}) result.sons[i] = r diff --git a/tests/generics/tobjecttyperel2.nim b/tests/generics/tobjecttyperel2.nim new file mode 100644 index 0000000000..d8c0751b75 --- /dev/null +++ b/tests/generics/tobjecttyperel2.nim @@ -0,0 +1,42 @@ +discard """ + output: '''1 +a +13''' +""" + +# bug #5621 #5615 +type + Obj5[T] = ref object of RootObj + x_impl: T + +proc x[T](v476205: Obj5[T]): T {.used.} = + v476205.x_impl + +type + Obj6[T, U] = ref object of Obj5[T] + y_impl: U + +proc newObj6[T, U](x: T; y: U): Obj6[T, U] = + new(result) + result.x_impl = x + result.y_impl = y + +proc x[T, U](v477606: Obj6[T, U]): T {.used.} = + v477606.x_impl + +proc y[T, U](v477608: Obj6[T, U]): U {.used.} = + v477608.y_impl + +let e = newObj6(1, "a") +echo e.x +echo e.y + +type + Fruit[T] = ref object of RootObj + Apple[T] = ref object of Fruit[T] + +proc getColor[T](v: Fruit[T]): T = 13 + +var w: Apple[int] +let r = getColor(w) +echo r From 2f9a698e8749f087a2c28a75f87e273aa14bc0fa Mon Sep 17 00:00:00 2001 From: andri lim Date: Sat, 1 Apr 2017 02:58:26 +0700 Subject: [PATCH 02/14] attempt to fix #5632 typedesc typeRel regression (#5634) --- compiler/sigmatch.nim | 2 +- tests/generics/tobjecttyperel3.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/generics/tobjecttyperel3.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ff7b0ae723..4661abda02 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1268,7 +1268,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # crossing path with metatypes/aliases, so we need to separate them # by checking sym.id let genericSubtype = isGenericSubType(c, x, f, depth, f) - if not (genericSubtype and aobj.sym.id != fobj.sym.id): + if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody: depth = -1 if depth >= 0: diff --git a/tests/generics/tobjecttyperel3.nim b/tests/generics/tobjecttyperel3.nim new file mode 100644 index 0000000000..3d8079e289 --- /dev/null +++ b/tests/generics/tobjecttyperel3.nim @@ -0,0 +1,12 @@ +discard """ + output: '''OK''' +""" +#bug #5632 +type + Option*[T] = object + +proc point*[A](v: A, t: typedesc[Option[A]]): Option[A] = + discard + +discard point(1, Option) +echo "OK" \ No newline at end of file From 2946c7a4b9db26d238ea5d6b639e35983b34aaf5 Mon Sep 17 00:00:00 2001 From: Silvio Date: Fri, 31 Mar 2017 22:00:48 +0200 Subject: [PATCH 03/14] Implementing `repr` for JS (#5578) --- compiler/jsgen.nim | 52 +++++- lib/system/reprjs.nim | 272 ++++++++++++++++++++++++++- tests/js/trepr.nim | 422 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 728 insertions(+), 18 deletions(-) create mode 100644 tests/js/trepr.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index d64e4f7ddf..0016a8492b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1626,22 +1626,56 @@ proc genToArray(p: PProc; n: PNode; r: var TCompRes) = localError(x.info, "'toArray' needs an array literal") r.res.add(")") +proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) = + useMagic(p, magic) + add(r.res, magic & "(") + var a: TCompRes + + gen(p, n.sons[1], a) + if magic == "reprAny": + # the pointer argument in reprAny is expandend to + # (pointedto, pointer), so we need to fill it + if a.address.isNil: + add(r.res, a.res) + add(r.res, ", null") + else: + add(r.res, "$1, $2" % [a.address, a.res]) + else: + add(r.res, a.res) + + if not typ.isNil: + add(r.res, ", ") + add(r.res, typ) + add(r.res, ")") + proc genRepr(p: PProc, n: PNode, r: var TCompRes) = if p.target == targetPHP: localError(n.info, "'repr' not available for PHP backend") return let t = skipTypes(n.sons[1].typ, abstractVarRange) - case t.kind - of tyInt..tyUInt64: - unaryExpr(p, n, r, "", "(\"\"+ ($1))") + case t.kind: + of tyInt..tyInt64, tyUInt..tyUInt64: + genReprAux(p, n, r, "reprInt") + of tyChar: + genReprAux(p, n, r, "reprChar") + of tyBool: + genReprAux(p, n, r, "reprBool") + of tyFloat..tyFloat128: + genReprAux(p, n, r, "reprFloat") + of tyString: + genReprAux(p, n, r, "reprStr") of tyEnum, tyOrdinal: - gen(p, n.sons[1], r) - useMagic(p, "cstrToNimstr") - r.kind = resExpr - r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res] + genReprAux(p, n, r, "reprEnum", genTypeInfo(p, t)) + of tySet: + genReprAux(p, n, r, "reprSet", genTypeInfo(p, t)) + of tyEmpty, tyVoid: + localError(n.info, "'repr' doesn't support 'void' type") + of tyPointer: + genReprAux(p, n, r, "reprPointer") + of tyOpenArray, tyVarargs: + genReprAux(p, n, r, "reprJSONStringify") else: - # XXX: - internalError(n.info, "genRepr: Not implemented") + genReprAux(p, n, r, "reprAny", genTypeInfo(p, t)) proc genOf(p: PProc, n: PNode, r: var TCompRes) = var x: TCompRes diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 57237cfffa..6b0e32191e 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -6,18 +6,272 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. # +# The generic ``repr`` procedure for the javascript backend. proc reprInt(x: int64): string {.compilerproc.} = return $x +proc reprFloat(x: float): string {.compilerproc.} = + # Js toString doesn't differentiate between 1.0 and 1, + # but we do. + if $x == $(x.int): $x & ".0" + else: $x + +proc reprPointer(p: pointer): string {.compilerproc.} = + # Do we need to generate the full 8bytes ? In js a pointer is an int anyway + var tmp: int + {. emit: """ + if (`p`_Idx == null) { + `tmp` = 0; + } else { + `tmp` = `p`_Idx; + } + """ .} + result = $tmp + +proc reprBool(x: bool): string {.compilerRtl.} = + if x: result = "true" + else: result = "false" + +proc isUndefined[T](x: T): bool {.inline.} = {.emit: "`result` = `x` === undefined;"} proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = - if ntfEnumHole notin typ.flags: - if e <% typ.node.len: - return $typ.node.sons[e].name + if not typ.node.sons[e].isUndefined: + result = $typ.node.sons[e].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!)" + result = $e & " (invalid data!)" + +proc reprChar(x: char): string {.compilerRtl.} = + result = "\'" + case x + of '"': add(result, "\\\"") + of '\\': add(result, "\\\\") + of '\128'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) ) + else: add(result, x) + add(result, "\'") +proc reprStrAux(result: var string, s: cstring, len: int) = + add(result, "\"") + for i in 0 .. "", but repr of an + # array of string that is not initialized is [nil, nil, ...] ?? + add(result, "nil") + else: + reprStrAux(result, s, s.len) + +proc addSetElem(result: var string, elem: int, typ: PNimType) = + # Dispatch each set element to the correct repr proc + case typ.kind: + of tyEnum: add(result, reprEnum(elem, typ)) + of tyBool: add(result, reprBool(bool(elem))) + of tyChar: add(result, reprChar(chr(elem))) + of tyRange: addSetElem(result, elem, typ.base) # Note the base to advance towards the element type + of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem) + else: # data corrupt --> inform the user + add(result, " (invalid data!)") + +iterator setKeys(s: int): int {.inline.} = + # The type of s is a lie, but it's expected to be a set. + # Iterate over the JS object representing a set + # and returns the keys as int. + var len: int + var yieldRes: int + var i: int = 0 + {. emit: """ + var setObjKeys = Object.getOwnPropertyNames(`s`); + `len` = setObjKeys.length; + """ .} + while i < len: + {. emit: "`yieldRes` = parseInt(setObjKeys[`i`],10);\n" .} + yield yieldRes + inc i + +proc reprSetAux(result: var string, s: int, typ: PNimType) = + add(result, "{") + var first: bool = true + for el in setKeys(s): + if first: + first = false + else: + add(result, ", ") + addSetElem(result, el, typ.base) + add(result, "}") + +proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} = + result = "" + reprSetAux(result, e, typ) + +type + ReprClosure {.final.} = object + recDepth: int # do not recurse endlessly + indent: int # indentation + +proc initReprClosure(cl: var ReprClosure) = + cl.recDepth = -1 # default is to display everything! + cl.indent = 0 + +proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure) + +proc reprArray(a: pointer, typ: PNimType, + cl: var ReprClosure): string {.compilerRtl.} = + var isNilArrayOrSeq: bool + # isnil is not enough here as it would try to deref `a` without knowing what's inside + {. emit: """ + if (`a` == null) { + `isNilArrayOrSeq` = true; + } else if (`a`[0] == null) { + `isNilArrayOrSeq` = true; + } else { + `isNilArrayOrSeq` = false; + }; + """ .} + if typ.kind == tySequence and isNilArrayOrSeq: + return "nil" + + # We prepend @ to seq, the C backend prepends the pointer to the seq. + result = if typ.kind == tySequence: "@[" else: "[" + var len: int = 0 + var i: int = 0 + + {. emit: "`len` = `a`.length;\n" .} + var dereffed: pointer = a + for i in 0 .. < len: + if i > 0 : + add(result, ", ") + # advance pointer and point to element at index + {. emit: """ + `dereffed`_Idx = `i`; + `dereffed` = `a`[`dereffed`_Idx]; + """ .} + reprAux(result, dereffed, typ.base, cl) + + add(result, "]") + +proc isPointedToNil(p: pointer): bool {.inline.}= + {. emit: "if (`p` === null) {`result` = true};\n" .} + +proc reprRef(result: var string, p: pointer, typ: PNimType, + cl: var ReprClosure) = + if p.isPointedToNil: + add(result , "nil") + return + add( result, "ref " & reprPointer(p) ) + add(result, " --> ") + if typ.base.kind != tyArray: + {. emit: """ + if (`p` != null && `p`.length > 0) { + `p` = `p`[`p`_Idx]; + } + """ .} + reprAux(result, p, typ.base, cl) + +proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) = + add(result, "[") + + var first: bool = true + var val: pointer = o + if typ.node.len == 0: + # if the object has only one field, len is 0 and sons is nil, the field is in node + let key: cstring = typ.node.name + add(result, $key & " = ") + {. emit: "`val` = `o`[`key`];\n" .} + reprAux(result, val, typ.node.typ, cl) + else: + # if the object has more than one field, sons is not nil and contains the fields. + for i in 0 .. unknown node type + e: uint = 1234567 + + doAssert(repr(a) == "254") + doAssert(repr(b) == "65300") + doAssert(repr(c) == "4294967290") + # doAssert(repr(d) == "18446744073709551610") + doAssert(repr(e) == "1234567") + +block floats: + let + a: float32 = 3.4e38'f32 + b: float64 = 1.7976931348623157e308'f64 + c: float = 1234.567e89 + + when defined js: + doAssert(repr(a) == "3.4e+38") # in C: 3.399999952144364e+038 + doAssert(repr(b) == "1.7976931348623157e+308") # in C: 1.797693134862316e+308 + doAssert(repr(c) == "1.234567e+92") # in C: 1.234567e+092 + +block bools: + let + a: bool = true + b: bool = false + + doAssert(repr(a) == "true") + doAssert(repr(b) == "false") + +block enums: + type + AnEnum = enum + aeA + aeB + aeC + HoledEnum = enum + heA = -12 + heB = 15 + heC = 123 + + doAssert(repr(aeA) == "aeA") + doAssert(repr(aeB) == "aeB") + doAssert(repr(aeC) == "aeC") + doAssert(repr(heA) == "heA") + doAssert(repr(heB) == "heB") + doAssert(repr(heC) == "heC") + +block chars: + let + a = 'a' + b = 'z' + one = '1' + nl = '\x0A' + + doAssert(repr(a) == "'a'") + doAssert(repr(b) == "'z'") + doAssert(repr(one) == "'1'") + doAssert(repr(nl) == "'\\10'") + +block strings: + let + a: string = "12345" + b: string = "hello,repr" + c: string = "hi\nthere" + when defined js: # C prepends the pointer, JS does not. + doAssert(repr(a) == "\"12345\"") + doAssert(repr(b) == "\"hello,repr\"") + doAssert(repr(c) == "\"hi\nthere\"") + +block sets: + let + a: set[int16] = {1'i16, 2'i16, 3'i16} + b: set[char] = {'A', 'k'} + + doAssert(repr(a) == "{1, 2, 3}") + doAssert(repr(b) == "{'A', 'k'}") + +block ranges: + let + a: range[0..12] = 6 + b: range[-12..0] = -6 + doAssert(repr(a) == "6") + doAssert(repr(b) == "-6") + +block tuples: + type + ATuple = tuple + a: int + b: float + c: string + d: OtherTuple + OtherTuple = tuple + a: bool + b: int8 + + let + ot: OtherTuple = (a: true, b: 120'i8) + t: ATuple = (a: 42, b: 12.34, c: "tuple", d: ot) + when defined js: + doAssert(repr(ot) == """ +[Field0 = true, +Field1 = 120] +""") + doAssert(repr(t) == """ +[Field0 = 42, +Field1 = 12.34, +Field2 = "tuple", +Field3 = [Field0 = true, +Field1 = 120]] +""") + +block objects: + type + AnObj = object + a: int + b: float + c: OtherObj + OtherObj = object + a: bool + b: int8 + let + oo: OtherObj = OtherObj(a: true, b: 120'i8) + o: AnObj = AnObj(a: 42, b: 12.34, c: oo) + + doAssert(repr(oo) == """ +[a = true, +b = 120] +""") + doAssert(repr(o) == """ +[a = 42, +b = 12.34, +c = [a = true, +b = 120]] +""") + +block arrays: + type + AObj = object + x: int + y: array[3,float] + let + a = [0.0, 1, 2] + b = [a, a, a] + o = AObj(x: 42, y: a) + c = [o, o, o] + d = ["hi", "array", "!"] + + doAssert(repr(a) == "[0.0, 1.0, 2.0]\n") + doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]\n") + doAssert(repr(c) == """ +[[x = 42, +y = [0.0, 1.0, 2.0]], [x = 42, +y = [0.0, 1.0, 2.0]], [x = 42, +y = [0.0, 1.0, 2.0]]] +""") + doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]\n") + +block seqs: + type + AObj = object + x: int + y: seq[float] + let + a = @[0.0, 1, 2] + b = @[a, a, a] + o = AObj(x: 42, y: a) + c = @[o, o, o] + d = @["hi", "array", "!"] + + doAssert(repr(a) == "@[0.0, 1.0, 2.0]\n") + doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]\n") + doAssert(repr(c) == """ +@[[x = 42, +y = @[0.0, 1.0, 2.0]], [x = 42, +y = @[0.0, 1.0, 2.0]], [x = 42, +y = @[0.0, 1.0, 2.0]]] +""") + doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]\n") + +block ptrs: + type + AObj = object + x: ptr array[2, AObj] + y: int + var + a = [12.0, 13.0, 14.0] + b = addr a[0] + c = addr a[2] + d = AObj() + + doAssert(repr(a) == "[12.0, 13.0, 14.0]\n") + doAssert(repr(b) == "ref 0 --> 12.0\n") + doAssert(repr(c) == "ref 2 --> 14.0\n") + doAssert(repr(d) == """ +[x = nil, +y = 0] +""") + +block ptrs: + type + AObj = object + x: ref array[2, AObj] + y: int + var + a = AObj() + + new(a.x) + + doAssert(repr(a) == """ +[x = ref 0 --> [[x = nil, +y = 0], [x = nil, +y = 0]], +y = 0] +""") + +block procs: + proc test(): int = + echo "hello" + var + ptest = test + nilproc: proc(): int + + doAssert(repr(test) == "0\n") + doAssert(repr(ptest) == "0\n") + doAssert(repr(nilproc) == "nil\n") + +block bunch: + type + AnEnum = enum + eA, eB, eC + B = object + a: string + b: seq[char] + A = object + a: uint32 + b: int + c: float + d: char + e: AnEnum + f: string + g: set[char] + h: set[int16] + i: array[3,string] + j: seq[string] + k: range[-12..12] + l: B + m: ref B + n: ptr B + o: tuple[x: B, y: string] + p: proc(b: B): ref B + q: cstring + + proc refB(b:B):ref B = + new result + result[] = b + + var + aa: A + bb: B = B(a: "inner", b: @['o', 'b', 'j']) + cc: A = A(a: 12, b: 1, c: 1.2, d: '\0', e: eC, + f: "hello", g: {'A'}, h: {2'i16}, + i: ["hello", "world", "array"], + j: @["hello", "world", "seq"], k: -1, + l: bb, m: refB(bb), n: addr bb, + o: (bb, "tuple!"), p: refB, q: "cstringtest" ) + + doAssert(repr(aa) == """ +[a = 0, +b = 0, +c = 0.0, +d = '\0', +e = eA, +f = nil, +g = {}, +h = {}, +i = [nil, nil, nil], +j = nil, +k = 0, +l = [a = nil, +b = nil], +m = nil, +n = nil, +o = [Field0 = [a = nil, +b = nil], +Field1 = nil], +p = nil, +q = nil] +""") + doAssert(repr(cc) == """ +[a = 12, +b = 1, +c = 1.2, +d = '\0', +e = eC, +f = "hello", +g = {'A'}, +h = {2}, +i = ["hello", "world", "array"], +j = @["hello", "world", "seq"], +k = -1, +l = [a = "inner", +b = @['o', 'b', 'j']], +m = ref 0 --> [a = "inner", +b = @['o', 'b', 'j']], +n = ref 0 --> [a = "inner", +b = @['o', 'b', 'j']], +o = [Field0 = [a = "inner", +b = @['o', 'b', 'j']], +Field1 = "tuple!"], +p = 0, +q = "cstringtest"] +""") + +block another: + type + Size1 = enum + s1a, s1b + Size2 = enum + s2c=0, s2d=20000 + Size3 = enum + s3e=0, s3f=2000000000 + + doAssert(repr([s1a, s1b]) == "[s1a, s1b]\n") + doAssert(repr([s2c, s2d]) == "[s2c, s2d]\n") + doAssert(repr([s3e, s3f]) == "[s3e, s3f]\n") + +block another2: + + type + AnEnum = enum + en1, en2, en3, en4, en5, en6 + + Point {.final.} = object + x, y, z: int + s: array[0..1, string] + e: AnEnum + + var + p: Point + q: ref Point + s: seq[ref Point] + + p.x = 0 + p.y = 13 + p.z = 45 + p.s[0] = "abc" + p.s[1] = "xyz" + p.e = en6 + + new(q) + q[] = p + + s = @[q, q, q, q] + + doAssert(repr(p) == """ +[x = 0, +y = 13, +z = 45, +s = ["abc", "xyz"], +e = en6] +""") + doAssert(repr(q) == """ +ref 0 --> [x = 0, +y = 13, +z = 45, +s = ["abc", "xyz"], +e = en6] +""") + doAssert(repr(s) == """ +@[ref 0 --> [x = 0, +y = 13, +z = 45, +s = ["abc", "xyz"], +e = en6], ref 1 --> [x = 0, +y = 13, +z = 45, +s = ["abc", "xyz"], +e = en6], ref 2 --> [x = 0, +y = 13, +z = 45, +s = ["abc", "xyz"], +e = en6], ref 3 --> [x = 0, +y = 13, +z = 45, +s = ["abc", "xyz"], +e = en6]] +""") + doAssert(repr(en4) == "en4") + + doAssert(repr({'a'..'p'}) == "{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}") From c480505797f9d82b9b19a72b5a5abde9c0cb0fd4 Mon Sep 17 00:00:00 2001 From: wt Date: Sat, 1 Apr 2017 04:13:06 +0800 Subject: [PATCH 04/14] Fix wrong value range of ntohs ... (#5390) --- lib/posix/posix.nim | 8 ++++---- tests/async/tnewasyncudp.nim | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index b29f43eb4c..0d11bd8536 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1862,10 +1862,10 @@ when hasAioH: a4: ptr SigEvent): cint {.importc, header: "".} # arpa/inet.h -proc htonl*(a1: int32): int32 {.importc, header: "".} -proc htons*(a1: int16): int16 {.importc, header: "".} -proc ntohl*(a1: int32): int32 {.importc, header: "".} -proc ntohs*(a1: int16): int16 {.importc, header: "".} +proc htonl*(a1: uint32): uint32 {.importc, header: "".} +proc htons*(a1: uint16): uint16 {.importc, header: "".} +proc ntohl*(a1: uint32): uint32 {.importc, header: "".} +proc ntohs*(a1: uint16): uint16 {.importc, header: "".} proc inet_addr*(a1: cstring): InAddrT {.importc, header: "".} proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index 6277e877c1..bf54c0d06a 100644 --- a/tests/async/tnewasyncudp.nim +++ b/tests/async/tnewasyncudp.nim @@ -33,8 +33,8 @@ proc prepareAddress(intaddr: uint32, intport: uint16): ptr Sockaddr_in = result.sin_family = toInt(nativesockets.AF_INET).int16 else: result.sin_family = toInt(nativesockets.AF_INET) - result.sin_port = htons(intport) - result.sin_addr.s_addr = htonl(intaddr) + result.sin_port = nativesockets.htons(intport) + result.sin_addr.s_addr = nativesockets.htonl(intaddr) proc launchSwarm(name: ptr SockAddr) {.async.} = var i = 0 @@ -90,7 +90,7 @@ proc readMessages(server: AsyncFD) {.async.} = await sendTo(server, addr grammString[0], len(grammString), cast[ptr SockAddr](addr saddr), slen) inc(msgCount) - saveReceivedPort(ntohs(saddr.sin_port).int) + saveReceivedPort(nativesockets.ntohs(saddr.sin_port).int) inc(i) proc createServer() {.async.} = From 57246cbcec2f747d5af33ebf4c8c0e0a531eba02 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 31 Mar 2017 22:14:13 +0200 Subject: [PATCH 05/14] updated news to take PR #5390 into account --- web/news/e031_version_0_16_2.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 8517dacb0d..8099100525 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -45,6 +45,8 @@ Changes affecting backwards compatibility - If the dispatcher parameter's value used in multi method is ``nil``, a ``NilError`` exception is raised. The old behavior was that the method would be a ``nop`` then. +- ``posix.nim``: the family of ``ntohs`` procs now takes unsigned integers + instead of signed integers. Library Additions From 7e351fc7fa96b4d560c5a51118bab22abb590585 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Fri, 31 Mar 2017 16:13:06 -0500 Subject: [PATCH 06/14] support for the Genode OS framework (#5560) --- compiler/ccgexprs.nim | 13 ++++-- compiler/cgen.nim | 16 +++++++ compiler/platform.nim | 7 ++- doc/manual/threads.txt | 2 +- lib/genode_cpp/syslocks.h | 78 ++++++++++++++++++++++++++++++++ lib/genode_cpp/threads.h | 69 ++++++++++++++++++++++++++++ lib/nimbase.h | 5 ++ lib/pure/concurrency/cpuinfo.nim | 7 ++- lib/pure/os.nim | 16 ++++--- lib/system.nim | 47 +++++++++++-------- lib/system/dyncalls.nim | 11 +++++ lib/system/osalloc.nim | 13 ++++++ lib/system/syslocks.nim | 22 +++++++++ lib/system/threads.nim | 75 +++++++++++++++++++++++++++++- 14 files changed, 348 insertions(+), 33 deletions(-) create mode 100644 lib/genode_cpp/syslocks.h create mode 100644 lib/genode_cpp/threads.h diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6e10379e6b..b03f7c5aa1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -944,7 +944,6 @@ proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. internalAssert n.kind == nkBracket - p.module.includeHeader("") var args: Rope = nil var a: TLoc for i in countup(0, n.len-1): @@ -953,9 +952,15 @@ proc genEcho(p: BProc, n: PNode) = else: initLocExpr(p, n.sons[i], a) addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) - linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeat("%s", n.len) & tnl), args) - linefmt(p, cpsStmts, "fflush(stdout);$n") + if platform.targetOS == osGenode: + # bypass libc and print directly to the Genode LOG session + p.module.includeHeader("") + linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) + else: + p.module.includeHeader("") + linefmt(p, cpsStmts, "printf($1$2);$n", + makeCString(repeat("%s", n.len) & tnl), args) + linefmt(p, cpsStmts, "fflush(stdout);$n") proc gcUsage(n: PNode) = if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ebdbffa5a2..fd15d07934 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -966,6 +966,19 @@ proc genMainProc(m: BModule) = MainProcs & "}$N$N" + GenodeNimMain = + "Libc::Env *genodeEnv;$N" & + NimMainBody + + ComponentConstruct = + "void Libc::Component::construct(Libc::Env &env) {$N" & + "\tgenodeEnv = &env;$N" & + "\tLibc::with_libc([&] () {$n\t" & + MainProcs & + "\t});$N" & + "\tenv.parent().exit(0);$N" & + "}$N$N" + var nimMain, otherMain: FormatStr if platform.targetOS == osWindows and gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}: @@ -976,6 +989,9 @@ proc genMainProc(m: BModule) = nimMain = WinNimDllMain otherMain = WinCDllMain m.includeHeader("") + elif platform.targetOS == osGenode: + nimMain = GenodeNimMain + otherMain = ComponentConstruct elif optGenDynLib in gGlobalOptions: nimMain = PosixNimDllMain otherMain = PosixCDllMain diff --git a/compiler/platform.nim b/compiler/platform.nim index 19d0d5853f..eb0aca186f 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -21,7 +21,7 @@ type # conditionals to condsyms (end of module). osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, - osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, + osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, osGenode osJS, osNimrodVM, osStandalone type @@ -147,6 +147,11 @@ const objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\", scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".", props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), + (name: "Genode", pardir: "..", dllFrmt: "$1.lib.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: "", curDir: "/", exeExt: "", extSep: ".", + props: {ospNeedsPIC, ospLacksThreadVars}), + (name: "JS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", diff --git a/doc/manual/threads.txt b/doc/manual/threads.txt index fe88123d25..15121ab6d9 100644 --- a/doc/manual/threads.txt +++ b/doc/manual/threads.txt @@ -39,7 +39,7 @@ directly or indirectly through a call to a GC unsafe proc. The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, otherwise this property is inferred by the compiler. Note that ``noSideEffect`` implies ``gcsafe``. The only way to create a thread is via ``spawn`` or -``createThead``. ``spawn`` is usually the preferable method. Either way +``createThread``. ``spawn`` is usually the preferable method. Either way the invoked proc must not use ``var`` parameters nor must any of its parameters contain a ``ref`` or ``closure`` type. This enforces the *no heap sharing restriction*. diff --git a/lib/genode_cpp/syslocks.h b/lib/genode_cpp/syslocks.h new file mode 100644 index 0000000000..8ba39abc21 --- /dev/null +++ b/lib/genode_cpp/syslocks.h @@ -0,0 +1,78 @@ +/* + * + * Nim's Runtime Library + * (c) Copyright 2017 Emery Hemingway + * + * See the file "copying.txt", included in this + * distribution, for details about the copyright. + * + */ + +#ifndef _GENODE_CPP__SYSLOCKS_H_ +#define _GENODE_CPP__SYSLOCKS_H_ + +/* Genode includes */ +#include +#include + +namespace Nim { + struct SysLock; + struct SysCond; +} + +struct Nim::SysLock +{ + Genode::Lock _lock_a, _lock_b; + bool _locked; + + void acquireSys() + { + _lock_a.lock(); + _locked = true; + _lock_a.unlock(); + _lock_b.lock(); + } + + bool tryAcquireSys() + { + if (_locked) + return false; + + _lock_a.lock(); + if (_locked) { + _lock_a.unlock(); + return false; + } else { + _locked = true; + _lock_b.lock(); + _lock_a.unlock(); + return true; + } + } + + void releaseSys() + { + _locked = false; + _lock_a.unlock(); + _lock_b.unlock(); + } +}; + +struct Nim::SysCond +{ + Genode::Semaphore _semaphore; + + void waitSysCond(SysLock &syslock) + { + syslock.releaseSys(); + _semaphore.down(); + syslock.acquireSys(); + } + + void signalSysCond() + { + _semaphore.up(); + } +}; + +#endif diff --git a/lib/genode_cpp/threads.h b/lib/genode_cpp/threads.h new file mode 100644 index 0000000000..a1cd61cd2c --- /dev/null +++ b/lib/genode_cpp/threads.h @@ -0,0 +1,69 @@ +/* + * + * Nim's Runtime Library + * (c) Copyright 2017 Emery Hemingway + * + * See the file "copying.txt", included in this + * distribution, for details about the copyright. + * + */ + + +#ifndef _GENODE_CPP__THREAD_H_ +#define _GENODE_CPP__THREAD_H_ + +#include +#include +#include + +namespace Nim { struct SysThread; } + +struct Nim::SysThread +{ + typedef void (Entry)(void*); + + struct Thread : Genode::Thread + { + void *_tls; + + Entry *_func; + void *_arg; + + void entry() override { + (_func)(_arg); } + + Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg) + : Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg) + { + Genode::Thread::start(); + } + }; + + Genode::Constructible _thread; + + void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) { + _thread.construct(*env, stack_size, func, arg); } + + void joinThread() { + _thread->join(); } + + static bool offMainThread() { + return dynamic_cast(Genode::Thread::myself()); } + + static void *threadVarGetValue() + { + SysThread::Thread *thr = + static_cast(Genode::Thread::myself()); + return thr->_tls; + } + + static void threadVarSetValue(void *value) + { + SysThread::Thread *thr = + static_cast(Genode::Thread::myself()); + thr->_tls = value; + } + +}; + +#endif diff --git a/lib/nimbase.h b/lib/nimbase.h index a535d0da93..714623d4ef 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -498,6 +498,11 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz # include #endif +#if defined(__GENODE__) +#include +extern Libc::Env *genodeEnv; +#endif + /* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */ #define NIM_CHECK_SIZE(typ, sz) \ _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size") diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 8c87c77df5..c3390573a7 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -37,6 +37,10 @@ when defined(macosx) or defined(bsd): a: var csize, b: pointer, c: int): cint {. importc: "sysctl", nodecl.} +when defined(genode): + proc affinitySpaceTotal(): cuint {. + importcpp: "genodeEnv->cpu().affinity_space().total()".} + proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## returns the numer of the processors/cores the machine has. ## Returns 0 if it cannot be detected. @@ -61,7 +65,8 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = elif defined(irix): var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "".}: cint result = sysconf(SC_NPROC_ONLN) + elif defined(genode): + result = affinitySpaceTotal().int else: result = sysconf(SC_NPROCESSORS_ONLN) if result <= 0: result = 1 - diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a7f7116ef6..82acb2a59c 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -825,16 +825,16 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = else: add environment, (key & '=' & val) indx = high(environment) - when defined(unix): - if c_putenv(environment[indx]) != 0'i32: - raiseOSError(osLastError()) - else: + when defined(windows): when useWinUnicode: var k = newWideCString(key) var v = newWideCString(val) if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) else: if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) + else: + if c_putenv(environment[indx]) != 0'i32: + raiseOSError(osLastError()) iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = ## Iterate over all `environments variables`:idx:. In the first component @@ -1091,7 +1091,7 @@ proc rawCreateDir(dir: string): bool = result = false else: raiseOSError(osLastError()) - elif defined(unix): + elif defined(posix): let res = mkdir(dir, 0o777) if res == 0'i32: result = true @@ -1452,7 +1452,9 @@ elif defined(windows): if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine()) return TaintedString(ownArgv[i]) -elif not defined(createNimRtl) and not(defined(posix) and appType == "lib"): +elif not defined(createNimRtl) and + not(defined(posix) and appType == "lib") and + not defined(genode): # On Posix, there is no portable way to get the command line from a DLL. var cmdCount {.importc: "cmdCount".}: cint @@ -1606,6 +1608,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = result = getApplAux("/proc/self/exe") elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") + elif defined(genode): + raiseOSError("POSIX command line not supported") elif defined(freebsd) or defined(dragonfly): result = getApplFreebsd() # little heuristic that may work on other POSIX-like systems: diff --git a/lib/system.nim b/lib/system.nim index 371cf8544d..f6133b7413 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1384,25 +1384,34 @@ var programResult* {.exportc: "nim_program_result".}: int ## under normal circumstances. When the program is terminated ## prematurely using ``quit``, this value is ignored. -proc quit*(errorcode: int = QuitSuccess) {. - magic: "Exit", importc: "exit", header: "", noreturn.} - ## Stops the program immediately with an exit code. - ## - ## Before stopping the program the "quit procedures" are called in the - ## opposite order they were added with `addQuitProc <#addQuitProc>`_. - ## ``quit`` never returns and ignores any exception that may have been raised - ## by the quit procedures. It does *not* call the garbage collector to free - ## all the memory, unless a quit procedure calls `GC_fullCollect - ## <#GC_fullCollect>`_. - ## - ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim - ## program finishes without incident. A raised unhandled exception is - ## equivalent to calling ``quit(QuitFailure)``. - ## - ## Note that this is a *runtime* call and using ``quit`` inside a macro won't - ## have any compile time effect. If you need to stop the compiler inside a - ## macro, use the `error `_ or `fatal - ## `_ pragmas. +when defined(nimdoc): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} + ## Stops the program immediately with an exit code. + ## + ## Before stopping the program the "quit procedures" are called in the + ## opposite order they were added with `addQuitProc <#addQuitProc>`_. + ## ``quit`` never returns and ignores any exception that may have been raised + ## by the quit procedures. It does *not* call the garbage collector to free + ## all the memory, unless a quit procedure calls `GC_fullCollect + ## <#GC_fullCollect>`_. + ## + ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim + ## program finishes without incident. A raised unhandled exception is + ## equivalent to calling ``quit(QuitFailure)``. + ## + ## Note that this is a *runtime* call and using ``quit`` inside a macro won't + ## have any compile time effect. If you need to stop the compiler inside a + ## macro, use the `error `_ or `fatal + ## `_ pragmas. + + +elif defined(genode): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn, + importcpp: "genodeEnv->parent().exit(@)", header: "".} + +else: + proc quit*(errorcode: int = QuitSuccess) {. + magic: "Exit", importc: "exit", header: "", noreturn.} template sysAssert(cond: bool, msg: string) = when defined(useSysAssert): diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index be6275338a..2b86ddf258 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -146,6 +146,17 @@ elif defined(windows) or defined(dos): if result != nil: return procAddrError(name) +elif defined(genode): + + proc nimUnloadLibrary(lib: LibHandle) {. + error: "nimUnloadLibrary not implemented".} + + proc nimLoadLibrary(path: string): LibHandle {. + error: "nimLoadLibrary not implemented".} + + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {. + error: "nimGetProcAddr not implemented".} + else: {.error: "no implementation for dyncalls".} diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index a57a88e8fd..65a057772c 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -77,6 +77,19 @@ when defined(emscripten): var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) munmap(mmapDescr.realPointer, mmapDescr.realSize) +elif defined(genode): + + proc osAllocPages(size: int): pointer {. + importcpp: "genodeEnv->rm().attach(genodeEnv->ram().alloc(@))".} + + proc osTryAllocPages(size: int): pointer = + {.emit: """try {""".} + result = osAllocPages size + {.emit: """} catch (...) { }""".} + + proc osDeallocPages(p: pointer, size: int) {. + importcpp: "genodeEnv->rm().detach(#)".} + elif defined(posix): const PROT_READ = 1 # page can be read diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index c3e23052b7..9d056611fb 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -75,6 +75,28 @@ when defined(Windows): proc waitSysCondWindows(cond: var SysCond) = discard waitForSingleObject(cond, -1'i32) +elif defined(genode): + const + Header = "genode_cpp/syslocks.h" + type + SysLock {.importcpp: "Nim::SysLock", pure, final, + header: Header.} = object + SysCond {.importcpp: "Nim::SysCond", pure, final, + header: Header.} = object + + proc initSysLock(L: var SysLock) = discard + proc deinitSys(L: var SysLock) = discard + proc acquireSys(L: var SysLock) {.noSideEffect, importcpp.} + proc tryAcquireSys(L: var SysLock): bool {.noSideEffect, importcpp.} + proc releaseSys(L: var SysLock) {.noSideEffect, importcpp.} + + proc initSysCond(L: var SysCond) = discard + proc deinitSysCond(L: var SysCond) = discard + proc waitSysCond(cond: var SysCond, lock: var SysLock) {. + noSideEffect, importcpp.} + proc signalSysCond(cond: var SysCond) {. + noSideEffect, importcpp.} + else: type SysLock {.importc: "pthread_mutex_t", pure, final, diff --git a/lib/system/threads.nim b/lib/system/threads.nim index dc9cc96562..4855a2b93f 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -46,7 +46,11 @@ const maxRegisters = 256 # don't think there is an arch with more registers useStackMaskHack = false ## use the stack mask hack for better performance StackGuardSize = 4096 - ThreadStackMask = 1024*256*sizeof(int)-1 + ThreadStackMask = + when defined(genode): + 1024*64*sizeof(int)-1 + else: + 1024*256*sizeof(int)-1 ThreadStackSize = ThreadStackMask+1 - StackGuardSize when defined(windows): @@ -115,6 +119,49 @@ when defined(windows): ## get the ID of the currently running thread. result = int(getCurrentThreadId()) +elif defined(genode): + const + GenodeHeader = "genode_cpp/threads.h" + type + SysThread* {.importcpp: "Nim::SysThread", + header: GenodeHeader, final, pure.} = object + GenodeThreadProc = proc (x: pointer) {.noconv.} + ThreadVarSlot = int + + proc initThread(s: var SysThread, + stackSize: culonglong, + entry: GenodeThreadProc, + arg: pointer) {. + importcpp: "#.initThread(genodeEnv, @)".} + + proc threadVarAlloc(): ThreadVarSlot = 0 + + proc offMainThread(): bool {. + importcpp: "Nim::SysThread::offMainThread", + header: GenodeHeader.} + + proc threadVarSetValue(value: pointer) {. + importcpp: "Nim::SysThread::threadVarSetValue(@)", + header: GenodeHeader.} + + proc threadVarGetValue(): pointer {. + importcpp: "Nim::SysThread::threadVarGetValue()", + header: GenodeHeader.} + + var mainTls: pointer + + proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} = + if offMainThread(): + threadVarSetValue(value); + else: + mainTls = value + + proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} = + if offMainThread(): + threadVarGetValue(); + else: + mainTls + else: when not defined(macosx): {.passL: "-pthread".} @@ -451,6 +498,9 @@ when defined(windows): proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} = threadProcWrapperBody(closure) # implicitly return 0 +elif defined(genode): + proc threadProcWrapper[TArg](closure: pointer) {.noconv.} = + threadProcWrapperBody(closure) else: proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} = threadProcWrapperBody(closure) @@ -482,6 +532,14 @@ when hostOS == "windows": cast[ptr SysThread](addr(a)), 1, -1) inc(k, MAXIMUM_WAIT_OBJECTS) +elif defined(genode): + proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.} + ## waits for the thread `t` to finish. + + proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = + ## waits for every thread in `t` to finish. + for i in 0..t.high: joinThread(t[i]) + else: proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = ## waits for the thread `t` to finish. @@ -531,6 +589,21 @@ when hostOS == "windows": ## shouldn't use this proc. setThreadAffinityMask(t.sys, uint(1 shl cpu)) +elif defined(genode): + proc createThread*[TArg](t: var Thread[TArg], + tp: proc (arg: TArg) {.thread, nimcall.}, + param: TArg) = + when TArg isnot void: t.data = param + t.dataFn = tp + when hasSharedHeap: t.stackSize = ThreadStackSize + t.sys.initThread( + ThreadStackSize.culonglong, + threadProcWrapper[TArg], addr(t)) + + proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = + {.hint: "cannot change Genode thread CPU affinity after initialization".} + discard + else: proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, From 5fdd03ad4d2a9a1f4e7c02aec0ef199edb6242fa Mon Sep 17 00:00:00 2001 From: Anatoly Galiulin Date: Sat, 1 Apr 2017 19:14:34 +0700 Subject: [PATCH 07/14] Fixed issue #5638 (#5639) --- compiler/semcall.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d6852859b9..2dd115b1bc 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -256,6 +256,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, f.ident.s[0..f.ident.s.len-2]).withInfo(n.info) let callOp = newIdentNode(getIdent".=", n.info) n.sons[0..1] = [callOp, n[1], calleeName] + excl(n.flags, nfDotSetter) orig.sons[0..1] = [callOp, orig[1], calleeName] pickBest(callOp) From 316b680f5d99da056e1ae89b7a2ca0eabc6244f8 Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Sun, 2 Apr 2017 09:32:54 +0300 Subject: [PATCH 08/14] Proper fix for osproc.nim on Android (#5646) --- lib/posix/posix.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 0d11bd8536..1ff861534b 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -2423,11 +2423,15 @@ proc sigset*(a1: int, a2: proc (x: cint) {.noconv.}) {. proc sigsuspend*(a1: var Sigset): cint {.importc, header: "".} when defined(android): - proc sigtimedwait*(a1: var Sigset, a2: var SigInfo, - a3: var Timespec, sigsetsize: csize = sizeof(culong)*2): cint {.importc: "__rt_sigtimedwait", header:"".} + proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "".} + var NR_rt_sigtimedwait {.importc: "__NR_rt_sigtimedwait", header: "".}: clong + var NSIGMAX {.importc: "NSIG", header: "".}: clong + + proc sigtimedwait*(a1: var Sigset, a2: var SigInfo, a3: var Timespec): cint = + result = cint(syscall(NR_rt_sigtimedwait, addr(a1), addr(a2), addr(a3), NSIGMAX div 8)) else: proc sigtimedwait*(a1: var Sigset, a2: var SigInfo, - a3: var Timespec): cint {.importc, header: "".} + a3: var Timespec): cint {.importc, header: "".} proc sigwait*(a1: var Sigset, a2: var cint): cint {. importc, header: "".} From 009277856ebdb45a28cc136f2eff1b4fbf139ff2 Mon Sep 17 00:00:00 2001 From: Daniil Yarancev Date: Sun, 2 Apr 2017 18:52:14 +0300 Subject: [PATCH 09/14] Fix #5611 --- lib/pure/httpclient.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 7f666fb357..62c7e20675 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -320,7 +320,7 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response = if line[linei] != ':': httpError("invalid headers") inc(linei) # Skip : - result.headers[name] = line[linei.. ^1].strip() + result.headers.add(name, line[linei.. ^1].strip()) # Ensure the server isn't trying to DoS us. if result.headers.len > headerLimit: httpError("too many headers") @@ -1010,7 +1010,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, if line[linei] != ':': httpError("invalid headers") inc(linei) # Skip : - result.headers[name] = line[linei.. ^1].strip() + result.headers.add(name, line[linei.. ^1].strip()) if result.headers.len > headerLimit: httpError("too many headers") From d587b6a25f9976abad9bf4b7039dd0c1f31b2913 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Apr 2017 15:05:04 +0200 Subject: [PATCH 10/14] language addition: colon-block for expressions in let/var context --- compiler/parser.nim | 26 +++++++++++++++---------- tests/parser/tletcolon.nim | 33 ++++++++++++++++++++++++++++++++ web/news/e031_version_0_16_2.rst | 13 +++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 tests/parser/tletcolon.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index 84402ea3b9..46953de1e8 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -696,7 +696,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr - else: + elif p.tok.tokType == tkDo: parseDoBlocks(p, result) of tkDo: # progress guaranteed @@ -972,10 +972,8 @@ proc optPragmas(p: var TParser): PNode = else: result = ast.emptyNode -proc parseDoBlock(p: var TParser): PNode = +proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragmas? colcom stmt - let info = parLineInfo(p) - getTok(p) let params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) colcom(p, result) @@ -985,11 +983,10 @@ proc parseDoBlock(p: var TParser): PNode = proc parseDoBlocks(p: var TParser, call: PNode) = #| doBlocks = doBlock ^* IND{=} - if p.tok.tokType == tkDo: - #withInd(p): - # addSon(call, parseDoBlock(p)) - while sameOrNoInd(p) and p.tok.tokType == tkDo: - addSon(call, parseDoBlock(p)) + while sameOrNoInd(p) and p.tok.tokType == tkDo: + let info = parLineInfo(p) + getTok(p) + addSon(call, parseDoBlock(p, info)) proc parseProcExpr(p: var TParser, isExpr: bool): PNode = #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? @@ -1889,9 +1886,18 @@ proc parseVarTuple(p: var TParser): PNode = addSon(result, parseExpr(p)) proc parseVariable(p: var TParser): PNode = - #| variable = (varTuple / identColonEquals) indAndComment + #| colonBody = colcom stmt doBlocks? + #| variable = (varTuple / identColonEquals) colonBody? indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) else: result = parseIdentColonEquals(p, {withPragma}) + if p.tok.tokType == tkColon and p.tok.indent < 0: + let last = result.len-1 + let ex = result.sons[last] + if ex.kind != nkEmpty: + let call = makeCall(ex) + call.add parseDoBlock(p, parLineInfo(p)) + parseDoBlocks(p, call) + result.sons[last] = call indAndComment(p, result) proc parseBind(p: var TParser, k: TNodeKind): PNode = diff --git a/tests/parser/tletcolon.nim b/tests/parser/tletcolon.nim new file mode 100644 index 0000000000..ec7c241067 --- /dev/null +++ b/tests/parser/tletcolon.nim @@ -0,0 +1,33 @@ +discard """ + output: '''boo +3 +44 3 +more body code +yes +yes''' +""" + +template x(body): untyped = + body + 44 + +template y(val, body): untyped = + body + val + +proc mana = + let foo = x: + echo "boo" + var foo2 = y 3: + echo "3" + echo foo, " ", foo2 + +mana() +let other = x: + echo "more body code" + if true: + echo "yes" + else: + echo "no" +let outer = y(5): + echo "yes" diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 8099100525..4c4cac1294 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -96,6 +96,19 @@ remove the need for the ``newException`` template. - A new pragma ``.used`` can be used for symbols to prevent the "declared but not used" warning. More details can be found `here `_. +- The popular "colon block of statements" syntax is now also supported for + ``let`` and ``var`` statements: + +.. code-block:: nim + template ve(value, effect): untyped = + effect + val + + let x = ve(4): + echo "welcome to Nim!" + +This is particularly useful for DSLs that help in tree construction. + Bugfixes From f520dfbfabe1134d92214c66f2e1fcd222053771 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Apr 2017 15:21:10 +0200 Subject: [PATCH 11/14] remove en-dash from the language --- compiler/idents.nim | 4 +--- compiler/lexer.nim | 23 +++++------------------ doc/manual/lexing.txt | 3 +-- lib/pure/etcpriv.nim | 23 ----------------------- lib/pure/hashes.nim | 6 +----- web/news/e031_version_0_16_2.rst | 2 ++ 6 files changed, 10 insertions(+), 51 deletions(-) delete mode 100644 lib/pure/etcpriv.nim diff --git a/compiler/idents.nim b/compiler/idents.nim index eecfa60a19..2cce4710e4 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -12,7 +12,7 @@ # id. This module is essential for the compiler's performance. import - hashes, strutils, etcpriv, wordrecg + hashes, strutils, wordrecg type TIdObj* = object of RootObj @@ -45,8 +45,6 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int = while j < blen: while a[i] == '_': inc(i) while b[j] == '_': inc(j) - while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth) - while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth) # tolower inlined: var aa = a[i] var bb = b[j] diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 2bb228f41e..e0875a1181 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -17,7 +17,7 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, - wordrecg, etcpriv + wordrecg const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -158,8 +158,6 @@ proc isNimIdentifier*(s: string): bool = while i < sLen: if s[i] == '_': inc(i) - elif isMagicIdentSeparatorRune(cstring s, i): - inc(i, magicIdentSeparatorRuneByteWidth) if s[i] notin SymChars: return inc(i) result = true @@ -782,27 +780,17 @@ proc getSymbol(L: var TLexer, tok: var TToken) = var c = buf[pos] case c of 'a'..'z', '0'..'9', '\x80'..'\xFF': - if c == '\226' and - buf[pos+1] == '\128' and - buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode - if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars or - isMagicIdentSeparatorRune(buf, pos+magicIdentSeparatorRuneByteWidth) or pos == L.bufpos: - lexMessage(L, errInvalidToken, "–") - break - inc(pos, magicIdentSeparatorRuneByteWidth) - else: - h = h !& ord(c) - inc(pos) + h = h !& ord(c) + inc(pos) of 'A'..'Z': c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() h = h !& ord(c) inc(pos) of '_': - if buf[pos+1] notin SymChars or isMagicIdentSeparatorRune(buf, pos+1): + if buf[pos+1] notin SymChars: lexMessage(L, errInvalidToken, "_") break inc(pos) - else: break tokenEnd(pos-1) h = !$h @@ -1117,8 +1105,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = inc(L.bufpos) of '_': inc(L.bufpos) - if L.buf[L.bufpos] notin SymChars+{'_'} and not - isMagicIdentSeparatorRune(L.buf, L.bufpos): + if L.buf[L.bufpos] notin SymChars+{'_'}: tok.tokType = tkSymbol tok.ident = L.cache.getIdent("_") else: diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt index 7ffd5eb1c8..d4c11adf78 100644 --- a/doc/manual/lexing.txt +++ b/doc/manual/lexing.txt @@ -133,8 +133,7 @@ Two identifiers are considered equal if the following algorithm returns true: a.replace(re"_|–", "").toLower == b.replace(re"_|–", "").toLower That means only the first letters are compared in a case sensitive manner. Other -letters are compared case insensitively and underscores and en-dash (Unicode -point U+2013) are ignored. +letters are compared case insensitively and underscores are ignored. This rather unorthodox way to do identifier comparisons is called `partial case insensitivity`:idx: and has some advantages over the conventional diff --git a/lib/pure/etcpriv.nim b/lib/pure/etcpriv.nim deleted file mode 100644 index 5b785b0519..0000000000 --- a/lib/pure/etcpriv.nim +++ /dev/null @@ -1,23 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Nim Authors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains utils that are less then easy to categorize and -## don't really warrant a specific module. They are private to compiler -## and stdlib usage, and should not be used outside of that - they may -## change or disappear at any time. - - -# Used by pure/hashes.nim, and the compiler parsing -const magicIdentSeparatorRuneByteWidth* = 3 - -# Used by pure/hashes.nim, and the compiler parsing -proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } = - result = cs[i] == '\226' and - cs[i + 1] == '\128' and - cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index d5759e5073..e8c8776c63 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -39,7 +39,7 @@ ## result = !$h import - strutils, etcpriv + strutils type Hash* = int ## a hash value; hash tables using these values should @@ -163,8 +163,6 @@ proc hashIgnoreStyle*(x: string): Hash = var c = x[i] if c == '_': inc(i) - elif isMagicIdentSeparatorRune(cstring(x), i): - inc(i, magicIdentSeparatorRuneByteWidth) else: if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() @@ -185,8 +183,6 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = var c = sBuf[i] if c == '_': inc(i) - elif isMagicIdentSeparatorRune(cstring(sBuf), i): - inc(i, magicIdentSeparatorRuneByteWidth) else: if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 4c4cac1294..d34c7de530 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -47,6 +47,8 @@ Changes affecting backwards compatibility would be a ``nop`` then. - ``posix.nim``: the family of ``ntohs`` procs now takes unsigned integers instead of signed integers. +- In Nim identifiers en-dash (Unicode point U+2013) is not an alias for the + underscore anymore. Use underscores and fix your programming font instead. Library Additions From 81cd7a868570bb3e2d170e5c072784512d6908bf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Apr 2017 21:01:26 +0200 Subject: [PATCH 12/14] minor todo.txt update --- todo.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/todo.txt b/todo.txt index 802e8eb6ad..7597c436af 100644 --- a/todo.txt +++ b/todo.txt @@ -4,7 +4,6 @@ version 1.0 battle plan - make 'not nil' the default (produce warnings instead of errors for a smooth migration path) - case objects needs to be safe and need to support pattern matching -- sanctionize some macro implementation for interfaces - implement a way to forward object type declarations across module boundaries; C++ style - fix "high priority" bugs From c65ff403b27e1655504298ea65457cf1e00f4ebd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Apr 2017 21:06:10 +0200 Subject: [PATCH 13/14] memory manager: use less memory; corruption prevention --- lib/system/alloc.nim | 106 +++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index edd8ececbe..bcbc5d92f6 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -53,9 +53,8 @@ type PSmallChunk = ptr SmallChunk BaseChunk {.pure, inheritable.} = object prevSize: int # size of previous chunk; for coalescing + # 0th bit == 1 if 'used size: int # if < PageSize it is a small chunk - origSize: int # 0th bit == 1 if 'used' - heapLink: PBigChunk # linked list of all chunks for bulk 'deallocPages' SmallChunk = object of BaseChunk next, prev: PSmallChunk # chunks of the same size @@ -93,6 +92,11 @@ type key, upperBound: int level: int + HeapLinks = object + len: int + chunks: array[30, (PBigChunk, int)] + next: ptr HeapLinks + MemRegion = object minLargeObj, maxLargeObj: int freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk] @@ -100,12 +104,12 @@ type currMem, maxMem, freeMem: int # memory sizes (allocated from OS) lastSize: int # needed for the case that OS gives us pages linearly freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access - heapLink: PBigChunk # used to link every chunk for bulk 'deallocPages' chunkStarts: IntSet root, deleted, last, freeAvlNodes: PAvlNode locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages. nextChunkSize: int bottomData: AvlNode + heapLinks: HeapLinks {.deprecated: [TMemRegion: MemRegion].} @@ -177,6 +181,20 @@ proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} = n.link[0] = a.freeAvlNodes a.freeAvlNodes = n +proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) = + var it = addr(a.heapLinks) + while it != nil and it.len >= it.chunks.len: it = it.next + if it == nil: + var n = cast[ptr HeapLinks](llAlloc(a, sizeof(HeapLinks))) + n.next = a.heapLinks.next + a.heapLinks.next = n + n.chunks[0] = (p, size) + n.len = 1 + else: + let L = it.len + it.chunks[L] = (p, size) + inc it.len + include "system/avltree" proc llDeallocAll(a: var MemRegion) = @@ -244,7 +262,7 @@ proc isSmallChunk(c: PChunk): bool {.inline.} = return c.size <= SmallChunkSize-smallChunkOverhead() proc chunkUnused(c: PChunk): bool {.inline.} = - result = (c.origSize and 1) == 0 + result = (c.prevSize and 1) == 0 iterator allObjects(m: var MemRegion): pointer {.inline.} = m.locked = true @@ -319,15 +337,13 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = incCurrMem(a, size) inc(a.freeMem, size) - result.heapLink = a.heapLink - result.origSize = size + a.addHeapLink(result, size) when defined(debugHeapLinks): cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a), result, result.heapLink, result.origSize) when defined(memtracker): trackLocation(addr result.origSize, sizeof(int)) - a.heapLink = result sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1") #zeroMem(result, size) @@ -340,7 +356,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = var next = cast[PChunk](nxt) if pageIndex(next) in a.chunkStarts: #echo("Next already allocated!") - next.prevSize = size + next.prevSize = size or (next.prevSize and 1) # set result.prevSize: var lastSize = if a.lastSize != 0: a.lastSize else: PageSize var prv = cast[ByteAddress](result) -% lastSize @@ -348,27 +364,12 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = var prev = cast[PChunk](prv) if pageIndex(prev) in a.chunkStarts and prev.size == lastSize: #echo("Prev already allocated!") - result.prevSize = lastSize + result.prevSize = lastSize or (result.prevSize and 1) else: - result.prevSize = 0 # unknown + result.prevSize = 0 or (result.prevSize and 1) # unknown + # but do not overwrite 'used' field a.lastSize = size # for next request -when false: - # with the new linked list design this is not possible anymore: - proc freeOsChunks(a: var MemRegion, p: pointer, size: int) = - # update next.prevSize: - var c = cast[PChunk](p) - var nxt = cast[ByteAddress](p) +% c.size - sysAssert((nxt and PageMask) == 0, "freeOsChunks") - var next = cast[PChunk](nxt) - if pageIndex(next) in a.chunkStarts: - next.prevSize = 0 # XXX used - excl(a.chunkStarts, pageIndex(p)) - osDeallocPages(p, size) - decCurrMem(a, size) - dec(a.freeMem, size) - #c_fprintf(stdout, "[Alloc] back to OS: %ld\n", size) - proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} = result = contains(a.chunkStarts, pageIndex(p)) @@ -406,7 +407,7 @@ proc updatePrevSize(a: var MemRegion, c: PBigChunk, var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize") if isAccessible(a, ri): - ri.prevSize = prevSize + ri.prevSize = prevSize or (ri.prevSize and 1) proc freeBigChunk(a: var MemRegion, c: PBigChunk) = var c = c @@ -422,8 +423,9 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = inc(c.size, ri.size) excl(a.chunkStarts, pageIndex(ri)) when coalescLeft: - if c.prevSize != 0: - var le = cast[PChunk](cast[ByteAddress](c) -% c.prevSize) + let prevSize = c.prevSize and not 1 + if prevSize != 0: + var le = cast[PChunk](cast[ByteAddress](c) -% prevSize) sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): sysAssert(not isSmallChunk(le), "freeBigChunk 5") @@ -433,25 +435,22 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = excl(a.chunkStarts, pageIndex(c)) c = cast[PBigChunk](le) - #if c.size < ChunkOsReturn or doNotUnmap or a.locked: incl(a, a.chunkStarts, pageIndex(c)) updatePrevSize(a, c, c.size) listAdd(a.freeChunksList, c) # set 'used' to false: - c.origSize = c.origSize and not 1 - track("setUsedToFalse", addr c.origSize, sizeof(int)) - #else: - # freeOsChunks(a, c, c.size) + c.prevSize = c.prevSize and not 1 proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) = var rest = cast[PBigChunk](cast[ByteAddress](c) +% size) sysAssert(rest notin a.freeChunksList, "splitChunk") rest.size = c.size - size - rest.origSize = rest.origSize and not 1 # not used track("rest.origSize", addr rest.origSize, sizeof(int)) rest.next = nil rest.prev = nil + # size and not used rest.prevSize = size + sysAssert((size and 1) == 0, "splitChunk 2") updatePrevSize(a, c, rest.size) c.size = size incl(a, a.chunkStarts, pageIndex(rest)) @@ -482,9 +481,9 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = # if we over allocated split the chunk: if result.size > size: splitChunk(a, result, size) - result.prevSize = 0 # XXX why is this needed? + # set 'used' to to true: - result.origSize = result.origSize or 1 + result.prevSize = 1 track("setUsedToFalse", addr result.origSize, sizeof(int)) incl(a, a.chunkStarts, pageIndex(result)) @@ -729,29 +728,18 @@ proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer = proc deallocOsPages(a: var MemRegion) = # we free every 'ordinarily' allocated page by iterating over the page bits: - var it = a.heapLink - while it != nil: - let next = it.heapLink - when defined(debugHeapLinks): - cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a), - it, it.origSize and not 1, next) - sysAssert it.origSize >= PageSize, "origSize too small" - # note: - osDeallocPages(it, it.origSize and not 1) + var it = addr(a.heapLinks) + while true: + let next = it.next + for i in 0..it.len-1: + let (p, size) = it.chunks[i] + when defined(debugHeapLinks): + cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a), + it, it.origSize, next) + sysAssert size >= PageSize, "origSize too small" + osDeallocPages(p, size) it = next - when false: - for p in elements(a.chunkStarts): - var page = cast[PChunk](p shl PageShift) - when not doNotUnmap: - var size = if page.size < PageSize: PageSize else: page.size - osDeallocPages(page, size) - else: - # Linux on PowerPC for example frees MORE than asked if 'munmap' - # receives the start of an originally mmap'ed memory block. This is not - # too bad, but we must not access 'page.size' then as that could trigger - # a segfault. But we don't need to access 'page.size' here anyway, - # because calling munmap with PageSize suffices: - osDeallocPages(page, PageSize) + if it == nil: break # And then we free the pages that are in use for the page bits: llDeallocAll(a) From cab2ce7e8770f35561f002bab601358a09535ef2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Apr 2017 23:42:53 +0200 Subject: [PATCH 14/14] update grammar.txt --- doc/grammar.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/grammar.txt b/doc/grammar.txt index c70a6737fd..f6b9b115f5 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -174,7 +174,8 @@ typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux indAndComment? varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr -variable = (varTuple / identColonEquals) indAndComment +colonBody = colcom stmt doBlocks? +variable = (varTuple / identColonEquals) colonBody? indAndComment bindStmt = 'bind' optInd qualifiedIdent ^+ comma mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma pragmaStmt = pragma (':' COMMENT? stmt)?