Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
Conflicts: lib/system/jssys.nim
This commit is contained in:
commit
10768904eb
36 changed files with 948 additions and 413 deletions
|
|
@ -263,33 +263,33 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
|
||||||
|
|
||||||
|
|
||||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
# This is called by return and break stmts.
|
# Called by return and break stmts.
|
||||||
# When jumping out of try/except/finally stmts,
|
# Deals with issues faced when jumping out of try/except/finally stmts,
|
||||||
# we need to pop safe points from try statements,
|
|
||||||
# execute finally-stmts, and pop exceptions
|
|
||||||
# from except stmts
|
|
||||||
|
|
||||||
let L = p.nestedTryStmts.len
|
|
||||||
|
|
||||||
# danger of endless recursion! we workaround this here by a temp stack
|
|
||||||
var stack: seq[PNode]
|
var stack: seq[PNode]
|
||||||
newSeq(stack, howManyTrys)
|
newSeq(stack, 0)
|
||||||
for i in countup(1, howManyTrys):
|
|
||||||
stack[i-1] = p.nestedTryStmts[L-i]
|
|
||||||
setLen(p.nestedTryStmts, L-howManyTrys)
|
|
||||||
|
|
||||||
var alreadyPoppedCnt = p.inExceptBlock
|
var alreadyPoppedCnt = p.inExceptBlock
|
||||||
for tryStmt in items(stack):
|
for i in countup(1, howManyTrys):
|
||||||
|
|
||||||
if gCmd != cmdCompileToCpp:
|
if gCmd != cmdCompileToCpp:
|
||||||
|
# Pop safe points generated by try
|
||||||
if alreadyPoppedCnt > 0:
|
if alreadyPoppedCnt > 0:
|
||||||
dec alreadyPoppedCnt
|
dec alreadyPoppedCnt
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||||
# Find finally-stmts for this try-stmt
|
|
||||||
# and generate a copy of the finally stmts here
|
# Pop this try-stmt of the list of nested trys
|
||||||
|
# so we don't infinite recurse on it in the next step.
|
||||||
|
var tryStmt = p.nestedTryStmts.pop
|
||||||
|
stack.add(tryStmt)
|
||||||
|
|
||||||
|
# Find finally-stmt for this try-stmt
|
||||||
|
# and generate a copy of its sons
|
||||||
var finallyStmt = lastSon(tryStmt)
|
var finallyStmt = lastSon(tryStmt)
|
||||||
if finallyStmt.kind == nkFinally:
|
if finallyStmt.kind == nkFinally:
|
||||||
genStmts(p, finallyStmt.sons[0])
|
genStmts(p, finallyStmt.sons[0])
|
||||||
|
|
||||||
# push old elements again:
|
# push old elements again:
|
||||||
for i in countdown(howManyTrys-1, 0):
|
for i in countdown(howManyTrys-1, 0):
|
||||||
p.nestedTryStmts.add(stack[i])
|
p.nestedTryStmts.add(stack[i])
|
||||||
|
|
@ -304,7 +304,14 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
p.beforeRetNeeded = true
|
p.beforeRetNeeded = true
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
|
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
|
||||||
blockLeaveActions(p, min(1, p.nestedTryStmts.len), p.inExceptBlock)
|
blockLeaveActions(p,
|
||||||
|
howManyTrys = p.nestedTryStmts.len,
|
||||||
|
howManyExcepts = p.inExceptBlock)
|
||||||
|
if (p.finallySafePoints.len > 0):
|
||||||
|
# If we're in a finally block, and we came here by exception
|
||||||
|
# consume it before we return.
|
||||||
|
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
|
||||||
|
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
|
||||||
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
||||||
|
|
||||||
proc genComputedGoto(p: BProc; n: PNode) =
|
proc genComputedGoto(p: BProc; n: PNode) =
|
||||||
|
|
@ -843,7 +850,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
discard pop(p.nestedTryStmts)
|
discard pop(p.nestedTryStmts)
|
||||||
endBlock(p) # end of else block
|
endBlock(p) # end of else block
|
||||||
if i < length and t.sons[i].kind == nkFinally:
|
if i < length and t.sons[i].kind == nkFinally:
|
||||||
|
p.finallySafePoints.add(safePoint)
|
||||||
exprBlock(p, t.sons[i].sons[0], d)
|
exprBlock(p, t.sons[i].sons[0], d)
|
||||||
|
discard pop(p.finallySafePoints)
|
||||||
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
|
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
|
||||||
|
|
||||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
||||||
|
|
|
||||||
|
|
@ -962,8 +962,8 @@ proc genMainProc(m: BModule) =
|
||||||
NimMainBody =
|
NimMainBody =
|
||||||
"N_CDECL(void, NimMain)(void) {$N" &
|
"N_CDECL(void, NimMain)(void) {$N" &
|
||||||
"\tPreMain();$N" &
|
"\tPreMain();$N" &
|
||||||
"$1$N" &
|
"$1" &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
PosixNimMain =
|
PosixNimMain =
|
||||||
"int cmdCount;$N" &
|
"int cmdCount;$N" &
|
||||||
|
|
@ -977,20 +977,20 @@ proc genMainProc(m: BModule) =
|
||||||
"\tcmdCount = argc;$N" &
|
"\tcmdCount = argc;$N" &
|
||||||
"\tgEnv = env;$N" &
|
"\tgEnv = env;$N" &
|
||||||
MainProcsWithResult &
|
MainProcsWithResult &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
StandaloneCMain =
|
StandaloneCMain =
|
||||||
"int main(void) {$N" &
|
"int main(void) {$N" &
|
||||||
MainProcs &
|
MainProcs &
|
||||||
"\treturn 0;$N" &
|
"\treturn 0;$N" &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
WinNimMain = NimMainBody
|
WinNimMain = NimMainBody
|
||||||
|
|
||||||
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
|
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
|
||||||
" HINSTANCE hPrevInstance, $N" &
|
" HINSTANCE hPrevInstance, $N" &
|
||||||
" LPSTR lpCmdLine, int nCmdShow) {$N" &
|
" LPSTR lpCmdLine, int nCmdShow) {$N" &
|
||||||
MainProcsWithResult & "}$N"
|
MainProcsWithResult & "}$N$N"
|
||||||
|
|
||||||
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
|
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
|
||||||
|
|
||||||
|
|
@ -998,14 +998,14 @@ proc genMainProc(m: BModule) =
|
||||||
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
|
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
|
||||||
" LPVOID lpvReserved) {$N" &
|
" LPVOID lpvReserved) {$N" &
|
||||||
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
|
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
|
||||||
"\treturn 1;$N}$N"
|
"\treturn 1;$N}$N$N"
|
||||||
|
|
||||||
PosixNimDllMain = WinNimDllMain
|
PosixNimDllMain = WinNimDllMain
|
||||||
|
|
||||||
PosixCDllMain =
|
PosixCDllMain =
|
||||||
"void NIM_POSIX_INIT NimMainInit(void) {$N" &
|
"void NIM_POSIX_INIT NimMainInit(void) {$N" &
|
||||||
MainProcs &
|
MainProcs &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
var nimMain, otherMain: TFormatStr
|
var nimMain, otherMain: TFormatStr
|
||||||
if platform.targetOS == osWindows and
|
if platform.targetOS == osWindows and
|
||||||
|
|
@ -1034,7 +1034,7 @@ proc genMainProc(m: BModule) =
|
||||||
platform.targetOS == osStandalone: "".toRope
|
platform.targetOS == osStandalone: "".toRope
|
||||||
else: ropecg(m, "\t#initStackBottom();$N")
|
else: ropecg(m, "\t#initStackBottom();$N")
|
||||||
inc(m.labels)
|
inc(m.labels)
|
||||||
appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N", [
|
appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N$N", [
|
||||||
mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
|
mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
|
||||||
|
|
||||||
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
|
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,13 @@ type
|
||||||
prc*: PSym # the Nimrod proc that this C proc belongs to
|
prc*: PSym # the Nimrod proc that this C proc belongs to
|
||||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||||
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
||||||
# (the vars must be volatile then)
|
# (the vars must be volatile then)
|
||||||
inExceptBlock*: int # are we currently inside an except block?
|
inExceptBlock*: int # are we currently inside an except block?
|
||||||
# leaving such scopes by raise or by return must
|
# leaving such scopes by raise or by return must
|
||||||
# execute any applicable finally blocks
|
# execute any applicable finally blocks
|
||||||
|
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
|
||||||
|
# using return in finally statements
|
||||||
labels*: Natural # for generating unique labels in the C proc
|
labels*: Natural # for generating unique labels in the C proc
|
||||||
blocks*: seq[TBlock] # nested blocks
|
blocks*: seq[TBlock] # nested blocks
|
||||||
breakIdx*: int # the block that will be exited
|
breakIdx*: int # the block that will be exited
|
||||||
|
|
@ -142,6 +144,7 @@ proc newProc*(prc: PSym, module: BModule): BProc =
|
||||||
else: result.options = gOptions
|
else: result.options = gOptions
|
||||||
newSeq(result.blocks, 1)
|
newSeq(result.blocks, 1)
|
||||||
result.nestedTryStmts = @[]
|
result.nestedTryStmts = @[]
|
||||||
|
result.finallySafePoints = @[]
|
||||||
|
|
||||||
iterator cgenModules*: var BModule =
|
iterator cgenModules*: var BModule =
|
||||||
for i in 0..high(gModules):
|
for i in 0..high(gModules):
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ type
|
||||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
|
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
|
||||||
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
||||||
|
errMacroBodyDependsOnGenericTypes,
|
||||||
errDestructorNotGenericEnough,
|
errDestructorNotGenericEnough,
|
||||||
|
|
||||||
errXExpectsTwoArguments,
|
errXExpectsTwoArguments,
|
||||||
|
|
@ -104,6 +105,7 @@ type
|
||||||
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
|
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
|
||||||
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
||||||
errXCannotBeClosure, errXMustBeCompileTime,
|
errXCannotBeClosure, errXMustBeCompileTime,
|
||||||
|
errCannotInferTypeOfTheLiteral,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||||
|
|
@ -325,6 +327,8 @@ const
|
||||||
errInstantiateXExplicitely: "instantiate '$1' explicitely",
|
errInstantiateXExplicitely: "instantiate '$1' explicitely",
|
||||||
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
||||||
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
||||||
|
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
|
||||||
|
"because the parameter '$1' has a generic type",
|
||||||
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
||||||
"A destructor must be associated will all instantiations of a generic type",
|
"A destructor must be associated will all instantiations of a generic type",
|
||||||
errXExpectsTwoArguments: "\'$1\' expects two arguments",
|
errXExpectsTwoArguments: "\'$1\' expects two arguments",
|
||||||
|
|
@ -348,6 +352,7 @@ const
|
||||||
errIllegalCaptureX: "illegal capture '$1'",
|
errIllegalCaptureX: "illegal capture '$1'",
|
||||||
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
|
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
|
||||||
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
|
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
|
||||||
|
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,25 @@ proc makeExternImport(s: PSym, extname: string) =
|
||||||
incl(s.flags, sfImportc)
|
incl(s.flags, sfImportc)
|
||||||
excl(s.flags, sfForward)
|
excl(s.flags, sfForward)
|
||||||
|
|
||||||
proc makeExternExport(s: PSym, extname: string) =
|
const invalidIdentChars = AllChars - IdentChars
|
||||||
|
|
||||||
|
proc validateExternCName(s: PSym, info: TLineInfo) =
|
||||||
|
## Validates that the symbol name in s.loc.r is a valid C identifier.
|
||||||
|
##
|
||||||
|
## Valid identifiers are those alphanumeric including the underscore not
|
||||||
|
## starting with a number. If the check fails, a generic error will be
|
||||||
|
## displayed to the user.
|
||||||
|
let target = ropeToStr(s.loc.r)
|
||||||
|
if target.len < 1 or (not (target[0] in IdentStartChars)) or
|
||||||
|
(not target.allCharsInSet(IdentChars)):
|
||||||
|
localError(info, errGenerated, "invalid exported symbol")
|
||||||
|
|
||||||
|
proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
|
||||||
setExternName(s, extname)
|
setExternName(s, extname)
|
||||||
|
case gCmd
|
||||||
|
of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC:
|
||||||
|
validateExternCName(s, info)
|
||||||
|
else: discard
|
||||||
incl(s.flags, sfExportc)
|
incl(s.flags, sfExportc)
|
||||||
|
|
||||||
proc processImportCompilerProc(s: PSym, extname: string) =
|
proc processImportCompilerProc(s: PSym, extname: string) =
|
||||||
|
|
@ -515,7 +532,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
if k in validPragmas:
|
if k in validPragmas:
|
||||||
case k
|
case k
|
||||||
of wExportc:
|
of wExportc:
|
||||||
makeExternExport(sym, getOptionalStr(c, it, "$1"))
|
makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
|
||||||
incl(sym.flags, sfUsed) # avoid wrong hints
|
incl(sym.flags, sfUsed) # avoid wrong hints
|
||||||
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
|
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
|
||||||
of wImportCompilerProc:
|
of wImportCompilerProc:
|
||||||
|
|
@ -603,7 +620,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
processDynLib(c, it, sym)
|
processDynLib(c, it, sym)
|
||||||
of wCompilerproc:
|
of wCompilerproc:
|
||||||
noVal(it) # compilerproc may not get a string!
|
noVal(it) # compilerproc may not get a string!
|
||||||
makeExternExport(sym, "$1")
|
makeExternExport(sym, "$1", it.info)
|
||||||
incl(sym.flags, sfCompilerProc)
|
incl(sym.flags, sfCompilerProc)
|
||||||
incl(sym.flags, sfUsed) # suppress all those stupid warnings
|
incl(sym.flags, sfUsed) # suppress all those stupid warnings
|
||||||
registerCompilerProc(sym)
|
registerCompilerProc(sym)
|
||||||
|
|
|
||||||
|
|
@ -143,10 +143,11 @@ proc discardCheck(c: PContext, result: PNode) =
|
||||||
while n.kind in skipForDiscardable:
|
while n.kind in skipForDiscardable:
|
||||||
n = n.lastSon
|
n = n.lastSon
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
elif c.inTypeClass > 0 and result.typ.kind == tyBool:
|
elif c.inTypeClass > 0:
|
||||||
let verdict = semConstExpr(c, result)
|
if result.typ.kind == tyBool:
|
||||||
if verdict.intVal == 0:
|
let verdict = semConstExpr(c, result)
|
||||||
localError(result.info, "type class predicate failed")
|
if verdict.intVal == 0:
|
||||||
|
localError(result.info, "type class predicate failed")
|
||||||
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
||||||
if result.typ.kind == tyNil:
|
if result.typ.kind == tyNil:
|
||||||
fixNilType(result)
|
fixNilType(result)
|
||||||
|
|
@ -349,7 +350,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
# BUGFIX: ``fitNode`` is needed here!
|
# BUGFIX: ``fitNode`` is needed here!
|
||||||
# check type compability between def.typ and typ:
|
# check type compability between def.typ and typ:
|
||||||
if typ != nil: def = fitNode(c, typ, def)
|
if typ != nil: def = fitNode(c, typ, def)
|
||||||
else: typ = skipIntLit(def.typ)
|
else:
|
||||||
|
typ = skipIntLit(def.typ)
|
||||||
|
if typ.kind in {tySequence, tyArray, tySet} and
|
||||||
|
typ.lastSon.kind == tyEmpty:
|
||||||
|
localError(def.info, errCannotInferTypeOfTheLiteral,
|
||||||
|
($typ.kind).substr(2).toLower)
|
||||||
else:
|
else:
|
||||||
def = ast.emptyNode
|
def = ast.emptyNode
|
||||||
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
||||||
|
|
|
||||||
|
|
@ -653,6 +653,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
|
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
|
template maybeLift(typ: PType): expr =
|
||||||
|
let lifted = liftingWalk(typ)
|
||||||
|
(if lifted != nil: lifted else: typ)
|
||||||
|
|
||||||
template addImplicitGeneric(e: expr): expr =
|
template addImplicitGeneric(e: expr): expr =
|
||||||
addImplicitGenericImpl(e, paramTypId)
|
addImplicitGenericImpl(e, paramTypId)
|
||||||
|
|
||||||
|
|
@ -663,15 +667,19 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
# proc(a: expr{string}, b: expr{nkLambda})
|
# proc(a: expr{string}, b: expr{nkLambda})
|
||||||
# overload on compile time values and AST trees
|
# overload on compile time values and AST trees
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons))
|
let base = paramType.base.maybeLift
|
||||||
|
if base.isMetaType and procKind == skMacro:
|
||||||
|
localError(info, errMacroBodyDependsOnGenericTypes, paramName)
|
||||||
|
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
|
||||||
result.flags.incl tfHasStatic
|
result.flags.incl tfHasStatic
|
||||||
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if tfUnresolved notin paramType.flags:
|
if tfUnresolved notin paramType.flags:
|
||||||
# naked typedescs are not bindOnce types
|
# naked typedescs are not bindOnce types
|
||||||
if paramType.sonsLen == 0 and paramTypId != nil and
|
if paramType.base.kind == tyNone and paramTypId != nil and
|
||||||
paramTypId.id == typedescId.id: paramTypId = nil
|
paramTypId.id == typedescId.id: paramTypId = nil
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
|
result = addImplicitGeneric(
|
||||||
|
c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
|
||||||
|
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if paramType.sonsLen == 1:
|
if paramType.sonsLen == 1:
|
||||||
|
|
@ -705,7 +713,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
|
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
|
||||||
result = instGenericContainer(c, paramType.sym.info, result,
|
result = instGenericContainer(c, paramType.sym.info, result,
|
||||||
allowMetaTypes = true)
|
allowMetaTypes = true)
|
||||||
result.lastSon.shouldHaveMeta
|
|
||||||
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
||||||
result = addImplicitGeneric(result)
|
result = addImplicitGeneric(result)
|
||||||
|
|
||||||
|
|
@ -1011,7 +1018,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||||
of mVarargs: result = semVarargs(c, n, prev)
|
of mVarargs: result = semVarargs(c, n, prev)
|
||||||
of mExpr, mTypeDesc:
|
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||||
|
of mExpr:
|
||||||
result = semTypeNode(c, n.sons[0], nil)
|
result = semTypeNode(c, n.sons[0], nil)
|
||||||
if result != nil:
|
if result != nil:
|
||||||
result = copyType(result, getCurrOwner(), false)
|
result = copyType(result, getCurrOwner(), false)
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# is difficult to handle:
|
# is difficult to handle:
|
||||||
var body = t.sons[0]
|
var body = t.sons[0]
|
||||||
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
|
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
|
||||||
var header: PType = nil
|
var header: PType = t
|
||||||
# search for some instantiation here:
|
# search for some instantiation here:
|
||||||
if cl.allowMetaTypes:
|
if cl.allowMetaTypes:
|
||||||
result = PType(idTableGet(cl.localCache, t))
|
result = PType(idTableGet(cl.localCache, t))
|
||||||
|
|
@ -232,11 +232,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
if x.kind == tyGenericParam:
|
if x.kind == tyGenericParam:
|
||||||
x = lookupTypeVar(cl, x)
|
x = lookupTypeVar(cl, x)
|
||||||
if x != nil:
|
if x != nil:
|
||||||
if header == nil: header = instCopyType(cl, t)
|
if header == t: header = instCopyType(cl, t)
|
||||||
header.sons[i] = x
|
header.sons[i] = x
|
||||||
propagateToOwner(header, x)
|
propagateToOwner(header, x)
|
||||||
|
else:
|
||||||
|
propagateToOwner(header, x)
|
||||||
|
|
||||||
if header != nil:
|
if header != t:
|
||||||
# search again after first pass:
|
# search again after first pass:
|
||||||
result = searchInstTypes(header)
|
result = searchInstTypes(header)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
|
|
@ -244,6 +246,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
header = instCopyType(cl, t)
|
header = instCopyType(cl, t)
|
||||||
|
|
||||||
result = newType(tyGenericInst, t.sons[0].owner)
|
result = newType(tyGenericInst, t.sons[0].owner)
|
||||||
|
result.flags = header.flags
|
||||||
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
|
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
|
||||||
result.sons = @[header.sons[0]]
|
result.sons = @[header.sons[0]]
|
||||||
# ugh need another pass for deeply recursive generic types (e.g. PActor)
|
# ugh need another pass for deeply recursive generic types (e.g. PActor)
|
||||||
|
|
|
||||||
|
|
@ -900,20 +900,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
result = isNone
|
result = isNone
|
||||||
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if a.kind != tyTypeDesc: return isNone
|
|
||||||
|
|
||||||
var prev = PType(idTableGet(c.bindings, f))
|
var prev = PType(idTableGet(c.bindings, f))
|
||||||
if prev == nil:
|
if prev == nil:
|
||||||
|
# proc foo(T: typedesc, x: T)
|
||||||
|
# when `f` is an unresolved typedesc, `a` could be any
|
||||||
|
# type, so we should not perform this check earlier
|
||||||
|
if a.kind != tyTypeDesc: return isNone
|
||||||
|
|
||||||
if f.base.kind == tyNone:
|
if f.base.kind == tyNone:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
else:
|
else:
|
||||||
result = typeRel(c, f.base, a.base)
|
result = typeRel(c, f.base, a.base)
|
||||||
|
|
||||||
if result != isNone:
|
if result != isNone:
|
||||||
put(c.bindings, f, a)
|
put(c.bindings, f, a)
|
||||||
else:
|
else:
|
||||||
let toMatch = if tfUnresolved in f.flags: a
|
if tfUnresolved in f.flags:
|
||||||
else: a.base
|
result = typeRel(c, prev.base, a)
|
||||||
result = typeRel(c, prev.base, toMatch)
|
elif a.kind == tyTypeDesc:
|
||||||
|
result = typeRel(c, prev.base, a.base)
|
||||||
|
else:
|
||||||
|
result = isNone
|
||||||
|
|
||||||
of tyStmt:
|
of tyStmt:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
|
|
@ -1022,6 +1029,28 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
|
|
||||||
r = typeRel(m, f, a)
|
r = typeRel(m, f, a)
|
||||||
|
|
||||||
|
if r != isNone and m.calleeSym != nil and
|
||||||
|
m.calleeSym.kind in {skMacro, skTemplate}:
|
||||||
|
# XXX: duplicating this is ugly, maybe we should move this
|
||||||
|
# directly into typeRel using return-like templates
|
||||||
|
case r
|
||||||
|
of isConvertible, isIntConv: inc(m.convMatches)
|
||||||
|
of isSubtype, isSubrange: inc(m.subtypeMatches)
|
||||||
|
of isGeneric, isInferred: inc(m.genericMatches)
|
||||||
|
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
|
||||||
|
of isFromIntLit: inc(m.intConvMatches, 256)
|
||||||
|
of isEqual: inc(m.exactMatches)
|
||||||
|
of isNone: discard
|
||||||
|
|
||||||
|
if f.kind == tyStmt and argOrig.kind == nkDo:
|
||||||
|
return argOrig[bodyPos]
|
||||||
|
elif f.kind == tyTypeDesc:
|
||||||
|
return arg
|
||||||
|
elif f.kind == tyStatic:
|
||||||
|
return arg.typ.n
|
||||||
|
else:
|
||||||
|
return argOrig
|
||||||
|
|
||||||
case r
|
case r
|
||||||
of isConvertible:
|
of isConvertible:
|
||||||
inc(m.convMatches)
|
inc(m.convMatches)
|
||||||
|
|
@ -1039,31 +1068,23 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
#result = copyTree(arg)
|
#result = copyTree(arg)
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
||||||
of isInferred, isInferredConvertible:
|
of isInferred, isInferredConvertible:
|
||||||
|
inc(m.genericMatches)
|
||||||
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
|
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
|
||||||
result = c.semInferredLambda(c, m.bindings, arg)
|
result = c.semInferredLambda(c, m.bindings, arg)
|
||||||
else:
|
else:
|
||||||
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
||||||
result = newSymNode(inferred, arg.info)
|
result = newSymNode(inferred, arg.info)
|
||||||
if r == isInferredConvertible:
|
if r == isInferredConvertible:
|
||||||
|
inc(m.convMatches)
|
||||||
result = implicitConv(nkHiddenStdConv, f, result, m, c)
|
result = implicitConv(nkHiddenStdConv, f, result, m, c)
|
||||||
of isGeneric:
|
of isGeneric:
|
||||||
inc(m.genericMatches)
|
inc(m.genericMatches)
|
||||||
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
|
result = copyTree(arg)
|
||||||
if f.kind == tyStmt and argOrig.kind == nkDo:
|
result.typ = getInstantiatedType(c, arg, m, f)
|
||||||
result = argOrig[bodyPos]
|
# BUG: f may not be the right key!
|
||||||
elif f.kind == tyTypeDesc:
|
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
||||||
result = arg
|
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
||||||
elif f.kind == tyStatic:
|
# BUGFIX: use ``result.typ`` and not `f` here
|
||||||
result = arg.typ.n
|
|
||||||
else:
|
|
||||||
result = argOrig
|
|
||||||
else:
|
|
||||||
result = copyTree(arg)
|
|
||||||
result.typ = getInstantiatedType(c, arg, m, f)
|
|
||||||
# BUG: f may not be the right key!
|
|
||||||
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
|
||||||
# BUGFIX: use ``result.typ`` and not `f` here
|
|
||||||
of isFromIntLit:
|
of isFromIntLit:
|
||||||
# too lazy to introduce another ``*matches`` field, so we conflate
|
# too lazy to introduce another ``*matches`` field, so we conflate
|
||||||
# ``isIntConv`` and ``isIntLit`` here:
|
# ``isIntConv`` and ``isIntLit`` here:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from posix import TSocketHandle
|
||||||
|
|
||||||
const
|
const
|
||||||
EPOLLIN* = 0x00000001
|
EPOLLIN* = 0x00000001
|
||||||
EPOLLPRI* = 0x00000002
|
EPOLLPRI* = 0x00000002
|
||||||
|
|
@ -33,8 +35,8 @@ const
|
||||||
type
|
type
|
||||||
epoll_data* {.importc: "union epoll_data",
|
epoll_data* {.importc: "union epoll_data",
|
||||||
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
||||||
thePtr* {.importc: "ptr".}: pointer # \
|
#thePtr* {.importc: "ptr".}: pointer
|
||||||
#fd*: cint
|
fd*: cint # \
|
||||||
#u32*: uint32
|
#u32*: uint32
|
||||||
#u64*: uint64
|
#u64*: uint64
|
||||||
|
|
||||||
|
|
@ -54,7 +56,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
|
||||||
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
||||||
## parameter has been dropped.
|
## parameter has been dropped.
|
||||||
|
|
||||||
proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {.
|
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {.
|
||||||
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
||||||
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
|
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
|
||||||
## -1 in case of error ( the "errno" variable will contain the
|
## -1 in case of error ( the "errno" variable will contain the
|
||||||
|
|
|
||||||
25
lib/posix/linux.nim
Normal file
25
lib/posix/linux.nim
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import posix
|
||||||
|
|
||||||
|
const
|
||||||
|
CSIGNAL* = 0x000000FF
|
||||||
|
CLONE_VM* = 0x00000100
|
||||||
|
CLONE_FS* = 0x00000200
|
||||||
|
CLONE_FILES* = 0x00000400
|
||||||
|
CLONE_SIGHAND* = 0x00000800
|
||||||
|
CLONE_PTRACE* = 0x00002000
|
||||||
|
CLONE_VFORK* = 0x00004000
|
||||||
|
CLONE_PARENT* = 0x00008000
|
||||||
|
CLONE_THREAD* = 0x00010000
|
||||||
|
CLONE_NEWNS* = 0x00020000
|
||||||
|
CLONE_SYSVSEM* = 0x00040000
|
||||||
|
CLONE_SETTLS* = 0x00080000
|
||||||
|
CLONE_PARENT_SETTID* = 0x00100000
|
||||||
|
CLONE_CHILD_CLEARTID* = 0x00200000
|
||||||
|
CLONE_DETACHED* = 0x00400000
|
||||||
|
CLONE_UNTRACED* = 0x00800000
|
||||||
|
CLONE_CHILD_SETTID* = 0x01000000
|
||||||
|
CLONE_STOPPED* = 0x02000000
|
||||||
|
|
||||||
|
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
||||||
|
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
||||||
|
arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
|
||||||
|
|
@ -2066,6 +2066,7 @@ proc pthread_spin_unlock*(a1: ptr Tpthread_spinlock): cint {.
|
||||||
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
||||||
|
|
||||||
|
|
||||||
|
proc exitnow*(code: int): void {.importc: "_exit", header: "<unistd.h>".}
|
||||||
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
||||||
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
||||||
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
||||||
|
|
@ -2356,7 +2357,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "<sys/select.h>".}
|
||||||
|
|
||||||
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
|
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
|
||||||
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
|
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
|
||||||
proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
|
proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
|
||||||
importc, header: "<sys/select.h>".}
|
importc, header: "<sys/select.h>".}
|
||||||
|
|
||||||
when hasSpawnH:
|
when hasSpawnH:
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@
|
||||||
|
|
||||||
import os, oids, tables, strutils, macros
|
import os, oids, tables, strutils, macros
|
||||||
|
|
||||||
import winlean
|
|
||||||
|
|
||||||
import sockets2, net
|
import sockets2, net
|
||||||
|
|
||||||
## Asyncio2
|
## Asyncio2
|
||||||
|
|
@ -93,7 +91,10 @@ proc failed*[T](future: PFuture[T]): bool =
|
||||||
## Determines whether ``future`` completed with an error.
|
## Determines whether ``future`` completed with an error.
|
||||||
future.error != nil
|
future.error != nil
|
||||||
|
|
||||||
when defined(windows):
|
# TODO: Get rid of register. Do it implicitly.
|
||||||
|
|
||||||
|
when defined(windows) or defined(nimdoc):
|
||||||
|
import winlean
|
||||||
type
|
type
|
||||||
TCompletionKey = dword
|
TCompletionKey = dword
|
||||||
|
|
||||||
|
|
@ -293,7 +294,10 @@ when defined(windows):
|
||||||
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
|
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
|
||||||
flags: int = 0): PFuture[string] =
|
flags: int = 0): PFuture[string] =
|
||||||
## Reads ``size`` bytes from ``socket``. Returned future will complete once
|
## Reads ``size`` bytes from ``socket``. Returned future will complete once
|
||||||
## all of the requested data is read.
|
## all of the requested data is read. If socket is disconnected during the
|
||||||
|
## recv operation then the future may complete with only a part of the
|
||||||
|
## requested data read. If socket is disconnected and no data is available
|
||||||
|
## to be read then the future will complete with a value of ``""``.
|
||||||
|
|
||||||
var retFuture = newFuture[string]()
|
var retFuture = newFuture[string]()
|
||||||
|
|
||||||
|
|
@ -448,24 +452,206 @@ when defined(windows):
|
||||||
|
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
|
|
||||||
## Accepts a new connection. Returns a future containing the client socket
|
|
||||||
## corresponding to that connection.
|
|
||||||
## The future will complete when the connection is successfully accepted.
|
|
||||||
var retFut = newFuture[TSocketHandle]()
|
|
||||||
var fut = p.acceptAddr(socket)
|
|
||||||
fut.callback =
|
|
||||||
proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
|
|
||||||
assert future.finished
|
|
||||||
if future.failed:
|
|
||||||
retFut.fail(future.error)
|
|
||||||
else:
|
|
||||||
retFut.complete(future.read.client)
|
|
||||||
return retFut
|
|
||||||
|
|
||||||
initAll()
|
initAll()
|
||||||
else:
|
else:
|
||||||
# TODO: Selectors.
|
import selectors
|
||||||
|
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
|
||||||
|
type
|
||||||
|
TCallback = proc (sock: TSocketHandle): bool {.closure.}
|
||||||
|
|
||||||
|
PData* = ref object of PObject
|
||||||
|
sock: TSocketHandle
|
||||||
|
readCBs: seq[TCallback]
|
||||||
|
writeCBs: seq[TCallback]
|
||||||
|
|
||||||
|
PDispatcher* = ref object
|
||||||
|
selector: PSelector
|
||||||
|
|
||||||
|
proc newDispatcher*(): PDispatcher =
|
||||||
|
new result
|
||||||
|
result.selector = newSelector()
|
||||||
|
|
||||||
|
proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
|
||||||
|
assert sock in p.selector
|
||||||
|
echo("Update: ", events)
|
||||||
|
if events == {}:
|
||||||
|
discard p.selector.unregister(sock)
|
||||||
|
else:
|
||||||
|
discard p.selector.update(sock, events)
|
||||||
|
|
||||||
|
proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
|
||||||
|
if sock notin p.selector:
|
||||||
|
var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[])
|
||||||
|
p.selector.register(sock, {EvRead}, data.PObject)
|
||||||
|
else:
|
||||||
|
p.selector[sock].data.PData.readCBs.add(cb)
|
||||||
|
p.update(sock, p.selector[sock].events + {EvRead})
|
||||||
|
|
||||||
|
proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
|
||||||
|
if sock notin p.selector:
|
||||||
|
var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb])
|
||||||
|
p.selector.register(sock, {EvWrite}, data.PObject)
|
||||||
|
else:
|
||||||
|
p.selector[sock].data.PData.writeCBs.add(cb)
|
||||||
|
p.update(sock, p.selector[sock].events + {EvWrite})
|
||||||
|
|
||||||
|
proc poll*(p: PDispatcher, timeout = 500) =
|
||||||
|
for info in p.selector.select(timeout):
|
||||||
|
let data = PData(info.key.data)
|
||||||
|
assert data.sock == info.key.fd
|
||||||
|
echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events)
|
||||||
|
|
||||||
|
if EvRead in info.events:
|
||||||
|
var newReadCBs: seq[TCallback] = @[]
|
||||||
|
for cb in data.readCBs:
|
||||||
|
if not cb(data.sock):
|
||||||
|
# Callback wants to be called again.
|
||||||
|
newReadCBs.add(cb)
|
||||||
|
data.readCBs = newReadCBs
|
||||||
|
|
||||||
|
if EvWrite in info.events:
|
||||||
|
var newWriteCBs: seq[TCallback] = @[]
|
||||||
|
for cb in data.writeCBs:
|
||||||
|
if not cb(data.sock):
|
||||||
|
# Callback wants to be called again.
|
||||||
|
newWriteCBs.add(cb)
|
||||||
|
data.writeCBs = newWriteCBs
|
||||||
|
|
||||||
|
var newEvents: set[TEvent]
|
||||||
|
if data.readCBs.len != 0: newEvents = {EvRead}
|
||||||
|
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
|
||||||
|
p.update(data.sock, newEvents)
|
||||||
|
|
||||||
|
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
||||||
|
af = AF_INET): PFuture[int] =
|
||||||
|
var retFuture = newFuture[int]()
|
||||||
|
|
||||||
|
proc cb(sock: TSocketHandle): bool =
|
||||||
|
# We have connected.
|
||||||
|
retFuture.complete(0)
|
||||||
|
return true
|
||||||
|
|
||||||
|
var aiList = getAddrInfo(address, port, af)
|
||||||
|
var success = false
|
||||||
|
var lastError: TOSErrorCode
|
||||||
|
var it = aiList
|
||||||
|
while it != nil:
|
||||||
|
var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen)
|
||||||
|
if ret == 0:
|
||||||
|
# Request to connect completed immediately.
|
||||||
|
success = true
|
||||||
|
retFuture.complete(0)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lastError = osLastError()
|
||||||
|
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||||
|
success = true
|
||||||
|
addWrite(p, socket, cb)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
success = false
|
||||||
|
it = it.ai_next
|
||||||
|
|
||||||
|
dealloc(aiList)
|
||||||
|
if not success:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
|
||||||
|
flags: int = 0): PFuture[string] =
|
||||||
|
var retFuture = newFuture[string]()
|
||||||
|
|
||||||
|
var readBuffer = newString(size)
|
||||||
|
var sizeRead = 0
|
||||||
|
|
||||||
|
proc cb(sock: TSocketHandle): bool =
|
||||||
|
result = true
|
||||||
|
let netSize = size - sizeRead
|
||||||
|
let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint)
|
||||||
|
if res < 0:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
result = false # We still want this callback to be called.
|
||||||
|
elif res == 0:
|
||||||
|
# Disconnected
|
||||||
|
if sizeRead == 0:
|
||||||
|
retFuture.complete("")
|
||||||
|
else:
|
||||||
|
readBuffer.setLen(sizeRead)
|
||||||
|
retFuture.complete(readBuffer)
|
||||||
|
else:
|
||||||
|
sizeRead.inc(res)
|
||||||
|
if res != netSize:
|
||||||
|
result = false # We want to read all the data requested.
|
||||||
|
else:
|
||||||
|
retFuture.complete(readBuffer)
|
||||||
|
|
||||||
|
addRead(p, socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
||||||
|
var retFuture = newFuture[int]()
|
||||||
|
|
||||||
|
var written = 0
|
||||||
|
|
||||||
|
proc cb(sock: TSocketHandle): bool =
|
||||||
|
result = true
|
||||||
|
let netSize = data.len-written
|
||||||
|
var d = data.cstring
|
||||||
|
let res = send(sock, addr d[written], netSize, 0.cint)
|
||||||
|
if res < 0:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
result = false # We still want this callback to be called.
|
||||||
|
else:
|
||||||
|
written.inc(res)
|
||||||
|
if res != netSize:
|
||||||
|
result = false # We still have data to send.
|
||||||
|
else:
|
||||||
|
retFuture.complete(0)
|
||||||
|
addWrite(p, socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
|
||||||
|
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
|
||||||
|
PFuture[tuple[address: string, client: TSocketHandle]] =
|
||||||
|
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
|
||||||
|
proc cb(sock: TSocketHandle): bool =
|
||||||
|
result = true
|
||||||
|
var sockAddress: Tsockaddr_in
|
||||||
|
var addrLen = sizeof(sockAddress).TSocklen
|
||||||
|
var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)),
|
||||||
|
addr(addrLen))
|
||||||
|
if client == osInvalidSocket:
|
||||||
|
let lastError = osLastError()
|
||||||
|
assert lastError.int32 notin {EWOULDBLOCK, EAGAIN}
|
||||||
|
if lastError.int32 == EINTR:
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client))
|
||||||
|
addRead(p, socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
|
||||||
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
|
## corresponding to that connection.
|
||||||
|
## The future will complete when the connection is successfully accepted.
|
||||||
|
var retFut = newFuture[TSocketHandle]()
|
||||||
|
var fut = p.acceptAddr(socket)
|
||||||
|
fut.callback =
|
||||||
|
proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
|
||||||
|
assert future.finished
|
||||||
|
if future.failed:
|
||||||
|
retFut.fail(future.error)
|
||||||
|
else:
|
||||||
|
retFut.complete(future.read.client)
|
||||||
|
return retFut
|
||||||
|
|
||||||
# -- Await Macro
|
# -- Await Macro
|
||||||
|
|
||||||
|
|
@ -665,8 +851,7 @@ when isMainModule:
|
||||||
|
|
||||||
var p = newDispatcher()
|
var p = newDispatcher()
|
||||||
var sock = socket()
|
var sock = socket()
|
||||||
#sock.setBlocking false
|
sock.setBlocking false
|
||||||
p.register(sock)
|
|
||||||
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -706,7 +891,7 @@ when isMainModule:
|
||||||
var recvF = p.recv(sock, 10)
|
var recvF = p.recv(sock, 10)
|
||||||
recvF.callback =
|
recvF.callback =
|
||||||
proc (future: PFuture[string]) =
|
proc (future: PFuture[string]) =
|
||||||
echo("Read: ", future.read)
|
echo("Read ", future.read.len, ": ", future.read.repr)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,18 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
|
||||||
dealloc(aiList)
|
dealloc(aiList)
|
||||||
osError(osLastError())
|
osError(osLastError())
|
||||||
dealloc(aiList)
|
dealloc(aiList)
|
||||||
|
|
||||||
|
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} =
|
||||||
|
## Sets blocking mode on socket
|
||||||
|
when defined(Windows):
|
||||||
|
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||||
|
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
|
||||||
|
osError(osLastError())
|
||||||
|
else: # BSD sockets
|
||||||
|
var x: int = fcntl(s, F_GETFL, 0)
|
||||||
|
if x == -1:
|
||||||
|
osError(osLastError())
|
||||||
|
else:
|
||||||
|
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||||
|
if fcntl(s, F_SETFL, mode) == -1:
|
||||||
|
osError(osLastError())
|
||||||
|
|
@ -13,13 +13,16 @@
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, os, strtabs, streams, sequtils
|
strutils, os, strtabs, streams
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
import winlean
|
import winlean
|
||||||
else:
|
else:
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
|
when defined(linux):
|
||||||
|
import linux
|
||||||
|
|
||||||
type
|
type
|
||||||
TProcess = object of TObject
|
TProcess = object of TObject
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -44,7 +47,7 @@ type
|
||||||
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
||||||
poParentStreams ## use the parent's streams
|
poParentStreams ## use the parent's streams
|
||||||
|
|
||||||
template poUseShell*: TProcessOption {.deprecated.} = poUsePath
|
const poUseShell* {.deprecated.} = poUsePath
|
||||||
## Deprecated alias for poUsePath.
|
## Deprecated alias for poUsePath.
|
||||||
|
|
||||||
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
||||||
|
|
@ -590,6 +593,23 @@ elif not defined(useNimRtl):
|
||||||
copyMem(result[i], addr(x[0]), x.len+1)
|
copyMem(result[i], addr(x[0]), x.len+1)
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
|
type TStartProcessData = object
|
||||||
|
sysCommand: cstring
|
||||||
|
sysArgs: cstringArray
|
||||||
|
sysEnv: cstringArray
|
||||||
|
workingDir: cstring
|
||||||
|
pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
|
||||||
|
optionPoUsePath: bool
|
||||||
|
optionPoParentStreams: bool
|
||||||
|
optionPoStdErrToStdOut: bool
|
||||||
|
|
||||||
|
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
|
||||||
|
proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
|
||||||
|
{.push stacktrace: off, profiler: off.}
|
||||||
|
proc startProcessAfterFork(data: ptr TStartProcessData) {.
|
||||||
|
tags: [FExecIO, FReadEnv], cdecl.}
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc startProcess(command: string,
|
proc startProcess(command: string,
|
||||||
workingDir: string = "",
|
workingDir: string = "",
|
||||||
args: openArray[string] = [],
|
args: openArray[string] = [],
|
||||||
|
|
@ -604,100 +624,48 @@ elif not defined(useNimRtl):
|
||||||
pipe(pStderr) != 0'i32:
|
pipe(pStderr) != 0'i32:
|
||||||
osError(osLastError())
|
osError(osLastError())
|
||||||
|
|
||||||
var sys_command: string
|
var sysCommand: string
|
||||||
var sys_args_raw: seq[string]
|
var sysArgsRaw: seq[string]
|
||||||
if poEvalCommand in options:
|
if poEvalCommand in options:
|
||||||
sys_command = "/bin/sh"
|
sysCommand = "/bin/sh"
|
||||||
sys_args_raw = @[sys_command, "-c", command]
|
sysArgsRaw = @[sysCommand, "-c", command]
|
||||||
assert args.len == 0
|
assert args.len == 0
|
||||||
else:
|
else:
|
||||||
sys_command = command
|
sysCommand = command
|
||||||
sys_args_raw = @[command]
|
sysArgsRaw = @[command]
|
||||||
for arg in args.items:
|
for arg in args.items:
|
||||||
sys_args_raw.add arg
|
sysArgsRaw.add arg
|
||||||
|
|
||||||
var sys_args = allocCStringArray(sys_args_raw)
|
|
||||||
finally: deallocCStringArray(sys_args)
|
|
||||||
|
|
||||||
var pid: TPid
|
var pid: TPid
|
||||||
when defined(posix_spawn) and not defined(useFork):
|
|
||||||
var attr: Tposix_spawnattr
|
|
||||||
var fops: Tposix_spawn_file_actions
|
|
||||||
|
|
||||||
template chck(e: expr) =
|
var sysArgs = allocCStringArray(sysArgsRaw)
|
||||||
if e != 0'i32: osError(osLastError())
|
finally: deallocCStringArray(sysArgs)
|
||||||
|
|
||||||
chck posix_spawn_file_actions_init(fops)
|
var sysEnv = if env == nil:
|
||||||
chck posix_spawnattr_init(attr)
|
envToCStringArray()
|
||||||
|
|
||||||
var mask: Tsigset
|
|
||||||
chck sigemptyset(mask)
|
|
||||||
chck posix_spawnattr_setsigmask(attr, mask)
|
|
||||||
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
|
||||||
|
|
||||||
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
|
||||||
POSIX_SPAWN_SETSIGMASK or
|
|
||||||
POSIX_SPAWN_SETPGROUP)
|
|
||||||
|
|
||||||
if poParentStreams notin options:
|
|
||||||
chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx])
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx)
|
|
||||||
chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx])
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx)
|
|
||||||
chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx])
|
|
||||||
if poStdErrToStdOut in options:
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
|
|
||||||
else:
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
|
|
||||||
|
|
||||||
var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
|
|
||||||
var res: cint
|
|
||||||
# This is incorrect!
|
|
||||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
|
||||||
if poUsePath in options:
|
|
||||||
res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
|
|
||||||
else:
|
else:
|
||||||
res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
|
envToCStringArray(env)
|
||||||
deallocCStringArray(sys_env)
|
|
||||||
discard posix_spawn_file_actions_destroy(fops)
|
|
||||||
discard posix_spawnattr_destroy(attr)
|
|
||||||
chck res
|
|
||||||
|
|
||||||
|
finally: deallocCStringArray(sysEnv)
|
||||||
|
|
||||||
|
var data: TStartProcessData
|
||||||
|
data.sysCommand = sysCommand
|
||||||
|
data.sysArgs = sysArgs
|
||||||
|
data.sysEnv = sysEnv
|
||||||
|
data.pStdin = pStdin
|
||||||
|
data.pStdout = pStdout
|
||||||
|
data.pStderr = pStderr
|
||||||
|
data.optionPoParentStreams = poParentStreams in options
|
||||||
|
data.optionPoUsePath = poUsePath in options
|
||||||
|
data.optionPoStdErrToStdOut = poStdErrToStdOut in options
|
||||||
|
data.workingDir = workingDir
|
||||||
|
|
||||||
|
|
||||||
|
when defined(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux):
|
||||||
|
pid = startProcessAuxSpawn(data)
|
||||||
else:
|
else:
|
||||||
pid = fork()
|
pid = startProcessAuxFork(data)
|
||||||
if pid < 0: osError(osLastError())
|
|
||||||
if pid == 0:
|
|
||||||
## child process:
|
|
||||||
|
|
||||||
if poParentStreams notin options:
|
|
||||||
discard close(p_stdin[writeIdx])
|
|
||||||
if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError())
|
|
||||||
discard close(p_stdout[readIdx])
|
|
||||||
if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError())
|
|
||||||
discard close(p_stderr[readIdx])
|
|
||||||
if poStdErrToStdOut in options:
|
|
||||||
if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError())
|
|
||||||
else:
|
|
||||||
if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError())
|
|
||||||
|
|
||||||
# Create a new process group
|
|
||||||
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
|
|
||||||
|
|
||||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
|
||||||
|
|
||||||
if env == nil:
|
|
||||||
if poUsePath in options:
|
|
||||||
discard execvp(sys_command, sys_args)
|
|
||||||
else:
|
|
||||||
discard execv(sys_command, sys_args)
|
|
||||||
else:
|
|
||||||
var c_env = envToCStringArray(env)
|
|
||||||
if poUsePath in options:
|
|
||||||
discard execvpe(sys_command, sys_args, c_env)
|
|
||||||
else:
|
|
||||||
discard execve(sys_command, sys_args, c_env)
|
|
||||||
# too risky to raise an exception here:
|
|
||||||
quit("execve call failed: " & $strerror(errno))
|
|
||||||
# Parent process. Copy process information.
|
# Parent process. Copy process information.
|
||||||
if poEchoCmd in options:
|
if poEchoCmd in options:
|
||||||
echo(command, " ", join(args, " "))
|
echo(command, " ", join(args, " "))
|
||||||
|
|
@ -723,6 +691,137 @@ elif not defined(useNimRtl):
|
||||||
discard close(pStdin[readIdx])
|
discard close(pStdin[readIdx])
|
||||||
discard close(pStdout[writeIdx])
|
discard close(pStdout[writeIdx])
|
||||||
|
|
||||||
|
proc startProcessAuxSpawn(data: TStartProcessData): TPid =
|
||||||
|
var attr: Tposix_spawnattr
|
||||||
|
var fops: Tposix_spawn_file_actions
|
||||||
|
|
||||||
|
template chck(e: expr) =
|
||||||
|
if e != 0'i32: osError(osLastError())
|
||||||
|
|
||||||
|
chck posix_spawn_file_actions_init(fops)
|
||||||
|
chck posix_spawnattr_init(attr)
|
||||||
|
|
||||||
|
var mask: Tsigset
|
||||||
|
chck sigemptyset(mask)
|
||||||
|
chck posix_spawnattr_setsigmask(attr, mask)
|
||||||
|
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
||||||
|
|
||||||
|
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
||||||
|
POSIX_SPAWN_SETSIGMASK or
|
||||||
|
POSIX_SPAWN_SETPGROUP)
|
||||||
|
|
||||||
|
if not data.optionPoParentStreams:
|
||||||
|
chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
|
||||||
|
chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
|
||||||
|
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
|
||||||
|
if data.optionPoStdErrToStdOut:
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
|
||||||
|
else:
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
|
||||||
|
|
||||||
|
var res: cint
|
||||||
|
# FIXME: chdir is global to process
|
||||||
|
if data.workingDir.len > 0:
|
||||||
|
setCurrentDir($data.workingDir)
|
||||||
|
var pid: TPid
|
||||||
|
|
||||||
|
if data.optionPoUsePath:
|
||||||
|
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||||
|
else:
|
||||||
|
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||||
|
|
||||||
|
discard posix_spawn_file_actions_destroy(fops)
|
||||||
|
discard posix_spawnattr_destroy(attr)
|
||||||
|
chck res
|
||||||
|
return pid
|
||||||
|
|
||||||
|
proc startProcessAuxFork(data: TStartProcessData): TPid =
|
||||||
|
if pipe(data.pErrorPipe) != 0:
|
||||||
|
osError(osLastError())
|
||||||
|
|
||||||
|
finally:
|
||||||
|
discard close(data.pErrorPipe[readIdx])
|
||||||
|
|
||||||
|
var pid: TPid
|
||||||
|
var dataCopy = data
|
||||||
|
|
||||||
|
when defined(useClone):
|
||||||
|
const stackSize = 65536
|
||||||
|
let stackEnd = cast[clong](alloc(stackSize))
|
||||||
|
let stack = cast[pointer](stackEnd + stackSize)
|
||||||
|
let fn: pointer = startProcessAfterFork
|
||||||
|
pid = clone(fn, stack,
|
||||||
|
cint(CLONE_VM or CLONE_VFORK or SIGCHLD),
|
||||||
|
pointer(addr dataCopy), nil, nil, nil)
|
||||||
|
discard close(data.pErrorPipe[writeIdx])
|
||||||
|
dealloc(stack)
|
||||||
|
else:
|
||||||
|
pid = fork()
|
||||||
|
if pid == 0:
|
||||||
|
startProcessAfterFork(addr(dataCopy))
|
||||||
|
exitnow(1)
|
||||||
|
|
||||||
|
discard close(data.pErrorPipe[writeIdx])
|
||||||
|
if pid < 0: osError(osLastError())
|
||||||
|
|
||||||
|
var error: cint
|
||||||
|
let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
|
||||||
|
if sizeRead == sizeof(error):
|
||||||
|
osError($strerror(error))
|
||||||
|
|
||||||
|
return pid
|
||||||
|
|
||||||
|
{.push stacktrace: off, profiler: off.}
|
||||||
|
proc startProcessFail(data: ptr TStartProcessData) =
|
||||||
|
var error: cint = errno
|
||||||
|
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||||
|
exitnow(1)
|
||||||
|
|
||||||
|
when defined(macosx):
|
||||||
|
var environ {.importc.}: cstringArray
|
||||||
|
|
||||||
|
proc startProcessAfterFork(data: ptr TStartProcessData) =
|
||||||
|
# Warning: no GC here!
|
||||||
|
# Or anythink that touches global structures - all called nimrod procs
|
||||||
|
# must be marked with noStackFrame. Inspect C code after making changes.
|
||||||
|
if not data.optionPoParentStreams:
|
||||||
|
discard close(data.pStdin[writeIdx])
|
||||||
|
if dup2(data.pStdin[readIdx], readIdx) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
discard close(data.pStdout[readIdx])
|
||||||
|
if dup2(data.pStdout[writeIdx], writeIdx) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
discard close(data.pStderr[readIdx])
|
||||||
|
if data.optionPoStdErrToStdOut:
|
||||||
|
if dup2(data.pStdout[writeIdx], 2) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
else:
|
||||||
|
if dup2(data.pStderr[writeIdx], 2) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
|
||||||
|
if data.workingDir.len > 0:
|
||||||
|
if chdir(data.workingDir) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
|
||||||
|
discard close(data.pErrorPipe[readIdx])
|
||||||
|
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||||
|
|
||||||
|
if data.optionPoUsePath:
|
||||||
|
when defined(macosx):
|
||||||
|
# MacOSX doesn't have execvpe, so we need workaround.
|
||||||
|
# On MacOSX we can arrive here only from fork, so this is safe:
|
||||||
|
environ = data.sysEnv
|
||||||
|
discard execvp(data.sysCommand, data.sysArgs)
|
||||||
|
else:
|
||||||
|
discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
|
||||||
|
else:
|
||||||
|
discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
|
||||||
|
|
||||||
|
startProcessFail(data)
|
||||||
|
{.pop}
|
||||||
|
|
||||||
proc close(p: PProcess) =
|
proc close(p: PProcess) =
|
||||||
if p.inStream != nil: close(p.inStream)
|
if p.inStream != nil: close(p.inStream)
|
||||||
if p.outStream != nil: close(p.outStream)
|
if p.outStream != nil: close(p.outStream)
|
||||||
|
|
|
||||||
|
|
@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
c.ml = 0
|
c.ml = 0
|
||||||
var L = rawMatch(s, pattern, i, c)
|
var L = rawMatch(s, pattern, i, c)
|
||||||
if L < 0: break
|
if L < 0:
|
||||||
yield substr(s, i, i+L-1)
|
inc(i, 1)
|
||||||
inc(i, L)
|
else:
|
||||||
|
yield substr(s, i, i+L-1)
|
||||||
|
inc(i, L)
|
||||||
|
|
||||||
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2013 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -9,212 +9,211 @@
|
||||||
|
|
||||||
# TODO: Docs.
|
# TODO: Docs.
|
||||||
|
|
||||||
import tables, os, unsigned
|
import tables, os, unsigned, hashes
|
||||||
when defined(windows):
|
|
||||||
import winlean
|
when defined(linux): import posix, epoll
|
||||||
else:
|
elif defined(windows): import winlean
|
||||||
import posix
|
|
||||||
|
proc hash*(x: TSocketHandle): THash {.borrow.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TEvent* = enum
|
TEvent* = enum
|
||||||
EvRead, EvWrite
|
EvRead, EvWrite
|
||||||
|
|
||||||
TSelectorKey* = object
|
PSelectorKey* = ref object
|
||||||
fd: cint
|
fd*: TSocketHandle
|
||||||
events: set[TEvent]
|
events*: set[TEvent] ## The events which ``fd`` listens for.
|
||||||
data: PObject
|
data*: PObject ## User object.
|
||||||
|
|
||||||
TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
|
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
|
||||||
|
|
||||||
PSelector* = ref object of PObject ## Selector interface.
|
when defined(linux) or defined(nimdoc):
|
||||||
fds*: TTable[cint, TSelectorKey]
|
|
||||||
registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
|
|
||||||
data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
|
|
||||||
unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
|
|
||||||
selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
|
|
||||||
closeImpl*: proc (s: PSelector) {.nimcall.}
|
|
||||||
|
|
||||||
template initSelector(r: expr) =
|
|
||||||
new r
|
|
||||||
r.fds = initTable[cint, TSelectorKey]()
|
|
||||||
|
|
||||||
proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
|
|
||||||
TSelectorKey =
|
|
||||||
if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
|
|
||||||
|
|
||||||
proc unregister*(s: PSelector, fd: cint): TSelectorKey =
|
|
||||||
##
|
|
||||||
## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
|
|
||||||
## will only have the ``fd`` field set. This is an optimisation and may
|
|
||||||
## change in the future if a viable use case is presented.
|
|
||||||
if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
|
|
||||||
|
|
||||||
proc select*(s: PSelector, timeout = 500): 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``.
|
|
||||||
|
|
||||||
if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
|
|
||||||
|
|
||||||
proc close*(s: PSelector) =
|
|
||||||
if not s.closeImpl.isNil: s.closeImpl(s)
|
|
||||||
|
|
||||||
# ---- Select() ----------------------------------------------------------------
|
|
||||||
|
|
||||||
type
|
|
||||||
PSelectSelector* = ref object of PSelector ## Implementation of select()
|
|
||||||
|
|
||||||
proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
|
|
||||||
data: PObject): TSelectorKey =
|
|
||||||
if s.fds.hasKey(fd):
|
|
||||||
raise newException(EInvalidValue, "FD already exists in selector.")
|
|
||||||
var sk = TSelectorKey(fd: fd, events: events, data: data)
|
|
||||||
s.fds[fd] = sk
|
|
||||||
result = sk
|
|
||||||
|
|
||||||
proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
|
|
||||||
result = s.fds[fd]
|
|
||||||
s.fds.del(fd)
|
|
||||||
|
|
||||||
proc ssClose(s: PSelector) = nil
|
|
||||||
|
|
||||||
proc timeValFromMilliseconds(timeout: int): TTimeVal =
|
|
||||||
if timeout != -1:
|
|
||||||
var seconds = timeout div 1000
|
|
||||||
result.tv_sec = seconds.int32
|
|
||||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
|
||||||
|
|
||||||
proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
|
|
||||||
m: var int) =
|
|
||||||
FD_ZERO(rd); FD_ZERO(wr)
|
|
||||||
for k, v in pairs(fds):
|
|
||||||
if EvRead in v.events:
|
|
||||||
m = max(m, int(k))
|
|
||||||
FD_SET(k, rd)
|
|
||||||
if EvWrite in v.events:
|
|
||||||
m = max(m, int(k))
|
|
||||||
FD_SET(k, wr)
|
|
||||||
|
|
||||||
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
|
|
||||||
seq[TReadyInfo] =
|
|
||||||
result = @[]
|
|
||||||
for k, v in pairs(fds):
|
|
||||||
var events: set[TEvent] = {}
|
|
||||||
if FD_ISSET(k, rd) != 0'i32:
|
|
||||||
events = events + {EvRead}
|
|
||||||
if FD_ISSET(k, wr) != 0'i32:
|
|
||||||
events = events + {EvWrite}
|
|
||||||
result.add((v, events))
|
|
||||||
|
|
||||||
proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
|
|
||||||
seq[TReadyInfo] =
|
|
||||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
|
||||||
|
|
||||||
var rd, wr: TFdSet
|
|
||||||
var m = 0
|
|
||||||
createFdSet(rd, wr, fds, m)
|
|
||||||
|
|
||||||
var retCode = 0
|
|
||||||
if timeout != -1:
|
|
||||||
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
|
|
||||||
else:
|
|
||||||
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
|
||||||
|
|
||||||
if retCode < 0:
|
|
||||||
OSError(OSLastError())
|
|
||||||
elif retCode == 0:
|
|
||||||
return @[]
|
|
||||||
else:
|
|
||||||
return getReadyFDs(rd, wr, fds)
|
|
||||||
|
|
||||||
proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
|
|
||||||
result = select(s.fds, timeout)
|
|
||||||
|
|
||||||
proc newSelectSelector*(): PSelectSelector =
|
|
||||||
initSelector(result)
|
|
||||||
result.registerImpl = ssRegister
|
|
||||||
result.unregisterImpl = ssUnregister
|
|
||||||
result.selectImpl = ssSelect
|
|
||||||
result.closeImpl = ssClose
|
|
||||||
|
|
||||||
# ---- Epoll -------------------------------------------------------------------
|
|
||||||
|
|
||||||
when defined(linux):
|
|
||||||
import epoll
|
|
||||||
type
|
type
|
||||||
PEpollSelector* = ref object of PSelector
|
PSelector* = ref object
|
||||||
epollFD: cint
|
epollFD: cint
|
||||||
events: array[64, ptr epoll_event]
|
events: array[64, ptr epoll_event]
|
||||||
|
fds: TTable[TSocketHandle, PSelectorKey]
|
||||||
|
|
||||||
TDataWrapper = object
|
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
|
||||||
fd: cint
|
|
||||||
boundEvents: set[TEvent] ## The events which ``fd`` listens for.
|
|
||||||
data: PObject ## User object.
|
|
||||||
|
|
||||||
proc esRegister(s: PSelector, fd: cint, events: set[TEvent],
|
|
||||||
data: PObject): TSelectorKey =
|
|
||||||
var es = PEpollSelector(s)
|
|
||||||
var event: epoll_event
|
|
||||||
if EvRead in events:
|
if EvRead in events:
|
||||||
event.events = EPOLLIN
|
result.events = EPOLLIN
|
||||||
if EvWrite in events:
|
if EvWrite in events:
|
||||||
event.events = event.events or EPOLLOUT
|
result.events = result.events or EPOLLOUT
|
||||||
|
result.data.fd = fd.cint
|
||||||
|
|
||||||
var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd
|
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||||
dw.fd = fd
|
data: PObject): PSelectorKey {.discardable.} =
|
||||||
dw.boundEvents = events
|
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||||
dw.data = data
|
## ``events``.
|
||||||
event.data.thePtr = dw
|
if s.fds.hasKey(fd):
|
||||||
|
raise newException(EInvalidValue, "File descriptor already exists.")
|
||||||
|
|
||||||
if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
var event = createEventStruct(events, fd)
|
||||||
|
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
|
|
||||||
result = TSelectorKey(fd: fd, events: events, data: data)
|
var key = PSelectorKey(fd: fd, events: events, data: data)
|
||||||
|
|
||||||
proc esUnregister(s: PSelector, fd: cint): TSelectorKey =
|
s.fds[fd] = key
|
||||||
# We cannot find out the information about this ``fd`` from the epoll
|
result = key
|
||||||
# context. As such I will simply return an almost empty TSelectorKey.
|
|
||||||
var es = PEpollSelector(s)
|
proc update*(s: PSelector, fd: TSocketHandle,
|
||||||
if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||||
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
|
if not s.fds.hasKey(fd):
|
||||||
|
raise newException(EInvalidValue, "File descriptor not found.")
|
||||||
|
var event = createEventStruct(events, fd)
|
||||||
|
|
||||||
|
s.fds[fd].events = events
|
||||||
|
echo("About to update")
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
# We could fill in the ``fds`` TTable to get the info, but that wouldn't
|
echo("finished updating")
|
||||||
# be nice for our memory.
|
result = s.fds[fd]
|
||||||
result = TSelectorKey(fd: fd, events: {}, data: nil)
|
|
||||||
|
|
||||||
proc esClose(s: PSelector) =
|
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||||
var es = PEpollSelector(s)
|
if not s.fds.hasKey(fd):
|
||||||
if es.epollFD.close() != 0: OSError(OSLastError())
|
raise newException(EInvalidValue, "File descriptor not found.")
|
||||||
dealloc(addr es.events) # TODO: Test this
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||||
|
OSError(OSLastError())
|
||||||
|
result = s.fds[fd]
|
||||||
|
s.fds.del(fd)
|
||||||
|
|
||||||
proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
|
proc close*(s: PSelector) =
|
||||||
|
if s.epollFD.close() != 0: OSError(OSLastError())
|
||||||
|
dealloc(addr s.events) # TODO: Test this
|
||||||
|
|
||||||
|
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``.
|
||||||
result = @[]
|
result = @[]
|
||||||
var es = PEpollSelector(s)
|
|
||||||
|
|
||||||
let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint)
|
let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint)
|
||||||
if evNum < 0: OSError(OSLastError())
|
if evNum < 0: OSError(OSLastError())
|
||||||
if evNum == 0: return @[]
|
if evNum == 0: return @[]
|
||||||
for i in 0 .. <evNum:
|
for i in 0 .. <evNum:
|
||||||
var evSet: set[TEvent] = {}
|
var evSet: set[TEvent] = {}
|
||||||
if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||||
if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||||
let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr)
|
|
||||||
|
|
||||||
let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents,
|
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
|
||||||
data: dw.data)
|
|
||||||
result.add((selectorKey, evSet))
|
result.add((selectorKey, evSet))
|
||||||
|
|
||||||
proc newEpollSelector*(): PEpollSelector =
|
proc newSelector*(): PSelector =
|
||||||
new result
|
new result
|
||||||
result.epollFD = epoll_create(64)
|
result.epollFD = epoll_create(64)
|
||||||
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
|
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
|
||||||
|
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||||
if result.epollFD < 0:
|
if result.epollFD < 0:
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
result.registerImpl = esRegister
|
|
||||||
result.unregisterImpl = esUnregister
|
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||||
result.closeImpl = esClose
|
## Determines whether selector contains a file descriptor.
|
||||||
result.selectImpl = esSelect
|
return s.fds.hasKey(fd)
|
||||||
|
|
||||||
|
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||||
|
## Retrieves the selector key for ``fd``.
|
||||||
|
return s.fds[fd]
|
||||||
|
|
||||||
|
elif defined(windows):
|
||||||
|
type
|
||||||
|
PSelector* = ref object
|
||||||
|
fds: TTable[TSocketHandle, PSelectorKey]
|
||||||
|
|
||||||
|
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||||
|
data: PObject): PSelectorKey {.discardable.} =
|
||||||
|
if s.fds.hasKey(fd):
|
||||||
|
raise newException(EInvalidValue, "File descriptor already exists.")
|
||||||
|
var sk = PSelectorKey(fd: fd, events: events, data: data)
|
||||||
|
s.fds[fd] = sk
|
||||||
|
result = sk
|
||||||
|
|
||||||
|
proc update*(s: PSelector, fd: TSocketHandle,
|
||||||
|
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||||
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
|
if not s.fds.hasKey(fd):
|
||||||
|
raise newException(EInvalidValue, "File descriptor not found.")
|
||||||
|
|
||||||
|
s.fds[fd].events = events
|
||||||
|
result = s.fds[fd]
|
||||||
|
|
||||||
|
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||||
|
result = s.fds[fd]
|
||||||
|
s.fds.del(fd)
|
||||||
|
|
||||||
|
proc close*(s: PSelector) = nil
|
||||||
|
|
||||||
|
proc timeValFromMilliseconds(timeout: int): TTimeVal =
|
||||||
|
if timeout != -1:
|
||||||
|
var seconds = timeout div 1000
|
||||||
|
result.tv_sec = seconds.int32
|
||||||
|
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||||
|
|
||||||
|
proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey],
|
||||||
|
m: var int) =
|
||||||
|
FD_ZERO(rd); FD_ZERO(wr)
|
||||||
|
for k, v in pairs(fds):
|
||||||
|
if EvRead in v.events:
|
||||||
|
m = max(m, int(k))
|
||||||
|
FD_SET(k, rd)
|
||||||
|
if EvWrite in v.events:
|
||||||
|
m = max(m, int(k))
|
||||||
|
FD_SET(k, wr)
|
||||||
|
|
||||||
|
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
|
||||||
|
seq[TReadyInfo] =
|
||||||
|
result = @[]
|
||||||
|
for k, v in pairs(fds):
|
||||||
|
var events: set[TEvent] = {}
|
||||||
|
if FD_ISSET(k, rd) != 0'i32:
|
||||||
|
events = events + {EvRead}
|
||||||
|
if FD_ISSET(k, wr) != 0'i32:
|
||||||
|
events = events + {EvWrite}
|
||||||
|
result.add((v, events))
|
||||||
|
|
||||||
|
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
|
||||||
|
seq[TReadyInfo] =
|
||||||
|
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||||
|
|
||||||
|
var rd, wr: TFdSet
|
||||||
|
var m = 0
|
||||||
|
createFdSet(rd, wr, fds, m)
|
||||||
|
|
||||||
|
var retCode = 0
|
||||||
|
if timeout != -1:
|
||||||
|
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, addr(tv)))
|
||||||
|
else:
|
||||||
|
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, nil))
|
||||||
|
|
||||||
|
if retCode < 0:
|
||||||
|
OSError(OSLastError())
|
||||||
|
elif retCode == 0:
|
||||||
|
return @[]
|
||||||
|
else:
|
||||||
|
return getReadyFDs(rd, wr, fds)
|
||||||
|
|
||||||
|
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||||
|
result = select(s.fds, timeout)
|
||||||
|
|
||||||
|
proc newSelector*(): PSelector =
|
||||||
|
new result
|
||||||
|
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||||
|
|
||||||
|
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||||
|
return s.fds.hasKey(fd)
|
||||||
|
|
||||||
|
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||||
|
return s.fds[fd]
|
||||||
|
|
||||||
|
elif defined(bsd) or defined(macosx):
|
||||||
|
# TODO: kqueue
|
||||||
|
{.error: "Sorry your platform is not supported yet.".}
|
||||||
|
else:
|
||||||
|
{.error: "Sorry your platform is not supported.".}
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
# Select()
|
# Select()
|
||||||
|
|
@ -224,11 +223,12 @@ when isMainModule:
|
||||||
sock: TSocket
|
sock: TSocket
|
||||||
|
|
||||||
var sock = socket()
|
var sock = socket()
|
||||||
|
sock.setBlocking(false)
|
||||||
sock.connect("irc.freenode.net", TPort(6667))
|
sock.connect("irc.freenode.net", TPort(6667))
|
||||||
|
|
||||||
var selector = newEpollSelector()
|
var selector = newSelector()
|
||||||
var data = PSockWrapper(sock: sock)
|
var data = PSockWrapper(sock: sock)
|
||||||
let key = selector.register(sock.getFD.cint, {EvRead}, data)
|
let key = selector.register(sock.getFD, {EvWrite}, data)
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
let ready = selector.select(1000)
|
let ready = selector.select(1000)
|
||||||
|
|
@ -236,6 +236,7 @@ when isMainModule:
|
||||||
if ready.len > 0: echo ready[0].events
|
if ready.len > 0: echo ready[0].events
|
||||||
i.inc
|
i.inc
|
||||||
if i == 6:
|
if i == 6:
|
||||||
|
assert selector.unregister(sock.getFD).fd == sock.getFD
|
||||||
selector.close()
|
selector.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,13 @@ when hostos == "solaris":
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
import winlean
|
import winlean
|
||||||
|
export ioctlsocket
|
||||||
else:
|
else:
|
||||||
import posix
|
import posix
|
||||||
|
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL
|
||||||
|
|
||||||
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
||||||
inet_ntoa
|
inet_ntoa, recv, `==`, connect, send, accept
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
|
@ -63,10 +65,10 @@ type
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
let
|
let
|
||||||
OSInvalidSocket* = winlean.INVALID_SOCKET
|
osInvalidSocket* = winlean.INVALID_SOCKET
|
||||||
else:
|
else:
|
||||||
let
|
let
|
||||||
OSInvalidSocket* = posix.INVALID_SOCKET
|
osInvalidSocket* = posix.INVALID_SOCKET
|
||||||
|
|
||||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
proc `==`*(a, b: TPort): bool {.borrow.}
|
||||||
## ``==`` for ports.
|
## ``==`` for ports.
|
||||||
|
|
@ -89,7 +91,7 @@ when defined(posix):
|
||||||
of AF_UNIX: result = posix.AF_UNIX
|
of AF_UNIX: result = posix.AF_UNIX
|
||||||
of AF_INET: result = posix.AF_INET
|
of AF_INET: result = posix.AF_INET
|
||||||
of AF_INET6: result = posix.AF_INET6
|
of AF_INET6: result = posix.AF_INET6
|
||||||
else: nil
|
else: discard
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
proc toInt(typ: TType): cint =
|
||||||
case typ
|
case typ
|
||||||
|
|
@ -97,7 +99,7 @@ when defined(posix):
|
||||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||||
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
||||||
of SOCK_RAW: result = posix.SOCK_RAW
|
of SOCK_RAW: result = posix.SOCK_RAW
|
||||||
else: nil
|
else: discard
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
proc toInt(p: TProtocol): cint =
|
||||||
case p
|
case p
|
||||||
|
|
@ -107,7 +109,7 @@ when defined(posix):
|
||||||
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
||||||
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
||||||
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
||||||
else: nil
|
else: discard
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc toInt(domain: TDomain): cint =
|
proc toInt(domain: TDomain): cint =
|
||||||
|
|
|
||||||
|
|
@ -2014,8 +2014,10 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
## Flushes `f`'s buffer.
|
## Flushes `f`'s buffer.
|
||||||
|
|
||||||
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
|
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
|
||||||
## Reads all data from the stream `file`. Raises an IO exception
|
## Reads all data from the stream `file`.
|
||||||
## in case of an error
|
##
|
||||||
|
## Raises an IO exception in case of an error. It is an error if the
|
||||||
|
## current file position is not at the beginning of the file.
|
||||||
|
|
||||||
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
|
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
|
||||||
## Opens a file named `filename` for reading. Then calls `readAll`
|
## Opens a file named `filename` for reading. Then calls `readAll`
|
||||||
|
|
|
||||||
|
|
@ -418,58 +418,58 @@ proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
return Math.floor(`a` % `b`);
|
return Math.floor(`a` % `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc NegInt(a: int): int {.compilerproc.} =
|
proc negInt(a: int): int {.compilerproc.} =
|
||||||
result = a*(-1)
|
result = a*(-1)
|
||||||
|
|
||||||
proc NegInt64(a: int64): int64 {.compilerproc.} =
|
proc negInt64(a: int64): int64 {.compilerproc.} =
|
||||||
result = a*(-1)
|
result = a*(-1)
|
||||||
|
|
||||||
proc AbsInt(a: int): int {.compilerproc.} =
|
proc absInt(a: int): int {.compilerproc.} =
|
||||||
result = if a < 0: a*(-1) else: a
|
result = if a < 0: a*(-1) else: a
|
||||||
|
|
||||||
proc AbsInt64(a: int64): int64 {.compilerproc.} =
|
proc absInt64(a: int64): int64 {.compilerproc.} =
|
||||||
result = if a < 0: a*(-1) else: a
|
result = if a < 0: a*(-1) else: a
|
||||||
|
|
||||||
proc LeU(a, b: int): bool {.compilerproc.} =
|
proc leU(a, b: int): bool {.compilerproc.} =
|
||||||
result = abs(a) <= abs(b)
|
result = abs(a) <= abs(b)
|
||||||
|
|
||||||
proc LtU(a, b: int): bool {.compilerproc.} =
|
proc ltU(a, b: int): bool {.compilerproc.} =
|
||||||
result = abs(a) < abs(b)
|
result = abs(a) < abs(b)
|
||||||
|
|
||||||
proc LeU64(a, b: int64): bool {.compilerproc.} =
|
proc leU64(a, b: int64): bool {.compilerproc.} =
|
||||||
result = abs(a) <= abs(b)
|
result = abs(a) <= abs(b)
|
||||||
|
proc ltU64(a, b: int64): bool {.compilerproc.} =
|
||||||
proc LtU64(a, b: int64): bool {.compilerproc.} =
|
|
||||||
result = abs(a) < abs(b)
|
result = abs(a) < abs(b)
|
||||||
|
|
||||||
proc AddU(a, b: int): int {.compilerproc.} =
|
proc addU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) + abs(b)
|
result = abs(a) + abs(b)
|
||||||
proc AddU64(a, b: int64): int64 {.compilerproc.} =
|
proc addU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) + abs(b)
|
result = abs(a) + abs(b)
|
||||||
|
|
||||||
proc SubU(a, b: int): int {.compilerproc.} =
|
proc subU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) - abs(b)
|
result = abs(a) - abs(b)
|
||||||
proc SubU64(a, b: int64): int64 {.compilerproc.} =
|
proc subU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) - abs(b)
|
result = abs(a) - abs(b)
|
||||||
|
|
||||||
proc MulU(a, b: int): int {.compilerproc.} =
|
proc mulU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) * abs(b)
|
result = abs(a) * abs(b)
|
||||||
proc MulU64(a, b: int64): int64 {.compilerproc.} =
|
proc mulU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) * abs(b)
|
result = abs(a) * abs(b)
|
||||||
|
|
||||||
proc DivU(a, b: int): int {.compilerproc.} =
|
proc divU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) div abs(b)
|
result = abs(a) div abs(b)
|
||||||
proc DivU64(a, b: int64): int64 {.compilerproc.} =
|
proc divU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) div abs(b)
|
result = abs(a) div abs(b)
|
||||||
|
|
||||||
proc ModU(a, b: int): int {.compilerproc.} =
|
proc modU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) mod abs(b)
|
result = abs(a) mod abs(b)
|
||||||
proc ModU64(a, b: int64): int64 {.compilerproc.} =
|
proc modU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) mod abs(b)
|
result = abs(a) mod abs(b)
|
||||||
|
|
||||||
proc Ze(a: int): int {.compilerproc.} =
|
proc ze*(a: int): int {.compilerproc.} =
|
||||||
result = a
|
result = a
|
||||||
proc Ze64(a: int64): int64 {.compilerproc.} =
|
|
||||||
|
proc ze64*(a: int64): int64 {.compilerproc.} =
|
||||||
result = a
|
result = a
|
||||||
|
|
||||||
proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} =
|
proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} =
|
||||||
|
|
@ -487,7 +487,6 @@ proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} =
|
||||||
return `a`;
|
return `a`;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
||||||
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
||||||
|
|
||||||
|
|
@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool =
|
||||||
tyArray, tyArrayConstr, tyTuple,
|
tyArray, tyArrayConstr, tyTuple,
|
||||||
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
||||||
|
|
||||||
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
||||||
|
|
||||||
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
|
proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkNone: sysAssert(false, "NimCopyAux")
|
of nkNone: sysAssert(false, "NimCopyAux")
|
||||||
of nkSlot:
|
of nkSlot:
|
||||||
|
|
@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc NimCopy(x: pointer, ti: PNimType): pointer =
|
proc nimCopy(x: pointer, ti: PNimType): pointer =
|
||||||
case ti.kind
|
case ti.kind
|
||||||
of tyPtr, tyRef, tyVar, tyNil:
|
of tyPtr, tyRef, tyVar, tyNil:
|
||||||
if not isFatPointer(ti):
|
if not isFatPointer(ti):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
line: 21
|
line: 21
|
||||||
errormsg: "invalid type: 'TTable'"
|
errormsg: "invalid type: 'TTable[string, proc (string)]'"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import tables
|
import tables
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.asyn
|
||||||
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
||||||
for i in 0 .. <swarmSize:
|
for i in 0 .. <swarmSize:
|
||||||
var sock = socket()
|
var sock = socket()
|
||||||
disp.register(sock)
|
#disp.register(sock)
|
||||||
discard await disp.connect(sock, "localhost", port)
|
discard await disp.connect(sock, "localhost", port)
|
||||||
when true:
|
when true:
|
||||||
discard await sendMessages(disp, sock)
|
discard await sendMessages(disp, sock)
|
||||||
|
|
@ -48,7 +48,7 @@ proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.asyn
|
||||||
|
|
||||||
proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
||||||
var server = socket()
|
var server = socket()
|
||||||
disp.register(server)
|
#disp.register(server)
|
||||||
server.bindAddr(port)
|
server.bindAddr(port)
|
||||||
server.listen()
|
server.listen()
|
||||||
while true:
|
while true:
|
||||||
|
|
|
||||||
40
tests/exception/tfinally4.nim
Normal file
40
tests/exception/tfinally4.nim
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
discard """
|
||||||
|
file: "tfinally4.nim"
|
||||||
|
output: "B1\nA1\n1\nB1\nB2\ncatch\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\ncatch\nA1\nA2\n0\nB1\nA1\n1\nB1\nB2\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\nA1\nA2\n3"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# More thorough test of return-in-finaly
|
||||||
|
|
||||||
|
var raiseEx = true
|
||||||
|
var returnA = true
|
||||||
|
var returnB = false
|
||||||
|
|
||||||
|
proc main: int =
|
||||||
|
try: #A
|
||||||
|
try: #B
|
||||||
|
if raiseEx:
|
||||||
|
raise newException(EOS, "")
|
||||||
|
return 3
|
||||||
|
finally: #B
|
||||||
|
echo "B1"
|
||||||
|
if returnB:
|
||||||
|
return 2
|
||||||
|
echo "B2"
|
||||||
|
except EOS: #A
|
||||||
|
echo "catch"
|
||||||
|
finally: #A
|
||||||
|
echo "A1"
|
||||||
|
if returnA:
|
||||||
|
return 1
|
||||||
|
echo "A2"
|
||||||
|
|
||||||
|
for x in [true, false]:
|
||||||
|
for y in [true, false]:
|
||||||
|
for z in [true, false]:
|
||||||
|
# echo "raiseEx: " & $x
|
||||||
|
# echo "returnA: " & $y
|
||||||
|
# echo "returnB: " & $z
|
||||||
|
raiseEx = x
|
||||||
|
returnA = y
|
||||||
|
returnB = z
|
||||||
|
echo main()
|
||||||
40
tests/exception/tnestedreturn.nim
Normal file
40
tests/exception/tnestedreturn.nim
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
discard """
|
||||||
|
file: "tnestedreturn.nim"
|
||||||
|
output: "A\nB\nC\n"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Various tests of return nested in double try/except statements
|
||||||
|
|
||||||
|
proc test1() =
|
||||||
|
|
||||||
|
finally: echo "A"
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise newException(EOS, "Problem")
|
||||||
|
except EOS:
|
||||||
|
return
|
||||||
|
|
||||||
|
test1()
|
||||||
|
|
||||||
|
|
||||||
|
proc test2() =
|
||||||
|
|
||||||
|
finally: echo "B"
|
||||||
|
|
||||||
|
try:
|
||||||
|
return
|
||||||
|
except EOS:
|
||||||
|
discard
|
||||||
|
|
||||||
|
test2()
|
||||||
|
|
||||||
|
proc test3() =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise newException(EOS, "Problem")
|
||||||
|
except EOS:
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
echo "C"
|
||||||
|
|
||||||
|
test3()
|
||||||
20
tests/exception/tnestedreturn2.nim
Normal file
20
tests/exception/tnestedreturn2.nim
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
discard """
|
||||||
|
file: "tnestedreturn.nim"
|
||||||
|
outputsub: "Error: unhandled exception: Problem [EOS]"
|
||||||
|
exitcode: "1"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc test4() =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise newException(EOS, "Problem")
|
||||||
|
except EOS:
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
discard
|
||||||
|
|
||||||
|
# Should cause unhandled exception error,
|
||||||
|
# but could cause segmentation fault if
|
||||||
|
# exceptions are not handled properly.
|
||||||
|
test4()
|
||||||
|
raise newException(EOS, "Problem")
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
output: "10\n10"
|
output: "10\n10\n1\n2\n3"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc test(x: proc (a, b: int): int) =
|
proc test(x: proc (a, b: int): int) =
|
||||||
|
|
@ -8,3 +8,11 @@ proc test(x: proc (a, b: int): int) =
|
||||||
test(proc (a, b): auto = a + b)
|
test(proc (a, b): auto = a + b)
|
||||||
|
|
||||||
test do (a, b) -> auto: a + b
|
test do (a, b) -> auto: a + b
|
||||||
|
|
||||||
|
proc foreach[T](s: seq[T], body: proc(x: T)) =
|
||||||
|
for e in s:
|
||||||
|
body(e)
|
||||||
|
|
||||||
|
foreach(@[1,2,3]) do (x):
|
||||||
|
echo x
|
||||||
|
|
||||||
|
|
|
||||||
15
tests/global/globalaux.nim
Normal file
15
tests/global/globalaux.nim
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
type
|
||||||
|
TObj*[T] = object
|
||||||
|
val*: T
|
||||||
|
|
||||||
|
var
|
||||||
|
totalGlobals* = 0
|
||||||
|
|
||||||
|
proc makeObj[T](x: T): TObj[T] =
|
||||||
|
totalGlobals += 1
|
||||||
|
result.val = x
|
||||||
|
|
||||||
|
proc globalInstance*[T]: var TObj[T] =
|
||||||
|
var g {.global.} = when T is int: makeObj(10) else: makeObj("hello")
|
||||||
|
result = g
|
||||||
|
|
||||||
4
tests/global/globalaux2.nim
Normal file
4
tests/global/globalaux2.nim
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import globalaux
|
||||||
|
|
||||||
|
echo "in globalaux2: ", globalInstance[int]().val
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
discard """
|
discard """
|
||||||
msg: '''
|
msg: '''int int
|
||||||
int
|
float float
|
||||||
float
|
int int
|
||||||
TFoo
|
TFoo TFoo
|
||||||
TFoo
|
int float
|
||||||
'''
|
TFoo TFoo'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import typetraits
|
import typetraits
|
||||||
|
|
@ -24,8 +24,7 @@ template reject(e: expr) =
|
||||||
|
|
||||||
proc genericParamRepeated[T: typedesc](a: T, b: T) =
|
proc genericParamRepeated[T: typedesc](a: T, b: T) =
|
||||||
static:
|
static:
|
||||||
echo a.name
|
echo a.name, " ", b.name
|
||||||
echo b.name
|
|
||||||
|
|
||||||
accept genericParamRepeated(int, int)
|
accept genericParamRepeated(int, int)
|
||||||
accept genericParamRepeated(float, float)
|
accept genericParamRepeated(float, float)
|
||||||
|
|
@ -35,8 +34,7 @@ reject genericParamRepeated(int, float)
|
||||||
|
|
||||||
proc genericParamOnce[T: typedesc](a, b: T) =
|
proc genericParamOnce[T: typedesc](a, b: T) =
|
||||||
static:
|
static:
|
||||||
echo a.name
|
echo a.name, " ", b.name
|
||||||
echo b.name
|
|
||||||
|
|
||||||
accept genericParamOnce(int, int)
|
accept genericParamOnce(int, int)
|
||||||
accept genericParamOnce(TFoo, TFoo)
|
accept genericParamOnce(TFoo, TFoo)
|
||||||
|
|
@ -68,8 +66,7 @@ reject typePairs2(string, int, TBAR, TBAR)
|
||||||
|
|
||||||
proc dontBind(a: typedesc, b: typedesc) =
|
proc dontBind(a: typedesc, b: typedesc) =
|
||||||
static:
|
static:
|
||||||
echo a.name
|
echo a.name, " ", b.name
|
||||||
echo b.name
|
|
||||||
|
|
||||||
accept dontBind(int, float)
|
accept dontBind(int, float)
|
||||||
accept dontBind(TFoo, TFoo)
|
accept dontBind(TFoo, TFoo)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ proc intval(x: int) = discard
|
||||||
# check real and virtual fields
|
# check real and virtual fields
|
||||||
type
|
type
|
||||||
TFoo = generic T
|
TFoo = generic T
|
||||||
intval T.x
|
T.x
|
||||||
|
y(T)
|
||||||
intval T.y
|
intval T.y
|
||||||
|
|
||||||
proc y(x: TObj): int = 10
|
proc y(x: TObj): int = 10
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tests/reject/trecincb.nim"
|
file: "tests/reject/trecincb.nim"
|
||||||
line: 9
|
line: 9
|
||||||
errormsg: "recursive dependency: 'tests/reject/trecincb.nim'"
|
errormsg: "recursive dependency: 'tests/module/trecincb.nim'"
|
||||||
"""
|
"""
|
||||||
# Test recursive includes
|
# Test recursive includes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "trecincb.nim"
|
file: "trecincb.nim"
|
||||||
line: 9
|
line: 9
|
||||||
errormsg: "recursive dependency: 'tests/reject/trecincb.nim'"
|
errormsg: "recursive dependency: 'tests/module/trecincb.nim'"
|
||||||
"""
|
"""
|
||||||
# Test recursive includes
|
# Test recursive includes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ type
|
||||||
channel: string
|
channel: string
|
||||||
timestamp: TTime
|
timestamp: TTime
|
||||||
case kind*: TSeenType
|
case kind*: TSeenType
|
||||||
of PSeenJoin: discard
|
of PSeenJoin: nil
|
||||||
of PSeenPart, PSeenQuit, PSeenMsg:
|
of PSeenPart, PSeenQuit, PSeenMsg:
|
||||||
msg: string
|
msg: string
|
||||||
of PSeenNick:
|
of PSeenNick:
|
||||||
|
|
|
||||||
1
tests/threads/nimrod.cfg
Normal file
1
tests/threads/nimrod.cfg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
threads:on
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
discard """
|
discard """
|
||||||
output: "he, no return type;abc a string"
|
output: '''12
|
||||||
|
empty
|
||||||
|
he, no return type;
|
||||||
|
abc a string
|
||||||
|
ha'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc ReturnT[T](x: T): T =
|
proc ReturnT[T](x: T): T =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue