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:
rec
2018-12-04 12:04:27 +01:00
committed by Andreas Rumpf
parent 07744b6e47
commit e3e5ae287f
6 changed files with 745 additions and 83 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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);

View File

@@ -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
View 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

View File

@@ -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]