array literals uses typed arrays; fix a jsgen bug (#16850)

* array litterals uses typed arrays
* Update compiler/jsgen.nim

Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
flywind 2021-02-15 04:28:11 -06:00 • committed by GitHub
commit 240879bf3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 31 deletions

View file

@ -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, - 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. 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 - 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 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. adding: `default-role:: code` directive inside the rst file, which is now handled by rst2html.

View file

@ -1991,6 +1991,23 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) =
genReset(p, n) genReset(p, n)
#lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) #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) = proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
var var
a: TCompRes a: TCompRes
@ -2054,8 +2071,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mNew, mNewFinalize: genNew(p, n) of mNew, mNewFinalize: genNew(p, n)
of mChr: gen(p, n[1], r) of mChr: gen(p, n[1], r)
of mArrToSeq: of mArrToSeq:
if needsNoCopy(p, n[1]): # only array literals doesn't need copy
gen(p, n[1], r) if n[1].kind == nkBracket:
genJSArrayConstr(p, n[1], r)
else: else:
var x: TCompRes var x: TCompRes
gen(p, n[1], x) gen(p, n[1], x)
@ -2172,21 +2190,26 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
r.res = tmp r.res = tmp
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
## 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 var a: TCompRes
r.res = rope("[") r.res = "new $1([" % [rope(jsTyp)]
r.kind = resExpr r.kind = resExpr
for i in 0..<n.len: for i in 0 ..< n.len:
if i > 0: r.res.add(", ") if i > 0: r.res.add(", ")
gen(p, n[i], a) 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(a.res)
r.res.add("]") r.res.add("])")
else:
genJSArrayConstr(p, n, r)
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes var a: TCompRes

View file

@ -23,8 +23,16 @@ when defined(js):
func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".} 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 = proc hasJsBigInt*(): bool =
asm """`result` = typeof BigInt != 'undefined'""" asm """`result` = typeof BigInt != 'undefined'"""
proc hasBigUint64Array*(): bool = proc hasBigUint64Array*(): bool =
asm """`result` = typeof BigUint64Array != 'undefined'""" asm """`result` = typeof BigUint64Array != 'undefined'"""

View file

@ -1,6 +1,5 @@
proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} import std/private/jsutils
## Returns the name of the JsObject's JavaScript type as a cstring.
# xxx replace jsffi.jsTypeOf with this definition and add tests
type JsBigIntImpl {.importc: "bigint".} = int type JsBigIntImpl {.importc: "bigint".} = int
type JsBigInt = distinct JsBigIntImpl type JsBigInt = distinct JsBigIntImpl

View file

@ -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 # bug #4139
type type
@ -17,8 +9,8 @@ proc onLoad() =
var foo = TestO(x: 0, y: 0) var foo = TestO(x: 0, y: 0)
test.add(foo) test.add(foo)
foo.x = 5 foo.x = 5
echo(test[0]) doAssert $test[0] == "(x: 0, y: 0)"
echo foo doAssert $foo == "(x: 5, y: 0)"
onLoad() onLoad()
@ -34,7 +26,7 @@ proc foo(x: var seq[MyObj]) =
var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)] var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)]
foo(s) foo(s)
echo s doAssert $s == """@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)]"""
# bug #5933 # bug #5933
import sequtils 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) 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]

21
tests/js/ttypedarray.nim Normal file
View file

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