diff options
-rw-r--r-- | changelog.md | 5 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 35 | ||||
-rw-r--r-- | compiler/ccgliterals.nim | 91 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 13 | ||||
-rw-r--r-- | compiler/cgen.nim | 17 | ||||
-rw-r--r-- | compiler/cgendata.nim | 3 | ||||
-rw-r--r-- | compiler/pragmas.nim | 2 | ||||
-rw-r--r-- | compiler/renderer.nim | 35 | ||||
-rw-r--r-- | doc/advopt.txt | 4 | ||||
-rw-r--r-- | lib/core/macros.nim | 44 | ||||
-rw-r--r-- | lib/core/seqs.nim | 6 | ||||
-rw-r--r-- | lib/core/strs.nim | 7 | ||||
-rw-r--r-- | lib/pure/algorithm.nim | 27 | ||||
-rw-r--r-- | lib/pure/collections/sets.nim | 2 | ||||
-rw-r--r-- | lib/pure/cookies.nim | 24 | ||||
-rw-r--r-- | lib/pure/json.nim | 2 | ||||
-rw-r--r-- | lib/pure/osproc.nim | 8 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 4 | ||||
-rw-r--r-- | lib/windows/winlean.nim | 5 | ||||
-rw-r--r-- | tests/parser/tpostexprblocks.nim | 9 | ||||
-rw-r--r-- | tests/stdlib/talgorithm.nim | 11 | ||||
-rw-r--r-- | tests/stdlib/tjsonmacro.nim | 15 | ||||
-rw-r--r-- | tests/vm/tnimnode.nim | 12 |
23 files changed, 243 insertions, 138 deletions
diff --git a/changelog.md b/changelog.md index b8d28b633..a1a893237 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,8 @@ - ``re.split`` for empty regular expressions now yields every character in the string which is what other programming languages chose to do. +- ``cookies.setCookie` no longer assumes UTC for the expiration date. + #### Breaking changes in the compiler ### Library additions @@ -18,6 +20,9 @@ ### Library changes +- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr`` + now escapes the content of string literals consistently. + ### Language additions ### Language changes diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index dc06c8482..6ee7d698e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -30,12 +30,6 @@ proc intLiteral(i: BiggestInt): Rope = else: result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc getStrLit(m: BModule, s: string): Rope = - discard cgsym(m, "TGenericSeq") - result = getTempName(m) - addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), rope(len(s))]) - proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = if ty == nil: internalError(n.info, "genLiteral: ty is nil") case n.kind @@ -65,19 +59,14 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: - if n.strVal.isNil: - result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) - elif skipTypes(ty, abstractVarRange).kind == tyString: - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - if id == p.module.labels: - # string literal not found in the cache: - result = ropecg(p.module, "((#NimStringDesc*) &$1)", - [getStrLit(p.module, n.strVal)]) - else: - result = ropecg(p.module, "((#NimStringDesc*) &$1$2)", - [p.module.tmpBase, rope(id)]) + case skipTypes(ty, abstractVarRange).kind + of tyNil: + result = genNilStringLiteral(p.module, n.info) + of tyString: + result = genStringLiteral(p.module, n) else: - result = makeCString(n.strVal) + if n.strVal.isNil: result = rope("NIM_NIL") + else: result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: @@ -818,16 +807,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), p.module.labels) - let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s) + let strLit = if id == p.module.labels: genStringLiteralDataOnly(p.module, field.name.s, e.info) else: p.module.tmpBase & rope(id) if op.magic == mNot: linefmt(p, cpsStmts, - "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if ($1) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) else: linefmt(p, cpsStmts, - "if (!($1)) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if (!($1)) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim new file mode 100644 index 000000000..b17a5eedd --- /dev/null +++ b/compiler/ccgliterals.nim @@ -0,0 +1,91 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This include file contains the logic to produce constant string +## and seq literals. The code here is responsible that +## ``const x = ["a", "b"]`` works without hidden runtime creation code. +## The price is that seqs and strings are not purely a library +## implementation. + +template detectVersion(field, corename) = + if m.g.field == 0: + let core = getCompilerProc(corename) + if core == nil or core.kind != skConst: + m.g.field = 1 + else: + m.g.field = int ast.getInt(core.ast) + result = m.g.field + +proc detectStrVersion(m: BModule): int = + detectVersion(strVersion, "nimStrVersion") + +proc detectSeqVersion(m: BModule): int = + detectVersion(seqVersion, "nimSeqVersion") + +# ----- Version 1: GC'ed strings and seqs -------------------------------- + +proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope = + discard cgsym(m, "TGenericSeq") + result = getTempName(m) + addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", + [result, makeCString(s), rope(len(s))]) + +proc genStringLiteralV1(m: BModule; n: PNode): Rope = + if s.isNil: + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + else: + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + if id == m.labels: + # string literal not found in the cache: + result = ropecg(m, "((#NimStringDesc*) &$1)", + [genStringLiteralDataOnlyV1(m, n.strVal)]) + else: + result = ropecg(m, "((#NimStringDesc*) &$1$2)", + [m.tmpBase, rope(id)]) + +# ------ Version 2: destructor based strings and seqs ----------------------- + +proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = + result = getTempName(m) + addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n", + [result, rope(len(s)+1), makeCString(s)]) + +proc genStringLiteralV2(m: BModule; n: PNode): Rope = + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + if id == m.labels: + # string literal not found in the cache: + let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) + result = getTempName(m) + addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n", + [result, rope(len(n.strVal)+1), pureLit]) + else: + result = m.tmpBase & rope(id) + +# ------ Version selector --------------------------------------------------- + +proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralDataOnlyV1(m, s) + of 2: result = genStringLiteralDataOnlyV2(m, s) + else: + localError(info, "cannot determine how to produce code for string literal") + +proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) &$1)", + [data]) + +proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + +proc genStringLiteral(m: BModule; n: PNode): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralV1(m, n) + of 2: result = genStringLiteralV2(m, n) + else: + localError(n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index ed44c577d..2a4a10555 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -267,10 +267,6 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = if isDefined("checkabi"): addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))]) -proc getTempName(m: BModule): Rope = - result = m.tmpBase & rope(m.labels) - inc m.labels - proc ccgIntroducedPtr(s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind @@ -316,8 +312,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyPointer: result = typeNameOrLiteral(m, typ, "void*") of tyString: - discard cgsym(m, "NimStringDesc") - result = typeNameOrLiteral(m, typ, "NimStringDesc*") + case detectStrVersion(m) + of 2: + discard cgsym(m, "string") + result = typeNameOrLiteral(m, typ, "NimStringV2") + else: + discard cgsym(m, "NimStringDesc") + result = typeNameOrLiteral(m, typ, "NimStringDesc*") of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 01610010b..05f222520 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -238,7 +238,12 @@ proc genProc(m: BModule, prc: PSym) template compileToCpp(m: BModule): untyped = gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags -include "ccgtypes.nim" +proc getTempName(m: BModule): Rope = + result = m.tmpBase & rope(m.labels) + inc m.labels + +include ccgliterals +include ccgtypes # ------------------------------ Manager of temporaries ------------------ @@ -551,11 +556,13 @@ proc loadDynamicLib(m: BModule, lib: PLib) = for i in countup(0, high(s)): inc(m.labels) if i > 0: add(loadlib, "||") - appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n", - [tmp, getStrLit(m, s[i])]) + let n = newStrNode(nkStrLit, s[i]) + n.info = lib.path.info + appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n", + [tmp, genStringLiteral(m, n)]) appcg(m, m.s[cfsDynLibInit], - "if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n", - [loadlib, getStrLit(m, lib.path.strVal)]) + "if (!($1)) #nimLoadLibraryError($2);$n", + [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) p.options = p.options - {optStackTrace, optEndb} diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index efa346934..f8167acdc 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -70,7 +70,7 @@ type threadVarAccessed*: bool # true if the proc already accessed some threadvar lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements currLineInfo*: TLineInfo # AST codegen will make this superfluous - nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] + nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] # in how many nested try statements we are # (the vars must be volatile then) # bool is true when are in the except part of a try block @@ -116,6 +116,7 @@ type breakpoints*: Rope # later the breakpoints are inserted into the main proc typeInfoMarker*: TypeCache config*: ConfigRef + strVersion*, seqVersion*: int # version of the string/seq implementation to use TCGen = object of TPassContext # represents a C source file s*: TCFileSections # sections of the C file diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1295ee18d..78ded578f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -65,7 +65,7 @@ const wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine, wUsed} + wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} diff --git a/compiler/renderer.nim b/compiler/renderer.nim index fa93c968a..0b1b0479f 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -173,25 +173,30 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) = else: g.pendingWhitespace = s.len -proc toNimChar(c: char): string = +proc addNimChar(dst: var string; c: char): void = case c - of '\0': result = "\\x00" # not "\\0" to avoid ambiguous cases like "\\012". - of '\a': result = "\\a" # \x07 - of '\b': result = "\\b" # \x08 - of '\t': result = "\\t" # \x09 - of '\L': result = "\\L" # \x0A - of '\v': result = "\\v" # \x0B - of '\f': result = "\\f" # \x0C - of '\c': result = "\\c" # \x0D - of '\e': result = "\\e" # \x1B + of '\0': dst.add "\\x00" # not "\\0" to avoid ambiguous cases like "\\012". + of '\a': dst.add "\\a" # \x07 + of '\b': dst.add "\\b" # \x08 + of '\t': dst.add "\\t" # \x09 + of '\L': dst.add "\\L" # \x0A + of '\v': dst.add "\\v" # \x0B + of '\f': dst.add "\\f" # \x0C + of '\c': dst.add "\\c" # \x0D + of '\e': dst.add "\\e" # \x1B of '\x01'..'\x06', '\x0E'..'\x1A', '\x1C'..'\x1F', '\x80'..'\xFF': - result = "\\x" & strutils.toHex(ord(c), 2) - of '\'', '\"', '\\': result = '\\' & c - else: result = c & "" + dst.add "\\x" + dst.add strutils.toHex(ord(c), 2) + of '\'', '\"', '\\': + dst.add '\\' + dst.add c + else: + dst.add c proc makeNimString(s: string): string = result = "\"" - for i in countup(0, len(s)-1): add(result, toNimChar(s[i])) + for c in s: + result.addNimChar c add(result, '\"') proc putComment(g: var TSrcGen, s: string) = @@ -364,7 +369,7 @@ proc atom(g: TSrcGen; n: PNode): string = of nkStrLit: result = makeNimString(n.strVal) of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"' of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\"" - of nkCharLit: result = '\'' & toNimChar(chr(int(n.intVal))) & '\'' + of nkCharLit: result = "\'"; result.addNimChar chr(int(n.intVal)); result.add '\'' of nkIntLit: result = litAux(g, n, n.intVal, 4) of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8" of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16" diff --git a/doc/advopt.txt b/doc/advopt.txt index 77531f5de..ac309c222 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -31,8 +31,8 @@ Advanced options: --nimcache:PATH set the path used for generated files --header:FILE the compiler should produce a .h file (FILE is optional) - -c, --compileOnly compile only; do not assemble or link - --noLinking compile but do not link + -c, --compileOnly compile Nim files only; do not assemble or link + --noLinking compile Nim and generated files but do not link --noMain do not generate a main procedure --genScript generate a compile script (in the 'nimcache' subdirectory named 'compile_$project$scriptext') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d09f5f933..5a4c4c1e9 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -620,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = of nnkNilLit: res.add(" nil") of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) - of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal) + of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal.newLit.repr) of nnkIdent: res.add(" ident\"" & $n.ident & '"') of nnkSym: res.add(" \"" & $n.symbol & '"') of nnkNone: assert false @@ -645,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = of nnkNilLit: add(result, "nil") of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) - of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: add(result, n.strVal.newLit.repr) of nnkIdent: add(result, "ident\"" & $n.ident & '"') of nnkSym: add(result, $n.symbol) of nnkNone: assert false @@ -681,33 +681,6 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} - proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = - ## Functions copied from strutils - proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} = - const - HexChars = "0123456789ABCDEF" - var - t = x - result = newString(len) - for j in countdown(len-1, 0): - result[j] = HexChars[int(t and 0xF)] - t = t shr 4 - # handle negative overflow - if t == 0 and x < 0: t = -1 - - result = newStringOfCap(s.len + s.len shr 2) - result.add(prefix) - for c in items(s): - case c - of '\0'..'\31', '\128'..'\255': - add(result, "\\x") - add(result, toHex(ord(c), 2)) - of '\\': add(result, "\\\\") - of '\'': add(result, "\\'") - of '\"': add(result, "\\\"") - else: add(result, c) - add(result, suffix) - proc traverse(res: var string, level: int, n: NimNode) {.benign.} = for i in 0..level-1: res.add " " if n.kind in NodeKinds: @@ -723,9 +696,9 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) - of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.escape()) - of nnkIdent: res.add(($n.ident).escape()) - of nnkSym: res.add(($n.symbol).escape()) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.newLit.repr) + of nnkIdent: res.add(($n.ident).newLit.repr()) + of nnkSym: res.add(($n.symbol).newLit.repr()) of nnkNone: assert false else: res.add(".newTree(") @@ -774,7 +747,6 @@ macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr ## Deprecated. - proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} = ## Create a new empty node result = newNimNode(nnkEmpty) @@ -1227,7 +1199,7 @@ proc customPragmaNode(n: NimNode): NimNode = let recList = typDef[2][2] for identDefs in recList: for i in 0 .. identDefs.len - 3: - if identDefs[i].kind == nnkPragmaExpr and + if identDefs[i].kind == nnkPragmaExpr and identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]: return identDefs[i][1] @@ -1237,7 +1209,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped = ## ## .. code-block:: nim ## template myAttr() {.pragma.} - ## type + ## type ## MyObj = object ## myField {.myAttr.}: int ## var o: MyObj @@ -1255,7 +1227,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## ## .. code-block:: nim ## template serializationKey(key: string) {.pragma.} - ## type + ## type ## MyObj = object ## myField {.serializationKey: "mf".}: int ## var o: MyObj diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index c32cf3690..02c192851 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -15,11 +15,11 @@ type len, cap: int data: ptr UncheckedArray[T] +const nimSeqVersion {.core.} = 2 + template frees(s) = dealloc(s.data, s.cap * sizeof(T)) # XXX make code memory safe for overflows in '*' -proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} = - seq[T](len: x.len, cap: x.len, data: x) when defined(nimHasTrace): proc `=trace`[T](s: seq[T]; a: Allocator) = @@ -120,7 +120,7 @@ proc `$`*[T](x: seq[T]): string = result = "@[" var firstElement = true for i in 0..<x.len: - let + let value = x.data[i] if firstElement: firstElement = false diff --git a/lib/core/strs.nim b/lib/core/strs.nim index 1958f4974..ff38aef1d 100644 --- a/lib/core/strs.nim +++ b/lib/core/strs.nim @@ -12,12 +12,11 @@ import allocators type - string {.core.} = object + string {.core, exportc: "NimStringV2".} = object len, cap: int data: ptr UncheckedArray[char] -proc nimStringLiteral(x: cstring; len: int): string {.core.} = - string(len: len, cap: len, data: x) +const nimStrVersion {.core.} = 2 template frees(s) = dealloc(s.data, s.cap + 1) @@ -80,7 +79,7 @@ proc newString*(len: int): string = if len > 0: result.data = alloc0(len+1) -converter toCString(x: string): cstring {.core.} = +converter toCString(x: string): cstring {.core, inline.} = if x.len == 0: cstring"" else: cast[cstring](x.data) proc newStringOfCap*(cap: int): string = diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 2f7b44b31..45e031574 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -24,16 +24,20 @@ proc `*`*(x: int, order: SortOrder): int {.inline.} = var y = order.ord - 1 result = (x xor y) - y -proc fill*[T](a: var openArray[T], first, last: Natural, value: T) = - ## fills the array ``a[first..last]`` with `value`. +template fillImpl[T](a: var openArray[T], first, last: int, value: T) = var x = first while x <= last: a[x] = value inc(x) +proc fill*[T](a: var openArray[T], first, last: Natural, value: T) = + ## fills the array ``a[first..last]`` with `value`. + fillImpl(a, first, last, value) + proc fill*[T](a: var openArray[T], value: T) = ## fills the array `a` with `value`. - fill(a, 0, a.high, value) + fillImpl(a, 0, a.high, value) + proc reverse*[T](a: var openArray[T], first, last: Natural) = ## reverses the array ``a[first..last]``. @@ -494,3 +498,20 @@ when isMainModule: doAssert s4 == "xxxefgabcdxxx" doAssert s5.rotateLeft(3 ..< 10, 11) == 6 doAssert s5 == "xxxefgabcdxxx" + + block product: + doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" + doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" + doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element" + doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements" + doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements" + doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" + + block lowerBound: + doAssert lowerBound([1,2,4], 3, system.cmp[int]) == 2 + doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4 + doAssert lowerBound([1,2,3,10], 11) == 4 + + block fillEmptySeq: + var s = newSeq[int]() + s.fill(0) \ No newline at end of file diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 9e9152fc8..32b6387ad 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -11,7 +11,7 @@ ## ordered hash set. ## ## Hash sets are different from the `built in set type -## <manual.html#set-type>`_. Sets allow you to store any value that can be +## <manual.html#types-set-type>`_. Sets allow you to store any value that can be ## `hashed <hashes.html>`_ and they don't contain duplicate entries. ## ## **Note**: The data types declared here have *value semantics*: This means diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 8f16717ac..aca0ac2b4 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -51,26 +51,26 @@ proc setCookie*(key, value: string, domain = "", path = "", if secure: result.add("; Secure") if httpOnly: result.add("; HttpOnly") -proc setCookie*(key, value: string, expires: DateTime, +proc setCookie*(key, value: string, expires: DateTime|Time, domain = "", path = "", noName = false, secure = false, httpOnly = false): string = ## Creates a command in the format of ## ``Set-Cookie: key=value; Domain=...; ...`` - ## - ## **Note:** UTC is assumed as the timezone for ``expires``. return setCookie(key, value, domain, path, - format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), + format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), noname, secure, httpOnly) when isMainModule: - var tim = fromUnix(getTime().toUnix + 76 * (60 * 60 * 24)) + let expire = fromUnix(0) + 1.seconds - let cookie = setCookie("test", "value", tim.utc) - when not defined(testing): - echo cookie - let start = "Set-Cookie: test=value; Expires=" - assert cookie[0..start.high] == start + let cookies = [ + setCookie("test", "value", expire), + setCookie("test", "value", expire.local), + setCookie("test", "value", expire.utc) + ] + let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT" + doAssert cookies == [expected, expected, expected] let table = parseCookies("uid=1; kp=2") - assert table["uid"] == "1" - assert table["kp"] == "2" + doAssert table["uid"] == "1" + doAssert table["kp"] == "2" diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 912cd6837..da1b66188 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1697,7 +1697,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = result = quote do: ( - if `lenientJsonNode`.isNil: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`) + if `lenientJsonNode`.isNil or `jsonNode`.kind == JNull: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`) ) of "table", "orderedtable": let tableKeyType = typeSym[1] diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index c1c727fc6..555626514 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -494,7 +494,7 @@ when defined(Windows) and not defined(useNimRtl): sa.nLength = sizeof(SECURITY_ATTRIBUTES).cint sa.lpSecurityDescriptor = nil sa.bInheritHandle = 1 - if createPipe(rdHandle, wrHandle, sa, 1024) == 0'i32: + if createPipe(rdHandle, wrHandle, sa, 0) == 0'i32: raiseOSError(osLastError()) proc fileClose(h: Handle) {.inline.} = @@ -524,6 +524,12 @@ when defined(Windows) and not defined(useNimRtl): he = ho else: createPipeHandles(he, si.hStdError) + if setHandleInformation(he, DWORD(1), DWORD(0)) == 0'i32: + raiseOsError(osLastError()) + if setHandleInformation(hi, DWORD(1), DWORD(0)) == 0'i32: + raiseOsError(osLastError()) + if setHandleInformation(ho, DWORD(1), DWORD(0)) == 0'i32: + raiseOsError(osLastError()) else: createAllPipeHandles(si, hi, ho, he, cast[int](result)) result.inHandle = FileHandle(hi) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 5bf90c895..ea0273340 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -293,7 +293,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. # XXX: zeroing out the memory can still result in crashes if a wiped-out # cell is aliased by another pointer (ie proc parameter or a let variable). - # This is a tought problem, because even if we don't zeroMem here, in the + # This is a tough problem, because even if we don't zeroMem here, in the # presence of user defined destructors, the user will expect the cell to be # "destroyed" thus creating the same problem. We can destoy the cell in the # finalizer of the sequence, but this makes destruction non-deterministic. @@ -420,7 +420,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, return 0 if s[i] in {'0'..'9'}: - first_digit = (s[i].ord - '0'.ord) + first_digit = (s[i].ord - '0'.ord) # Integer part? while s[i] in {'0'..'9'}: inc(kdigits) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index a0c784637..f40344396 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -704,6 +704,11 @@ proc duplicateHandle*(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, dwDesiredAccess: DWORD, bInheritHandle: WINBOOL, dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "DuplicateHandle".} + +proc setHandleInformation*(hObject: HANDLE, dwMask: DWORD, + dwFlags: DWORD): WINBOOL {.stdcall, + dynlib: "kernel32", importc: "SetHandleInformation".} + proc getCurrentProcess*(): HANDLE{.stdcall, dynlib: "kernel32", importc: "GetCurrentProcess".} diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index 3b9c956c2..e97b9c7ee 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -22,26 +22,26 @@ StmtList Empty Call Ident ident"foo070" - StrLit test + StrLit "test" StmtList DiscardStmt Empty Call Ident ident"foo080" - StrLit test + StrLit "test" StmtList DiscardStmt Empty Command Ident ident"foo090" - StrLit test + StrLit "test" StmtList DiscardStmt Empty Command Ident ident"foo100" Call - StrLit test + StrLit "test" StmtList DiscardStmt Empty @@ -540,4 +540,3 @@ dumpTree: foo380.add((quote do: discard )[0]) - diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim deleted file mode 100644 index f200e54c5..000000000 --- a/tests/stdlib/talgorithm.nim +++ /dev/null @@ -1,11 +0,0 @@ -import algorithm - -doAssert product[int](newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" -doAssert product[int](@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" -doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element" -doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements" -doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements" -doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" -doAssert lowerBound([1,2,4], 3, system.cmp[int]) == 2 -doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4 -doAssert lowerBound([1,2,3,10], 11) == 4 diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index e8740c591..f13d2e5cb 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -330,6 +330,21 @@ when isMainModule: doAssert jDeser[0].n2.get() == "ABC" doAssert jDeser[1].n2.isNone() + # Issue #6902 + type + Obj = object + n1: int + n2: Option[int] + n3: Option[string] + n4: Option[bool] + + var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""") + let j0Deser = j0.to(Obj) + doAssert j0Deser.n1 == 1 + doAssert j0Deser.n2.isNone() + doAssert j0Deser.n3.isNone() + doAssert j0Deser.n4.isNone() + # Table[T, Y] support. block: type diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 0614b9807..2727df290 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -26,12 +26,12 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = seqAppend.add(arg) # bit this creates a copy arg.add newCall(ident"echo", newLit("Hello World")) - assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" echo "OK" |