# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This is the JavaScript code generator. # Soon also a PHP code generator. ;-) discard """ The JS code generator contains only 2 tricks: Trick 1 ------- Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``) which are pairs (array, index). The derefence operation is then 'array[index]'. Check ``mapType`` for the details. Trick 2 ------- It is preferable to generate '||' and '&&' if possible since that is more idiomatic and hence should be friendlier for the JS JIT implementation. However code like ``foo and (let bar = baz())`` cannot be translated this way. Instead the expressions need to be transformed into statements. ``isSimpleExpr`` implements the required case distinction. """ import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, intsets, cgmeth, lowerings type TTarget = enum targetJS, targetPHP TJSGen = object of TPassContext module: PSym target: TTarget BModule = ref TJSGen TJSTypeKind = enum # necessary JS "types" etyNone, # no type etyNull, # null type etyProc, # proc type etyBool, # bool type etyInt, # JavaScript's int etyFloat, # JavaScript's float etyString, # JavaScript's string etyObject, # JavaScript's reference to an object etyBaseIndex # base + index needed TResKind = enum resNone, # not set resExpr, # is some complex expression resVal, # is a temporary/value/l-value resCallee # expression is callee TCompRes = object kind: TResKind typ: TJSTypeKind res: Rope # result part; index if this is an # (address, index)-tuple address: Rope # address of an (address, index)-tuple TBlock = object id: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) isLoop: bool # whether it's a 'block' or 'while' TGlobals = object typeInfo, constants, code: Rope forwarded: seq[PSym] generatedSyms: IntSet typeInfoGenerated: IntSet classes: seq[(PType, Rope)] unique: int # for temp identifier generation PGlobals = ref TGlobals PProc = ref TProc TProc = object procDef: PNode prc: PSym globals, locals, body: Rope options: TOptions module: BModule g: PGlobals beforeRetNeeded: bool target: TTarget # duplicated here for faster dispatching unique: int # for temp identifier generation blocks: seq[TBlock] up: PProc # up the call chain; required for closure support declaredGlobals: IntSet template `|`(a, b: expr): expr {.immediate, dirty.} = (if p.target == targetJS: a else: b) proc newGlobals(): PGlobals = new(result) result.forwarded = @[] result.generatedSyms = initIntSet() result.typeInfoGenerated = initIntSet() result.classes = @[] proc initCompRes(r: var TCompRes) = r.address = nil r.res = nil r.typ = etyNone r.kind = resNone proc rdLoc(a: TCompRes): Rope {.inline.} = result = a.res when false: if a.typ != etyBaseIndex: result = a.res else: result = "$1[$2]" % [a.address, a.res] proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = result = PProc( blocks: @[], options: options, module: module, procDef: procDef, g: globals, target: module.target) if procDef != nil: result.prc = procDef.sons[namePos].sym if result.target == targetPHP: result.declaredGlobals = initIntSet() proc declareGlobal(p: PProc; id: int; r: Rope) = if p.prc != nil and not p.declaredGlobals.containsOrIncl(id): p.locals.addf("global $1;$n", [r]) const MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyBigNum, tyVarargs} proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) case t.kind of tyVar, tyRef, tyPtr: if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: result = etyObject else: result = etyBaseIndex of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyBaseIndex of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: result = mapType(t.sons[0]) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat of tySet: result = etyObject # map a set to a table of tyString, tySequence: result = etyInt # little hack to get right semantics of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum, tyVarargs: result = etyObject of tyNil: result = etyNull of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation, tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor, tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses, tyVoid: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString proc mapType(p: PProc; typ: PType): TJSTypeKind = if p.target == targetPHP: result = etyObject else: result = mapType(typ) proc mangleName(s: PSym; target: TTarget): Rope = result = s.loc.r if result == nil: if target == targetJS or s.kind == skTemp: result = rope(mangle(s.name.s)) else: var x = newStringOfCap(s.name.s.len) var i = 0 while i < s.name.s.len: let c = s.name.s[i] case c of 'A'..'Z': if i > 0 and s.name.s[i-1] in {'a'..'z'}: x.add '_' x.add(chr(c.ord - 'A'.ord + 'a'.ord)) of 'a'..'z', '_', '0'..'9': x.add c else: x.add("HEX" & toHex(ord(c), 2)) inc i result = rope(x) if s.name.s != "this" and s.kind != skField: add(result, "_") add(result, rope(s.id)) s.loc.r = result proc escapeJSString(s: string): string = result = newStringOfCap(s.len + s.len shr 2) result.add("\"") for c in items(s): case c of '\l': result.add("\\n") of '\r': result.add("\\r") of '\t': result.add("\\t") of '\b': result.add("\\b") of '\a': result.add("\\a") of '\e': result.add("\\e") of '\v': result.add("\\v") of '\\': result.add("\\\\") of '\"': result.add("\\\"") else: add(result, c) result.add("\"") proc makeJSString(s: string, escapeNonAscii = true): Rope = if s.isNil: result = "null".rope elif escapeNonAscii: result = strutils.escape(s).rope else: result = escapeJSString(s).rope include jstypes proc gen(p: PProc, n: PNode, r: var TCompRes) proc genStmt(p: PProc, n: PNode) proc genProc(oldProc: PProc, prc: PSym): Rope proc genConstant(p: PProc, c: PSym) proc useMagic(p: PProc, name: string) = if name.len == 0: return var s = magicsys.getCompilerProc(name) if s != nil: internalAssert s.kind in {skProc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): let code = genProc(p, s) add(p.g.constants, code) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so # we're picky here for the system module too: if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name) else: rawMessage(errSystemNeeds, name) proc isSimpleExpr(p: PProc; n: PNode): bool = # calls all the way down --> can stay expression based if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar} or (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}): for c in n: if not p.isSimpleExpr(c): return false result = true elif n.isAtom: result = true proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) if p.target == targetJS: result = "Tmp$1" % [rope(p.unique)] if defineInLocals: addf(p.locals, "var $1;$n", [result]) else: result = "$$Tmp$1" % [rope(p.unique)] proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone var x, y: TCompRes if p.isSimpleExpr(a) and p.isSimpleExpr(b): gen(p, a, x) gen(p, b, y) r.kind = resExpr r.res = "($1 && $2)" % [x.rdLoc, y.rdLoc] else: r.res = p.getTemp r.kind = resVal # while a and b: # --> # while true: # aa # if not a: tmp = false # else: # bb # tmp = b # tmp gen(p, a, x) p.body.addf("if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc]) gen(p, b, y) p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc]) proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone var x, y: TCompRes if p.isSimpleExpr(a) and p.isSimpleExpr(b): gen(p, a, x) gen(p, b, y) r.kind = resExpr r.res = "($1 || $2)" % [x.rdLoc, y.rdLoc] else: r.res = p.getTemp r.kind = resVal gen(p, a, x) p.body.addf("if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc]) gen(p, b, y) p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc]) type TMagicFrmt = array[0..3, string] TMagicOps = array[mAddI..mStrToStr, TMagicFrmt] const # magic checked op; magic unchecked op; checked op; unchecked op jsOps: TMagicOps = [ ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 ["", "", "($1 - $2)", "($1 - $2)"], # SubF64 ["", "", "($1 * $2)", "($1 * $2)"], # MulF64 ["", "", "($1 / $2)", "($1 / $2)"], # DivF64 ["", "", "", ""], # ShrI ["", "", "($1 << $2)", "($1 << $2)"], # ShlI ["", "", "($1 & $2)", "($1 & $2)"], # BitandI ["", "", "($1 | $2)", "($1 | $2)"], # BitorI ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["", "", "", ""], # addU ["", "", "", ""], # subU ["", "", "", ""], # mulU ["", "", "", ""], # divU ["", "", "($1 % $2)", "($1 % $2)"], # modU ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # leU ["", "", "($1 < $2)", "($1 < $2)"], # ltU ["", "", "($1 <= $2)", "($1 <= $2)"], # leU64 ["", "", "($1 < $2)", "($1 < $2)"], # ltU64 ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum ["", "", "($1 == $2)", "($1 == $2)"], # EqCh ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh ["", "", "($1 < $2)", "($1 < $2)"], # LtCh ["", "", "($1 == $2)", "($1 == $2)"], # EqB ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB ["", "", "($1 < $2)", "($1 < $2)"], # LtB ["", "", "($1 == $2)", "($1 == $2)"], # EqRef ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr ["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 == $2)", "($1 == $2)"], # EqProc ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI ["", "", "!($1)", "!($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64 ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8 ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16 ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 ["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = frmt % [x.rdLoc, y.rdLoc] r.kind = resExpr proc unsignedTrimmerJS(size: BiggestInt): Rope = case size of 1: rope"& 0xff" of 2: rope"& 0xffff" of 4: rope">>> 0" else: rope"" proc unsignedTrimmerPHP(size: BiggestInt): Rope = case size of 1: rope"& 0xff" of 2: rope"& 0xffff" of 4: rope"& 0xffffffff" else: rope"" template unsignedTrimmer(size: BiggestInt): Rope = size.unsignedTrimmerJS | size.unsignedTrimmerPHP proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: bool = false) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) if reassign: r.res = "$1 = (($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] else: r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y, z: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) gen(p, n.sons[3], z) r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = useMagic(p, magic) gen(p, n.sons[1], r) r.res = frmt % [r.rdLoc] r.kind = resExpr proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) = var x, y: TCompRes let i = ord(optOverflowCheck notin p.options) useMagic(p, ops[op][i]) if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc] else: gen(p, n.sons[1], r) r.res = ops[op][i + 2] % [r.rdLoc] proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = case op of mAddU: binaryUintExpr(p, n, r, "+") of mSubU: binaryUintExpr(p, n, r, "-") of mMulU: binaryUintExpr(p, n, r, "*") of mDivU: binaryUintExpr(p, n, r, "/") of mDivI: if p.target == targetPHP: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] else: arithAux(p, n, r, op, jsOps) of mModI: if p.target == targetPHP: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] else: arithAux(p, n, r, op, jsOps) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) if p.target == targetPHP: # XXX prevent multi evaluations r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc] else: r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: if p.target == targetPHP: if op == mEnumToStr: var x: TCompRes gen(p, n.sons[1], x) r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc] elif op == mCharToStr: var x: TCompRes gen(p, n.sons[1], x) r.res = "chr($#)" % [x.rdLoc] else: gen(p, n.sons[1], r) else: arithAux(p, n, r, op, jsOps) else: arithAux(p, n, r, op, jsOps) r.kind = resExpr proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if optLineDir in p.options: addf(p.body, "// line $2 \"$1\"$n", [rope(toFilename(n.info)), rope(line)]) if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and ((p.prc == nil) or sfPure notin p.prc.flags): useMagic(p, "endb") addf(p.body, "endb($1);$n", [rope(line)]) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and ((p.prc == nil) or not (sfPure in p.prc.flags)): addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) proc genWhileStmt(p: PProc, n: PNode) = var cond: TCompRes internalAssert isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) var length = len(p.blocks) setLen(p.blocks, length + 1) p.blocks[length].id = -p.unique p.blocks[length].isLoop = true let labl = p.unique.rope addf(p.body, "L$1: while (true) {$n" | "while (true) {$n", [labl]) gen(p, n.sons[0], cond) addf(p.body, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", [cond.res, labl]) genStmt(p, n.sons[1]) addf(p.body, "}$n" | "}L$#:;$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = if src.kind != resNone: if dest.kind != resNone: p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc]) else: p.body.addf("$1;$n", [src.rdLoc]) src.kind = resNone src.res = nil proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: # # ++excHandler; # try { # stmts; # } catch (EXC) { # var prevJSError = lastJSError; lastJSError = EXC; # --excHandler; # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # stmts; # } else if (e.typ && e.typ == NTI32342) { # stmts; # } else { # stmts; # } # lastJSError = prevJSError; # } finally { # stmts; # } genLineDir(p, n) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) inc(p.unique) var i = 1 var length = sonsLen(n) var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch if catchBranchesExist and p.target == targetJS: add(p.body, "++excHandler;" & tnl) var safePoint = "Tmp$1" % [rope(p.unique)] if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) addf(p.body, "try {$n", []) if p.target == targetPHP and p.globals == nil: p.globals = "global $lastJSError; global $prevJSError;".rope var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) var generalCatchBranchExists = false let dollar = rope(if p.target == targetJS: "" else: "$") if p.target == targetJS and catchBranchesExist: addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXC;$n --excHandler;$n", []) elif p.target == targetPHP: addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: generalCatchBranchExists = true if i > 1: addf(p.body, "else {$n", []) gen(p, n.sons[i].sons[0], a) moveInto(p, a, r) if i > 1: addf(p.body, "}$n", []) else: var orExpr: Rope = nil useMagic(p, "isObj") for j in countup(0, blen - 2): if n.sons[i].sons[j].kind != nkType: internalError(n.info, "genTryStmt") if orExpr != nil: add(orExpr, "||") addf(orExpr, "isObj($2lastJSError.m_type, $1)", [genTypeInfo(p, n.sons[i].sons[j].typ), dollar]) if i > 1: add(p.body, "else ") addf(p.body, "if ($3lastJSError && ($2)) {$n", [safePoint, orExpr, dollar]) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) addf(p.body, "}$n", []) inc(i) if catchBranchesExist: if not generalCatchBranchExists: useMagic(p, "reraiseException") add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) if p.target == targetJS: add(p.body, "} finally {" & tnl) if p.target == targetPHP: # XXX ugly hack for PHP codegen add(p.body, "}" & tnl) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) if p.target == targetPHP: # XXX ugly hack for PHP codegen add(p.body, "if($lastJSError) throw($lastJSError);" & tnl) if p.target == targetJS: add(p.body, "}" & tnl) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) if n.sons[0].kind != nkEmpty: var a: TCompRes gen(p, n.sons[0], a) let typ = skipTypes(n.sons[0].typ, abstractPtrs) useMagic(p, "raiseException") addf(p.body, "raiseException($1, $2);$n", [a.rdLoc, makeJSString(typ.sym.name.s)]) else: useMagic(p, "reraiseException") add(p.body, "reraiseException();" & tnl) proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch and p.target == targetJS: useMagic(p, "toJSStr") addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: addf(p.body, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] case it.kind of nkOfBranch: for j in countup(0, sonsLen(it) - 2): let e = it.sons[j] if e.kind == nkRange: var v = copyNode(e.sons[0]) while v.intVal <= e.sons[1].intVal: gen(p, v, cond) addf(p.body, "case $1: ", [cond.rdLoc]) inc(v.intVal) else: if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ", [makeJSString(e.strVal, false)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) addf(p.body, "case $1: ", [cond.rdLoc]) gen(p, lastSon(it), stmt) moveInto(p, stmt, r) addf(p.body, "$nbreak;$n", []) of nkElse: addf(p.body, "default: $n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) addf(p.body, "break;$n", []) else: internalError(it.info, "jsgen.genCaseStmt") addf(p.body, "}$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) let idx = len(p.blocks) if n.sons[0].kind != nkEmpty: # named block? if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock") var sym = n.sons[0].sym sym.loc.k = locOther sym.position = idx+1 setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet let labl = p.unique addf(p.body, "L$1: do {$n" | "", [labl.rope]) gen(p, n.sons[1], r) addf(p.body, "} while(false);$n" | "$nL$#:;$n", [labl.rope]) setLen(p.blocks, idx) proc genBreakStmt(p: PProc, n: PNode) = var idx: int genLineDir(p, n) if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) let sym = n.sons[0].sym assert(sym.loc.k == locOther) idx = sym.position-1 else: # an unnamed 'break' can only break a loop after 'transf' pass: idx = len(p.blocks) - 1 while idx >= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: internalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used addf(p.body, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) of nkSym: let v = n.sons[i].sym if p.target == targetPHP and v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: add(p.body, "$") add(p.body, mangleName(v, p.target)) else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()") proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes var toClose = 0 if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if sonsLen(it) != 1: if i > 0: addf(p.body, "else {$n", []) inc(toClose) gen(p, it.sons[0], cond) addf(p.body, "if ($1) {$n", [cond.rdLoc]) gen(p, it.sons[1], stmt) else: # else part: addf(p.body, "else {$n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) addf(p.body, "}$n", []) add(p.body, repeat('}', toClose) & tnl) proc generateHeader(p: PProc, typ: PType): Rope = result = nil for i in countup(1, sonsLen(typ.n) - 1): assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if result != nil: add(result, ", ") var name = mangleName(param, p.target) if p.target == targetJS: add(result, name) if mapType(param.typ) == etyBaseIndex: add(result, ", ") add(result, name) add(result, "_Idx") elif not (i == 1 and param.name.s == "this"): let k = param.typ.skipTypes({tyGenericInst}).kind if k in { tyVar, tyRef, tyPtr, tyPointer }: add(result, "&") add(result, "$") add(result, name) # XXX I think something like this is needed for PHP to really support # ptr "inside" strings and seq #if mapType(param.typ) == etyBaseIndex: # add(result, ", $") # add(result, name) # add(result, "_Idx") const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} proc needsNoCopy(p: PProc; y: PNode): bool = result = (y.kind in nodeKindsNeedNoCopy) or (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) or p.target == targetPHP proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes if p.target == targetPHP and x.kind == nkBracketExpr and x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}: var c: TCompRes gen(p, x[0], a) gen(p, x[1], b) gen(p, y, c) addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) return let xtyp = mapType(p, x.typ) if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: gen(p, x.sons[0], a) let tmp = p.getTemp(false) addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc]) a.res = "$1[0][$1[1]]" % [tmp] else: gen(p, x, a) gen(p, y, b) case xtyp of etyObject: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") addf(p.body, "nimCopy($1, $2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if y.kind == nkCall: let tmp = p.getTemp(false) addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) else: internalError(x.info, "genAsgn") else: addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: addf(p.body, "$1 = $2;$n", [a.res, b.res]) proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP) proc genFastAsgn(p: PProc, n: PNode) = genLineDir(p, n) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true) proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) var tmp = p.getTemp(false) if mapType(p, skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: let tmp2 = p.getTemp(false) if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | "$1 = $2; $2 = $3; $3 = $1;$n", [ tmp, a.address, b.address]) tmp = tmp2 addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" | "$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) proc getFieldPosition(f: PNode): int = case f.kind of nkIntLit..nkUInt64Lit: result = int(f.intVal) of nkSym: result = f.sym.position else: internalError(f.info, "genFieldPosition") proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.typ = etyBaseIndex let b = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, b.sons[0], a) if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: if p.target == targetJS: r.res = makeJSString( "Field" & $getFieldPosition(b.sons[1]) ) else: r.res = getFieldPosition(b.sons[1]).rope else: if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") var f = b.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f, p.target) r.res = makeJSString($f.loc.r) internalAssert a.typ != etyBaseIndex r.address = a.res r.kind = resExpr proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone gen(p, n.sons[0], r) let otyp = skipTypes(n.sons[0].typ, abstractVarRange) if otyp.kind == tyTuple: r.res = ("$1.Field$2" | "$1[$2]") % [r.res, getFieldPosition(n.sons[1]).rope] else: if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f, p.target) if p.target == targetJS: r.res = "$1.$2" % [r.res, f.loc.r] else: if {sfImportc, sfExportc} * f.flags != {}: r.res = "$1->$2" % [r.res, f.loc.r] else: r.res = "$1['$2']" % [r.res, f.loc.r] r.kind = resExpr proc genAddr(p: PProc, n: PNode, r: var TCompRes) proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n internalAssert m.kind == nkCheckedFieldExpr genAddr(p, m, r) # XXX proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = genFieldAccess(p, n.sons[0], r) # XXX proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes first: BiggestInt r.typ = etyBaseIndex let m = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, m.sons[0], a) gen(p, m.sons[1], b) internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex r.address = a.res var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0]) else: first = 0 if optBoundsCheck in p.options and not isConstExpr(m.sons[1]): useMagic(p, "chckIndx") if p.target == targetPHP: if typ.kind != tyString: r.res = "chckIndx($1, $2, count($3))-$2" % [b.res, rope(first), a.res] else: r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] else: r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: r.res = b.res r.kind = resExpr proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) of tyTuple: if p.target == targetPHP: genFieldAccess(p, n, r) return genFieldAddr(p, n, r) else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = etyNone if r.res == nil: internalError(n.info, "genArrayAccess") if p.target == targetPHP: if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}: useMagic(p, "nimAt") if ty.kind in {tyString, tyCString}: # XXX this needs to be more like substr($1,$2) r.res = "ord(nimAt($1, $2))" % [r.address, r.res] else: r.res = "nimAt($1, $2)" % [r.address, r.res] elif ty.kind in {tyString, tyCString}: # XXX this needs to be more like substr($1,$2) r.res = "ord(@$1[$2])" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.kind = resExpr template isIndirect(x: PSym): bool = let v = x ({sfAddrTaken, sfGlobal} * v.flags != {} and #(mapType(v.typ) != etyObject) and {sfImportc, sfVolatile, sfExportc} * v.flags == {} and v.kind notin {skProc, skConverter, skMethod, skIterator, skConst, skTemp, skLet} and p.target == targetJS) proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind of nkSym: let s = n.sons[0].sym if s.loc.r == nil: internalError(n.info, "genAddr: 3") case s.kind of skVar, skLet, skResult: r.kind = resExpr let jsType = mapType(p, n.typ) if jsType == etyObject: # make addr() a no-op: r.typ = etyNone if isIndirect(s): r.res = s.loc.r & "[0]" elif p.target == targetPHP: r.res = "&" & s.loc.r else: r.res = s.loc.r r.address = nil elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: # for ease of code generation, we do not distinguish between # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex r.address = s.loc.r r.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: gen(p, n.sons[0], r) #internalError(n.info, "genAddr: 4 " & renderTree(n)) else: internalError(n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) of nkDotExpr: if mapType(p, n.typ) == etyBaseIndex: genFieldAddr(p, n.sons[0], r) else: genFieldAccess(p, n.sons[0], r) of nkBracketExpr: var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in MappedToObject: gen(p, n.sons[0], r) else: let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind case kindOfIndexedExpr of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n.sons[0], r) of tyTuple: genFieldAddr(p, n.sons[0], r) else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') of nkObjDownConv: gen(p, n.sons[0], r) else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind) proc thisParam(p: PProc; typ: PType): PType = if p.target == targetPHP: # XXX Might be very useful for the JS backend too? let typ = skipTypes(typ, abstractInst) assert(typ.kind == tyProc) if 1 < sonsLen(typ.n): assert(typ.n.sons[1].kind == nkSym) let param = typ.n.sons[1].sym if param.name.s == "this": result = param.typ.skipTypes(abstractVar) proc attachProc(p: PProc; content: Rope; s: PSym) = let otyp = thisParam(p, s.typ) if otyp != nil: for i, cls in p.g.classes: if sameType(cls[0], otyp): add(p.g.classes[i][1], content) return p.g.classes.add((otyp, content)) else: add(p.g.code, content) proc attachProc(p: PProc; s: PSym) = let newp = genProc(p, s) attachProc(p, newp, s) proc genProcForSymIfNeeded(p: PProc, s: PSym) = if not p.g.generatedSyms.containsOrIncl(s.id): let newp = genProc(p, s) var owner = p while owner != nil and owner.prc != s.owner: owner = owner.up if owner != nil: add(owner.locals, newp) else: attachProc(p, newp, s) proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult, skForVar: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) if p.target == targetJS: let k = mapType(p, s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: r.address = "$1[0]" % [s.loc.r] r.res = "$1[1]" % [s.loc.r] else: r.address = s.loc.r r.res = s.loc.r & "_Idx" elif isIndirect(s): r.res = "$1[0]" % [s.loc.r] else: r.res = s.loc.r else: r.res = "$" & s.loc.r if sfGlobal in s.flags: p.declareGlobal(s.id, r.res) of skConst: genConstant(p, s) if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) if p.target == targetJS: r.res = s.loc.r else: r.res = "$" & s.loc.r p.declareGlobal(s.id, r.res) of skProc, skConverter, skMethod: discard mangleName(s, p.target) if p.target == targetPHP and r.kind != resCallee: r.res = makeJsString($s.loc.r) else: r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard elif s.kind == skMethod and s.getBody.kind == nkEmpty: # we cannot produce code for the dispatcher yet: discard elif sfForward in s.flags: p.g.forwarded.add(s) else: genProcForSymIfNeeded(p, s) else: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = if mapType(p, n.sons[0].typ) == etyObject: gen(p, n.sons[0], r) else: var a: TCompRes gen(p, n.sons[0], a) if a.typ == etyBaseIndex: r.res = "$1[$2]" % [a.address, a.res] elif n.sons[0].kind == nkCall: let tmp = p.getTemp r.res = "($1 = $2, $1[0][$1[1]])" % [tmp, a.res] else: internalError(n.info, "genDeref") proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) if a.typ == etyBaseIndex: add(r.res, a.address) add(r.res, ", ") add(r.res, a.res) else: add(r.res, a.res) proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) = var a: TCompRes gen(p, n, a) if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and a.typ == etyBaseIndex: add(r.res, "$1[$2]" % [a.address, a.res]) elif a.typ == etyBaseIndex: add(r.res, a.address) add(r.res, ", ") add(r.res, a.res) else: add(r.res, a.res) proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) = add(r.res, "(") var hasArgs = false var typ = skipTypes(n.sons[0].typ, abstractInst) assert(typ.kind == tyProc) assert(sonsLen(typ) == sonsLen(typ.n)) for i in countup(start, sonsLen(n) - 1): let it = n.sons[i] var paramType: PNode = nil if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) paramType = typ.n.sons[i] if paramType.typ.isCompileTimeOnly: continue if hasArgs: add(r.res, ", ") if paramType.isNil: genArgNoParam(p, it, r) else: genArg(p, it, paramType.sym, r) hasArgs = true add(r.res, ")") r.kind = resExpr proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; generated: var int; r: var TCompRes) = let it = n[i] var paramType: PNode = nil if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) paramType = typ.n.sons[i] if paramType.typ.isCompileTimeOnly: return if paramType.isNil: genArgNoParam(p, it, r) else: genArg(p, it, paramType.sym, r) proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; r: var TCompRes) = var i = 0 var j = 1 while i < pat.len: case pat[i] of '@': var generated = 0 for k in j ..
#
#
#            Nim's Runtime Library
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# Until std_arg!!
# done: ipc, pwd, stat, semaphore, sys/types, sys/utsname, pthread, unistd,
# statvfs, mman, time, wait, signal, nl_types, sched, spawn, select, ucontext,
# net/if, sys/socket, sys/uio, netinet/in, netinet/tcp, netdb

## This is a raw POSIX interface module. It does not not provide any
## convenience: cstrings are used instead of proper Nim strings and
## return codes indicate errors. If you want exceptions
## and a proper Nim-like interface, use the OS module or write a wrapper.
##
## For high-level wrappers specialized for Linux and BSDs see:
## `posix_utils <posix_utils.html>`_
##
## Coding conventions:
## ALL types are named the same as in the POSIX standard except that they start
## with 'T' or 'P' (if they are pointers) and without the '_t' suffix to be
## consistent with Nim conventions. If an identifier is a Nim keyword
## the \`identifier\` notation is used.
##
## This library relies on the header files of your C compiler. The
## resulting C code will just `#include <XYZ.h>` and *not* define the
## symbols declared here.

# Dead code elimination ensures that we don't accidentally generate #includes
# for files that might not exist on a specific platform! The user will get an
# error only if they actually try to use the missing declaration

when defined(nimHasStyleChecks):
  {.push styleChecks: off.}

when defined(nimPreviewSlimSystem):
  import std/syncio

# TODO these constants don't seem to be fetched from a header file for unknown
#      platforms - where do they come from and why are they here?
when false:
  const
    C_IRUSR = 0o000400 ## Read by owner.
    C_IWUSR = 0o000200 ## Write by owner.
    C_IXUSR = 0o000100 ## Execute by owner.
    C_IRGRP = 0o000040 ## Read by group.
    C_IWGRP = 0o000020 ## Write by group.
    C_IXGRP = 0o000010 ## Execute by group.
    C_IROTH = 0o000004 ## Read by others.
    C_IWOTH = 0o000002 ## Write by others.
    C_IXOTH = 0o000001 ## Execute by others.
    C_ISUID = 0o004000 ## Set user ID.
    C_ISGID = 0o002000 ## Set group ID.
    C_ISVTX = 0o001000 ## On directories, restricted deletion flag.
    C_ISDIR = 0o040000 ## Directory.
    C_ISFIFO = 0o010000 ##FIFO.
    C_ISREG = 0o100000 ## Regular file.
    C_ISBLK = 0o060000 ## Block special.
    C_ISCHR = 0o020000 ## Character special.
    C_ISCTG = 0o110000 ## Reserved.
    C_ISLNK = 0o120000 ## Symbolic link.</p>
    C_ISSOCK = 0o140000 ## Socket.

const
  MM_NULLLBL* = nil
  MM_NULLSEV* = 0
  MM_NULLMC* = 0
  MM_NULLTXT* = nil
  MM_NULLACT* = nil
  MM_NULLTAG* = nil

  STDERR_FILENO* = 2 ## File number of stderr;
  STDIN_FILENO* = 0  ## File number of stdin;
  STDOUT_FILENO* = 1 ## File number of stdout;

  DT_UNKNOWN* = 0 ## Unknown file type.
  DT_FIFO* = 1    ## Named pipe, or FIFO.
  DT_CHR* = 2     ## Character device.
  DT_DIR* = 4     ## Directory.
  DT_BLK* = 6     ## Block device.
  DT_REG* = 8     ## Regular file.
  DT_LNK* = 10    ## Symbolic link.
  DT_SOCK* = 12   ## UNIX domain socket.
  DT_WHT* = 14

# Special types
type Sighandler = proc (a: cint) {.noconv.}

const StatHasNanoseconds* = defined(linux) or defined(freebsd) or
    defined(osx) or defined(openbsd) or defined(dragonfly) or defined(haiku) ## \
  ## Boolean flag that indicates if the system supports nanosecond time
  ## resolution in the fields of `Stat`. Note that the nanosecond based fields
  ## (`Stat.st_atim`, `Stat.st_mtim` and `Stat.st_ctim`) can be accessed
  ## without checking this flag, because this module defines fallback procs
  ## when they are not available.

# Platform specific stuff

when (defined(linux) and not defined(android)) and defined(amd64):
  include posix_linux_amd64
elif defined(openbsd) and defined(amd64):
  include posix_openbsd_amd64
elif (defined(macos) or defined(macosx) or defined(bsd)) and defined(cpu64):
  include posix_macos_amd64
elif defined(nintendoswitch):
  include posix_nintendoswitch
elif defined(haiku):
  include posix_haiku
else:
  include posix_other

# There used to be this name in posix.nim a long time ago, not sure why!

when StatHasNanoseconds:
  proc st_atime*(s: Stat): Time {.inline.} =
    ## Second-granularity time of last access.
    result = s.st_atim.tv_sec
  proc st_mtime*(s: Stat): Time {.inline.} =
    ## Second-granularity time of last data modification.
    result = s.st_mtim.tv_sec
  proc st_ctime*(s: Stat): Time {.inline.} =
    ## Second-granularity time of last status change.
    result = s.st_ctim.tv_sec
else:
  proc st_atim*(s: Stat): Timespec {.inline.} =
    ## Nanosecond-granularity time of last access.
    result.tv_sec = s.st_atime
  proc st_mtim*(s: Stat): Timespec {.inline.} =
    ## Nanosecond-granularity time of last data modification.
    result.tv_sec = s.st_mtime
  proc st_ctim*(s: Stat): Timespec {.inline.} =
    ## Nanosecond-granularity time of last data modification.
    result.tv_sec = s.st_ctime

when hasAioH:
  proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}
  proc aio_error*(a1: ptr Taiocb): cint {.importc, header: "<aio.h>".}
  proc aio_fsync*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}
  proc aio_read*(a1: ptr Taiocb): cint {.importc, header: "<aio.h>".}
  proc aio_return*(a1: ptr Taiocb): int {.importc, header: "<aio.h>".}
  proc aio_suspend*(a1: ptr ptr Taiocb, a2: cint, a3: ptr Timespec): cint {.
                   importc, header: "<aio.h>".}
  proc aio_write*(a1: ptr Taiocb): cint {.importc, header: "<aio.h>".}
  proc lio_listio*(a1: cint, a2: ptr ptr Taiocb, a3: cint,
               a4: ptr SigEvent): cint {.importc, header: "<aio.h>".}

# arpa/inet.h
proc htonl*(a1: uint32): uint32 {.importc, header: "<arpa/inet.h>".}
proc htons*(a1: uint16): uint16 {.importc, header: "<arpa/inet.h>".}
proc ntohl*(a1: uint32): uint32 {.importc, header: "<arpa/inet.h>".}
proc ntohs*(a1: uint16): uint16 {.importc, header: "<arpa/inet.h>".}

when not defined(zephyr):
  proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".}
  proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".}

proc ine