summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/jsgen.nim120
-rw-r--r--lib/system/jssys.nim25
-rw-r--r--lib/system/reprjs.nim14
-rw-r--r--tests/js/t12672.nim12
-rw-r--r--tests/js/t14153.nim22
-rw-r--r--tests/js/tjshello.nim2
-rw-r--r--tests/js/tjshello_stacktrace.nim9
-rw-r--r--tests/js/tmangle.nim4
-rw-r--r--tests/js/trepr.nim18
-rw-r--r--tests/js/ttempgen.nim79
10 files changed, 240 insertions, 65 deletions
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 751c043c5..eb9e97fac 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -86,6 +86,7 @@ type
     generatedSyms: IntSet
     typeInfoGenerated: IntSet
     unique: int    # for temp identifier generation
+    inSystem: bool
 
   PProc = ref TProc
   TProc = object
@@ -159,6 +160,14 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
     extraIndent: int(procDef != nil))
   if procDef != nil: result.prc = procDef[namePos].sym
 
+proc initProcOptions(module: BModule): TOptions =
+  result = module.config.options
+  if PGlobals(module.graph.backend).inSystem:
+    result.excl(optStackTrace)
+
+proc newInitProc(globals: PGlobals, module: BModule): PProc =
+  result = newProc(globals, module, nil, initProcOptions(module))
+
 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])
@@ -455,7 +464,48 @@ proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
   else:
     (a: a, tmp: b)
 
-template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+proc maybeMakeTempAssignable(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]
+      result = (a: a, tmp: b)
+    elif x.tmpLoc != nil and n.kind == nkBracketExpr:
+      # genArrayAddr
+      var
+        address, index: TCompRes
+        first: Int128
+      gen(p, n[0], address)
+      gen(p, n[1], index)
+      let (m1, tmp1) = maybeMakeTemp(p, n[0], address)
+      let typ = skipTypes(n[0].typ, abstractPtrs)
+      if typ.kind == tyArray:
+        first = firstOrd(p.config, typ[0])
+      if optBoundsCheck in p.options:
+        useMagic(p, "chckIndx")
+        if first == 0: # save a couple chars
+          index.res = "chckIndx($1, 0, ($2).length-1)" % [index.res, tmp1]
+        else:
+          index.res = "chckIndx($1, $2, ($3).length+($2)-1)-($2)" % [
+            index.res, rope(first), tmp1]
+      elif first != 0:
+        index.res = "($1)-($2)" % [index.res, rope(first)]
+      else:
+        discard # index.res = index.res
+      let (n1, tmp2) = maybeMakeTemp(p, n[1], index)
+      result = (a: "$1[$2]" % [m1, n1], tmp: "$1[$2]" % [tmp1, tmp2])
+    # could also put here: nkDotExpr -> genFieldAccess, nkCheckedFieldExpr -> genCheckedFieldOp
+    # but the uses of maybeMakeTempAssignable don't need them
+    else:
+      result = (a: a, tmp: b)
+  else:
+    result = (a: a, tmp: b)
+
+template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
+                    reassign = false) =
   # $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
@@ -467,8 +517,11 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   var
     a, tmp = x.rdLoc
     b, tmp2 = y.rdLoc
-  when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
-  when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y)
+  when reassign:
+    (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
+  else:
+    when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
+    when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y)
 
   r.res = frmt % [a, b, tmp, tmp2]
   r.kind = resExpr
@@ -485,13 +538,13 @@ template unsignedTrimmer(size: BiggestInt): Rope =
   size.unsignedTrimmerJS
 
 proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
-                    reassign = false) =
+                    reassign: static[bool] = false) =
   var x, y: TCompRes
   gen(p, n[1], x)
   gen(p, n[2], y)
   let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
-  if reassign:
-    let (a, tmp) = maybeMakeTemp(p, n[1], x)
+  when reassign:
+    let (a, tmp) = maybeMakeTempAssignable(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]
@@ -1161,7 +1214,11 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
     first = firstOrd(p.config, typ[0])
   if optBoundsCheck in p.options:
     useMagic(p, "chckIndx")
-    r.res = "chckIndx($1, $2, ($3 != null ? $3.length : 0)+$2-1)-($2)" % [b.res, rope(first), tmp]
+    if first == 0: # save a couple chars
+      r.res = "chckIndx($1, 0, ($2).length-1)" % [b.res, tmp]
+    else:
+      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:
@@ -1631,7 +1688,11 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
       result = putToSeq("[null, 0]", indirect)
     else:
       result = putToSeq("null", indirect)
-  of tySequence, tyOpt, tyString, tyCString, tyProc:
+  of tySequence, tyString:
+    result = putToSeq("[]", indirect)
+  of tyCString:
+    result = putToSeq("\"\"", indirect)
+  of tyOpt, tyProc:
     result = putToSeq("null", indirect)
   of tyStatic:
     if t.n != nil:
@@ -1862,7 +1923,7 @@ proc genReset(p: PProc, n: PNode) =
   if x.typ == etyBaseIndex:
     lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
   else:
-    let (a, tmp) = maybeMakeTemp(p, n[1], x)
+    let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
     lineF(p, "$1 = genericReset($3, $2);$n", [a,
                   genTypeInfo(p, n[1].typ), tmp])
 
@@ -1888,37 +1949,33 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mSwap: genSwap(p, n)
   of mAppendStrCh:
     binaryExpr(p, n, r, "addChar",
-        "if ($1 != null) { addChar($3, $2); } else { $3 = [$2]; }")
+        "addChar($1, $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[1].typ, abstractVarRange).kind == tyCString:
-      r.res = "if ($1 != null) { $3 += $2; } else { $3 = $2; }" % [
-        a, rhs.rdLoc, tmp]
+      r.res = "$1 += $2;" % [lhs.rdLoc, rhs.rdLoc]
     else:
-      r.res = "if ($1 != null) { $4 = ($4).concat($2); } else { $4 = $2$3; }" % [
-          lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp]
+      let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
+      r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
     r.kind = resExpr
   of mAppendSeqElem:
     var x, y: TCompRes
     gen(p, n[1], x)
     gen(p, n[2], y)
-    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]
+      r.res = "$1.push($2);" % [x.rdLoc, c]
     elif needsNoCopy(p, n[2]):
-      r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, y.rdLoc, tmp]
+      r.res = "$1.push($2);" % [x.rdLoc, y.rdLoc]
     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) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp]
+      r.res = "$1.push($2);" % [x.rdLoc, c]
     r.kind = resExpr
   of mConStrStr:
     genConStrStr(p, n, r)
@@ -1950,23 +2007,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   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 ? $2.length : 0)")
+    unaryExpr(p, n, r, "", "($1).length")
   of mHigh:
-    unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)")
+    unaryExpr(p, n, r, "", "(($1).length-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($3, $2)")
+      else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true)
   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($3, $2)")
+      else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true)
   of mSetLengthStr:
-    binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)")
+    binaryExpr(p, n, r, "mnewString", "($1.length = $2)")
   of mSetLengthSeq:
     var x, y: TCompRes
     gen(p, n[1], x)
@@ -1974,8 +2031,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     let t = skipTypes(n[1].typ, abstractVar)[0]
     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); }
+    r.res = """if ($1.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)")
@@ -2482,6 +2538,8 @@ proc newModule(g: ModuleGraph; module: PSym): BModule =
     g.backend = newGlobals()
   result.graph = g
   result.config = g.config
+  if sfSystemModule in module.flags:
+    PGlobals(g.backend).inSystem = true
 
 proc genHeader(): Rope =
   result = rope("""/* Generated by the Nim Compiler v$1 */
@@ -2555,7 +2613,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   if passes.skipCodegen(m.config, n): return n
   if m.module == nil: internalError(m.config, n.info, "myProcess")
   let globals = PGlobals(m.graph.backend)
-  var p = newProc(globals, m, nil, m.module.options)
+  var p = newInitProc(globals, m)
   p.unique = globals.unique
   genModule(p, n)
   p.g.code.add(p.locals)
@@ -2565,14 +2623,14 @@ proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
   let globals = PGlobals(graph.backend)
   for prc in globals.forwarded:
     if not globals.generatedSyms.containsOrIncl(prc.id):
-      var p = newProc(globals, m, nil, m.module.options)
+      var p = newInitProc(globals, m)
       attachProc(p, prc)
 
   var disp = generateMethodDispatchers(graph)
   for i in 0..<disp.len:
     let prc = disp[i].sym
     if not globals.generatedSyms.containsOrIncl(prc.id):
-      var p = newProc(globals, m, nil, m.module.options)
+      var p = newInitProc(globals, m)
       attachProc(p, prc)
 
   result = globals.typeInfo & globals.constants & globals.code
@@ -2592,6 +2650,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   if sfMainModule in m.module.flags:
     for destructorCall in graph.globalDestructors:
       n.add destructorCall
+  if sfSystemModule in m.module.flags:
+    PGlobals(graph.backend).inSystem = false
   if passes.skipCodegen(m.config, n): return n
   if sfMainModule in m.module.flags:
     var code = genHeader() & wholeCode(graph, m)
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 98a7a2032..106283490 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -397,24 +397,29 @@ else:
     """.}
 
 # Arithmetic:
+proc checkOverflowInt(a: int) {.asmNoStackFrame, compilerproc.} =
+  asm """
+    if (`a` > 2147483647 || `a` < -2147483648) `raiseOverflow`();
+  """
+
 proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   asm """
     var result = `a` + `b`;
-    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    `checkOverflowInt`(result);
     return result;
   """
 
 proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   asm """
     var result = `a` - `b`;
-    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    `checkOverflowInt`(result);
     return result;
   """
 
 proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   asm """
     var result = `a` * `b`;
-    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    `checkOverflowInt`(result);
     return result;
   """
 
@@ -432,27 +437,29 @@ proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
     return Math.trunc(`a` % `b`);
   """
 
+proc checkOverflowInt64(a: int) {.asmNoStackFrame, compilerproc.} =
+  asm """
+    if (`a` > 9223372036854775807 || `a` < -9223372036854775808) `raiseOverflow`();
+  """
+
 proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   asm """
     var result = `a` + `b`;
-    if (result > 9223372036854775807
-    || result < -9223372036854775808) `raiseOverflow`();
+    `checkOverflowInt64`(result);
     return result;
   """
 
 proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   asm """
     var result = `a` - `b`;
-    if (result > 9223372036854775807
-    || result < -9223372036854775808) `raiseOverflow`();
+    `checkOverflowInt64`(result);
     return result;
   """
 
 proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   asm """
     var result = `a` * `b`;
-    if (result > 9223372036854775807
-    || result < -9223372036854775808) `raiseOverflow`();
+    `checkOverflowInt64`(result);
     return result;
   """
 
diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim
index c45053537..c3f3199a4 100644
--- a/lib/system/reprjs.nim
+++ b/lib/system/reprjs.nim
@@ -130,20 +130,6 @@ proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
 
 proc reprArray(a: pointer, typ: PNimType,
               cl: var ReprClosure): string {.compilerRtl.} =
-  var isNilArrayOrSeq: bool
-  # isnil is not enough here as it would try to deref `a` without knowing what's inside
-  {. emit: """
-    if (`a` == null) {
-      `isNilArrayOrSeq` = true;
-    } else if (`a`[0] == null) {
-      `isNilArrayOrSeq` = true;
-    } else {
-      `isNilArrayOrSeq` = false;
-    };
-    """ .}
-  if typ.kind == tySequence and isNilArrayOrSeq:
-    return "nil"
-
   # We prepend @ to seq, the C backend prepends the pointer to the seq.
   result = if typ.kind == tySequence: "@[" else: "["
   var len: int = 0
diff --git a/tests/js/t12672.nim b/tests/js/t12672.nim
new file mode 100644
index 000000000..a658fbcbe
--- /dev/null
+++ b/tests/js/t12672.nim
@@ -0,0 +1,12 @@
+discard """
+  output: ""
+"""
+
+proc foo =
+  var x: seq[seq[int]]
+  for row in x.mitems:
+    let i = 1
+    echo row
+    inc row[i-1]
+
+foo()
diff --git a/tests/js/t14153.nim b/tests/js/t14153.nim
new file mode 100644
index 000000000..350bbd83b
--- /dev/null
+++ b/tests/js/t14153.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+index 5 not in 0 .. 2
+index 5 not in 0 .. 2
+'''
+"""
+
+var x = @[1, 2, 3]
+
+try:
+  echo x[5]
+except IndexError:
+  echo getCurrentExceptionMsg()
+except:
+  doAssert false
+
+try:
+  x[5] = 8
+except IndexError:
+  echo getCurrentExceptionMsg()
+except:
+  doAssert false
diff --git a/tests/js/tjshello.nim b/tests/js/tjshello.nim
index 19e0b90ae..8e090b3d2 100644
--- a/tests/js/tjshello.nim
+++ b/tests/js/tjshello.nim
@@ -1,4 +1,5 @@
 discard """
+  cmd: "nim $target $options --stackTrace:off --lineTrace:off $file"
   output: "Hello World"
   maxcodesize: 1000
   ccodecheck: "!@'function'"
@@ -7,4 +8,3 @@ discard """
 import jsconsole
 
 console.log "Hello World"
-
diff --git a/tests/js/tjshello_stacktrace.nim b/tests/js/tjshello_stacktrace.nim
new file mode 100644
index 000000000..d5e1c36eb
--- /dev/null
+++ b/tests/js/tjshello_stacktrace.nim
@@ -0,0 +1,9 @@
+discard """
+  output: "Hello World"
+  maxcodesize: 4500
+  ccodecheck: "!@'function'"
+"""
+
+import jsconsole
+
+console.log "Hello World"
diff --git a/tests/js/tmangle.nim b/tests/js/tmangle.nim
index c97bf7029..393043722 100644
--- a/tests/js/tmangle.nim
+++ b/tests/js/tmangle.nim
@@ -62,8 +62,8 @@ block:
     result = result and obj1.`&&`.addr[] == "bar".cstring
     result = result and obj2.`if` == 0
     result = result and obj2.`for` == 0
-    result = result and obj2.`==`.isNil()
-    result = result and obj2.`&&`.isNil()
+    result = result and obj2.`==`.len == 0
+    result = result and obj2.`&&`.len == 0
   echo test()
 
 # Test codegen for fields with uppercase letters:
diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim
index 366d247c5..ffa9d4de0 100644
--- a/tests/js/trepr.nim
+++ b/tests/js/trepr.nim
@@ -308,21 +308,21 @@ b = 0,
 c = 0.0,
 d = '\0',
 e = eA,
-f = nil,
+f = "",
 g = {},
 h = {},
-i = [nil, nil, nil],
-j = nil,
+i = ["", "", ""],
+j = @[],
 k = 0,
-l = [a = nil,
-b = nil],
+l = [a = "",
+b = @[]],
 m = nil,
 n = nil,
-o = [Field0 = [a = nil,
-b = nil],
-Field1 = nil],
+o = [Field0 = [a = "",
+b = @[]],
+Field1 = ""],
 p = nil,
-q = nil]
+q = ""]
 """)
   doAssert(repr(cc) == """
 [a = 12,
diff --git a/tests/js/ttempgen.nim b/tests/js/ttempgen.nim
new file mode 100644
index 000000000..badc66c1b
--- /dev/null
+++ b/tests/js/ttempgen.nim
@@ -0,0 +1,79 @@
+discard """
+  output: '''
+foo
+'''
+"""
+
+block: # #12672
+  var a = @[1]
+  let i = 1
+  inc a[i-1]
+
+  var b: seq[int]
+  doAssertRaises(IndexDefect): inc b[0]
+  doAssertRaises(IndexDefect): inc b[i-1]
+
+  var x: seq[seq[int]]
+  doAssertRaises(IndexDefect): # not TypeError
+    inc x[0][i-1]
+
+block: # #14087
+  type Obj = object
+    str: string
+
+  var s = @[Obj(str: "abc"), Obj(str: "def")]
+  s[1].str.add("ghi")
+  s[s.len - 1].str.add("jkl")
+  s[^1].str.add("mno")
+  s[s.high].str.add("pqr")
+
+  let slen = s.len
+  s[slen - 1].str.add("stu")
+
+  let shigh = s.high
+  s[shigh].str.add("vwx")
+
+  proc foo(): int =
+    echo "foo"
+    shigh
+  s[foo()].str.add("yz")
+  doAssert s[1].str == "defghijklmnopqrstuvwxyz"
+
+block: # #14117
+  type
+    A = object
+      case kind: bool
+      of true:
+        sons: seq[int]
+      else: discard
+
+  var a = A(kind: true)
+  doAssert a.sons.len == 0
+  a.sons.add(1)
+  doAssert a.sons.len == 1
+
+import tables
+
+block: # #13966
+  var t: Table[int8, array[int8, seq[tuple[]]]]
+
+  t[0] = default(array[int8, seq[tuple[]]])
+  t[0][0].add ()
+
+block: # #11783
+  proc fun(): string =
+    discard
+
+  var ret: string
+  ret.add fun()
+  doAssert ret == ""
+
+block: # #12256
+  var x: bool
+
+  doAssert x == false
+
+  reset x
+
+  doAssert x == false
+  doAssert x != true