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
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] = [
mUnaryMinusI: "((NI$2)-($1))",
mUnaryMinusI64: "-($1)",
mAbsI: "(NI$2)abs($1)",
mAbsI: "($1 > 0? ($1) : -($1))",
mAbsI64: "($1 > 0? ($1) : -($1))"]
var
a: TLoc

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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>`_
interface to the ODBC driver.
* `sphinx <sphinx.html>`_
Nimrod wrapper for ``shpinx``.
Nimrod wrapper for ``sphinx``.
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.
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

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

View File

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

View File

@@ -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>".}

View File

@@ -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: ...

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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`