mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-21 16:31:39 +00:00
Fix fat pointers, object copying, magic double evals on JS (#9411) [backport]
* Add a test for issue #9389 * Fixes #9389. * Make object contructors copy objects properly by checking whether the expressions passed to them don't need to be copied. * Make mArrToSeq implementation actually check if a copy needs to be made. * Avoid unnecessary copy in mChr impl * Assume set constructor elements need no copy * Add a test for issue #9410 * Add a test * fix passing fat pointers (#9410) * Enhance tests * More tests and fixes * Add more (failing) tests [ci skip] * Added equality operator for fat pointers, more tests and fixes * Fix printing uninitialized strings * Fix mInc, mDec double eval, add more tests * Tests * Refactored, fixed multiple evals, revamped the tests, added missing ops * Fix ups * Fix #9643 and #9644 * add pointer normalization
This commit is contained in:
@@ -66,6 +66,12 @@ type
|
||||
res: Rope # result part; index if this is an
|
||||
# (address, index)-tuple
|
||||
address: Rope # address of an (address, index)-tuple
|
||||
tmpLoc: Rope # tmp var which stores the (address, index)
|
||||
# pair to prevent multiple evals.
|
||||
# the tmp is initialized upon evaling the
|
||||
# address.
|
||||
# might be nil.
|
||||
# (see `maybeMakeTemp`)
|
||||
|
||||
TBlock = object
|
||||
id: int # the ID of the label; positive means that it
|
||||
@@ -131,16 +137,15 @@ proc newGlobals(): PGlobals =
|
||||
proc initCompRes(r: var TCompRes) =
|
||||
r.address = nil
|
||||
r.res = nil
|
||||
r.tmpLoc = 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]
|
||||
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 =
|
||||
@@ -447,12 +452,48 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
|
||||
["", "", "$1", "$1"]]
|
||||
|
||||
proc needsTemp(p: PProc; n: PNode): bool =
|
||||
# check if n contains a call to determine
|
||||
# if a temp should be made to prevent multiple evals
|
||||
if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}:
|
||||
return true
|
||||
for c in n:
|
||||
if needsTemp(p, c):
|
||||
return true
|
||||
|
||||
proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
|
||||
var
|
||||
a = x.rdLoc
|
||||
b = a
|
||||
if needsTemp(p, n):
|
||||
# if we have tmp just use it
|
||||
if x.tmpLoc != nil and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
|
||||
b = "$1[0][$1[1]]" % [x.tmpLoc]
|
||||
(a: a, tmp: b)
|
||||
else:
|
||||
let tmp = p.getTemp
|
||||
b = tmp
|
||||
a = "($1 = $2, $1)" % [tmp, a]
|
||||
(a: a, tmp: b)
|
||||
else:
|
||||
(a: a, tmp: b)
|
||||
|
||||
proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
# $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
|
||||
# if $3 or $4 are present they will be substituted with temps for
|
||||
# lhs and rhs respectively
|
||||
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]
|
||||
|
||||
var
|
||||
a, tmp = x.rdLoc
|
||||
b, tmp2 = y.rdLoc
|
||||
if "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
if "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
|
||||
r.res = frmt % [a, b, tmp, tmp2]
|
||||
r.kind = resExpr
|
||||
|
||||
proc unsignedTrimmerJS(size: BiggestInt): Rope =
|
||||
@@ -473,7 +514,8 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
|
||||
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]
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
|
||||
else:
|
||||
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
|
||||
|
||||
@@ -487,9 +529,12 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
r.kind = resExpr
|
||||
|
||||
proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
# $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
|
||||
useMagic(p, magic)
|
||||
gen(p, n.sons[1], r)
|
||||
r.res = frmt % [r.rdLoc]
|
||||
var a, tmp = r.rdLoc
|
||||
if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r)
|
||||
r.res = frmt % [a, tmp]
|
||||
r.kind = resExpr
|
||||
|
||||
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
@@ -524,6 +569,14 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
|
||||
mCStrToStr, mStrToStr, mEnumToStr:
|
||||
arithAux(p, n, r, op)
|
||||
of mEqRef, mEqUntracedRef:
|
||||
if mapType(n[1].typ) != etyBaseIndex:
|
||||
arithAux(p, n, r, op)
|
||||
else:
|
||||
var x, y: TCompRes
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res]
|
||||
else:
|
||||
arithAux(p, n, r, op)
|
||||
r.kind = resExpr
|
||||
@@ -801,6 +854,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
|
||||
# A fat pointer is disguised as an array
|
||||
r.res = r.address
|
||||
r.address = nil
|
||||
r.typ = etyNone
|
||||
elif r.typ == etyBaseIndex:
|
||||
# Deference first
|
||||
r.res = "$1[$2]" % [r.address, r.res]
|
||||
@@ -863,26 +917,42 @@ proc countJsParams(typ: PType): int =
|
||||
|
||||
const
|
||||
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString,
|
||||
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, 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, tyLent, tyVar})
|
||||
# if the node is a literal object constructor we have to recursively
|
||||
# check the expressions passed into it
|
||||
case y.kind
|
||||
of nkObjConstr:
|
||||
for arg in y.sons[1..^1]:
|
||||
if not needsNoCopy(p, arg[1]):
|
||||
return false
|
||||
of nkTupleConstr:
|
||||
for arg in y.sons:
|
||||
var arg = arg
|
||||
if arg.kind == nkExprColonExpr:
|
||||
arg = arg[1]
|
||||
if not needsNoCopy(p, arg):
|
||||
return false
|
||||
of nkBracket:
|
||||
for arg in y.sons:
|
||||
if not needsNoCopy(p, arg):
|
||||
return false
|
||||
of nodeKindsNeedNoCopy:
|
||||
return true
|
||||
else:
|
||||
return (mapType(y.typ) != etyBaseIndex and
|
||||
(skipTypes(y.typ, abstractInst).kind in
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCString} + IntegralTypes))
|
||||
return true
|
||||
|
||||
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
var a, b: TCompRes
|
||||
var 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)
|
||||
lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc])
|
||||
a.res = "$1[0][$1[1]]" % [tmp]
|
||||
else:
|
||||
gen(p, x, a)
|
||||
|
||||
gen(p, x, a)
|
||||
genLineDir(p, y)
|
||||
gen(p, y, b)
|
||||
|
||||
@@ -911,13 +981,13 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
let tmp = p.getTemp(false)
|
||||
lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
|
||||
elif b.typ == etyBaseIndex:
|
||||
lineF(p, "$# = $#;$n", [a.res, b.rdLoc])
|
||||
lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
|
||||
else:
|
||||
internalError(p.config, x.info, "genAsgn")
|
||||
else:
|
||||
lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
|
||||
else:
|
||||
lineF(p, "$1 = $2;$n", [a.res, b.res])
|
||||
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
||||
|
||||
proc genAsgn(p: PProc, n: PNode) =
|
||||
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false)
|
||||
@@ -971,17 +1041,30 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.kind = resExpr
|
||||
|
||||
proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyNone
|
||||
gen(p, n.sons[0], r)
|
||||
r.typ = mapType(n.typ)
|
||||
let otyp = skipTypes(n.sons[0].typ, abstractVarRange)
|
||||
|
||||
template mkTemp(i: int) =
|
||||
if r.typ == etyBaseIndex:
|
||||
if needsTemp(p, n[i]):
|
||||
let tmp = p.getTemp
|
||||
r.address = "($1 = $2, $1)[0]" % [tmp, r.res]
|
||||
r.res = "$1[1]" % [tmp]
|
||||
r.tmpLoc = tmp
|
||||
else:
|
||||
r.address = "$1[0]" % [r.res]
|
||||
r.res = "$1[1]" % [r.res]
|
||||
if otyp.kind == tyTuple:
|
||||
r.res = ("$1.Field$2") %
|
||||
[r.res, getFieldPosition(p, n.sons[1]).rope]
|
||||
mkTemp(0)
|
||||
else:
|
||||
if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess")
|
||||
var f = n.sons[1].sym
|
||||
if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
|
||||
r.res = "$1.$2" % [r.res, f.loc.r]
|
||||
mkTemp(1)
|
||||
r.kind = resExpr
|
||||
|
||||
proc genAddr(p: PProc, n: PNode, r: var TCompRes)
|
||||
@@ -1039,14 +1122,15 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
|
||||
gen(p, m.sons[0], a)
|
||||
gen(p, m.sons[1], b)
|
||||
internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
|
||||
r.address = a.res
|
||||
#internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
|
||||
let (x, tmp) = maybeMakeTemp(p, m[0], a)
|
||||
r.address = x
|
||||
var typ = skipTypes(m.sons[0].typ, abstractPtrs)
|
||||
if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0])
|
||||
else: first = 0
|
||||
if optBoundsCheck in p.options:
|
||||
useMagic(p, "chckIndx")
|
||||
r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
|
||||
r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), tmp]
|
||||
elif first != 0:
|
||||
r.res = "($1)-$2" % [b.res, rope(first)]
|
||||
else:
|
||||
@@ -1062,13 +1146,22 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of tyTuple:
|
||||
genFieldAddr(p, n, r)
|
||||
else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
||||
r.typ = etyNone
|
||||
r.typ = mapType(n.typ)
|
||||
if r.res == nil: internalError(p.config, n.info, "genArrayAccess")
|
||||
if ty.kind == tyCString:
|
||||
r.res = "$1.charCodeAt($2)" % [r.address, r.res]
|
||||
elif r.typ == etyBaseIndex:
|
||||
if needsTemp(p, n[0]):
|
||||
let tmp = p.getTemp
|
||||
r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
|
||||
r.res = "$1[1]" % [tmp]
|
||||
r.tmpLoc = tmp
|
||||
else:
|
||||
let x = r.rdLoc
|
||||
r.address = "$1[0]" % [x]
|
||||
r.res = "$1[1]" % [x]
|
||||
else:
|
||||
r.res = "$1[$2]" % [r.address, r.res]
|
||||
r.address = nil
|
||||
r.kind = resExpr
|
||||
|
||||
template isIndirect(x: PSym): bool =
|
||||
@@ -1169,8 +1262,12 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
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]
|
||||
if isIndirect(s):
|
||||
r.address = "$1[0][0]" % [s.loc.r]
|
||||
r.res = "$1[0][1]" % [s.loc.r]
|
||||
else:
|
||||
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"
|
||||
@@ -1210,14 +1307,17 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
var a: TCompRes
|
||||
gen(p, it, a)
|
||||
r.kind = resExpr
|
||||
if a.typ == etyBaseIndex:
|
||||
r.res = "$1[$2]" % [a.address, a.res]
|
||||
elif it.kind == nkCall:
|
||||
r.kind = a.kind
|
||||
r.typ = mapType(p, n.typ)
|
||||
if r.typ == etyBaseIndex:
|
||||
let tmp = p.getTemp
|
||||
r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res]
|
||||
elif t == etyBaseIndex:
|
||||
r.res = "$1[0]" % [a.res]
|
||||
r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc]
|
||||
r.res = "$1[1]" % [tmp]
|
||||
r.tmpLoc = tmp
|
||||
elif a.typ == etyBaseIndex:
|
||||
if a.tmpLoc != nil:
|
||||
r.tmpLoc = a.tmpLoc
|
||||
r.res = a.rdLoc
|
||||
else:
|
||||
internalError(p.config, n.info, "genDeref")
|
||||
|
||||
@@ -1242,7 +1342,7 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int =
|
||||
add(r.res, ", ")
|
||||
add(r.res, a.res)
|
||||
if emitted != nil: inc emitted[]
|
||||
elif n.typ.kind in {tyVar, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
|
||||
elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
|
||||
# this fixes bug #5608:
|
||||
let tmp = getTemp(p)
|
||||
add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
|
||||
@@ -1366,6 +1466,14 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) =
|
||||
return
|
||||
gen(p, n.sons[0], r)
|
||||
genArgs(p, n, r)
|
||||
if n.typ != nil:
|
||||
let t = mapType(n.typ)
|
||||
if t == etyBaseIndex:
|
||||
let tmp = p.getTemp
|
||||
r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
|
||||
r.res = "$1[1]" % [tmp]
|
||||
r.tmpLoc = tmp
|
||||
r.typ = t
|
||||
|
||||
proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let n = n[1].skipConv
|
||||
@@ -1472,12 +1580,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
createObjInitList(p, t, initIntSet(), initList)
|
||||
result = ("{$1}") % [initList]
|
||||
if indirect: result = "[$1]" % [result]
|
||||
of tyVar, tyPtr, tyLent, tyRef:
|
||||
of tyVar, tyPtr, tyLent, tyRef, tyPointer:
|
||||
if mapType(p, t) == etyBaseIndex:
|
||||
result = putToSeq("[null, 0]", indirect)
|
||||
else:
|
||||
result = putToSeq("null", indirect)
|
||||
of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc:
|
||||
of tySequence, tyOpt, tyString, tyCString, tyProc:
|
||||
result = putToSeq("null", indirect)
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
@@ -1511,10 +1619,13 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||
varCode = v.constraint.strVal
|
||||
|
||||
if n.kind == nkEmpty:
|
||||
lineF(p, varCode & " = $3;$n",
|
||||
[returnType, varName, createVar(p, v.typ, isIndirect(v))])
|
||||
if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex:
|
||||
if not isIndirect(v) and
|
||||
v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex:
|
||||
lineF(p, "var $1 = null;$n", [varName])
|
||||
lineF(p, "var $1_Idx = 0;$n", [varName])
|
||||
else:
|
||||
lineF(p, varCode & " = $3;$n",
|
||||
[returnType, varName, createVar(p, v.typ, isIndirect(v))])
|
||||
else:
|
||||
gen(p, n, a)
|
||||
case mapType(p, v.typ)
|
||||
@@ -1531,8 +1642,12 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||
lineF(p, varCode & " = $3, $2_Idx = $4;$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
else:
|
||||
lineF(p, varCode & " = [$3, $4];$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
if isIndirect(v):
|
||||
lineF(p, varCode & " = [[$3, $4]];$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
else:
|
||||
lineF(p, varCode & " = [$3, $4];$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
else:
|
||||
if targetBaseIndex:
|
||||
let tmp = p.getTemp
|
||||
@@ -1579,7 +1694,12 @@ proc genNew(p: PProc, n: PNode) =
|
||||
var a: TCompRes
|
||||
gen(p, n.sons[1], a)
|
||||
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
|
||||
lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
|
||||
if mapType(t) == etyObject:
|
||||
lineF(p, "$1 = $2;$n", [a.rdLoc, createVar(p, t, false)])
|
||||
elif a.typ == etyBaseIndex:
|
||||
lineF(p, "$1 = [$3]; $2 = 0;$n", [a.address, a.res, createVar(p, t, false)])
|
||||
else:
|
||||
lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)])
|
||||
|
||||
proc genNewSeq(p: PProc, n: PNode) =
|
||||
var x, y: TCompRes
|
||||
@@ -1603,20 +1723,20 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
|
||||
r.res.add("[$1].concat(" % [a.res])
|
||||
else:
|
||||
r.res.add("($1).concat(" % [a.res])
|
||||
r.res.add("($1 || []).concat(" % [a.res])
|
||||
|
||||
for i in countup(2, sonsLen(n) - 2):
|
||||
gen(p, n.sons[i], a)
|
||||
if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
|
||||
r.res.add("[$1]," % [a.res])
|
||||
else:
|
||||
r.res.add("$1," % [a.res])
|
||||
r.res.add("$1 || []," % [a.res])
|
||||
|
||||
gen(p, n.sons[sonsLen(n) - 1], a)
|
||||
if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar:
|
||||
r.res.add("[$1])" % [a.res])
|
||||
else:
|
||||
r.res.add("$1)" % [a.res])
|
||||
r.res.add("$1 || [])" % [a.res])
|
||||
|
||||
proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
|
||||
# we map mArray to PHP's array constructor, a mild hack:
|
||||
@@ -1701,8 +1821,12 @@ proc genReset(p: PProc, n: PNode) =
|
||||
var x: TCompRes
|
||||
useMagic(p, "genericReset")
|
||||
gen(p, n.sons[1], x)
|
||||
addf(p.body, "$1 = genericReset($1, $2);$n", [x.res,
|
||||
genTypeInfo(p, n.sons[1].typ)])
|
||||
if x.typ == etyBaseIndex:
|
||||
lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
|
||||
else:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
lineF(p, "$1 = genericReset($3, $2);$n", [a,
|
||||
genTypeInfo(p, n.sons[1].typ), tmp])
|
||||
|
||||
proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var
|
||||
@@ -1721,32 +1845,37 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
|
||||
of mAppendStrCh:
|
||||
binaryExpr(p, n, r, "addChar",
|
||||
"if ($1 != null) { addChar($1, $2); } else { $1 = [$2]; }")
|
||||
"if ($1 != null) { addChar($3, $2); } else { $3 = [$2]; }")
|
||||
of mAppendStrStr:
|
||||
var lhs, rhs: TCompRes
|
||||
gen(p, n[1], lhs)
|
||||
gen(p, n[2], rhs)
|
||||
|
||||
let rhsIsLit = n[2].kind in nkStrKinds
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
|
||||
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
|
||||
r.res = "if ($1 != null) { $1 += $2; } else { $1 = $2$3; }" % [
|
||||
lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()"]
|
||||
r.res = "if ($1 != null) { $4 += $2; } else { $4 = $2$3; }" % [
|
||||
a, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp]
|
||||
else:
|
||||
r.res = "if ($1 != null) { $1 = ($1).concat($2); } else { $1 = $2$3; }" % [
|
||||
lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()"]
|
||||
r.res = "if ($1 != null) { $4 = ($4).concat($2); } else { $4 = $2$3; }" % [
|
||||
lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp]
|
||||
r.kind = resExpr
|
||||
of mAppendSeqElem:
|
||||
var x, y: TCompRes
|
||||
gen(p, n.sons[1], x)
|
||||
gen(p, n.sons[2], y)
|
||||
if needsNoCopy(p, n[2]):
|
||||
r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc]
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
if mapType(n[2].typ) == etyBaseIndex:
|
||||
let c = "[$1, $2]" % [y.address, y.res]
|
||||
r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp]
|
||||
elif needsNoCopy(p, n[2]):
|
||||
r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, y.rdLoc, tmp]
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
let c = getTemp(p, defineInLocals=false)
|
||||
lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
|
||||
[c, y.rdLoc, genTypeInfo(p, n[2].typ)])
|
||||
r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c]
|
||||
r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp]
|
||||
r.kind = resExpr
|
||||
of mConStrStr:
|
||||
genConStrStr(p, n, r)
|
||||
@@ -1756,39 +1885,56 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
|
||||
of mLtStr:
|
||||
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
|
||||
of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
|
||||
of mIsNil:
|
||||
if mapType(n[1].typ) != etyBaseIndex:
|
||||
unaryExpr(p, n, r, "", "($1 === null)")
|
||||
else:
|
||||
var x: TCompRes
|
||||
gen(p, n[1], x)
|
||||
r.res = "($# === null && $# === 0)" % [x.address, x.res]
|
||||
of mEnumToStr: genRepr(p, n, r)
|
||||
of mNew, mNewFinalize: genNew(p, n)
|
||||
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
|
||||
of mChr: gen(p, n.sons[1], r)
|
||||
of mArrToSeq:
|
||||
if needsNoCopy(p, n.sons[1]):
|
||||
gen(p, n.sons[1], r)
|
||||
else:
|
||||
var x: TCompRes
|
||||
gen(p, n.sons[1], x)
|
||||
useMagic(p, "nimCopy")
|
||||
r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)]
|
||||
of mDestroy: discard "ignore calls to the default destructor"
|
||||
of mOrd: genOrd(p, n, r)
|
||||
of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
|
||||
unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
|
||||
unaryExpr(p, n, r, "", "($1 != null ? $2.length : 0)")
|
||||
of mXLenStr, mXLenSeq:
|
||||
unaryExpr(p, n, r, "", "$1.length")
|
||||
of mHigh:
|
||||
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
|
||||
unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)")
|
||||
of mInc:
|
||||
if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
|
||||
binaryUintExpr(p, n, r, "+", true)
|
||||
else:
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
|
||||
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
|
||||
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)")
|
||||
of ast.mDec:
|
||||
if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
|
||||
binaryUintExpr(p, n, r, "-", true)
|
||||
else:
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
|
||||
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
|
||||
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)")
|
||||
of mSetLengthStr:
|
||||
binaryExpr(p, n, r, "", "$1.length = $2")
|
||||
binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)")
|
||||
of mSetLengthSeq:
|
||||
var x, y: TCompRes
|
||||
gen(p, n.sons[1], x)
|
||||
gen(p, n.sons[2], y)
|
||||
let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
|
||||
r.res = """if ($1.length < $2) { for (var i=$1.length;i<$2;++i) $1.push($3); }
|
||||
else { $1.length = $2; }""" % [x.rdLoc, y.rdLoc, createVar(p, t, false)]
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
let (b, tmp2) = maybeMakeTemp(p, n[2], y)
|
||||
r.res = """if ($1 === null) $4 = [];
|
||||
if ($4.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); }
|
||||
else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2]
|
||||
r.kind = resExpr
|
||||
of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
|
||||
of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
|
||||
@@ -1856,7 +2002,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
if i > 0: add(r.res, ", ")
|
||||
gen(p, n.sons[i], a)
|
||||
add(r.res, a.res)
|
||||
if a.typ == etyBaseIndex:
|
||||
addf(r.res, "[$1, $2]", [a.address, a.res])
|
||||
else:
|
||||
add(r.res, a.res)
|
||||
add(r.res, "]")
|
||||
|
||||
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -1868,7 +2017,10 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var it = n.sons[i]
|
||||
if it.kind == nkExprColonExpr: it = it.sons[1]
|
||||
gen(p, it, a)
|
||||
addf(r.res, "Field$#: $#", [i.rope, a.res])
|
||||
if a.typ == etyBaseIndex:
|
||||
addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res])
|
||||
else:
|
||||
addf(r.res, "Field$#: $#", [i.rope, a.res])
|
||||
r.res.add("}")
|
||||
|
||||
proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -1888,12 +2040,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
|
||||
let typ = val.typ.skipTypes(abstractInst)
|
||||
if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and
|
||||
mapType(p, typ) != etyBaseIndex) or needsNoCopy(p, it.sons[1]):
|
||||
mapType(p, typ) != etyBaseIndex) or
|
||||
a.typ == etyBaseIndex or
|
||||
needsNoCopy(p, it.sons[1]):
|
||||
discard
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
addf(initList, "$#: $#", [f.loc.r, a.res])
|
||||
if a.typ == etyBaseIndex:
|
||||
addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res])
|
||||
else:
|
||||
addf(initList, "$#: $#", [f.loc.r, a.res])
|
||||
let t = skipTypes(n.typ, abstractInst + skipPtrs)
|
||||
createObjInitList(p, t, fieldIDs, initList)
|
||||
r.res = ("{$1}") % [initList]
|
||||
@@ -2011,11 +2168,14 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||
if prc.typ.sons[0] != nil and sfPure notin prc.flags:
|
||||
resultSym = prc.ast.sons[resultPos].sym
|
||||
let mname = mangleName(p.module, resultSym)
|
||||
let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
|
||||
resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
|
||||
if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and
|
||||
if not isindirect(resultSym) and
|
||||
resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and
|
||||
mapType(p, resultSym.typ) == etyBaseIndex:
|
||||
resultAsgn = p.indentLine(("var $# = null;$n") % [mname])
|
||||
resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
|
||||
else:
|
||||
let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
|
||||
resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
|
||||
gen(p, prc.ast.sons[resultPos], a)
|
||||
if mapType(p, resultSym.typ) == etyBaseIndex:
|
||||
returnStmt = "return [$#, $#];$n" % [a.address, a.res]
|
||||
@@ -2107,6 +2267,13 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of 4: "0xfffffffe"
|
||||
else: ""
|
||||
r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]
|
||||
elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
|
||||
r.address = r.res
|
||||
r.res = ~"null"
|
||||
r.typ = etyBaseIndex
|
||||
elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer:
|
||||
r.res = r.address
|
||||
r.typ = etyObject
|
||||
|
||||
proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyNone
|
||||
|
||||
@@ -2522,12 +2522,13 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
|
||||
when not defined(JS):
|
||||
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
|
||||
result = cast[pointer](x)
|
||||
else:
|
||||
proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
|
||||
asm """return `x`"""
|
||||
|
||||
if seqToPtr(x) == seqToPtr(y):
|
||||
return true
|
||||
if seqToPtr(x) == seqToPtr(y):
|
||||
return true
|
||||
else:
|
||||
var sameObject = false
|
||||
asm """`sameObject` = `x` === `y`"""
|
||||
if sameObject: return true
|
||||
|
||||
when not defined(nimNoNil):
|
||||
if x.isNil or y.isNil:
|
||||
|
||||
@@ -519,8 +519,11 @@ proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} =
|
||||
`dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ);
|
||||
"""
|
||||
of nkList:
|
||||
for i in 0..n.len-1:
|
||||
nimCopyAux(dest, src, n.sons[i])
|
||||
asm """
|
||||
for (var i = 0; i < `n`.sons.length; i++) {
|
||||
nimCopyAux(`dest`, `src`, `n`.sons[i]);
|
||||
}
|
||||
"""
|
||||
of nkCase:
|
||||
asm """
|
||||
`dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ);
|
||||
|
||||
@@ -64,7 +64,9 @@ proc reprStrAux(result: var string, s: cstring, len: int) =
|
||||
|
||||
proc reprStr(s: string): string {.compilerRtl.} =
|
||||
result = ""
|
||||
if cast[pointer](s).isNil:
|
||||
var sIsNil = false
|
||||
asm """`sIsNil` = `s` === null"""
|
||||
if sIsNil: # cast[pointer](s).isNil:
|
||||
# Handle nil strings here because they don't have a length field in js
|
||||
# TODO: check for null/undefined before generating call to length in js?
|
||||
# Also: c backend repr of a nil string is <pointer>"", but repr of an
|
||||
|
||||
454
tests/js/t9410.nim
Normal file
454
tests/js/t9410.nim
Normal file
@@ -0,0 +1,454 @@
|
||||
template doAssert(exp: untyped) =
|
||||
when defined(echot9410):
|
||||
let r = exp
|
||||
echo $(instantiationInfo().line) & ":\n " & astToStr(exp) & "\n was " & repr(r)
|
||||
when not defined(noassertt9410):
|
||||
system.doAssert r
|
||||
else:
|
||||
when not defined(noassertt9410):
|
||||
system.doAssert exp
|
||||
|
||||
template tests =
|
||||
block:
|
||||
var i = 0
|
||||
i = 2
|
||||
|
||||
var y: ptr int
|
||||
doAssert y == nil
|
||||
doAssert isNil(y)
|
||||
y = i.addr
|
||||
y[] = 3
|
||||
doAssert i == 3
|
||||
doAssert i == y[]
|
||||
|
||||
let z = i.addr
|
||||
z[] = 4
|
||||
doAssert i == 4
|
||||
doAssert i == y[] and y[] == z[]
|
||||
|
||||
var hmm = (a: (b: z))
|
||||
var hmmptr = hmm.a.b.addr
|
||||
hmmptr[][] = 5
|
||||
|
||||
doAssert i == 5
|
||||
doAssert y == z
|
||||
doAssert z == hmmptr[]
|
||||
doAssert 5 == y[] and 5 == z[] and 5 == hmmptr[][]
|
||||
|
||||
block:
|
||||
var someint = 500
|
||||
|
||||
let p: ptr int = someint.addr
|
||||
let tup = (f: p)
|
||||
let tcopy = tup
|
||||
var vtcopy = tcopy
|
||||
p[] = 654
|
||||
doAssert p[] == 654
|
||||
doAssert tup.f[] == 654
|
||||
doAssert tcopy.f[] == 654
|
||||
doAssert vtcopy.f[] == 654
|
||||
|
||||
block:
|
||||
var someint = 500
|
||||
|
||||
var p: ptr int = someint.addr
|
||||
let arr = [p]
|
||||
let arrc = arr
|
||||
p[] = 256
|
||||
doAssert someint == 256
|
||||
doAssert p[] == 256
|
||||
doAssert arr[0][] == 256
|
||||
doAssert arrc[0][] == 256
|
||||
|
||||
block:
|
||||
var someref: ref int
|
||||
new(someref)
|
||||
var someref2 = someref
|
||||
|
||||
var tup1 = (f: someref)
|
||||
tup1.f = someref
|
||||
let tup2 = tup1
|
||||
|
||||
someref[] = 543
|
||||
|
||||
proc passref(r: var ref int): var ref int = r
|
||||
new(passref(someref))
|
||||
|
||||
doAssert someref[] == 0
|
||||
doAssert tup1.f[] == 543
|
||||
doAssert tup2.f[] == 543
|
||||
doAssert someref2[] == 543
|
||||
|
||||
block:
|
||||
type Whatever = object
|
||||
i: ref int
|
||||
|
||||
var someref: ref int
|
||||
new(someref)
|
||||
someref[] = 10
|
||||
|
||||
let w = Whatever(i: someref)
|
||||
var wcopy = w
|
||||
|
||||
someref[] = 20
|
||||
|
||||
doAssert w.i[] == 20
|
||||
doAssert someref[] == 20
|
||||
doAssert wcopy.i[] == 20
|
||||
doAssert w.i == wcopy.i
|
||||
#echo w.i[], " ", someref[], " ", wcopy.i[]
|
||||
|
||||
block:
|
||||
var oneseq: ref seq[ref int]
|
||||
new(oneseq)
|
||||
var aref: ref int
|
||||
new(aref)
|
||||
aref[] = 123
|
||||
let arefs = [aref]
|
||||
oneseq[] &= arefs[0]
|
||||
oneseq[] &= aref
|
||||
aref[] = 222
|
||||
new(aref)
|
||||
doAssert oneseq[0] == oneseq[1]
|
||||
doAssert oneseq[0][] == 222
|
||||
doAssert oneseq[1][] == 222
|
||||
doAssert aref[] == 0
|
||||
|
||||
block:
|
||||
var seqs: ref seq[ref seq[ref int]]
|
||||
new(seqs)
|
||||
seqs[] = newSeq[ref seq[ref int]](1)
|
||||
new(seqs[0])
|
||||
seqs[0][] = newSeq[ref int](0)
|
||||
|
||||
var aref: ref int
|
||||
new aref
|
||||
aref[] = 654
|
||||
|
||||
let arefs = [aref]
|
||||
doAssert arefs[0] == aref
|
||||
seqs[0][] &= arefs[0]
|
||||
seqs[0][] &= aref
|
||||
seqs[0][1][] = 456
|
||||
let seqs2 = seqs
|
||||
let same = seqs2[0][0] == seqs2[0][1]
|
||||
doAssert arefs[0] == aref
|
||||
doAssert aref[] == 456
|
||||
doAssert seqs[].len == 1
|
||||
doAssert seqs[0][].len == 2
|
||||
doAssert seqs[0][0][] == 456
|
||||
doAssert seqs[0][1][] == 456
|
||||
doAssert same
|
||||
|
||||
block:
|
||||
type Obj = object
|
||||
x, y: int
|
||||
|
||||
var objrefs: seq[ref Obj] = @[(ref Obj)(nil), nil, nil]
|
||||
objrefs[2].new
|
||||
objrefs[2][] = Obj(x: 123, y: 321)
|
||||
objrefs[1] = objrefs[2]
|
||||
doAssert objrefs[0] == nil
|
||||
doAssert objrefs[1].y == 321
|
||||
doAssert objrefs[2].y == 321
|
||||
doAssert objrefs[1] == objrefs[2]
|
||||
|
||||
block:
|
||||
var refs: seq[ref string] = @[(ref string)(nil), nil, nil]
|
||||
refs[1].new
|
||||
refs[1][] = "it's a ref!"
|
||||
refs[0] = refs[1]
|
||||
refs[2] = refs[1]
|
||||
new(refs[0])
|
||||
doAssert refs[0][] == ""
|
||||
doAssert refs[1][] == "it's a ref!"
|
||||
doAssert refs[2][] == "it's a ref!"
|
||||
doAssert refs[1] == refs[2]
|
||||
|
||||
block:
|
||||
var retaddr_calls = 0
|
||||
proc retaddr(p: var int): var int =
|
||||
retaddr_calls += 1
|
||||
p
|
||||
|
||||
var tfoo_calls = 0
|
||||
proc tfoo(x: var int) =
|
||||
tfoo_calls += 1
|
||||
x += 10
|
||||
var y = x.addr
|
||||
y[] += 20
|
||||
retaddr(x) += 30
|
||||
let z = retaddr(x).addr
|
||||
z[] += 40
|
||||
|
||||
var ints = @[1, 2, 3]
|
||||
tfoo(ints[1])
|
||||
doAssert retaddr_calls == 2
|
||||
doAssert tfoo_calls == 1
|
||||
doAssert ints[1] == 102
|
||||
|
||||
var tbar_calls = 0
|
||||
proc tbar(x: var int): var int =
|
||||
tbar_calls += 1
|
||||
x
|
||||
|
||||
tbar(ints[2]) += 10
|
||||
tbar(ints[2]) *= 2
|
||||
doAssert tbar_calls == 2
|
||||
|
||||
var tqux_calls = 0
|
||||
proc tqux(x: var int): ptr int =
|
||||
tqux_calls += 1
|
||||
x.addr
|
||||
|
||||
discard tqux(ints[2]) == tqux(ints[2])
|
||||
doAssert tqux_calls == 2
|
||||
doAssert isNil(tqux(ints[2])) == false
|
||||
doAssert tqux_calls == 3
|
||||
|
||||
var tseq_calls = 0
|
||||
proc tseq(x: var seq[int]): var seq[int] =
|
||||
tseq_calls += 1
|
||||
x
|
||||
|
||||
tseq(ints) &= 999
|
||||
doAssert tseq_calls == 1
|
||||
doAssert ints == @[1, 102, 26, 999]
|
||||
|
||||
var rawints = @[555]
|
||||
rawints &= 666
|
||||
doAssert rawints == @[555, 666]
|
||||
|
||||
var resetints_calls = 0
|
||||
proc resetInts(): int =
|
||||
resetints_calls += 1
|
||||
ints = @[0, 0, 0]
|
||||
1
|
||||
|
||||
proc incr(x: var int; b: int): var int =
|
||||
x = x + b
|
||||
x
|
||||
|
||||
var q = 0
|
||||
var qp = q.addr
|
||||
qp[] += 123
|
||||
doAssert q == 123
|
||||
# check order of evaluation
|
||||
doAssert (resetInts() + incr(q, tqux(ints[2])[])) == 124
|
||||
|
||||
block: # reset
|
||||
var calls = 0
|
||||
proc passsomething(x: var int): var int =
|
||||
calls += 1
|
||||
x
|
||||
|
||||
var
|
||||
a = 123
|
||||
b = 500
|
||||
c = a.addr
|
||||
reset(passsomething(a))
|
||||
doAssert calls == 1
|
||||
reset(b)
|
||||
doAssert a == b
|
||||
reset(c)
|
||||
doAssert c == nil
|
||||
|
||||
block: # strings
|
||||
var calls = 0
|
||||
proc stringtest(s: var string): var string =
|
||||
calls += 1
|
||||
s
|
||||
|
||||
var somestr: string
|
||||
|
||||
stringtest(somestr) &= 'a'
|
||||
stringtest(somestr) &= 'b'
|
||||
doAssert calls == 2
|
||||
doAssert somestr == "ab"
|
||||
stringtest(somestr) &= "woot!"
|
||||
doAssert somestr == "abwoot!"
|
||||
doAssert calls == 3
|
||||
|
||||
doAssert stringtest(somestr).len == 7
|
||||
doAssert calls == 4
|
||||
doAssert high(stringtest(somestr)) == 6
|
||||
doAssert calls == 5
|
||||
|
||||
var somestr2: string
|
||||
stringtest(somestr2).setLen(stringtest(somestr).len)
|
||||
doAssert calls == 7
|
||||
doAssert somestr2.len == somestr.len
|
||||
|
||||
var somestr3: string
|
||||
doAssert (somestr3 & "foo") == "foo"
|
||||
|
||||
block:
|
||||
var a, b, c, d: string
|
||||
d = a & b & c
|
||||
doAssert d == ""
|
||||
d = stringtest(a) & stringtest(b) & stringtest(c)
|
||||
doAssert calls == 10
|
||||
doAssert d == ""
|
||||
|
||||
block: # seqs
|
||||
var calls = 0
|
||||
proc seqtest(s: var seq[int]): var seq[int] =
|
||||
calls += 1
|
||||
s
|
||||
|
||||
var someseq: seq[int]
|
||||
|
||||
seqtest(someseq) &= 1
|
||||
seqtest(someseq) &= 2
|
||||
doAssert calls == 2
|
||||
doAssert someseq == @[1, 2]
|
||||
seqtest(someseq) &= @[3, 4, 5]
|
||||
doAssert someseq == @[1, 2, 3, 4, 5]
|
||||
doAssert calls == 3
|
||||
|
||||
doAssert seqtest(someseq).len == 5
|
||||
doAssert calls == 4
|
||||
doAssert high(seqtest(someseq)) == 4
|
||||
doAssert calls == 5
|
||||
|
||||
# genArrayAddr
|
||||
doAssert seqtest(someseq)[2] == 3
|
||||
doAssert calls == 6
|
||||
|
||||
seqtest(someseq).setLen(seqtest(someseq).len)
|
||||
doAssert calls == 8
|
||||
|
||||
var somenilseq: seq[int]
|
||||
seqtest(somenilseq).setLen(3)
|
||||
doAssert calls == 9
|
||||
doAssert somenilseq[1] == 0
|
||||
|
||||
someseq = @[1, 2, 3]
|
||||
doAssert (seqtest(someseq) & seqtest(someseq)) == @[1, 2, 3, 1, 2, 3]
|
||||
|
||||
|
||||
block: # mInc, mDec
|
||||
var calls = 0
|
||||
proc someint(x: var int): var int =
|
||||
calls += 1
|
||||
x
|
||||
|
||||
var x = 10
|
||||
|
||||
inc(someint(x))
|
||||
doAssert x == 11
|
||||
doAssert calls == 1
|
||||
|
||||
dec(someint(x))
|
||||
doAssert x == 10
|
||||
doAssert calls == 2
|
||||
|
||||
block: # uints
|
||||
var calls = 0
|
||||
proc passuint(x: var uint32): var uint32 =
|
||||
calls += 1
|
||||
x
|
||||
|
||||
var u: uint32 = 5
|
||||
passuint(u) += 1
|
||||
doAssert u == 6
|
||||
doAssert calls == 1
|
||||
|
||||
passuint(u) -= 1
|
||||
doAssert u == 5
|
||||
doAssert calls == 2
|
||||
|
||||
passuint(u) *= 2
|
||||
doAssert u == 10
|
||||
doAssert calls == 3
|
||||
|
||||
block: # objs
|
||||
type Thing = ref object
|
||||
x, y: int
|
||||
|
||||
var a, b: Thing
|
||||
a = Thing()
|
||||
b = a
|
||||
|
||||
doAssert a == b
|
||||
|
||||
var calls = 0
|
||||
proc passobj(o: var Thing): var Thing =
|
||||
calls += 1
|
||||
o
|
||||
|
||||
passobj(b) = Thing(x: 123)
|
||||
doAssert calls == 1
|
||||
doAssert a != b
|
||||
doAssert b.x == 123
|
||||
|
||||
var passobjptr_calls = 0
|
||||
proc passobjptr(o: var Thing): ptr Thing =
|
||||
passobjptr_calls += 1
|
||||
o.addr
|
||||
|
||||
passobjptr(b)[] = Thing(x: 234)
|
||||
doAssert passobjptr_calls == 1
|
||||
doAssert a != b
|
||||
doAssert b.x == 234
|
||||
passobjptr(b)[].x = 500
|
||||
doAssert b.x == 500
|
||||
|
||||
var pptr = passobjptr(b)
|
||||
pptr.x += 100
|
||||
doAssert b.x == 600
|
||||
|
||||
proc getuninitptr(): ptr int =
|
||||
return
|
||||
|
||||
doAssert getuninitptr() == nil
|
||||
|
||||
block: # pointer casting
|
||||
var obj = (x: 321, y: 543)
|
||||
var x = 500
|
||||
|
||||
var objptr = obj.addr
|
||||
var xptr = x.addr
|
||||
|
||||
var p1, p2: pointer
|
||||
p1 = cast[pointer](objptr)
|
||||
p2 = cast[pointer](xptr)
|
||||
doAssert p1 != p2
|
||||
|
||||
p1 = cast[pointer](objptr)
|
||||
p2 = cast[pointer](objptr)
|
||||
doAssert p1 == p2
|
||||
|
||||
let objptr2 = cast[type(objptr)](p2)
|
||||
doAssert objptr == objptr2
|
||||
|
||||
p1 = cast[pointer](xptr)
|
||||
p2 = cast[pointer](xptr)
|
||||
doAssert p1 == p2
|
||||
|
||||
let xptr2 = cast[type(xptr)](p2)
|
||||
doAssert xptr == xptr2
|
||||
|
||||
when false:
|
||||
block: # openarray
|
||||
# Error: internal error: genAddr: nkStmtListExpr
|
||||
var calls = 0
|
||||
proc getvarint(x: var openarray[int]): var int =
|
||||
calls += 1
|
||||
if true:
|
||||
x[1]
|
||||
else:
|
||||
x[0]
|
||||
|
||||
var arr = [1, 2, 3]
|
||||
getvarint(arr) += 5
|
||||
doAssert calls == 1
|
||||
doAssert arr[1] == 7
|
||||
|
||||
proc tests_in_proc =
|
||||
tests
|
||||
|
||||
# since pointers are handled differently in global/local contexts
|
||||
# let's just run all of them twice
|
||||
tests_in_proc()
|
||||
tests
|
||||
@@ -2,6 +2,10 @@ discard """
|
||||
output: '''123
|
||||
2 9
|
||||
2 9
|
||||
1 124
|
||||
true false
|
||||
100 300 100
|
||||
1
|
||||
'''
|
||||
"""
|
||||
|
||||
@@ -35,3 +39,34 @@ block:
|
||||
obj.ary2[1] = 9
|
||||
|
||||
echo ary1[1], " ", obj.ary2[1]
|
||||
|
||||
block:
|
||||
type TestObj = object
|
||||
x, y: int
|
||||
|
||||
let obj = TestObj(x: 1, y: 2)
|
||||
var s = @[obj]
|
||||
s[0].x += 123
|
||||
echo obj.x, " ", s[0].x
|
||||
|
||||
block:
|
||||
var nums = {1, 2, 3, 4}
|
||||
let obj = (n: nums)
|
||||
nums.incl 5
|
||||
echo (5 in nums), " ", (5 in obj.n)
|
||||
|
||||
block:
|
||||
let tup1 = (a: 100)
|
||||
var tup2 = (t: (t2: tup1))
|
||||
var tup3 = tup1
|
||||
tup2.t.t2.a = 300
|
||||
echo tup1.a, " ", tup2.t.t2.a, " ", tup3.a
|
||||
|
||||
block:
|
||||
proc foo(arr: array[2, int]) =
|
||||
var s = @arr
|
||||
s[0] = 500
|
||||
|
||||
var nums = [1, 2]
|
||||
foo(nums)
|
||||
echo nums[0]
|
||||
Reference in New Issue
Block a user