mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-29 10:01:39 +00:00
Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
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>`_
|
||||
interface to the ODBC driver.
|
||||
* `sphinx <sphinx.html>`_
|
||||
Nimrod wrapper for ``shpinx``.
|
||||
Nimrod wrapper for ``sphinx``.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -152,7 +152,7 @@ proc boot(args: string) =
|
||||
copyExe(findStartNimrod(), 0.thVersion)
|
||||
for i in 0..2:
|
||||
echo "iteration: ", i+1
|
||||
exec i.thVersion & " cc $# $# compiler" / "nimrod.nim" % [bootOptions, args]
|
||||
exec i.thVersion & " c $# $# compiler" / "nimrod.nim" % [bootOptions, args]
|
||||
if sameFileContent(output, i.thVersion):
|
||||
copyExe(output, finalDest)
|
||||
echo "executables are equal: SUCCESS!"
|
||||
@@ -167,7 +167,7 @@ const
|
||||
cleanExt = [
|
||||
".ppu", ".o", ".obj", ".dcu", ".~pas", ".~inc", ".~dsk", ".~dpr",
|
||||
".map", ".tds", ".err", ".bak", ".pyc", ".exe", ".rod", ".pdb", ".idb",
|
||||
".idx"
|
||||
".idx", ".ilk"
|
||||
]
|
||||
ignore = [
|
||||
".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore"
|
||||
|
||||
@@ -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>".}
|
||||
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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])))
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
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`
|
||||
Reference in New Issue
Block a user