Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf
2017-04-02 23:44:50 +02:00
27 changed files with 1153 additions and 67 deletions

View File

@@ -944,7 +944,6 @@ proc genEcho(p: BProc, n: PNode) =
# this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
# is threadsafe.
internalAssert n.kind == nkBracket
p.module.includeHeader("<stdio.h>")
var args: Rope = nil
var a: TLoc
for i in countup(0, n.len-1):
@@ -953,9 +952,15 @@ proc genEcho(p: BProc, n: PNode) =
else:
initLocExpr(p, n.sons[i], a)
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeat("%s", n.len) & tnl), args)
linefmt(p, cpsStmts, "fflush(stdout);$n")
if platform.targetOS == osGenode:
# bypass libc and print directly to the Genode LOG session
p.module.includeHeader("<base/log.h>")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else:
p.module.includeHeader("<stdio.h>")
linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeat("%s", n.len) & tnl), args)
linefmt(p, cpsStmts, "fflush(stdout);$n")
proc gcUsage(n: PNode) =
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)

View File

@@ -966,6 +966,19 @@ proc genMainProc(m: BModule) =
MainProcs &
"}$N$N"
GenodeNimMain =
"Libc::Env *genodeEnv;$N" &
NimMainBody
ComponentConstruct =
"void Libc::Component::construct(Libc::Env &env) {$N" &
"\tgenodeEnv = &env;$N" &
"\tLibc::with_libc([&] () {$n\t" &
MainProcs &
"\t});$N" &
"\tenv.parent().exit(0);$N" &
"}$N$N"
var nimMain, otherMain: FormatStr
if platform.targetOS == osWindows and
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
@@ -976,6 +989,9 @@ proc genMainProc(m: BModule) =
nimMain = WinNimDllMain
otherMain = WinCDllMain
m.includeHeader("<windows.h>")
elif platform.targetOS == osGenode:
nimMain = GenodeNimMain
otherMain = ComponentConstruct
elif optGenDynLib in gGlobalOptions:
nimMain = PosixNimDllMain
otherMain = PosixCDllMain

View File

@@ -1626,22 +1626,56 @@ proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
localError(x.info, "'toArray' needs an array literal")
r.res.add(")")
proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) =
useMagic(p, magic)
add(r.res, magic & "(")
var a: TCompRes
gen(p, n.sons[1], a)
if magic == "reprAny":
# the pointer argument in reprAny is expandend to
# (pointedto, pointer), so we need to fill it
if a.address.isNil:
add(r.res, a.res)
add(r.res, ", null")
else:
add(r.res, "$1, $2" % [a.address, a.res])
else:
add(r.res, a.res)
if not typ.isNil:
add(r.res, ", ")
add(r.res, typ)
add(r.res, ")")
proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
if p.target == targetPHP:
localError(n.info, "'repr' not available for PHP backend")
return
let t = skipTypes(n.sons[1].typ, abstractVarRange)
case t.kind
of tyInt..tyUInt64:
unaryExpr(p, n, r, "", "(\"\"+ ($1))")
case t.kind:
of tyInt..tyInt64, tyUInt..tyUInt64:
genReprAux(p, n, r, "reprInt")
of tyChar:
genReprAux(p, n, r, "reprChar")
of tyBool:
genReprAux(p, n, r, "reprBool")
of tyFloat..tyFloat128:
genReprAux(p, n, r, "reprFloat")
of tyString:
genReprAux(p, n, r, "reprStr")
of tyEnum, tyOrdinal:
gen(p, n.sons[1], r)
useMagic(p, "cstrToNimstr")
r.kind = resExpr
r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res]
genReprAux(p, n, r, "reprEnum", genTypeInfo(p, t))
of tySet:
genReprAux(p, n, r, "reprSet", genTypeInfo(p, t))
of tyEmpty, tyVoid:
localError(n.info, "'repr' doesn't support 'void' type")
of tyPointer:
genReprAux(p, n, r, "reprPointer")
of tyOpenArray, tyVarargs:
genReprAux(p, n, r, "reprJSONStringify")
else:
# XXX:
internalError(n.info, "genRepr: Not implemented")
genReprAux(p, n, r, "reprAny", genTypeInfo(p, t))
proc genOf(p: PProc, n: PNode, r: var TCompRes) =
var x: TCompRes

View File

@@ -21,7 +21,7 @@ type
# conditionals to condsyms (end of module).
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks,
osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, osGenode
osJS, osNimrodVM, osStandalone
type
@@ -147,6 +147,11 @@ const
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "Genode", pardir: "..", dllFrmt: "$1.lib.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: "", curDir: "/", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospLacksThreadVars}),
(name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",

View File

@@ -256,6 +256,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName]
excl(n.flags, nfDotSetter)
orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp)

View File

@@ -472,7 +472,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
var r = replaceTypeVarsT(cl, result.sons[i])
if result.kind == tyObject:
# carefully coded to not skip the precious tyGenericInst:
let r2 = r.skipTypes({tyGenericInst, tyAlias})
let r2 = r.skipTypes({tyAlias})
if r2.kind in {tyPtr, tyRef}:
r = skipTypes(r2, {tyPtr, tyRef})
result.sons[i] = r

View File

@@ -1268,7 +1268,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# crossing path with metatypes/aliases, so we need to separate them
# by checking sym.id
let genericSubtype = isGenericSubType(c, x, f, depth, f)
if not (genericSubtype and aobj.sym.id != fobj.sym.id):
if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody:
depth = -1
if depth >= 0:

View File

@@ -174,7 +174,8 @@ typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
indAndComment?
varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
variable = (varTuple / identColonEquals) indAndComment
colonBody = colcom stmt doBlocks?
variable = (varTuple / identColonEquals) colonBody? indAndComment
bindStmt = 'bind' optInd qualifiedIdent ^+ comma
mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
pragmaStmt = pragma (':' COMMENT? stmt)?

View File

@@ -39,7 +39,7 @@ directly or indirectly through a call to a GC unsafe proc.
The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe,
otherwise this property is inferred by the compiler. Note that ``noSideEffect``
implies ``gcsafe``. The only way to create a thread is via ``spawn`` or
``createThead``. ``spawn`` is usually the preferable method. Either way
``createThread``. ``spawn`` is usually the preferable method. Either way
the invoked proc must not use ``var`` parameters nor must any of its parameters
contain a ``ref`` or ``closure`` type. This enforces
the *no heap sharing restriction*.

78
lib/genode_cpp/syslocks.h Normal file
View File

@@ -0,0 +1,78 @@
/*
*
* Nim's Runtime Library
* (c) Copyright 2017 Emery Hemingway
*
* See the file "copying.txt", included in this
* distribution, for details about the copyright.
*
*/
#ifndef _GENODE_CPP__SYSLOCKS_H_
#define _GENODE_CPP__SYSLOCKS_H_
/* Genode includes */
#include <base/semaphore.h>
#include <base/lock.h>
namespace Nim {
struct SysLock;
struct SysCond;
}
struct Nim::SysLock
{
Genode::Lock _lock_a, _lock_b;
bool _locked;
void acquireSys()
{
_lock_a.lock();
_locked = true;
_lock_a.unlock();
_lock_b.lock();
}
bool tryAcquireSys()
{
if (_locked)
return false;
_lock_a.lock();
if (_locked) {
_lock_a.unlock();
return false;
} else {
_locked = true;
_lock_b.lock();
_lock_a.unlock();
return true;
}
}
void releaseSys()
{
_locked = false;
_lock_a.unlock();
_lock_b.unlock();
}
};
struct Nim::SysCond
{
Genode::Semaphore _semaphore;
void waitSysCond(SysLock &syslock)
{
syslock.releaseSys();
_semaphore.down();
syslock.acquireSys();
}
void signalSysCond()
{
_semaphore.up();
}
};
#endif

69
lib/genode_cpp/threads.h Normal file
View File

@@ -0,0 +1,69 @@
/*
*
* Nim's Runtime Library
* (c) Copyright 2017 Emery Hemingway
*
* See the file "copying.txt", included in this
* distribution, for details about the copyright.
*
*/
#ifndef _GENODE_CPP__THREAD_H_
#define _GENODE_CPP__THREAD_H_
#include <base/thread.h>
#include <util/avl_tree.h>
#include <util/reconstructible.h>
namespace Nim { struct SysThread; }
struct Nim::SysThread
{
typedef void (Entry)(void*);
struct Thread : Genode::Thread
{
void *_tls;
Entry *_func;
void *_arg;
void entry() override {
(_func)(_arg); }
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg)
: Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg)
{
Genode::Thread::start();
}
};
Genode::Constructible<Thread> _thread;
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) {
_thread.construct(*env, stack_size, func, arg); }
void joinThread() {
_thread->join(); }
static bool offMainThread() {
return dynamic_cast<SysThread::Thread*>(Genode::Thread::myself()); }
static void *threadVarGetValue()
{
SysThread::Thread *thr =
static_cast<SysThread::Thread*>(Genode::Thread::myself());
return thr->_tls;
}
static void threadVarSetValue(void *value)
{
SysThread::Thread *thr =
static_cast<SysThread::Thread*>(Genode::Thread::myself());
thr->_tls = value;
}
};
#endif

View File

@@ -498,6 +498,11 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
# include <sys/types.h>
#endif
#if defined(__GENODE__)
#include <libc/component.h>
extern Libc::Env *genodeEnv;
#endif
/* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
#define NIM_CHECK_SIZE(typ, sz) \
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")

View File

@@ -1862,10 +1862,10 @@ when hasAioH:
a4: ptr SigEvent): cint {.importc, header: "<aio.h>".}
# arpa/inet.h
proc htonl*(a1: int32): int32 {.importc, header: "<arpa/inet.h>".}
proc htons*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".}
proc ntohl*(a1: int32): int32 {.importc, header: "<arpa/inet.h>".}
proc ntohs*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".}
proc htonl*(a1: uint32): uint32 {.importc, header: "<arpa/inet.h>".}
proc htons*(a1: uint16): uint16 {.importc, header: "<arpa/inet.h>".}
proc ntohl*(a1: uint32): uint32 {.importc, header: "<arpa/inet.h>".}
proc ntohs*(a1: uint16): uint16 {.importc, header: "<arpa/inet.h>".}
proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".}
proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".}
@@ -2423,11 +2423,15 @@ proc sigset*(a1: int, a2: proc (x: cint) {.noconv.}) {.
proc sigsuspend*(a1: var Sigset): cint {.importc, header: "<signal.h>".}
when defined(android):
proc sigtimedwait*(a1: var Sigset, a2: var SigInfo,
a3: var Timespec, sigsetsize: csize = sizeof(culong)*2): cint {.importc: "__rt_sigtimedwait", header:"<signal.h>".}
proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
var NR_rt_sigtimedwait {.importc: "__NR_rt_sigtimedwait", header: "<sys/syscall.h>".}: clong
var NSIGMAX {.importc: "NSIG", header: "<signal.h>".}: clong
proc sigtimedwait*(a1: var Sigset, a2: var SigInfo, a3: var Timespec): cint =
result = cint(syscall(NR_rt_sigtimedwait, addr(a1), addr(a2), addr(a3), NSIGMAX div 8))
else:
proc sigtimedwait*(a1: var Sigset, a2: var SigInfo,
a3: var Timespec): cint {.importc, header: "<signal.h>".}
a3: var Timespec): cint {.importc, header: "<signal.h>".}
proc sigwait*(a1: var Sigset, a2: var cint): cint {.
importc, header: "<signal.h>".}

View File

@@ -37,6 +37,10 @@ when defined(macosx) or defined(bsd):
a: var csize, b: pointer, c: int): cint {.
importc: "sysctl", nodecl.}
when defined(genode):
proc affinitySpaceTotal(): cuint {.
importcpp: "genodeEnv->cpu().affinity_space().total()".}
proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected.
@@ -61,7 +65,8 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
elif defined(irix):
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
result = sysconf(SC_NPROC_ONLN)
elif defined(genode):
result = affinitySpaceTotal().int
else:
result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 1

View File

@@ -320,7 +320,7 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
if line[linei] != ':': httpError("invalid headers")
inc(linei) # Skip :
result.headers[name] = line[linei.. ^1].strip()
result.headers.add(name, line[linei.. ^1].strip())
# Ensure the server isn't trying to DoS us.
if result.headers.len > headerLimit:
httpError("too many headers")
@@ -1010,7 +1010,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
if line[linei] != ':': httpError("invalid headers")
inc(linei) # Skip :
result.headers[name] = line[linei.. ^1].strip()
result.headers.add(name, line[linei.. ^1].strip())
if result.headers.len > headerLimit:
httpError("too many headers")

View File

@@ -825,16 +825,16 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
else:
add environment, (key & '=' & val)
indx = high(environment)
when defined(unix):
if c_putenv(environment[indx]) != 0'i32:
raiseOSError(osLastError())
else:
when defined(windows):
when useWinUnicode:
var k = newWideCString(key)
var v = newWideCString(val)
if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
else:
if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
else:
if c_putenv(environment[indx]) != 0'i32:
raiseOSError(osLastError())
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
## Iterate over all `environments variables`:idx:. In the first component
@@ -1091,7 +1091,7 @@ proc rawCreateDir(dir: string): bool =
result = false
else:
raiseOSError(osLastError())
elif defined(unix):
elif defined(posix):
let res = mkdir(dir, 0o777)
if res == 0'i32:
result = true
@@ -1452,7 +1452,9 @@ elif defined(windows):
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
return TaintedString(ownArgv[i])
elif not defined(createNimRtl) and not(defined(posix) and appType == "lib"):
elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and
not defined(genode):
# On Posix, there is no portable way to get the command line from a DLL.
var
cmdCount {.importc: "cmdCount".}: cint
@@ -1606,6 +1608,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
result = getApplAux("/proc/self/exe")
elif defined(solaris):
result = getApplAux("/proc/" & $getpid() & "/path/a.out")
elif defined(genode):
raiseOSError("POSIX command line not supported")
elif defined(freebsd) or defined(dragonfly):
result = getApplFreebsd()
# little heuristic that may work on other POSIX-like systems:

View File

@@ -1384,25 +1384,34 @@ var programResult* {.exportc: "nim_program_result".}: int
## under normal circumstances. When the program is terminated
## prematurely using ``quit``, this value is ignored.
proc quit*(errorcode: int = QuitSuccess) {.
magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
## Stops the program immediately with an exit code.
##
## Before stopping the program the "quit procedures" are called in the
## opposite order they were added with `addQuitProc <#addQuitProc>`_.
## ``quit`` never returns and ignores any exception that may have been raised
## by the quit procedures. It does *not* call the garbage collector to free
## all the memory, unless a quit procedure calls `GC_fullCollect
## <#GC_fullCollect>`_.
##
## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
## program finishes without incident. A raised unhandled exception is
## equivalent to calling ``quit(QuitFailure)``.
##
## Note that this is a *runtime* call and using ``quit`` inside a macro won't
## have any compile time effect. If you need to stop the compiler inside a
## macro, use the `error <manual.html#error-pragma>`_ or `fatal
## <manual.html#fatal-pragma>`_ pragmas.
when defined(nimdoc):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
## Stops the program immediately with an exit code.
##
## Before stopping the program the "quit procedures" are called in the
## opposite order they were added with `addQuitProc <#addQuitProc>`_.
## ``quit`` never returns and ignores any exception that may have been raised
## by the quit procedures. It does *not* call the garbage collector to free
## all the memory, unless a quit procedure calls `GC_fullCollect
## <#GC_fullCollect>`_.
##
## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
## program finishes without incident. A raised unhandled exception is
## equivalent to calling ``quit(QuitFailure)``.
##
## Note that this is a *runtime* call and using ``quit`` inside a macro won't
## have any compile time effect. If you need to stop the compiler inside a
## macro, use the `error <manual.html#error-pragma>`_ or `fatal
## <manual.html#fatal-pragma>`_ pragmas.
elif defined(genode):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn,
importcpp: "genodeEnv->parent().exit(@)", header: "<base/env.h>".}
else:
proc quit*(errorcode: int = QuitSuccess) {.
magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
template sysAssert(cond: bool, msg: string) =
when defined(useSysAssert):

View File

@@ -146,6 +146,17 @@ elif defined(windows) or defined(dos):
if result != nil: return
procAddrError(name)
elif defined(genode):
proc nimUnloadLibrary(lib: LibHandle) {.
error: "nimUnloadLibrary not implemented".}
proc nimLoadLibrary(path: string): LibHandle {.
error: "nimLoadLibrary not implemented".}
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.
error: "nimGetProcAddr not implemented".}
else:
{.error: "no implementation for dyncalls".}

View File

@@ -77,6 +77,19 @@ when defined(emscripten):
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
munmap(mmapDescr.realPointer, mmapDescr.realSize)
elif defined(genode):
proc osAllocPages(size: int): pointer {.
importcpp: "genodeEnv->rm().attach(genodeEnv->ram().alloc(@))".}
proc osTryAllocPages(size: int): pointer =
{.emit: """try {""".}
result = osAllocPages size
{.emit: """} catch (...) { }""".}
proc osDeallocPages(p: pointer, size: int) {.
importcpp: "genodeEnv->rm().detach(#)".}
elif defined(posix):
const
PROT_READ = 1 # page can be read

View File

@@ -6,18 +6,272 @@
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# The generic ``repr`` procedure for the javascript backend.
proc reprInt(x: int64): string {.compilerproc.} = return $x
proc reprFloat(x: float): string {.compilerproc.} =
# Js toString doesn't differentiate between 1.0 and 1,
# but we do.
if $x == $(x.int): $x & ".0"
else: $x
proc reprPointer(p: pointer): string {.compilerproc.} =
# Do we need to generate the full 8bytes ? In js a pointer is an int anyway
var tmp: int
{. emit: """
if (`p`_Idx == null) {
`tmp` = 0;
} else {
`tmp` = `p`_Idx;
}
""" .}
result = $tmp
proc reprBool(x: bool): string {.compilerRtl.} =
if x: result = "true"
else: result = "false"
proc isUndefined[T](x: T): bool {.inline.} = {.emit: "`result` = `x` === undefined;"}
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
if ntfEnumHole notin typ.flags:
if e <% typ.node.len:
return $typ.node.sons[e].name
if not typ.node.sons[e].isUndefined:
result = $typ.node.sons[e].name
else:
# ugh we need a slow linear search:
var n = typ.node
var s = n.sons
for i in 0 .. n.len-1:
if s[i].offset == e: return $s[i].name
result = $e & " (invalid data!)"
result = $e & " (invalid data!)"
proc reprChar(x: char): string {.compilerRtl.} =
result = "\'"
case x
of '"': add(result, "\\\"")
of '\\': add(result, "\\\\")
of '\128'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) )
else: add(result, x)
add(result, "\'")
proc reprStrAux(result: var string, s: cstring, len: int) =
add(result, "\"")
for i in 0 .. <len:
let c = s[i]
case c
of '"': add(result, "\\\"")
of '\\': add(result, "\\\\")
of '\10': add(result, "\\10\"\n\"")
of '\128'..'\255', '\0'..'\9', '\11'..'\31':
add( result, "\\" & reprInt(ord(c)) )
else:
add( result, reprInt(ord(c)) ) # Not sure about this.
add(result, "\"")
proc reprStr(s: string): string {.compilerRtl.} =
result = ""
if cast[pointer](s).isNil:
# Handle nil strings here because they don't have a length field in js
# TODO: check for null/undefined before generating call to length in js?
# Also: c backend repr of a nil string is <pointer>"", but repr of an
# array of string that is not initialized is [nil, nil, ...] ??
add(result, "nil")
else:
reprStrAux(result, s, s.len)
proc addSetElem(result: var string, elem: int, typ: PNimType) =
# Dispatch each set element to the correct repr<Type> proc
case typ.kind:
of tyEnum: add(result, reprEnum(elem, typ))
of tyBool: add(result, reprBool(bool(elem)))
of tyChar: add(result, reprChar(chr(elem)))
of tyRange: addSetElem(result, elem, typ.base) # Note the base to advance towards the element type
of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
else: # data corrupt --> inform the user
add(result, " (invalid data!)")
iterator setKeys(s: int): int {.inline.} =
# The type of s is a lie, but it's expected to be a set.
# Iterate over the JS object representing a set
# and returns the keys as int.
var len: int
var yieldRes: int
var i: int = 0
{. emit: """
var setObjKeys = Object.getOwnPropertyNames(`s`);
`len` = setObjKeys.length;
""" .}
while i < len:
{. emit: "`yieldRes` = parseInt(setObjKeys[`i`],10);\n" .}
yield yieldRes
inc i
proc reprSetAux(result: var string, s: int, typ: PNimType) =
add(result, "{")
var first: bool = true
for el in setKeys(s):
if first:
first = false
else:
add(result, ", ")
addSetElem(result, el, typ.base)
add(result, "}")
proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} =
result = ""
reprSetAux(result, e, typ)
type
ReprClosure {.final.} = object
recDepth: int # do not recurse endlessly
indent: int # indentation
proc initReprClosure(cl: var ReprClosure) =
cl.recDepth = -1 # default is to display everything!
cl.indent = 0
proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
proc reprArray(a: pointer, typ: PNimType,
cl: var ReprClosure): string {.compilerRtl.} =
var isNilArrayOrSeq: bool
# isnil is not enough here as it would try to deref `a` without knowing what's inside
{. emit: """
if (`a` == null) {
`isNilArrayOrSeq` = true;
} else if (`a`[0] == null) {
`isNilArrayOrSeq` = true;
} else {
`isNilArrayOrSeq` = false;
};
""" .}
if typ.kind == tySequence and isNilArrayOrSeq:
return "nil"
# We prepend @ to seq, the C backend prepends the pointer to the seq.
result = if typ.kind == tySequence: "@[" else: "["
var len: int = 0
var i: int = 0
{. emit: "`len` = `a`.length;\n" .}
var dereffed: pointer = a
for i in 0 .. < len:
if i > 0 :
add(result, ", ")
# advance pointer and point to element at index
{. emit: """
`dereffed`_Idx = `i`;
`dereffed` = `a`[`dereffed`_Idx];
""" .}
reprAux(result, dereffed, typ.base, cl)
add(result, "]")
proc isPointedToNil(p: pointer): bool {.inline.}=
{. emit: "if (`p` === null) {`result` = true};\n" .}
proc reprRef(result: var string, p: pointer, typ: PNimType,
cl: var ReprClosure) =
if p.isPointedToNil:
add(result , "nil")
return
add( result, "ref " & reprPointer(p) )
add(result, " --> ")
if typ.base.kind != tyArray:
{. emit: """
if (`p` != null && `p`.length > 0) {
`p` = `p`[`p`_Idx];
}
""" .}
reprAux(result, p, typ.base, cl)
proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
add(result, "[")
var first: bool = true
var val: pointer = o
if typ.node.len == 0:
# if the object has only one field, len is 0 and sons is nil, the field is in node
let key: cstring = typ.node.name
add(result, $key & " = ")
{. emit: "`val` = `o`[`key`];\n" .}
reprAux(result, val, typ.node.typ, cl)
else:
# if the object has more than one field, sons is not nil and contains the fields.
for i in 0 .. <typ.node.len:
if first: first = false
else: add(result, ",\n")
let key: cstring = typ.node.sons[i].name
add(result, $key & " = ")
{. emit: "`val` = `o`[`key`];\n" .} # access the field by name
reprAux(result, val, typ.node.sons[i].typ, cl)
add(result, "]")
proc reprRecord(o: pointer, typ: PNimType, cl: var ReprClosure): string {.compilerRtl.} =
result = ""
reprRecordAux(result, o, typ,cl)
proc reprJSONStringify(p: int): string {.compilerRtl.} =
# As a last resort, use stringify
# We use this for tyOpenArray, tyVarargs while genTypeInfo is not implemented
var tmp: cstring
{. emit: "`tmp` = JSON.stringify(`p`);\n" .}
result = $tmp
proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var ReprClosure) =
if cl.recDepth == 0:
add(result, "...")
return
dec(cl.recDepth)
case typ.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
add( result, reprInt(cast[int](p)) )
of tyChar:
add( result, reprChar(cast[char](p)) )
of tyBool:
add( result, reprBool(cast[bool](p)) )
of tyFloat..tyFloat128:
add( result, reprFloat(cast[float](p)) )
of tyString:
var fp: int
{. emit: "`fp` = `p`;\n" .}
if cast[string](fp).isNil:
add(result, "nil")
else:
add( result, reprStr(cast[string](p)) )
of tyCString:
var fp: cstring
{. emit: "`fp` = `p`;\n" .}
if fp.isNil:
add(result, "nil")
else:
reprStrAux(result, fp, fp.len)
of tyEnum, tyOrdinal:
var fp: int
{. emit: "`fp` = `p`;\n" .}
add(result, reprEnum(fp, typ))
of tySet:
var fp: int
{. emit: "`fp` = `p`;\n" .}
add(result, reprSet(fp, typ))
of tyRange: reprAux(result, p, typ.base, cl)
of tyObject, tyTuple:
add(result, reprRecord(p, typ, cl))
of tyArray, tyArrayConstr, tySequence:
add(result, reprArray(p, typ, cl))
of tyPointer:
add(result, reprPointer(p))
of tyPtr, tyRef:
reprRef(result, p, typ, cl)
of tyProc:
if p.isPointedToNil:
add(result, "nil")
else:
add(result, reprPointer(p))
else:
add( result, "(invalid data!)" & reprJsonStringify(cast[int](p)) )
inc(cl.recDepth)
proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} =
var cl: ReprClosure
initReprClosure(cl)
result = ""
reprAux(result, p, typ, cl)
add(result, "\n")

View File

@@ -75,6 +75,28 @@ when defined(Windows):
proc waitSysCondWindows(cond: var SysCond) =
discard waitForSingleObject(cond, -1'i32)
elif defined(genode):
const
Header = "genode_cpp/syslocks.h"
type
SysLock {.importcpp: "Nim::SysLock", pure, final,
header: Header.} = object
SysCond {.importcpp: "Nim::SysCond", pure, final,
header: Header.} = object
proc initSysLock(L: var SysLock) = discard
proc deinitSys(L: var SysLock) = discard
proc acquireSys(L: var SysLock) {.noSideEffect, importcpp.}
proc tryAcquireSys(L: var SysLock): bool {.noSideEffect, importcpp.}
proc releaseSys(L: var SysLock) {.noSideEffect, importcpp.}
proc initSysCond(L: var SysCond) = discard
proc deinitSysCond(L: var SysCond) = discard
proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
noSideEffect, importcpp.}
proc signalSysCond(cond: var SysCond) {.
noSideEffect, importcpp.}
else:
type
SysLock {.importc: "pthread_mutex_t", pure, final,

View File

@@ -46,7 +46,11 @@ const
maxRegisters = 256 # don't think there is an arch with more registers
useStackMaskHack = false ## use the stack mask hack for better performance
StackGuardSize = 4096
ThreadStackMask = 1024*256*sizeof(int)-1
ThreadStackMask =
when defined(genode):
1024*64*sizeof(int)-1
else:
1024*256*sizeof(int)-1
ThreadStackSize = ThreadStackMask+1 - StackGuardSize
when defined(windows):
@@ -115,6 +119,49 @@ when defined(windows):
## get the ID of the currently running thread.
result = int(getCurrentThreadId())
elif defined(genode):
const
GenodeHeader = "genode_cpp/threads.h"
type
SysThread* {.importcpp: "Nim::SysThread",
header: GenodeHeader, final, pure.} = object
GenodeThreadProc = proc (x: pointer) {.noconv.}
ThreadVarSlot = int
proc initThread(s: var SysThread,
stackSize: culonglong,
entry: GenodeThreadProc,
arg: pointer) {.
importcpp: "#.initThread(genodeEnv, @)".}
proc threadVarAlloc(): ThreadVarSlot = 0
proc offMainThread(): bool {.
importcpp: "Nim::SysThread::offMainThread",
header: GenodeHeader.}
proc threadVarSetValue(value: pointer) {.
importcpp: "Nim::SysThread::threadVarSetValue(@)",
header: GenodeHeader.}
proc threadVarGetValue(): pointer {.
importcpp: "Nim::SysThread::threadVarGetValue()",
header: GenodeHeader.}
var mainTls: pointer
proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
if offMainThread():
threadVarSetValue(value);
else:
mainTls = value
proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
if offMainThread():
threadVarGetValue();
else:
mainTls
else:
when not defined(macosx):
{.passL: "-pthread".}
@@ -451,6 +498,9 @@ when defined(windows):
proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
threadProcWrapperBody(closure)
# implicitly return 0
elif defined(genode):
proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
threadProcWrapperBody(closure)
else:
proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
threadProcWrapperBody(closure)
@@ -482,6 +532,14 @@ when hostOS == "windows":
cast[ptr SysThread](addr(a)), 1, -1)
inc(k, MAXIMUM_WAIT_OBJECTS)
elif defined(genode):
proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.}
## waits for the thread `t` to finish.
proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish.
for i in 0..t.high: joinThread(t[i])
else:
proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish.
@@ -531,6 +589,21 @@ when hostOS == "windows":
## shouldn't use this proc.
setThreadAffinityMask(t.sys, uint(1 shl cpu))
elif defined(genode):
proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread, nimcall.},
param: TArg) =
when TArg isnot void: t.data = param
t.dataFn = tp
when hasSharedHeap: t.stackSize = ThreadStackSize
t.sys.initThread(
ThreadStackSize.culonglong,
threadProcWrapper[TArg], addr(t))
proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
{.hint: "cannot change Genode thread CPU affinity after initialization".}
discard
else:
proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread, nimcall.},

View File

@@ -33,8 +33,8 @@ proc prepareAddress(intaddr: uint32, intport: uint16): ptr Sockaddr_in =
result.sin_family = toInt(nativesockets.AF_INET).int16
else:
result.sin_family = toInt(nativesockets.AF_INET)
result.sin_port = htons(intport)
result.sin_addr.s_addr = htonl(intaddr)
result.sin_port = nativesockets.htons(intport)
result.sin_addr.s_addr = nativesockets.htonl(intaddr)
proc launchSwarm(name: ptr SockAddr) {.async.} =
var i = 0
@@ -90,7 +90,7 @@ proc readMessages(server: AsyncFD) {.async.} =
await sendTo(server, addr grammString[0], len(grammString),
cast[ptr SockAddr](addr saddr), slen)
inc(msgCount)
saveReceivedPort(ntohs(saddr.sin_port).int)
saveReceivedPort(nativesockets.ntohs(saddr.sin_port).int)
inc(i)
proc createServer() {.async.} =

View File

@@ -0,0 +1,42 @@
discard """
output: '''1
a
13'''
"""
# bug #5621 #5615
type
Obj5[T] = ref object of RootObj
x_impl: T
proc x[T](v476205: Obj5[T]): T {.used.} =
v476205.x_impl
type
Obj6[T, U] = ref object of Obj5[T]
y_impl: U
proc newObj6[T, U](x: T; y: U): Obj6[T, U] =
new(result)
result.x_impl = x
result.y_impl = y
proc x[T, U](v477606: Obj6[T, U]): T {.used.} =
v477606.x_impl
proc y[T, U](v477608: Obj6[T, U]): U {.used.} =
v477608.y_impl
let e = newObj6(1, "a")
echo e.x
echo e.y
type
Fruit[T] = ref object of RootObj
Apple[T] = ref object of Fruit[T]
proc getColor[T](v: Fruit[T]): T = 13
var w: Apple[int]
let r = getColor(w)
echo r

View File

@@ -0,0 +1,12 @@
discard """
output: '''OK'''
"""
#bug #5632
type
Option*[T] = object
proc point*[A](v: A, t: typedesc[Option[A]]): Option[A] =
discard
discard point(1, Option)
echo "OK"

422
tests/js/trepr.nim Normal file
View File

@@ -0,0 +1,422 @@
discard """
action: run
"""
block ints:
let
na: int8 = -120'i8
nb: int16 = -32700'i16
nc: int32 = -2147483000'i32
nd: int64 = -9223372036854775000'i64
ne: int = -1234567
pa: int8 = 120'i8
pb: int16 = 32700'i16
pc: int32 = 2147483000'i32
pd: int64 = 9223372036854775000'i64
pe: int = 1234567
doAssert(repr(na) == "-120")
doAssert(repr(nb) == "-32700")
doAssert(repr(nc) == "-2147483000")
doAssert(repr(nd) == "-9223372036854775000")
doAssert(repr(ne) == "-1234567")
doAssert(repr(pa) == "120")
doAssert(repr(pb) == "32700")
doAssert(repr(pc) == "2147483000")
doAssert(repr(pd) == "9223372036854775000")
doAssert(repr(pe) == "1234567")
block uints:
let
a: uint8 = 254'u8
b: uint16 = 65300'u16
c: uint32 = 4294967290'u32
# d: uint64 = 18446744073709551610'u64 -> unknown node type
e: uint = 1234567
doAssert(repr(a) == "254")
doAssert(repr(b) == "65300")
doAssert(repr(c) == "4294967290")
# doAssert(repr(d) == "18446744073709551610")
doAssert(repr(e) == "1234567")
block floats:
let
a: float32 = 3.4e38'f32
b: float64 = 1.7976931348623157e308'f64
c: float = 1234.567e89
when defined js:
doAssert(repr(a) == "3.4e+38") # in C: 3.399999952144364e+038
doAssert(repr(b) == "1.7976931348623157e+308") # in C: 1.797693134862316e+308
doAssert(repr(c) == "1.234567e+92") # in C: 1.234567e+092
block bools:
let
a: bool = true
b: bool = false
doAssert(repr(a) == "true")
doAssert(repr(b) == "false")
block enums:
type
AnEnum = enum
aeA
aeB
aeC
HoledEnum = enum
heA = -12
heB = 15
heC = 123
doAssert(repr(aeA) == "aeA")
doAssert(repr(aeB) == "aeB")
doAssert(repr(aeC) == "aeC")
doAssert(repr(heA) == "heA")
doAssert(repr(heB) == "heB")
doAssert(repr(heC) == "heC")
block chars:
let
a = 'a'
b = 'z'
one = '1'
nl = '\x0A'
doAssert(repr(a) == "'a'")
doAssert(repr(b) == "'z'")
doAssert(repr(one) == "'1'")
doAssert(repr(nl) == "'\\10'")
block strings:
let
a: string = "12345"
b: string = "hello,repr"
c: string = "hi\nthere"
when defined js: # C prepends the pointer, JS does not.
doAssert(repr(a) == "\"12345\"")
doAssert(repr(b) == "\"hello,repr\"")
doAssert(repr(c) == "\"hi\nthere\"")
block sets:
let
a: set[int16] = {1'i16, 2'i16, 3'i16}
b: set[char] = {'A', 'k'}
doAssert(repr(a) == "{1, 2, 3}")
doAssert(repr(b) == "{'A', 'k'}")
block ranges:
let
a: range[0..12] = 6
b: range[-12..0] = -6
doAssert(repr(a) == "6")
doAssert(repr(b) == "-6")
block tuples:
type
ATuple = tuple
a: int
b: float
c: string
d: OtherTuple
OtherTuple = tuple
a: bool
b: int8
let
ot: OtherTuple = (a: true, b: 120'i8)
t: ATuple = (a: 42, b: 12.34, c: "tuple", d: ot)
when defined js:
doAssert(repr(ot) == """
[Field0 = true,
Field1 = 120]
""")
doAssert(repr(t) == """
[Field0 = 42,
Field1 = 12.34,
Field2 = "tuple",
Field3 = [Field0 = true,
Field1 = 120]]
""")
block objects:
type
AnObj = object
a: int
b: float
c: OtherObj
OtherObj = object
a: bool
b: int8
let
oo: OtherObj = OtherObj(a: true, b: 120'i8)
o: AnObj = AnObj(a: 42, b: 12.34, c: oo)
doAssert(repr(oo) == """
[a = true,
b = 120]
""")
doAssert(repr(o) == """
[a = 42,
b = 12.34,
c = [a = true,
b = 120]]
""")
block arrays:
type
AObj = object
x: int
y: array[3,float]
let
a = [0.0, 1, 2]
b = [a, a, a]
o = AObj(x: 42, y: a)
c = [o, o, o]
d = ["hi", "array", "!"]
doAssert(repr(a) == "[0.0, 1.0, 2.0]\n")
doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]\n")
doAssert(repr(c) == """
[[x = 42,
y = [0.0, 1.0, 2.0]], [x = 42,
y = [0.0, 1.0, 2.0]], [x = 42,
y = [0.0, 1.0, 2.0]]]
""")
doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]\n")
block seqs:
type
AObj = object
x: int
y: seq[float]
let
a = @[0.0, 1, 2]
b = @[a, a, a]
o = AObj(x: 42, y: a)
c = @[o, o, o]
d = @["hi", "array", "!"]
doAssert(repr(a) == "@[0.0, 1.0, 2.0]\n")
doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]\n")
doAssert(repr(c) == """
@[[x = 42,
y = @[0.0, 1.0, 2.0]], [x = 42,
y = @[0.0, 1.0, 2.0]], [x = 42,
y = @[0.0, 1.0, 2.0]]]
""")
doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]\n")
block ptrs:
type
AObj = object
x: ptr array[2, AObj]
y: int
var
a = [12.0, 13.0, 14.0]
b = addr a[0]
c = addr a[2]
d = AObj()
doAssert(repr(a) == "[12.0, 13.0, 14.0]\n")
doAssert(repr(b) == "ref 0 --> 12.0\n")
doAssert(repr(c) == "ref 2 --> 14.0\n")
doAssert(repr(d) == """
[x = nil,
y = 0]
""")
block ptrs:
type
AObj = object
x: ref array[2, AObj]
y: int
var
a = AObj()
new(a.x)
doAssert(repr(a) == """
[x = ref 0 --> [[x = nil,
y = 0], [x = nil,
y = 0]],
y = 0]
""")
block procs:
proc test(): int =
echo "hello"
var
ptest = test
nilproc: proc(): int
doAssert(repr(test) == "0\n")
doAssert(repr(ptest) == "0\n")
doAssert(repr(nilproc) == "nil\n")
block bunch:
type
AnEnum = enum
eA, eB, eC
B = object
a: string
b: seq[char]
A = object
a: uint32
b: int
c: float
d: char
e: AnEnum
f: string
g: set[char]
h: set[int16]
i: array[3,string]
j: seq[string]
k: range[-12..12]
l: B
m: ref B
n: ptr B
o: tuple[x: B, y: string]
p: proc(b: B): ref B
q: cstring
proc refB(b:B):ref B =
new result
result[] = b
var
aa: A
bb: B = B(a: "inner", b: @['o', 'b', 'j'])
cc: A = A(a: 12, b: 1, c: 1.2, d: '\0', e: eC,
f: "hello", g: {'A'}, h: {2'i16},
i: ["hello", "world", "array"],
j: @["hello", "world", "seq"], k: -1,
l: bb, m: refB(bb), n: addr bb,
o: (bb, "tuple!"), p: refB, q: "cstringtest" )
doAssert(repr(aa) == """
[a = 0,
b = 0,
c = 0.0,
d = '\0',
e = eA,
f = nil,
g = {},
h = {},
i = [nil, nil, nil],
j = nil,
k = 0,
l = [a = nil,
b = nil],
m = nil,
n = nil,
o = [Field0 = [a = nil,
b = nil],
Field1 = nil],
p = nil,
q = nil]
""")
doAssert(repr(cc) == """
[a = 12,
b = 1,
c = 1.2,
d = '\0',
e = eC,
f = "hello",
g = {'A'},
h = {2},
i = ["hello", "world", "array"],
j = @["hello", "world", "seq"],
k = -1,
l = [a = "inner",
b = @['o', 'b', 'j']],
m = ref 0 --> [a = "inner",
b = @['o', 'b', 'j']],
n = ref 0 --> [a = "inner",
b = @['o', 'b', 'j']],
o = [Field0 = [a = "inner",
b = @['o', 'b', 'j']],
Field1 = "tuple!"],
p = 0,
q = "cstringtest"]
""")
block another:
type
Size1 = enum
s1a, s1b
Size2 = enum
s2c=0, s2d=20000
Size3 = enum
s3e=0, s3f=2000000000
doAssert(repr([s1a, s1b]) == "[s1a, s1b]\n")
doAssert(repr([s2c, s2d]) == "[s2c, s2d]\n")
doAssert(repr([s3e, s3f]) == "[s3e, s3f]\n")
block another2:
type
AnEnum = enum
en1, en2, en3, en4, en5, en6
Point {.final.} = object
x, y, z: int
s: array[0..1, string]
e: AnEnum
var
p: Point
q: ref Point
s: seq[ref Point]
p.x = 0
p.y = 13
p.z = 45
p.s[0] = "abc"
p.s[1] = "xyz"
p.e = en6
new(q)
q[] = p
s = @[q, q, q, q]
doAssert(repr(p) == """
[x = 0,
y = 13,
z = 45,
s = ["abc", "xyz"],
e = en6]
""")
doAssert(repr(q) == """
ref 0 --> [x = 0,
y = 13,
z = 45,
s = ["abc", "xyz"],
e = en6]
""")
doAssert(repr(s) == """
@[ref 0 --> [x = 0,
y = 13,
z = 45,
s = ["abc", "xyz"],
e = en6], ref 1 --> [x = 0,
y = 13,
z = 45,
s = ["abc", "xyz"],
e = en6], ref 2 --> [x = 0,
y = 13,
z = 45,
s = ["abc", "xyz"],
e = en6], ref 3 --> [x = 0,
y = 13,
z = 45,
s = ["abc", "xyz"],
e = en6]]
""")
doAssert(repr(en4) == "en4")
doAssert(repr({'a'..'p'}) == "{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}")

View File

@@ -45,11 +45,12 @@ Changes affecting backwards compatibility
- If the dispatcher parameter's value used in multi method is ``nil``,
a ``NilError`` exception is raised. The old behavior was that the method
would be a ``nop`` then.
- ``posix.nim``: the family of ``ntohs`` procs now takes unsigned integers
instead of signed integers.
- In Nim identifiers en-dash (Unicode point U+2013) is not an alias for the
underscore anymore. Use underscores and fix your programming font instead.
Library Additions
-----------------