Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
04a1555f4a
32 changed files with 294 additions and 286 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
152
doc/abstypes.txt
152
doc/abstypes.txt
|
|
@ -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)
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
4
koch.nim
4
koch.nim
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>".}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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: ...
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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?
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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])))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
17
tests/ccgbugs/tbug1081.nim
Normal file
17
tests/ccgbugs/tbug1081.nim
Normal 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`
|
||||||
Loading…
Add table
Add a link
Reference in a new issue