diff options
author | Araq <rumpf_a@web.de> | 2014-05-25 21:20:26 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-05-25 21:20:26 +0200 |
commit | 04a1555f4aa90eadc4d974a04abbf50d1e8b7134 (patch) | |
tree | 2e26496b1158a1b14c8c0bb307c670ea2d100cb5 | |
parent | b230303fd6dd1d593aecf792ee8f72552e7e5946 (diff) | |
parent | 1d6c05edc399e31919114f2b07519ae79ae1b804 (diff) | |
download | Nim-04a1555f4aa90eadc4d974a04abbf50d1e8b7134.tar.gz |
Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
32 files changed, 294 insertions, 286 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 94a6f4781..39333a80d 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -484,7 +484,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = opr: array[mUnaryMinusI..mAbsI64, string] = [ mUnaryMinusI: "((NI$2)-($1))", mUnaryMinusI64: "-($1)", - mAbsI: "(NI$2)abs($1)", + mAbsI: "($1 > 0? ($1) : -($1))", mAbsI64: "($1 > 0? ($1) : -($1))"] var a: TLoc diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f51e66897..7c11d3e9a 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -11,49 +11,10 @@ # ------------------------- Name Mangling -------------------------------- -proc mangleField(name: string): string = - case name[0] - of 'a'..'z': - result = "" - add(result, chr(ord(name[0]) - ord('a') + ord('A'))) - of '0'..'9', 'A'..'Z': - result = "" - add(result, name[0]) - else: result = "HEX" & toHex(ord(name[0]), 2) - for i in countup(1, len(name) - 1): - case name[i] - of 'A'..'Z': - add(result, chr(ord(name[i]) - ord('A') + ord('a'))) - of '_': - discard - of 'a'..'z', '0'..'9': - add(result, name[i]) - else: - add(result, "HEX") - add(result, toHex(ord(name[i]), 2)) - -proc mangle(name: string): string = - when false: - case name[0] - of 'a'..'z': - result = "" - add(result, chr(ord(name[0]) - ord('a') + ord('A'))) - of '0'..'9', 'A'..'Z': - result = "" - add(result, name[0]) - else: result = "HEX" & toHex(ord(name[0]), 2) - result = "" - for i in countup(0, len(name) - 1): - case name[i] - of 'A'..'Z': - add(result, chr(ord(name[i]) - ord('A') + ord('a'))) - of '_': - discard - of 'a'..'z', '0'..'9': - add(result, name[i]) - else: - add(result, "HEX") - add(result, toHex(ord(name[i]), 2)) +proc mangleField(name: string): string = + result = mangle(name) + if name[0] in 'a'..'z': + result[0] = name[0].toUpper proc isKeyword(w: PIdent): bool = # nimrod and C++ share some keywords diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 1d8f0158b..9beb08a21 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -161,6 +161,27 @@ proc makeSingleLineCString*(s: string): string = result.add(c.toCChar) result.add('\"') +proc mangle*(name: string): string = + result = "" + case name[0] + of Letters: + result.add(name[0].toLower) + of Digits: + result.add("N" & name[0]) + else: + result = "HEX" & toHex(ord(name[0]), 2) + for i in 1..(name.len-1): + let c = name[i] + case c + of 'A'..'Z': + add(result, c.toLower) + of '_': + discard + of 'a'..'z', '0'..'9': + add(result, c) + else: + add(result, "HEX" & toHex(ord(c), 2)) + proc makeLLVMString*(s: string): PRope = const MaxLineLength = 64 result = nil diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8d66d7a3b..198b1187d 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -503,7 +503,8 @@ proc assignLocalVar(p: BProc, s: PSym) = if sfRegister in s.flags: app(decl, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # app(decl, " GC_GUARD") - if sfVolatile in s.flags or p.nestedTryStmts.len > 0: + if sfVolatile in s.flags or (p.nestedTryStmts.len > 0 and + gCmd != cmdCompileToCpp): app(decl, " volatile") appf(decl, " $1;$n", [s.loc.r]) else: diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 373a11e9a..6687e2e8e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -136,18 +136,6 @@ proc mapType(typ: PType): TJSTypeKind = of tyProc: result = etyProc of tyCString: result = etyString -proc mangle(name: string): string = - result = "" - for i in countup(0, len(name) - 1): - case name[i] - of 'A'..'Z': - add(result, chr(ord(name[i]) - ord('A') + ord('a'))) - of '_': - discard - of 'a'..'z', '0'..'9': - add(result, name[i]) - else: add(result, 'X' & toHex(ord(name[i]), 2)) - proc mangleName(s: PSym): PRope = result = s.loc.r if result == nil: diff --git a/compiler/main.nim b/compiler/main.nim index f833394f7..b4af49248 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -310,7 +310,7 @@ proc mainCommand* = of "cpp", "compiletocpp": extccomp.cExt = ".cpp" gCmd = cmdCompileToCpp - if cCompiler == ccGcc: setCC("gpp") + if cCompiler == ccGcc: setCC("gcc") wantMainModule() defineSymbol("cpp") commandCompileToC() diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini index 0dc44a7c9..8b2353aab 100644 --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -3,7 +3,7 @@ Name: "Nimrod" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips + linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc freebsd: i386;amd64 diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 4433ed4ab..09b92cd8a 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -10,7 +10,7 @@ ## Serialization utilities for the compiler. import strutils -proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", nodecl, varargs.} +proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} proc toStrMaxPrecision*(f: BiggestFloat): string = if f != f: diff --git a/doc/abstypes.txt b/doc/abstypes.txt deleted file mode 100644 index c5827745a..000000000 --- a/doc/abstypes.txt +++ /dev/null @@ -1,152 +0,0 @@ -============== -Abstract types -============== - -.. contents:: - -Abstract types in Nimrod provide a means to model different `units`:idx: of -a `base type`:idx:. - - -Use case 1: SQL strings ------------------------ -An SQL statement that is passed from Nimrod to an SQL database might be -modelled as a string. However, using string templates and filling in the -values is vulnerable to the famous `SQL injection attack`:idx:\: - -.. code-block:: nimrod - proc query(db: TDbHandle, statement: TSQL) = ... - - var - username: string - - db.query("SELECT FROM users WHERE name = '$1'" % username) - # Horrible security hole, but the compiler does not mind! - -This can be avoided by distinguishing strings that contain SQL from strings -that don't. Abstract types provide a means to introduce a new string type -``TSQL`` that is incompatible with ``string``: - -.. code-block:: nimrod - type - TSQL = abstract string - - proc query(db: TDbHandle, statement: TSQL) = ... - - var - username: string - - db.query("SELECT FROM users WHERE name = '$1'" % username) - # Error at compile time: `query` expects an SQL string! - - -It is an essential property of abstract types that they **do not** imply a -subtype relation between the abtract type and its base type. Explict type -conversions from ``string`` to ``TSQL`` are allowed: - -.. code-block:: nimrod - proc properQuote(s: string): TSQL = - # quotes a string properly for an SQL statement - ... - - proc `%` (frmt: TSQL, values: openarray[string]): TSQL = - # quote each argument: - var v = values.each(properQuote) - # we need a temporary type for the type conversion :-( - type TStrSeq = seq[string] - # call strutils.`%`: - result = TSQL(string(frmt) % TStrSeq(v)) - - db.query("SELECT FROM users WHERE name = $1".TSQL % username) - -Now we have compile-time checking against SQL injection attacks. -Since ``"".TSQL`` is transformed to ``TSQL("")`` no new syntax is needed -for nice looking ``TSQL`` string literals. - - - -Use case 2: Money ------------------ -Different currencies should not be mixed in monetary calculations. Abstract -types are a perfect tool to model different currencies: - -.. code-block:: nimrod - type - TDollar = abstract int - TEuro = abstract int - - var - d: TDollar - e: TEuro - - echo d + 12 - # Error: cannot add a number with no unit with a ``TDollar`` - -Unfortunetaly, ``d + 12.TDollar`` is not allowed either, -because ``+`` is defined for ``int`` (among others), not for ``TDollar``. So -we define our own ``+`` for dollars: - -.. code-block:: - proc `+` (x, y: TDollar): TDollar = - result = TDollar(int(x) + int(y)) - -It does not make sense to multiply a dollar with a dollar, but with a -number without unit; and the same holds for division: - -.. code-block:: - proc `*` (x: TDollar, y: int): TDollar = - result = TDollar(int(x) * y) - - proc `*` (x: int, y: TDollar): TDollar = - result = TDollar(x * int(y)) - - proc `div` ... - -This quickly gets tedious. The implementations are trivial and the compiler -should not generate all this code only to optimize it away later - after all -``+`` for dollars should produce the same binary code as ``+`` for ints. -The pragma ``borrow`` has been designed to solve this problem; in principle -it generates the trivial implementation for us: - -.. code-block:: nimrod - proc `*` (x: TDollar, y: int): TDollar {.borrow.} - proc `*` (x: int, y: TDollar): TDollar {.borrow.} - proc `div` (x: TDollar, y: int): TDollar {.borrow.} - -The ``borrow`` pragma makes the compiler to use the same implementation as -the proc that deals with the abstract type's base type, so no code is -generated. - -But it seems we still have to repeat all this boilerplate code for -the ``TEuro`` currency. Fortunately, Nimrod has a template mechanism: - -.. code-block:: nimrod - template Additive(typ: typeDesc): stmt = - proc `+` *(x, y: typ): typ {.borrow.} - proc `-` *(x, y: typ): typ {.borrow.} - - # unary operators: - proc `+` *(x: typ): typ {.borrow.} - proc `-` *(x: typ): typ {.borrow.} - - template Multiplicative(typ, base: typeDesc): stmt = - proc `*` *(x: typ, y: base): typ {.borrow.} - proc `*` *(x: base, y: typ): typ {.borrow.} - proc `div` *(x: typ, y: base): typ {.borrow.} - proc `mod` *(x: typ, y: base): typ {.borrow.} - - template Comparable(typ: typeDesc): stmt = - proc `<` * (x, y: typ): bool {.borrow.} - proc `<=` * (x, y: typ): bool {.borrow.} - proc `==` * (x, y: typ): bool {.borrow.} - - template DefineCurrency(typ, base: expr): stmt = - type - typ* = abstract base - Additive(typ) - Multiplicative(typ, base) - Comparable(typ) - - DefineCurrency(TDollar, int) - DefineCurrency(TEuro, int) - diff --git a/doc/lib.txt b/doc/lib.txt index 3ca519c9e..2da753007 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -535,7 +535,7 @@ Database support * `odbcsql <odbcsql.html>`_ interface to the ODBC driver. * `sphinx <sphinx.html>`_ - Nimrod wrapper for ``shpinx``. + Nimrod wrapper for ``sphinx``. XML Processing diff --git a/doc/manual.txt b/doc/manual.txt index 39e2bad2a..a87abab7a 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -123,7 +123,7 @@ This means that all the control structures are recognized by indentation. Indentation consists only of spaces; tabulators are not allowed. The indentation handling is implemented as follows: The lexer annotates the -following token with the preceeding number of spaces; indentation is not +following token with the preceding number of spaces; indentation is not a separate token. This trick allows parsing of Nimrod with only 1 token of lookahead. @@ -617,7 +617,7 @@ Ordinal types Integers, bool, characters and enumeration types (and subranges of these types) belong to ordinal types. For reasons of simplicity of implementation -the types ``uint`` and ``uint64`` are no ordinal types. +the types ``uint`` and ``uint64`` are not ordinal types. Pre-defined integer types @@ -686,7 +686,7 @@ kinds of integer types are used: the smaller type is converted to the larger. A `narrowing type conversion`:idx: converts a larger to a smaller type (for example ``int32 -> int16``. A `widening type conversion`:idx: converts a smaller type to a larger type (for example ``int16 -> int32``). In Nimrod only -widening type conversion are *implicit*: +widening type conversions are *implicit*: .. code-block:: nimrod var myInt16 = 5i16 @@ -965,6 +965,14 @@ stack roots conservatively. One can use the builtin procs ``GC_ref`` and ``GC_unref`` to keep the string data alive for the rare cases where it does not work. +A `$` proc is defined for cstrings that returns a string. Thus to get a nimrod +string from a cstring: + +.. code-block:: nimrod + var str: string = "Hello!" + var cstr: cstring = s + var newstr: string = $cstr + Structured types ---------------- @@ -1519,7 +1527,7 @@ Most calling conventions exist only for the Windows 32-bit platform. Assigning/passing a procedure to a procedural variable is only allowed if one of the following conditions hold: -1) The procedure that is accessed resists in the current module. +1) The procedure that is accessed resides in the current module. 2) The procedure is marked with the ``procvar`` pragma (see `procvar pragma`_). 3) The procedure has a calling convention that differs from ``nimcall``. 4) The procedure is anonymous. @@ -1527,8 +1535,8 @@ of the following conditions hold: The rules' purpose is to prevent the case that extending a non-``procvar`` procedure with default parameters breaks client code. -The default calling convention is ``nimcall``, unless it is an inner proc ( -a proc inside of a proc). For an inner proc an analysis is performed whether it +The default calling convention is ``nimcall``, unless it is an inner proc (a +proc inside of a proc). For an inner proc an analysis is performed whether it accesses its environment. If it does so, it has the calling convention ``closure``, otherwise it has the calling convention ``nimcall``. @@ -1542,6 +1550,10 @@ of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its base type and vice versa are allowed. + +Modelling currencies +~~~~~~~~~~~~~~~~~~~~ + A distinct type can be used to model different physical `units`:idx: with a numerical base type, for example. The following example models currencies. @@ -1649,6 +1661,67 @@ certain builtin operations to be lifted: Currently only the dot accessor can be borrowed in this way. +Avoiding SQL injection attacks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An SQL statement that is passed from Nimrod to an SQL database might be +modelled as a string. However, using string templates and filling in the +values is vulnerable to the famous `SQL injection attack`:idx:\: + +.. code-block:: nimrod + import strutils + + proc query(db: TDbHandle, statement: string) = ... + + var + username: string + + db.query("SELECT FROM users WHERE name = '$1'" % username) + # Horrible security hole, but the compiler does not mind! + +This can be avoided by distinguishing strings that contain SQL from strings +that don't. Distinct types provide a means to introduce a new string type +``TSQL`` that is incompatible with ``string``: + +.. code-block:: nimrod + type + TSQL = distinct string + + proc query(db: TDbHandle, statement: TSQL) = ... + + var + username: string + + db.query("SELECT FROM users WHERE name = '$1'" % username) + # Error at compile time: `query` expects an SQL string! + + +It is an essential property of abstract types that they **do not** imply a +subtype relation between the abtract type and its base type. Explict type +conversions from ``string`` to ``TSQL`` are allowed: + +.. code-block:: nimrod + import strutils, sequtils + + proc properQuote(s: string): TSQL = + # quotes a string properly for an SQL statement + return TSQL(s) + + proc `%` (frmt: TSQL, values: openarray[string]): TSQL = + # quote each argument: + let v = values.mapIt(TSQL, properQuote(it)) + # we need a temporary type for the type conversion :-( + type TStrSeq = seq[string] + # call strutils.`%`: + result = TSQL(string(frmt) % TStrSeq(v)) + + db.query("SELECT FROM users WHERE name = '$1'".TSQL % [username]) + +Now we have compile-time checking against SQL injection attacks. Since +``"".TSQL`` is transformed to ``TSQL("")`` no new syntax is needed for nice +looking ``TSQL`` string literals. The hypothetical ``TSQL`` type actually +exists in the library as the `TSqlQuery type <db_sqlite.html#TSqlQuery>`_ of +modules like `db_sqlite <db_sqlite.html>`_. Void type diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 52e0a6eaf..d1925547e 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -167,7 +167,7 @@ might contain some cruft even when dead code elimination is turned on. So the final release build should be done with ``--symbolFiles:off``. Due to the aggregation of C code it is also recommended that each project -resists in its own directory so that the generated ``nimcache`` directory +resides in its own directory so that the generated ``nimcache`` directory is not shared between different projects. diff --git a/koch.nim b/koch.nim index 79acc7791..4d0ac0254 100644 --- a/koch.nim +++ b/koch.nim @@ -152,7 +152,7 @@ proc boot(args: string) = copyExe(findStartNimrod(), 0.thVersion) for i in 0..2: echo "iteration: ", i+1 - exec i.thVersion & " cc $# $# compiler" / "nimrod.nim" % [bootOptions, args] + exec i.thVersion & " c $# $# compiler" / "nimrod.nim" % [bootOptions, args] if sameFileContent(output, i.thVersion): copyExe(output, finalDest) echo "executables are equal: SUCCESS!" @@ -167,7 +167,7 @@ const cleanExt = [ ".ppu", ".o", ".obj", ".dcu", ".~pas", ".~inc", ".~dsk", ".~dpr", ".map", ".tds", ".err", ".bak", ".pyc", ".exe", ".rod", ".pdb", ".idb", - ".idx" + ".idx", ".ilk" ] ignore = [ ".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore" diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index e206447cc..cdca826ca 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -846,7 +846,7 @@ var FE_UPWARD* {.importc, header: "<fenv.h>".}: cint FE_DFL_ENV* {.importc, header: "<fenv.h>".}: cint -when not defined(haiku): +when not defined(haiku) and not defined(OpenBSD): var MM_HARD* {.importc, header: "<fmtmsg.h>".}: cint ## Source of the condition is hardware. @@ -1816,7 +1816,7 @@ proc feholdexcept*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".} proc fesetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".} proc feupdateenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".} -when not defined(haiku): +when not defined(haiku) and not defined(OpenBSD): proc fmtmsg*(a1: int, a2: cstring, a3: cint, a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".} diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index fcf947831..87ee83ad9 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -71,6 +71,11 @@ proc fail*[T](future: PFuture[T], error: ref EBase) = future.error = error if future.cb != nil: future.cb() + else: + # This is to prevent exceptions from being silently ignored when a future + # is discarded. + # TODO: This may turn out to be a bad idea. + raise error proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) = ## Sets the callback proc to be called when the future completes. @@ -532,11 +537,15 @@ when defined(windows) or defined(nimdoc): result.TSocketHandle.setBlocking(false) register(result) - proc close*(socket: TAsyncFD) = + proc closeSocket*(socket: TAsyncFD) = ## Closes a socket and ensures that it is unregistered. socket.TSocketHandle.close() getGlobalDispatcher().handles.excl(socket) + proc unregister*(fd: TAsyncFD) = + ## Unregisters ``fd``. + getGlobalDispatcher().handles.excl(fd) + initAll() else: import selectors @@ -581,11 +590,14 @@ else: result.TSocketHandle.setBlocking(false) register(result) - proc close*(sock: TAsyncFD) = + proc closeSocket*(sock: TAsyncFD) = let disp = getGlobalDispatcher() sock.TSocketHandle.close() disp.selector.unregister(sock.TSocketHandle) + proc unregister*(fd: TAsyncFD) = + getGlobalDispatcher().selector.unregister(fd.TSocketHandle) + proc addRead(sock: TAsyncFD, cb: TCallback) = let p = getGlobalDispatcher() if sock.TSocketHandle notin p.selector: @@ -801,8 +813,9 @@ proc generateExceptionCheck(futSym, elseNode[0].add rootReceiver result.add elseNode -template createVar(futSymName: string, asyncProc: PNimrodNode, - valueReceiver, rootReceiver: expr) {.immediate, dirty.} = +template createVar(result: var PNimrodNode, futSymName: string, + asyncProc: PNimrodNode, + valueReceiver, rootReceiver: expr) = result = newNimNode(nnkStmtList) var futSym = genSym(nskVar, "future") result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y @@ -839,7 +852,7 @@ proc processBody(node, retFutureSym: PNimrodNode, of nnkCall: # await foo(p, x) var futureValue: PNimrodNode - createVar("future" & $node[1][0].toStrLit, node[1], futureValue, + result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue, futureValue) else: error("Invalid node kind in 'await', got: " & $node[1].kind) @@ -847,7 +860,7 @@ proc processBody(node, retFutureSym: PNimrodNode, node[1][0].ident == !"await": # foo await x var newCommand = node - createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1], + result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1], newCommand) of nnkVarSection, nnkLetSection: @@ -856,7 +869,7 @@ proc processBody(node, retFutureSym: PNimrodNode, if node[0][2][0].ident == !"await": # var x = await y var newVarSection = node # TODO: Should this use copyNimNode? - createVar("future" & $node[0][0].ident, node[0][2][1], + result.createVar("future" & $node[0][0].ident, node[0][2][1], newVarSection[0][2], newVarSection) else: discard of nnkAsgn: @@ -865,14 +878,14 @@ proc processBody(node, retFutureSym: PNimrodNode, if node[1][0].ident == !"await": # x = await y var newAsgn = node - createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn) + result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn) else: discard of nnkDiscardStmt: # discard await x if node[0].kind != nnkEmpty and node[0][0].kind == nnkIdent and node[0][0].ident == !"await": var newDiscard = node - createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], + result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], newDiscard[0], newDiscard) of nnkTryStmt: # try: await x; except: ... diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 9394078c8..d16c85c58 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -220,7 +220,7 @@ proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) = proc close*(socket: PAsyncSocket) = ## Closes the socket. - socket.fd.TAsyncFD.close() + socket.fd.TAsyncFD.closeSocket() # TODO SSL when isMainModule: diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim index 5481272f0..db1d50569 100644 --- a/lib/pure/collections/queues.nim +++ b/lib/pure/collections/queues.nim @@ -59,7 +59,7 @@ proc enqueue*[T](q: var TQueue[T], item: T) = proc dequeue*[T](q: var TQueue[T]): T = ## removes and returns the first element of the queue `q`. - assert q.count > 0 + assert q.len > 0 dec q.count result = q.data[q.rd] q.rd = (q.rd + 1) and q.mask diff --git a/lib/pure/future.nim b/lib/pure/future.nim index e0e4c4176..b7df05207 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -18,7 +18,6 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} = result = newNimNode(nnkProcTy) var formalParams = newNimNode(nnkFormalParams) - expectKind(b, nnkIdent) formalParams.add b case p.kind diff --git a/lib/pure/math.nim b/lib/pure/math.nim index e4aecd272..78ea02cbf 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -135,12 +135,12 @@ proc random*(max: int): int {.gcsafe.} ## which initializes the random number generator with a "random" ## number, i.e. a tickcount. -when not defined(windows): - proc random*(max: float): float {.gcsafe.} - ## returns a random number in the range 0..<max. The sequence of - ## random number is always the same, unless `randomize` is called - ## which initializes the random number generator with a "random" - ## number, i.e. a tickcount. This is currently not supported for windows. +proc random*(max: float): float {.gcsafe.} + ## returns a random number in the range 0..<max. The sequence of + ## random number is always the same, unless `randomize` is called + ## which initializes the random number generator with a "random" + ## number, i.e. a tickcount. This has a 16-bit resolution on windows + ## and a 48-bit resolution on other platforms. proc randomize*() {.gcsafe.} ## initializes the random number generator with a "random" @@ -205,7 +205,14 @@ when not defined(JS): proc drand48(): float {.importc: "drand48", header: "<stdlib.h>".} proc random(max: float): float = result = drand48() * max - + when defined(windows): + proc random(max: float): float = + # we are hardcodeing this because + # importcing macros is extremely problematic + # and because the value is publicly documented + # on MSDN and very unlikely to change + const rand_max = 32767 + result = (float(rand()) / float(rand_max)) * max proc randomize() = randomize(cast[int](epochTime())) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 807f3da43..31fefc6c8 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -74,9 +74,22 @@ proc unmapMem*(f: var TMemFile, p: pointer, size: int) = proc open*(filename: string, mode: TFileMode = fmRead, mappedSize = -1, offset = 0, newFileSize = -1): TMemFile = ## opens a memory mapped file. If this fails, ``EOS`` is raised. - ## `newFileSize` can only be set if the file is not opened with ``fmRead`` - ## access. `mappedSize` and `offset` can be used to map only a slice of - ## the file. + ## `newFileSize` can only be set if the file does not exist and is opened + ## with write access (e.g., with fmReadWrite). `mappedSize` and `offset` + ## can be used to map only a slice of the file. Example: + ## + ## .. code-block:: nimrod + ## var + ## mm, mm_full, mm_half: TMemFile + ## + ## mm = memfiles.open("/tmp/test.mmap", mode = fmWrite, newFileSize = 1024) # Create a new file + ## mm.close() + ## + ## # Read the whole file, would fail if newFileSize was set + ## mm_full = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = -1) + ## + ## # Read the first 512 bytes + ## mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512) # The file can be resized only when write mode is used: assert newFileSize == -1 or mode != fmRead @@ -165,8 +178,11 @@ proc open*(filename: string, mode: TFileMode = fmRead, if newFileSize != -1: flags = flags or O_CREAT or O_TRUNC + var permissions_mode = S_IRUSR or S_IWUSR + result.handle = open(filename, flags, permissions_mode) + else: + result.handle = open(filename, flags) - result.handle = open(filename, flags) if result.handle == -1: # XXX: errno is supposed to be set here # Is there an exception that wraps it? diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 3d0cc2154..ab7cd1944 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -58,8 +58,9 @@ when not defined(memProfiler): ## instruction count measure instead then. if intervalInUs <= 0: interval = 0 else: interval = intervalInUs * 1000 - tickCountCorrection - + when withThreads: + import locks var profilingLock: TLock @@ -72,7 +73,7 @@ proc hookAux(st: TStackTrace, costs: int) = var last = high(st) while last > 0 and isNil(st[last]): dec last var h = hash(pointer(st[last])) and high(profileData) - + # we use probing for maxChainLen entries and replace the encountered entry # with the minimal 'total' value: if emptySlots == 0: @@ -133,7 +134,7 @@ else: hookAux(st, 1) elif getticks() - t0 > interval: hookAux(st, 1) - t0 = getticks() + t0 = getticks() proc getTotal(x: ptr TProfileEntry): int = result = if isNil(x): 0 else: x.total @@ -145,7 +146,7 @@ proc `//`(a, b: int): string = result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2)) proc writeProfile() {.noconv.} = - when defined(system.TStackTrace): + when defined(system.TStackTrace): system.profilerHook = nil const filename = "profile_results.txt" echo "writing " & filename & "..." @@ -156,7 +157,7 @@ proc writeProfile() {.noconv.} = var entries = 0 for i in 0..high(profileData): if profileData[i] != nil: inc entries - + var perProc = initCountTable[string]() for i in 0..entries-1: var dups = initSet[string]() @@ -166,7 +167,7 @@ proc writeProfile() {.noconv.} = let p = $procname if not containsOrIncl(dups, p): perProc.inc(p, profileData[i].total) - + var sum = 0 # only write the first 100 entries: for i in 0..min(100, entries-1): diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index b3e74d2a1..2843e6c65 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -62,9 +62,9 @@ var proc genOid*(): TOid = ## generates a new OID. - proc rand(): cint {.importc: "rand", nodecl.} + proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.} proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".} - proc srand(seed: cint) {.importc: "srand", nodecl.} + proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.} var t = gettime(nil) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6e250f9d5..d2ada7014 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -903,7 +903,7 @@ elif not defined(useNimRtl): createStream(p.errStream, p.errHandle, fmRead) return p.errStream - proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} + proc csystem(cmd: cstring): cint {.nodecl, importc: "system", header: "<stdlib.h>".} proc execCmd(command: string): int = when defined(linux): diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index f630ba235..3af5f699c 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -11,9 +11,12 @@ import tables, os, unsigned, hashes -when defined(linux): import posix, epoll -elif defined(windows): import winlean -else: import posix +when defined(linux): + import posix, epoll +elif defined(windows): + import winlean +else: + import posix proc hash*(x: TSocketHandle): THash {.borrow.} proc `$`*(x: TSocketHandle): string {.borrow.} @@ -29,7 +32,36 @@ type TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] -when defined(linux) or defined(nimdoc): +when defined(nimdoc): + type + PSelector* = ref object + ## An object which holds file descripters to be checked for read/write + ## status. + fds: TTable[TSocketHandle, PSelectorKey] + + proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], + data: PObject): PSelectorKey {.discardable.} = + ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent + ## ``events``. + + proc update*(s: PSelector, fd: TSocketHandle, + events: set[TEvent]): PSelectorKey {.discardable.} = + ## Updates the events which ``fd`` wants notifications for. + + proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = + ## The ``events`` field of the returned ``key`` contains the original events + ## for which the ``fd`` was bound. This is contrary to the ``events`` field + ## of the ``TReadyInfo`` tuple which determines which events are ready + ## on the ``fd``. + + proc contains*(s: PSelector, fd: TSocketHandle): bool = + ## Determines whether selector contains a file descriptor. + + proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = + ## Retrieves the selector key for ``fd``. + + +elif defined(linux): type PSelector* = ref object epollFD: cint @@ -49,9 +81,10 @@ when defined(linux) or defined(nimdoc): ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## ``events``. var event = createEventStruct(events, fd) - if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: - OSError(OSLastError()) - + if events != {}: + if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: + OSError(OSLastError()) + var key = PSelectorKey(fd: fd, events: events, data: data) s.fds[fd] = key @@ -61,11 +94,27 @@ when defined(linux) or defined(nimdoc): events: set[TEvent]): PSelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. if s.fds[fd].events != events: - var event = createEventStruct(events, fd) + if events == {}: + # This fd is idle -- it should not be registered to epoll. + # But it should remain a part of this selector instance. + # This is to prevent epoll_wait from returning immediately + # because its got fds which are waiting for no events and + # are therefore constantly ready. (leading to 100% CPU usage). + if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: + OSError(OSLastError()) + s.fds[fd].events = events + else: + var event = createEventStruct(events, fd) + if s.fds[fd].events == {}: + # This fd is idle. It's not a member of this epoll instance and must + # be re-registered. + if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: + OSError(OSLastError()) + else: + if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + OSError(OSLastError()) + s.fds[fd].events = events - s.fds[fd].events = events - if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: - OSError(OSLastError()) result = s.fds[fd] proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = @@ -123,7 +172,10 @@ when defined(linux) or defined(nimdoc): ## Determines whether selector contains a file descriptor. if s.fds.hasKey(fd): # Ensure the underlying epoll instance still contains this fd. - result = epollHasFd(s, fd) + if s.fds[fd].events != {}: + result = epollHasFd(s, fd) + else: + result = true else: return false @@ -131,7 +183,7 @@ when defined(linux) or defined(nimdoc): ## Retrieves the selector key for ``fd``. return s.fds[fd] -else: +elif not defined(nimdoc): # TODO: kqueue for bsd/mac os x. type PSelector* = ref object @@ -230,7 +282,7 @@ proc contains*(s: PSelector, key: PSelectorKey): bool = ## the new one may have the same value. return key.fd in s and s.fds[key.fd] == key -when isMainModule: +when isMainModule and not defined(nimdoc): # Select() import sockets type diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 8d96cbaaf..7b8b3d557 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -295,7 +295,7 @@ when defined(ssl): of protSSLv23: newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. of protSSLv2: - when not defined(linux): + when not defined(linux) and not defined(OpenBSD): newCTX = SSL_CTX_new(SSLv2_method()) else: SSLError() diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 2d33965e3..5111bc3cf 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -57,6 +57,7 @@ when not defined(SIGINT): SIGINT = cint(2) SIGSEGV = cint(11) SIGTERM = cint(15) + SIGPIPE = cint(13) else: {.error: "SIGABRT not ported to your platform".} else: @@ -66,6 +67,8 @@ when not defined(SIGINT): SIGABRT {.importc: "SIGABRT", nodecl.}: cint SIGFPE {.importc: "SIGFPE", nodecl.}: cint SIGILL {.importc: "SIGILL", nodecl.}: cint + when defined(macosx) or defined(linux): + var SIGPIPE {.importc: "SIGPIPE", nodecl.}: cint when defined(macosx): when NoFakeVars: diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 2dc134eaf..63a61183f 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -298,7 +298,13 @@ when not defined(noSignalHandler): elif s == SIGILL: action("SIGILL: Illegal operation.\n") elif s == SIGBUS: action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") - else: action("unknown signal\n") + else: + block platformSpecificSignal: + when defined(SIGPIPE): + if s == SIGPIPE: + action("SIGPIPE: Pipe closed.\n") + break platformSpecificSignal + action("unknown signal\n") # print stack trace and quit when hasSomeStackTrace: @@ -323,6 +329,8 @@ when not defined(noSignalHandler): c_signal(SIGFPE, signalHandler) c_signal(SIGILL, signalHandler) c_signal(SIGBUS, signalHandler) + when defined(SIGPIPE): + c_signal(SIGPIPE, signalHandler) registerSignalHandler() # call it in initialization section diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 043d37533..794c65cb8 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -10,7 +10,7 @@ # set handling type - TNimSet = array [0..4*2048-1, int8] + TNimSet = array [0..4*2048-1, uint8] proc countBits32(n: int32): int {.compilerproc.} = var v = n @@ -25,4 +25,4 @@ proc countBits64(n: int64): int {.compilerproc.} = proc cardSet(s: TNimSet, len: int): int {.compilerproc.} = result = 0 for i in countup(0, len-1): - inc(result, countBits32(int32(ze(s[i])))) + inc(result, countBits32(int32(s[i]))) diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim index dd743ffa4..df6ad954b 100644 --- a/lib/windows/windows.nim +++ b/lib/windows/windows.nim @@ -62,7 +62,7 @@ type # BaseTsd.h -- Type definitions for the basic sized types type # WinDef.h -- Basic Windows Type Definitions # BaseTypes - UINT = int32 + WINUINT* = int32 ULONG* = int PULONG* = ptr int USHORT* = int16 @@ -137,7 +137,7 @@ type # WinDef.h -- Basic Windows Type Definitions HFILE* = HANDLE HCURSOR* = HANDLE # = HICON - COLORREF* = int + COLORREF* = DWORD LPCOLORREF* = ptr COLORREF POINT* {.final, pure.} = object @@ -238,7 +238,7 @@ type CALTYPE* = int CALID* = int CCHAR* = char - TCOLORREF* = int + TCOLORREF* = COLORREF WINT* = int32 PINTEGER* = ptr int32 PBOOL* = ptr WINBOOL @@ -19683,7 +19683,7 @@ proc SetSysColors*(cElements: int32, lpaElements: var wINT, dynlib: "user32", importc: "SetSysColors".} proc DrawFocusRect*(hDC: HDC, lprc: var RECT): WINBOOL{.stdcall, dynlib: "user32", importc: "DrawFocusRect".} -proc FillRect*(hDC: HDC, lprc: RECT, hbr: HBRUSH): int32{.stdcall, +proc FillRect*(hDC: HDC, lprc: var RECT, hbr: HBRUSH): int32{.stdcall, dynlib: "user32", importc: "FillRect".} proc FrameRect*(hDC: HDC, lprc: var RECT, hbr: HBRUSH): int32{.stdcall, dynlib: "user32", importc: "FrameRect".} @@ -22758,12 +22758,12 @@ proc LocalDiscard*(hlocMem: HLOCAL): HLOCAL = # WinGDI.h -proc GetGValue*(rgb: int32): int8 = - result = toU8(rgb shr 8'i32) +discard """proc GetGValue*(rgb: int32): int8 = + result = toU8(rgb shr 8'i32)""" proc RGB*(r, g, b: int): COLORREF = result = toU32(r) or (toU32(g) shl 8) or (toU32(b) shl 16) proc RGB*(r, g, b: range[0 .. 255]): COLORREF = - result = r or g shl 8 or b shl 16 + result = toU32(r) or (toU32(g) shl 8) or (toU32(b) shl 16) proc PALETTERGB*(r, g, b: range[0..255]): COLORREF = result = 0x02000000 or RGB(r, g, b) @@ -23481,7 +23481,7 @@ proc ListView_EnsureVisible(hwndLV: HWND, i, fPartialOK: int32): LRESULT = MAKELPARAM(fPartialOK, 0)) proc ListView_FindItem(wnd: HWND, iStart: int32, lvfi: var LV_FINDINFO): int32 = - result = SendMessage(wnd, LVM_FINDITEM, WPARAM(iStart), + result = SendMessage(wnd, LVM_FINDITEM, WPARAM(iStart), cast[LPARAM](addr(lvfi))).int32 proc ListView_GetBkColor(wnd: HWND): LRESULT = diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 90c398dce..bbcb2175e 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -270,7 +270,7 @@ proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} when not defined(windows): proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, - dynlib: DLLSSLName, importc.} + dynlib: DLLUtilName, importc.} proc CRYPTO_malloc_init*() = when not defined(windows): diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index ffceeaee6..da4952677 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -23,19 +23,19 @@ proc launchSwarm(port: TPort) {.async.} = await connect(sock, "localhost", port) when true: await sendMessages(sock) - close(sock) + closeSocket(sock) else: # Issue #932: https://github.com/Araq/Nimrod/issues/932 var msgFut = sendMessages(sock) msgFut.callback = proc () = - close(sock) + closeSocket(sock) proc readMessages(client: TAsyncFD) {.async.} = while true: var line = await recvLine(client) if line == "": - close(client) + closeSocket(client) clientCount.inc break else: diff --git a/tests/ccgbugs/tbug1081.nim b/tests/ccgbugs/tbug1081.nim new file mode 100644 index 000000000..71628feec --- /dev/null +++ b/tests/ccgbugs/tbug1081.nim @@ -0,0 +1,17 @@ +discard """ + output: '''1 +0 +0 +0''' +""" + +proc `1/1`() = echo(1 div 1) +template `1/2`() = echo(1 div 2) +var `1/3` = 1 div 4 +`1/3` = 1 div 3 # oops, 1/3!=1/4 +let `1/4` = 1 div 4 + +`1/1`() +`1/2`() +echo `1/3` +echo `1/4` |