Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

This commit is contained in:
Araq 2014-05-25 21:20:26 +02:00
commit 04a1555f4a
32 changed files with 294 additions and 286 deletions

View file

@ -484,7 +484,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
opr: array[mUnaryMinusI..mAbsI64, string] = [ opr: array[mUnaryMinusI..mAbsI64, string] = [
mUnaryMinusI: "((NI$2)-($1))", mUnaryMinusI: "((NI$2)-($1))",
mUnaryMinusI64: "-($1)", mUnaryMinusI64: "-($1)",
mAbsI: "(NI$2)abs($1)", mAbsI: "($1 > 0? ($1) : -($1))",
mAbsI64: "($1 > 0? ($1) : -($1))"] mAbsI64: "($1 > 0? ($1) : -($1))"]
var var
a: TLoc a: TLoc

View file

@ -12,48 +12,9 @@
# ------------------------- Name Mangling -------------------------------- # ------------------------- Name Mangling --------------------------------
proc mangleField(name: string): string = proc mangleField(name: string): string =
case name[0] result = mangle(name)
of 'a'..'z': if name[0] in 'a'..'z':
result = "" result[0] = name[0].toUpper
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 isKeyword(w: PIdent): bool = proc isKeyword(w: PIdent): bool =
# nimrod and C++ share some keywords # nimrod and C++ share some keywords

View file

@ -161,6 +161,27 @@ proc makeSingleLineCString*(s: string): string =
result.add(c.toCChar) result.add(c.toCChar)
result.add('\"') 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 = proc makeLLVMString*(s: string): PRope =
const MaxLineLength = 64 const MaxLineLength = 64
result = nil result = nil

View file

@ -503,7 +503,8 @@ proc assignLocalVar(p: BProc, s: PSym) =
if sfRegister in s.flags: app(decl, " register") if sfRegister in s.flags: app(decl, " register")
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
# app(decl, " GC_GUARD") # 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") app(decl, " volatile")
appf(decl, " $1;$n", [s.loc.r]) appf(decl, " $1;$n", [s.loc.r])
else: else:

View file

@ -136,18 +136,6 @@ proc mapType(typ: PType): TJSTypeKind =
of tyProc: result = etyProc of tyProc: result = etyProc
of tyCString: result = etyString 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 = proc mangleName(s: PSym): PRope =
result = s.loc.r result = s.loc.r
if result == nil: if result == nil:

View file

@ -310,7 +310,7 @@ proc mainCommand* =
of "cpp", "compiletocpp": of "cpp", "compiletocpp":
extccomp.cExt = ".cpp" extccomp.cExt = ".cpp"
gCmd = cmdCompileToCpp gCmd = cmdCompileToCpp
if cCompiler == ccGcc: setCC("gpp") if cCompiler == ccGcc: setCC("gcc")
wantMainModule() wantMainModule()
defineSymbol("cpp") defineSymbol("cpp")
commandCompileToC() commandCompileToC()

View file

@ -3,7 +3,7 @@ Name: "Nimrod"
Version: "$version" Version: "$version"
Platforms: """ Platforms: """
windows: i386;amd64 windows: i386;amd64
linux: i386;amd64;powerpc64;arm;sparc;mips linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc
macosx: i386;amd64;powerpc64 macosx: i386;amd64;powerpc64
solaris: i386;amd64;sparc solaris: i386;amd64;sparc
freebsd: i386;amd64 freebsd: i386;amd64

View file

@ -10,7 +10,7 @@
## Serialization utilities for the compiler. ## Serialization utilities for the compiler.
import strutils 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 = proc toStrMaxPrecision*(f: BiggestFloat): string =
if f != f: if f != f:

View file

@ -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)

View file

@ -535,7 +535,7 @@ Database support
* `odbcsql <odbcsql.html>`_ * `odbcsql <odbcsql.html>`_
interface to the ODBC driver. interface to the ODBC driver.
* `sphinx <sphinx.html>`_ * `sphinx <sphinx.html>`_
Nimrod wrapper for ``shpinx``. Nimrod wrapper for ``sphinx``.
XML Processing XML Processing

View file

@ -123,7 +123,7 @@ This means that all the control structures are recognized by indentation.
Indentation consists only of spaces; tabulators are not allowed. Indentation consists only of spaces; tabulators are not allowed.
The indentation handling is implemented as follows: The lexer annotates the 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 a separate token. This trick allows parsing of Nimrod with only 1 token of
lookahead. lookahead.
@ -617,7 +617,7 @@ Ordinal types
Integers, bool, characters and enumeration types (and subranges of these Integers, bool, characters and enumeration types (and subranges of these
types) belong to ordinal types. For reasons of simplicity of implementation 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 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 A `narrowing type conversion`:idx: converts a larger to a smaller type (for
example ``int32 -> int16``. A `widening type conversion`:idx: converts a example ``int32 -> int16``. A `widening type conversion`:idx: converts a
smaller type to a larger type (for example ``int16 -> int32``). In Nimrod only 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 .. code-block:: nimrod
var myInt16 = 5i16 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 ``GC_unref`` to keep the string data alive for the rare cases where it does
not work. 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 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 Assigning/passing a procedure to a procedural variable is only allowed if one
of the following conditions hold: 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`_). 2) The procedure is marked with the ``procvar`` pragma (see `procvar pragma`_).
3) The procedure has a calling convention that differs from ``nimcall``. 3) The procedure has a calling convention that differs from ``nimcall``.
4) The procedure is anonymous. 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`` The rules' purpose is to prevent the case that extending a non-``procvar``
procedure with default parameters breaks client code. procedure with default parameters breaks client code.
The default calling convention is ``nimcall``, unless it is an inner proc ( The default calling convention is ``nimcall``, unless it is an inner proc (a
a proc inside of a proc). For an inner proc an analysis is performed whether it 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 accesses its environment. If it does so, it has the calling convention
``closure``, otherwise it has the calling convention ``nimcall``. ``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 and its base type. Explicit type conversions from a distinct type to its
base type and vice versa are allowed. base type and vice versa are allowed.
Modelling currencies
~~~~~~~~~~~~~~~~~~~~
A distinct type can be used to model different physical `units`:idx: with a A distinct type can be used to model different physical `units`:idx: with a
numerical base type, for example. The following example models currencies. 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. 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 Void type

View file

@ -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``. the final release build should be done with ``--symbolFiles:off``.
Due to the aggregation of C code it is also recommended that each project 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. is not shared between different projects.

View file

@ -152,7 +152,7 @@ proc boot(args: string) =
copyExe(findStartNimrod(), 0.thVersion) copyExe(findStartNimrod(), 0.thVersion)
for i in 0..2: for i in 0..2:
echo "iteration: ", i+1 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): if sameFileContent(output, i.thVersion):
copyExe(output, finalDest) copyExe(output, finalDest)
echo "executables are equal: SUCCESS!" echo "executables are equal: SUCCESS!"
@ -167,7 +167,7 @@ const
cleanExt = [ cleanExt = [
".ppu", ".o", ".obj", ".dcu", ".~pas", ".~inc", ".~dsk", ".~dpr", ".ppu", ".o", ".obj", ".dcu", ".~pas", ".~inc", ".~dsk", ".~dpr",
".map", ".tds", ".err", ".bak", ".pyc", ".exe", ".rod", ".pdb", ".idb", ".map", ".tds", ".err", ".bak", ".pyc", ".exe", ".rod", ".pdb", ".idb",
".idx" ".idx", ".ilk"
] ]
ignore = [ ignore = [
".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore" ".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore"

View file

@ -846,7 +846,7 @@ var
FE_UPWARD* {.importc, header: "<fenv.h>".}: cint FE_UPWARD* {.importc, header: "<fenv.h>".}: cint
FE_DFL_ENV* {.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 var
MM_HARD* {.importc, header: "<fmtmsg.h>".}: cint MM_HARD* {.importc, header: "<fmtmsg.h>".}: cint
## Source of the condition is hardware. ## 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 fesetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
proc feupdateenv*(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, proc fmtmsg*(a1: int, a2: cstring, a3: cint,
a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".} a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".}

View file

@ -71,6 +71,11 @@ proc fail*[T](future: PFuture[T], error: ref EBase) =
future.error = error future.error = error
if future.cb != nil: if future.cb != nil:
future.cb() 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.}) = proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
## Sets the callback proc to be called when the future completes. ## 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) result.TSocketHandle.setBlocking(false)
register(result) register(result)
proc close*(socket: TAsyncFD) = proc closeSocket*(socket: TAsyncFD) =
## Closes a socket and ensures that it is unregistered. ## Closes a socket and ensures that it is unregistered.
socket.TSocketHandle.close() socket.TSocketHandle.close()
getGlobalDispatcher().handles.excl(socket) getGlobalDispatcher().handles.excl(socket)
proc unregister*(fd: TAsyncFD) =
## Unregisters ``fd``.
getGlobalDispatcher().handles.excl(fd)
initAll() initAll()
else: else:
import selectors import selectors
@ -581,11 +590,14 @@ else:
result.TSocketHandle.setBlocking(false) result.TSocketHandle.setBlocking(false)
register(result) register(result)
proc close*(sock: TAsyncFD) = proc closeSocket*(sock: TAsyncFD) =
let disp = getGlobalDispatcher() let disp = getGlobalDispatcher()
sock.TSocketHandle.close() sock.TSocketHandle.close()
disp.selector.unregister(sock.TSocketHandle) disp.selector.unregister(sock.TSocketHandle)
proc unregister*(fd: TAsyncFD) =
getGlobalDispatcher().selector.unregister(fd.TSocketHandle)
proc addRead(sock: TAsyncFD, cb: TCallback) = proc addRead(sock: TAsyncFD, cb: TCallback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if sock.TSocketHandle notin p.selector: if sock.TSocketHandle notin p.selector:
@ -801,8 +813,9 @@ proc generateExceptionCheck(futSym,
elseNode[0].add rootReceiver elseNode[0].add rootReceiver
result.add elseNode result.add elseNode
template createVar(futSymName: string, asyncProc: PNimrodNode, template createVar(result: var PNimrodNode, futSymName: string,
valueReceiver, rootReceiver: expr) {.immediate, dirty.} = asyncProc: PNimrodNode,
valueReceiver, rootReceiver: expr) =
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
var futSym = genSym(nskVar, "future") var futSym = genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
@ -839,7 +852,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
of nnkCall: of nnkCall:
# await foo(p, x) # await foo(p, x)
var futureValue: PNimrodNode var futureValue: PNimrodNode
createVar("future" & $node[1][0].toStrLit, node[1], futureValue, result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
futureValue) futureValue)
else: else:
error("Invalid node kind in 'await', got: " & $node[1].kind) error("Invalid node kind in 'await', got: " & $node[1].kind)
@ -847,7 +860,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
node[1][0].ident == !"await": node[1][0].ident == !"await":
# foo await x # foo await x
var newCommand = node 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) newCommand)
of nnkVarSection, nnkLetSection: of nnkVarSection, nnkLetSection:
@ -856,7 +869,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
if node[0][2][0].ident == !"await": if node[0][2][0].ident == !"await":
# var x = await y # var x = await y
var newVarSection = node # TODO: Should this use copyNimNode? 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) newVarSection[0][2], newVarSection)
else: discard else: discard
of nnkAsgn: of nnkAsgn:
@ -865,14 +878,14 @@ proc processBody(node, retFutureSym: PNimrodNode,
if node[1][0].ident == !"await": if node[1][0].ident == !"await":
# x = await y # x = await y
var newAsgn = node 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 else: discard
of nnkDiscardStmt: of nnkDiscardStmt:
# discard await x # discard await x
if node[0].kind != nnkEmpty and node[0][0].kind == nnkIdent and if node[0].kind != nnkEmpty and node[0][0].kind == nnkIdent and
node[0][0].ident == !"await": node[0][0].ident == !"await":
var newDiscard = node 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) newDiscard[0], newDiscard)
of nnkTryStmt: of nnkTryStmt:
# try: await x; except: ... # try: await x; except: ...

View file

@ -220,7 +220,7 @@ proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) =
proc close*(socket: PAsyncSocket) = proc close*(socket: PAsyncSocket) =
## Closes the socket. ## Closes the socket.
socket.fd.TAsyncFD.close() socket.fd.TAsyncFD.closeSocket()
# TODO SSL # TODO SSL
when isMainModule: when isMainModule:

View file

@ -59,7 +59,7 @@ proc enqueue*[T](q: var TQueue[T], item: T) =
proc dequeue*[T](q: var TQueue[T]): T = proc dequeue*[T](q: var TQueue[T]): T =
## removes and returns the first element of the queue `q`. ## removes and returns the first element of the queue `q`.
assert q.count > 0 assert q.len > 0
dec q.count dec q.count
result = q.data[q.rd] result = q.data[q.rd]
q.rd = (q.rd + 1) and q.mask q.rd = (q.rd + 1) and q.mask

View file

@ -18,7 +18,6 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} =
result = newNimNode(nnkProcTy) result = newNimNode(nnkProcTy)
var formalParams = newNimNode(nnkFormalParams) var formalParams = newNimNode(nnkFormalParams)
expectKind(b, nnkIdent)
formalParams.add b formalParams.add b
case p.kind case p.kind

View file

@ -135,12 +135,12 @@ proc random*(max: int): int {.gcsafe.}
## which initializes the random number generator with a "random" ## which initializes the random number generator with a "random"
## number, i.e. a tickcount. ## number, i.e. a tickcount.
when not defined(windows): proc random*(max: float): float {.gcsafe.}
proc random*(max: float): float {.gcsafe.} ## returns a random number in the range 0..<max. The sequence of
## returns a random number in the range 0..<max. The sequence of ## random number is always the same, unless `randomize` is called
## random number is always the same, unless `randomize` is called ## which initializes the random number generator with a "random"
## which initializes the random number generator with a "random" ## number, i.e. a tickcount. This has a 16-bit resolution on windows
## number, i.e. a tickcount. This is currently not supported for windows. ## and a 48-bit resolution on other platforms.
proc randomize*() {.gcsafe.} proc randomize*() {.gcsafe.}
## initializes the random number generator with a "random" ## 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 drand48(): float {.importc: "drand48", header: "<stdlib.h>".}
proc random(max: float): float = proc random(max: float): float =
result = drand48() * max 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() = proc randomize() =
randomize(cast[int](epochTime())) randomize(cast[int](epochTime()))

View file

@ -74,9 +74,22 @@ proc unmapMem*(f: var TMemFile, p: pointer, size: int) =
proc open*(filename: string, mode: TFileMode = fmRead, proc open*(filename: string, mode: TFileMode = fmRead,
mappedSize = -1, offset = 0, newFileSize = -1): TMemFile = mappedSize = -1, offset = 0, newFileSize = -1): TMemFile =
## opens a memory mapped file. If this fails, ``EOS`` is raised. ## opens a memory mapped file. If this fails, ``EOS`` is raised.
## `newFileSize` can only be set if the file is not opened with ``fmRead`` ## `newFileSize` can only be set if the file does not exist and is opened
## access. `mappedSize` and `offset` can be used to map only a slice of ## with write access (e.g., with fmReadWrite). `mappedSize` and `offset`
## the file. ## 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: # The file can be resized only when write mode is used:
assert newFileSize == -1 or mode != fmRead assert newFileSize == -1 or mode != fmRead
@ -165,8 +178,11 @@ proc open*(filename: string, mode: TFileMode = fmRead,
if newFileSize != -1: if newFileSize != -1:
flags = flags or O_CREAT or O_TRUNC 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: if result.handle == -1:
# XXX: errno is supposed to be set here # XXX: errno is supposed to be set here
# Is there an exception that wraps it? # Is there an exception that wraps it?

View file

@ -60,6 +60,7 @@ when not defined(memProfiler):
else: interval = intervalInUs * 1000 - tickCountCorrection else: interval = intervalInUs * 1000 - tickCountCorrection
when withThreads: when withThreads:
import locks
var var
profilingLock: TLock profilingLock: TLock

View file

@ -62,9 +62,9 @@ var
proc genOid*(): TOid = proc genOid*(): TOid =
## generates a new OID. ## 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 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) var t = gettime(nil)

View file

@ -903,7 +903,7 @@ elif not defined(useNimRtl):
createStream(p.errStream, p.errHandle, fmRead) createStream(p.errStream, p.errHandle, fmRead)
return p.errStream 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 = proc execCmd(command: string): int =
when defined(linux): when defined(linux):

View file

@ -11,9 +11,12 @@
import tables, os, unsigned, hashes import tables, os, unsigned, hashes
when defined(linux): import posix, epoll when defined(linux):
elif defined(windows): import winlean import posix, epoll
else: import posix elif defined(windows):
import winlean
else:
import posix
proc hash*(x: TSocketHandle): THash {.borrow.} proc hash*(x: TSocketHandle): THash {.borrow.}
proc `$`*(x: TSocketHandle): string {.borrow.} proc `$`*(x: TSocketHandle): string {.borrow.}
@ -29,7 +32,36 @@ type
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] 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 type
PSelector* = ref object PSelector* = ref object
epollFD: cint epollFD: cint
@ -49,8 +81,9 @@ when defined(linux) or defined(nimdoc):
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
## ``events``. ## ``events``.
var event = createEventStruct(events, fd) var event = createEventStruct(events, fd)
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: if events != {}:
OSError(OSLastError()) if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
var key = PSelectorKey(fd: fd, events: events, data: data) var key = PSelectorKey(fd: fd, events: events, data: data)
@ -61,11 +94,27 @@ when defined(linux) or defined(nimdoc):
events: set[TEvent]): PSelectorKey {.discardable.} = events: set[TEvent]): PSelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for. ## Updates the events which ``fd`` wants notifications for.
if s.fds[fd].events != events: 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] result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = 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. ## Determines whether selector contains a file descriptor.
if s.fds.hasKey(fd): if s.fds.hasKey(fd):
# Ensure the underlying epoll instance still contains this 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: else:
return false return false
@ -131,7 +183,7 @@ when defined(linux) or defined(nimdoc):
## Retrieves the selector key for ``fd``. ## Retrieves the selector key for ``fd``.
return s.fds[fd] return s.fds[fd]
else: elif not defined(nimdoc):
# TODO: kqueue for bsd/mac os x. # TODO: kqueue for bsd/mac os x.
type type
PSelector* = ref object PSelector* = ref object
@ -230,7 +282,7 @@ proc contains*(s: PSelector, key: PSelectorKey): bool =
## the new one may have the same value. ## the new one may have the same value.
return key.fd in s and s.fds[key.fd] == key return key.fd in s and s.fds[key.fd] == key
when isMainModule: when isMainModule and not defined(nimdoc):
# Select() # Select()
import sockets import sockets
type type

View file

@ -295,7 +295,7 @@ when defined(ssl):
of protSSLv23: of protSSLv23:
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
of protSSLv2: of protSSLv2:
when not defined(linux): when not defined(linux) and not defined(OpenBSD):
newCTX = SSL_CTX_new(SSLv2_method()) newCTX = SSL_CTX_new(SSLv2_method())
else: else:
SSLError() SSLError()

View file

@ -57,6 +57,7 @@ when not defined(SIGINT):
SIGINT = cint(2) SIGINT = cint(2)
SIGSEGV = cint(11) SIGSEGV = cint(11)
SIGTERM = cint(15) SIGTERM = cint(15)
SIGPIPE = cint(13)
else: else:
{.error: "SIGABRT not ported to your platform".} {.error: "SIGABRT not ported to your platform".}
else: else:
@ -66,6 +67,8 @@ when not defined(SIGINT):
SIGABRT {.importc: "SIGABRT", nodecl.}: cint SIGABRT {.importc: "SIGABRT", nodecl.}: cint
SIGFPE {.importc: "SIGFPE", nodecl.}: cint SIGFPE {.importc: "SIGFPE", nodecl.}: cint
SIGILL {.importc: "SIGILL", nodecl.}: cint SIGILL {.importc: "SIGILL", nodecl.}: cint
when defined(macosx) or defined(linux):
var SIGPIPE {.importc: "SIGPIPE", nodecl.}: cint
when defined(macosx): when defined(macosx):
when NoFakeVars: when NoFakeVars:

View file

@ -298,7 +298,13 @@ when not defined(noSignalHandler):
elif s == SIGILL: action("SIGILL: Illegal operation.\n") elif s == SIGILL: action("SIGILL: Illegal operation.\n")
elif s == SIGBUS: elif s == SIGBUS:
action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") 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 # print stack trace and quit
when hasSomeStackTrace: when hasSomeStackTrace:
@ -323,6 +329,8 @@ when not defined(noSignalHandler):
c_signal(SIGFPE, signalHandler) c_signal(SIGFPE, signalHandler)
c_signal(SIGILL, signalHandler) c_signal(SIGILL, signalHandler)
c_signal(SIGBUS, signalHandler) c_signal(SIGBUS, signalHandler)
when defined(SIGPIPE):
c_signal(SIGPIPE, signalHandler)
registerSignalHandler() # call it in initialization section registerSignalHandler() # call it in initialization section

View file

@ -10,7 +10,7 @@
# set handling # set handling
type type
TNimSet = array [0..4*2048-1, int8] TNimSet = array [0..4*2048-1, uint8]
proc countBits32(n: int32): int {.compilerproc.} = proc countBits32(n: int32): int {.compilerproc.} =
var v = n var v = n
@ -25,4 +25,4 @@ proc countBits64(n: int64): int {.compilerproc.} =
proc cardSet(s: TNimSet, len: int): int {.compilerproc.} = proc cardSet(s: TNimSet, len: int): int {.compilerproc.} =
result = 0 result = 0
for i in countup(0, len-1): for i in countup(0, len-1):
inc(result, countBits32(int32(ze(s[i])))) inc(result, countBits32(int32(s[i])))

View file

@ -62,7 +62,7 @@ type # BaseTsd.h -- Type definitions for the basic sized types
type # WinDef.h -- Basic Windows Type Definitions type # WinDef.h -- Basic Windows Type Definitions
# BaseTypes # BaseTypes
UINT = int32 WINUINT* = int32
ULONG* = int ULONG* = int
PULONG* = ptr int PULONG* = ptr int
USHORT* = int16 USHORT* = int16
@ -137,7 +137,7 @@ type # WinDef.h -- Basic Windows Type Definitions
HFILE* = HANDLE HFILE* = HANDLE
HCURSOR* = HANDLE # = HICON HCURSOR* = HANDLE # = HICON
COLORREF* = int COLORREF* = DWORD
LPCOLORREF* = ptr COLORREF LPCOLORREF* = ptr COLORREF
POINT* {.final, pure.} = object POINT* {.final, pure.} = object
@ -238,7 +238,7 @@ type
CALTYPE* = int CALTYPE* = int
CALID* = int CALID* = int
CCHAR* = char CCHAR* = char
TCOLORREF* = int TCOLORREF* = COLORREF
WINT* = int32 WINT* = int32
PINTEGER* = ptr int32 PINTEGER* = ptr int32
PBOOL* = ptr WINBOOL PBOOL* = ptr WINBOOL
@ -19683,7 +19683,7 @@ proc SetSysColors*(cElements: int32, lpaElements: var wINT,
dynlib: "user32", importc: "SetSysColors".} dynlib: "user32", importc: "SetSysColors".}
proc DrawFocusRect*(hDC: HDC, lprc: var RECT): WINBOOL{.stdcall, proc DrawFocusRect*(hDC: HDC, lprc: var RECT): WINBOOL{.stdcall,
dynlib: "user32", importc: "DrawFocusRect".} 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".} dynlib: "user32", importc: "FillRect".}
proc FrameRect*(hDC: HDC, lprc: var RECT, hbr: HBRUSH): int32{.stdcall, proc FrameRect*(hDC: HDC, lprc: var RECT, hbr: HBRUSH): int32{.stdcall,
dynlib: "user32", importc: "FrameRect".} dynlib: "user32", importc: "FrameRect".}
@ -22758,12 +22758,12 @@ proc LocalDiscard*(hlocMem: HLOCAL): HLOCAL =
# WinGDI.h # WinGDI.h
proc GetGValue*(rgb: int32): int8 = discard """proc GetGValue*(rgb: int32): int8 =
result = toU8(rgb shr 8'i32) result = toU8(rgb shr 8'i32)"""
proc RGB*(r, g, b: int): COLORREF = proc RGB*(r, g, b: int): COLORREF =
result = toU32(r) or (toU32(g) shl 8) or (toU32(b) shl 16) result = toU32(r) or (toU32(g) shl 8) or (toU32(b) shl 16)
proc RGB*(r, g, b: range[0 .. 255]): COLORREF = 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 = proc PALETTERGB*(r, g, b: range[0..255]): COLORREF =
result = 0x02000000 or RGB(r, g, b) result = 0x02000000 or RGB(r, g, b)

View file

@ -270,7 +270,7 @@ proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
when not defined(windows): when not defined(windows):
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
dynlib: DLLSSLName, importc.} dynlib: DLLUtilName, importc.}
proc CRYPTO_malloc_init*() = proc CRYPTO_malloc_init*() =
when not defined(windows): when not defined(windows):

View file

@ -23,19 +23,19 @@ proc launchSwarm(port: TPort) {.async.} =
await connect(sock, "localhost", port) await connect(sock, "localhost", port)
when true: when true:
await sendMessages(sock) await sendMessages(sock)
close(sock) closeSocket(sock)
else: else:
# Issue #932: https://github.com/Araq/Nimrod/issues/932 # Issue #932: https://github.com/Araq/Nimrod/issues/932
var msgFut = sendMessages(sock) var msgFut = sendMessages(sock)
msgFut.callback = msgFut.callback =
proc () = proc () =
close(sock) closeSocket(sock)
proc readMessages(client: TAsyncFD) {.async.} = proc readMessages(client: TAsyncFD) {.async.} =
while true: while true:
var line = await recvLine(client) var line = await recvLine(client)
if line == "": if line == "":
close(client) closeSocket(client)
clientCount.inc clientCount.inc
break break
else: else:

View file

@ -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`