From 240879bf3d746059accd73deacc7405c53aa0704 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 15 Feb 2021 04:28:11 -0600 Subject: [PATCH] array literals uses typed arrays; fix a jsgen bug (#16850) * array litterals uses typed arrays * Update compiler/jsgen.nim Co-authored-by: Timothee Cour Co-authored-by: Andreas Rumpf --- changelog.md | 2 ++ compiler/jsgen.nim | 55 +++++++++++++++++++++++++----------- lib/std/private/jsutils.nim | 8 ++++++ tests/js/tbigint_backend.nim | 5 ++-- tests/js/tseqops.nim | 21 ++++++-------- tests/js/ttypedarray.nim | 21 ++++++++++++++ 6 files changed, 81 insertions(+), 31 deletions(-) create mode 100644 tests/js/ttypedarray.nim diff --git a/changelog.md b/changelog.md index 4005dc123..b1dff53ba 100644 --- a/changelog.md +++ b/changelog.md @@ -194,6 +194,8 @@ provided by the operating system. - TLS: OSX now uses native TLS (`--tlsEmulation:off`), TLS now works with importcpp non-POD types, such types must use `.cppNonPod` and `--tlsEmulation:off`should be used. +- Now array literals(JS backend) uses JS typed arrays when the corresponding js typed array exists, for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`. + - docgen: rst files can now use single backticks instead of double backticks and correctly render in both rst2html (as before) as well as common tools rendering rst directly (e.g. github), by adding: `default-role:: code` directive inside the rst file, which is now handled by rst2html. diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 1bf204c0f..04009250e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1991,6 +1991,23 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = genReset(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) +proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = + var a: TCompRes + r.res = rope("[") + r.kind = resExpr + for i in 0 ..< n.len: + if i > 0: r.res.add(", ") + gen(p, n[i], a) + if a.typ == etyBaseIndex: + r.res.addf("[$1, $2]", [a.address, a.res]) + else: + if not needsNoCopy(p, n[i]): + let typ = n[i].typ.skipTypes(abstractInst) + useMagic(p, "nimCopy") + a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] + r.res.add(a.res) + r.res.add("]") + proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -2054,8 +2071,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mNew, mNewFinalize: genNew(p, n) of mChr: gen(p, n[1], r) of mArrToSeq: - if needsNoCopy(p, n[1]): - gen(p, n[1], r) + # only array literals doesn't need copy + if n[1].kind == nkBracket: + genJSArrayConstr(p, n[1], r) else: var x: TCompRes gen(p, n[1], x) @@ -2172,21 +2190,26 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = r.res = tmp proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes - r.res = rope("[") - r.kind = resExpr - for i in 0.. 0: r.res.add(", ") - gen(p, n[i], a) - if a.typ == etyBaseIndex: - r.res.addf("[$1, $2]", [a.address, a.res]) - else: - if not needsNoCopy(p, n[i]): - let typ = n[i].typ.skipTypes(abstractInst) - useMagic(p, "nimCopy") - a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] + ## Constructs array or sequence. + ## Nim array of uint8..uint32, int8..int32 maps to JS typed arrays. + ## Nim sequence maps to JS array. + var t = skipTypes(n.typ, abstractInst) + let e = elemType(t) + let jsTyp = arrayTypeForElemType(e) + if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0: + # generate typed array + # for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]` + # TODO use `set` or loop to initialize typed array which improves performances in some situations + var a: TCompRes + r.res = "new $1([" % [rope(jsTyp)] + r.kind = resExpr + for i in 0 ..< n.len: + if i > 0: r.res.add(", ") + gen(p, n[i], a) r.res.add(a.res) - r.res.add("]") + r.res.add("])") + else: + genJSArrayConstr(p, n, r) proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index aa2e984ff..14104261e 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -23,8 +23,16 @@ when defined(js): func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".} + proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} + ## Returns the name of the JsObject's JavaScript type as a cstring. + # xxx replace jsffi.jsTypeOf with this definition and add tests + + proc jsConstructorName*[T](a: T): cstring = + asm """`result` = `a`.constructor.name""" + proc hasJsBigInt*(): bool = asm """`result` = typeof BigInt != 'undefined'""" proc hasBigUint64Array*(): bool = asm """`result` = typeof BigUint64Array != 'undefined'""" + diff --git a/tests/js/tbigint_backend.nim b/tests/js/tbigint_backend.nim index db7ebb065..8f2f6c4f4 100644 --- a/tests/js/tbigint_backend.nim +++ b/tests/js/tbigint_backend.nim @@ -1,6 +1,5 @@ -proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} - ## Returns the name of the JsObject's JavaScript type as a cstring. - # xxx replace jsffi.jsTypeOf with this definition and add tests +import std/private/jsutils + type JsBigIntImpl {.importc: "bigint".} = int type JsBigInt = distinct JsBigIntImpl diff --git a/tests/js/tseqops.nim b/tests/js/tseqops.nim index af664222c..8cfb50886 100644 --- a/tests/js/tseqops.nim +++ b/tests/js/tseqops.nim @@ -1,11 +1,3 @@ -discard """ - output: '''(x: 0, y: 0) -(x: 5, y: 0) -@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)] -@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)] -''' -""" - # bug #4139 type @@ -17,8 +9,8 @@ proc onLoad() = var foo = TestO(x: 0, y: 0) test.add(foo) foo.x = 5 - echo(test[0]) - echo foo + doAssert $test[0] == "(x: 0, y: 0)" + doAssert $foo == "(x: 5, y: 0)" onLoad() @@ -34,7 +26,7 @@ proc foo(x: var seq[MyObj]) = var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)] foo(s) -echo s +doAssert $s == """@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)]""" # bug #5933 import sequtils @@ -48,4 +40,9 @@ var test = @[Test(a: "1", b: 1), Test(a: "2", b: 2)] test.insert(@[Test(a: "3", b: 3)], 0) -echo test +doAssert $test == """@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)]""" + +proc hello(): array[5, int] = discard +var x = @(hello()) +x.add(2) +doAssert x == @[0, 0, 0, 0, 0, 2] diff --git a/tests/js/ttypedarray.nim b/tests/js/ttypedarray.nim new file mode 100644 index 000000000..222f66569 --- /dev/null +++ b/tests/js/ttypedarray.nim @@ -0,0 +1,21 @@ +import std/private/jsutils + +proc main()= + template fn(a): untyped = jsConstructorName(a) + doAssert fn(array[2, int8].default) == "Int8Array" + doAssert fn(array[2, uint8].default) == "Uint8Array" + doAssert fn(array[2, byte].default) == "Uint8Array" + # doAssert fn(array[2, char].default) == "Uint8Array" # xxx fails; bug? + doAssert fn(array[2, uint64].default) == "Array" + # pending https://github.com/nim-lang/RFCs/issues/187 maybe use `BigUint64Array` + doAssert fn([1'u8]) == "Uint8Array" + doAssert fn([1'u16]) == "Uint16Array" + doAssert fn([byte(1)]) == "Uint8Array" + doAssert fn([1.0'f32]) == "Float32Array" + doAssert fn(array[2, float32].default) == "Float32Array" + doAssert fn(array[2, float].default) == "Float64Array" + doAssert fn(array[2, float64].default) == "Float64Array" + doAssert fn([1.0]) == "Float64Array" + doAssert fn([1.0'f64]) == "Float64Array" + +main()