Extent json.to testing to VM, add workrounds for VM bugs. (#12493)

fixes #12479
This commit is contained in:
Arne Döring 2019-10-28 10:06:16 +01:00 • committed by Andreas Rumpf
commit 5ed99f8d3f
3 changed files with 261 additions and 172 deletions

View file

@ -966,30 +966,38 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
] ]
raise newException(JsonKindError, msg) raise newException(JsonKindError, msg)
when defined(nimFixedForwardGeneric): when defined(nimFixedForwardGeneric):
macro isRefSkipDistinct(arg: typed): untyped =
var impl = getTypeImpl(arg)
if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"):
impl = getTypeImpl(impl[1])
while impl.kind == nnkDistinctTy:
impl = getTypeImpl(impl[0])
result = newLit(impl.kind == nnkRefTy)
# The following forward declarations don't work in older versions of Nim # The following forward declarations don't work in older versions of Nim
# forward declare all initFromJson # forward declare all initFromJson
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string) proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string) proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string) proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string) proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string)
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string) proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string) proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string) proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string) proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string) proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string) proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string) proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string) proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: string) proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string) proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string)
# initFromJson definitions # initFromJson definitions
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string) = proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) =
verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
# since strings don't have a nil state anymore, this mapping of # since strings don't have a nil state anymore, this mapping of
# JNull to the default string is questionable. `none(string)` and # JNull to the default string is questionable. `none(string)` and
@ -999,91 +1007,115 @@ when defined(nimFixedForwardGeneric):
else: else:
dst = jsonNode.str dst = jsonNode.str
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string) = proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) =
verifyJsonKind(jsonNode, {JBool}, jsonPath) verifyJsonKind(jsonNode, {JBool}, jsonPath)
dst = jsonNode.bval dst = jsonNode.bval
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string) = proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) =
dst = jsonNode.copy dst = jsonNode.copy
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string) = proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) =
verifyJsonKind(jsonNode, {JInt}, jsonPath) verifyJsonKind(jsonNode, {JInt}, jsonPath)
dst = T(jsonNode.num) dst = T(jsonNode.num)
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath)
if jsonNode.kind == JFloat: if jsonNode.kind == JFloat:
dst = T(jsonNode.fnum) dst = T(jsonNode.fnum)
else: else:
dst = T(jsonNode.num) dst = T(jsonNode.num)
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
verifyJsonKind(jsonNode, {JString}, jsonPath) verifyJsonKind(jsonNode, {JString}, jsonPath)
dst = parseEnum[T](jsonNode.getStr) dst = parseEnum[T](jsonNode.getStr)
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) =
verifyJsonKind(jsonNode, {JArray}, jsonPath) verifyJsonKind(jsonNode, {JArray}, jsonPath)
dst.setLen jsonNode.len dst.setLen jsonNode.len
let orignalJsonPathLen = jsonPath.len
for i in 0 ..< jsonNode.len: for i in 0 ..< jsonNode.len:
initFromJson(dst[i], jsonNode[i], jsonPath & "[" & $i & "]") jsonPath.add '['
jsonPath.addInt i
jsonPath.add ']'
initFromJson(dst[i], jsonNode[i], jsonPath)
jsonPath.setLen orignalJsonPathLen
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) =
verifyJsonKind(jsonNode, {JArray}, jsonPath) verifyJsonKind(jsonNode, {JArray}, jsonPath)
let originalJsonPathLen = jsonPath.len
for i in 0 ..< jsonNode.len: for i in 0 ..< jsonNode.len:
initFromJson(dst[i], jsonNode[i], jsonPath & "[" & $i & "]") jsonPath.add '['
jsonPath.addInt i
jsonPath.add ']'
initFromJson(dst[i], jsonNode[i], jsonPath)
jsonPath.setLen originalJsonPathLen
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: var string) =
dst = initTable[string, T]() dst = initTable[string, T]()
verifyJsonKind(jsonNode, {JObject}, jsonPath) verifyJsonKind(jsonNode, {JObject}, jsonPath)
let originalJsonPathLen = jsonPath.len
for key in keys(jsonNode.fields): for key in keys(jsonNode.fields):
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath & "." & key) jsonPath.add '.'
jsonPath.add key
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
jsonPath.setLen originalJsonPathLen
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: var string) =
dst = initOrderedTable[string,T]() dst = initOrderedTable[string,T]()
verifyJsonKind(jsonNode, {JObject}, jsonPath) verifyJsonKind(jsonNode, {JObject}, jsonPath)
let originalJsonPathLen = jsonPath.len
for key in keys(jsonNode.fields): for key in keys(jsonNode.fields):
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath & "." & key) jsonPath.add '.'
jsonPath.add key
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
jsonPath.setLen originalJsonPathLen
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) =
if jsonNode.kind == JNull: if jsonNode.kind == JNull:
dst = nil dst = nil
else: else:
dst = new(ref T) dst = new(T)
initFromJson(dst[], jsonNode, jsonPath) initFromJson(dst[], jsonNode, jsonPath)
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) =
if jsonNode != nil and jsonNode.kind != JNull: if jsonNode != nil and jsonNode.kind != JNull:
dst = some(default(T)) dst = some(default(T))
initFromJson(dst.get, jsonNode, jsonPath) initFromJson(dst.get, jsonNode, jsonPath)
macro assignDistinctImpl[T : distinct](dst: var T;jsonNode: JsonNode; jsonPath: string) = macro assignDistinctImpl[T : distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) =
let typInst = getTypeInst(dst) let typInst = getTypeInst(dst)
let typImpl = getTypeImpl(dst) let typImpl = getTypeImpl(dst)
let baseTyp = typImpl[0] let baseTyp = typImpl[0]
result = quote do: result = quote do:
when nimvm:
# workaround #12282
var tmp: `baseTyp`
initFromJson( tmp, `jsonNode`, `jsonPath`)
`dst` = `typInst`(tmp)
else:
initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`)
proc initFromJson[T : distinct](dst: var T; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T : distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
assignDistinctImpl(dst, jsonNode, jsonPath) assignDistinctImpl(dst, jsonNode, jsonPath)
proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void = proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void =
if typeExpr.kind == nnkTupleConstr: if typeExpr.kind == nnkTupleConstr:
error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)
proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath: NimNode, depth: int): void {.compileTime.} = proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode): void {.compileTime.} =
if depth > 150:
error("recursion limit reached", typeNode)
case typeNode.kind case typeNode.kind
of nnkEmpty: of nnkEmpty:
discard discard
of nnkRecList, nnkTupleTy: of nnkRecList, nnkTupleTy:
for it in typeNode: for it in typeNode:
foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, depth + 1) foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
of nnkIdentDefs: of nnkIdentDefs:
typeNode.expectLen 3 typeNode.expectLen 3
let fieldSym = typeNode[0] let fieldSym = typeNode[0]
let fieldNameLit = newLit(fieldSym.strVal) let fieldNameLit = newLit(fieldSym.strVal)
let fieldPathLit = newLit("." & fieldSym.strVal)
let fieldType = typeNode[1] let fieldType = typeNode[1]
# Detecting incompatiple tuple types in `assignObjectImpl` only # Detecting incompatiple tuple types in `assignObjectImpl` only
@ -1092,16 +1124,30 @@ when defined(nimFixedForwardGeneric):
detectIncompatibleType(fieldType, fieldSym) detectIncompatibleType(fieldType, fieldSym)
dst.add quote do: dst.add quote do:
initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath` & "." & `fieldNameLit`) jsonPath.add `fieldPathLit`
when nimvm:
when isRefSkipDistinct(`tmpSym`.`fieldSym`):
# workaround #12489
var tmp: `fieldType`
initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
`tmpSym`.`fieldSym` = tmp
else:
initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
else:
initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
jsonPath.setLen `originalJsonPathLen`
of nnkRecCase: of nnkRecCase:
let kindSym = typeNode[0][0] let kindSym = typeNode[0][0]
let kindNameLit = newLit(kindSym.strVal) let kindNameLit = newLit(kindSym.strVal)
let kindPathLit = newLit("." & kindSym.strVal)
let kindType = typeNode[0][1] let kindType = typeNode[0][1]
let kindOffsetLit = newLit(uint(getOffset(kindSym))) let kindOffsetLit = newLit(uint(getOffset(kindSym)))
dst.add quote do: dst.add quote do:
var kindTmp: `kindType` var kindTmp: `kindType`
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath` & "." & `kindNameLit`) jsonPath.add `kindPathLit`
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`)
jsonPath.setLen `originalJsonPathLen`
when defined js: when defined js:
`tmpSym`.`kindSym` = kindTmp `tmpSym`.`kindSym` = kindTmp
else: else:
@ -1112,14 +1158,14 @@ when defined(nimFixedForwardGeneric):
((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym)) dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
for i in 1 ..< typeNode.len: for i in 1 ..< typeNode.len:
foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, depth + 1) foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
of nnkOfBranch, nnkElse: of nnkOfBranch, nnkElse:
let ofBranch = newNimNode(typeNode.kind) let ofBranch = newNimNode(typeNode.kind)
for i in 0 ..< typeNode.len-1: for i in 0 ..< typeNode.len-1:
ofBranch.add copyNimTree(typeNode[i]) ofBranch.add copyNimTree(typeNode[i])
let dstInner = newNimNode(nnkStmtListExpr) let dstInner = newNimNode(nnkStmtListExpr)
foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, depth + 1) foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
# resOuter now contains the inner stmtList # resOuter now contains the inner stmtList
ofBranch.add dstInner ofBranch.add dstInner
dst[^1].expectKind nnkCaseStmt dst[^1].expectKind nnkCaseStmt
@ -1133,26 +1179,29 @@ when defined(nimFixedForwardGeneric):
var impl = getTypeImpl(base) var impl = getTypeImpl(base)
while impl.kind in {nnkRefTy, nnkPtrTy}: while impl.kind in {nnkRefTy, nnkPtrTy}:
impl = getTypeImpl(impl[0]) impl = getTypeImpl(impl[0])
foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, depth + 1) foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
let body = typeNode[2] let body = typeNode[2]
foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, depth + 1) foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
else: else:
error("unhandled kind: " & $typeNode.kind, typeNode) error("unhandled kind: " & $typeNode.kind, typeNode)
macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: string) = macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
let typeSym = getTypeInst(dst) let typeSym = getTypeInst(dst)
let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen")
result = newStmtList() result = newStmtList()
result.add quote do:
let `originalJsonPathLen` = len(`jsonPath`)
if typeSym.kind in {nnkTupleTy, nnkTupleConstr}: if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
# both, `dst` and `typeSym` don't have good lineinfo. But nothing # both, `dst` and `typeSym` don't have good lineinfo. But nothing
# else is available here. # else is available here.
detectIncompatibleType(typeSym, dst) detectIncompatibleType(typeSym, dst)
foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, 0) foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen)
else: else:
foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, 0) foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen)
proc initFromJson[T : object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string) = proc initFromJson[T : object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
assignObjectImpl(dst, jsonNode, jsonPath) assignObjectImpl(dst, jsonNode, jsonPath)
proc to*[T](node: JsonNode, t: typedesc[T]): T = proc to*[T](node: JsonNode, t: typedesc[T]): T =
@ -1191,7 +1240,8 @@ when defined(nimFixedForwardGeneric):
## doAssert data.person.age == 21 ## doAssert data.person.age == 21
## doAssert data.list == @[1, 2, 3, 4] ## doAssert data.list == @[1, 2, 3, 4]
initFromJson(result, node, "") var jsonPath = ""
initFromJson(result, node, jsonPath)
when false: when false:
import os import os
@ -1424,3 +1474,30 @@ when isMainModule:
res.add($x) res.add($x)
res.add " " res.add " "
doAssert res == fragments doAssert res == fragments
# test isRefSkipDistinct
type
MyRef = ref object
MyObject = object
MyDistinct = distinct MyRef
MyOtherDistinct = distinct MyRef
var x0: ref int
var x1: MyRef
var x2: MyObject
var x3: MyDistinct
var x4: MyOtherDistinct
doAssert isRefSkipDistinct(x0)
doAssert isRefSkipDistinct(x1)
doAssert not isRefSkipDistinct(x2)
doAssert isRefSkipDistinct(x3)
doAssert isRefSkipDistinct(x4)
doAssert isRefSkipDistinct(ref int)
doAssert isRefSkipDistinct(MyRef)
doAssert not isRefSkipDistinct(MyObject)
doAssert isRefSkipDistinct(MyDistinct)
doAssert isRefSkipDistinct(MyOtherDistinct)

View file

@ -3872,6 +3872,12 @@ elif defined(JS):
proc deallocShared(p: pointer) = discard proc deallocShared(p: pointer) = discard
proc reallocShared(p: pointer, newsize: Natural): pointer = discard proc reallocShared(p: pointer, newsize: Natural): pointer = discard
proc addInt*(result: var string; x: int64) =
result.add $x
proc addFloat*(result: var string; x: float) =
result.add $x
when defined(JS) and not defined(nimscript): when defined(JS) and not defined(nimscript):
include "system/jssys" include "system/jssys"
include "system/reprjs" include "system/reprjs"
@ -4326,9 +4332,9 @@ proc addQuoted*[T](s: var string, x: T) =
s.addEscapedChar(x) s.addEscapedChar(x)
s.add("'") s.add("'")
# prevent temporary string allocation # prevent temporary string allocation
elif T is SomeSignedInt and not defined(JS): elif T is SomeSignedInt:
s.addInt(x) s.addInt(x)
elif T is SomeFloat and not defined(JS): elif T is SomeFloat:
s.addFloat(x) s.addFloat(x)
elif compiles(s.add(x)): elif compiles(s.add(x)):
s.add(x) s.add(x)

View file

@ -5,17 +5,26 @@ discard """
import json, strutils, options, tables import json, strutils, options, tables
when true: # The definition of the `%` proc needs to be here, since the `% c` calls below
# can only find our custom `%` proc for `Pix` if defined in global scope.
type
Pix = tuple[x, y: uint8, ch: uint16]
proc `%`(p: Pix): JsonNode =
result = %* { "x" : % p.x,
"y" : % p.y,
"ch" : % p.ch }
proc testJson() =
# Tests inspired by own use case (with some additional tests). # Tests inspired by own use case (with some additional tests).
# This should succeed. # This should succeed.
type type
Point[T] = object Point[T] = object
x, y: T x, y: T
ReplayEventKind* = enum ReplayEventKind = enum
FoodAppeared, FoodEaten, DirectionChanged FoodAppeared, FoodEaten, DirectionChanged
ReplayEvent* = object ReplayEvent = object
time*: float time*: float
case kind*: ReplayEventKind case kind*: ReplayEventKind
of FoodAppeared, FoodEaten: of FoodAppeared, FoodEaten:
@ -28,7 +37,7 @@ when true:
of DirectionChanged: of DirectionChanged:
playerPos*: float playerPos*: float
Replay* = ref object Replay = ref object
events*: seq[ReplayEvent] events*: seq[ReplayEvent]
test: int test: int
test2: string test2: string
@ -286,6 +295,8 @@ when true:
doAssert parsed.color == Red doAssert parsed.color == Red
block: block:
when not defined(js):
# disable on js because of #12492
type type
Car = object Car = object
engine: tuple[name: string, capacity: float] engine: tuple[name: string, capacity: float]
@ -296,8 +307,8 @@ when true:
""" """
var i = 0 var i = 0
proc mulTest: JsonNode = proc mulTest(): JsonNode =
i.inc() inc i
return parseJson(j) return parseJson(j)
let parsed = mulTest().to(Car) let parsed = mulTest().to(Car)
@ -424,6 +435,10 @@ when true:
block: block:
let s = """{"a": 1, "b": 2}""" let s = """{"a": 1, "b": 2}"""
let t = parseJson(s).to(Table[string, int]) let t = parseJson(s).to(Table[string, int])
when not defined(js):
# For some reason on the JS backend `{"b": 2, "a": 0}` is
# sometimes the value of `t`. This needs investigation. I can't
# reproduce it right now in an isolated test.
doAssert t["a"] == 1 doAssert t["a"] == 1
doAssert t["b"] == 2 doAssert t["b"] == 2
@ -480,6 +495,7 @@ when true:
doAssert Table[string,int](t.dict)["a"] == 1 doAssert Table[string,int](t.dict)["a"] == 1
doAssert Table[string,int](t.dict)["b"] == 2 doAssert Table[string,int](t.dict)["b"] == 2
doAssert array[3, float](t.arr) == [1.0,2.0,7.0] doAssert array[3, float](t.arr) == [1.0,2.0,7.0]
doAssert MyRef(t.person).name == "boney" doAssert MyRef(t.person).name == "boney"
doAssert MyObj(t.distFruit).color == 11 doAssert MyObj(t.distFruit).color == 11
doAssert t.dog.name == "honey" doAssert t.dog.name == "honey"
@ -526,16 +542,8 @@ when true:
doAssert v.name == "smith" doAssert v.name == "smith"
doAssert MyRef(w).name == "smith" doAssert MyRef(w).name == "smith"
# bug #12015 block:
# The definition of the `%` proc needs to be here, since the `% c` calls below # bug #12015
# can only find our custom `%` proc for `Pix` if defined in global scope.
type
Pix = tuple[x, y: uint8, ch: uint16]
proc `%`(p: Pix): JsonNode =
result = %* { "x" : % p.x,
"y" : % p.y,
"ch" : % p.ch }
block:
type type
Cluster = object Cluster = object
works: tuple[x, y: uint8, ch: uint16] # working works: tuple[x, y: uint8, ch: uint16] # working
@ -546,7 +554,7 @@ block:
let cFromJson = (% c).to(Cluster) let cFromJson = (% c).to(Cluster)
doAssert c == cFromJson doAssert c == cFromJson
block: block:
# bug related to #12015 # bug related to #12015
type type
PixInt = tuple[x, y, ch: int] PixInt = tuple[x, y, ch: int]
@ -562,47 +570,8 @@ block:
let cFromJson = (% c).to(RecoEvent[Pix]) let cFromJson = (% c).to(RecoEvent[Pix])
doAssert c == cFromJson doAssert c == cFromJson
# TODO: when the issue with the limeted vm registers is solved, the
# exact same test as above should be evaluated at compile time as
# well, to ensure that the vm functionality won't diverge from the
# runtime functionality. Until then, the following test should do it.
static: block:
var t = parseJson("""
{
"name":"Bongo",
"email":"bongo@bingo.com",
"list": [11,7,15],
"year": 1975,
"dict": {"a": 1, "b": 2},
"arr": [1.0, 2.0, 7.0],
"person": {"name": "boney"},
"dog": {"name": "honey"},
"fruit": {"color": 10},
"distfruit": {"color": 11},
"emails": ["abc", "123"]
}
""")
doAssert t["name"].getStr == "Bongo"
doAssert t["email"].getStr == "bongo@bingo.com"
doAssert t["list"][0].getInt == 11
doAssert t["list"][1].getInt == 7
doAssert t["list"][2].getInt == 15
doAssert t["year"].getInt == 1975
doAssert t["dict"]["a"].getInt == 1
doAssert t["dict"]["b"].getInt == 2
doAssert t["arr"][0].getFloat == 1.0
doAssert t["arr"][1].getFloat == 2.0
doAssert t["arr"][2].getFloat == 7.0
doAssert t["person"]["name"].getStr == "boney"
doAssert t["distfruit"]["color"].getInt == 11
doAssert t["dog"]["name"].getStr == "honey"
doAssert t["fruit"]["color"].getInt == 10
doAssert t["emails"][0].getStr == "abc"
doAssert t["emails"][1].getStr == "123"
block:
# ref objects with cycles. # ref objects with cycles.
type type
Misdirection = object Misdirection = object
@ -619,7 +588,7 @@ block:
let dataParsed = parseJson(data) let dataParsed = parseJson(data)
let dataDeser = to(dataParsed, Misdirection) let dataDeser = to(dataParsed, Misdirection)
block: block:
# ref object from #12316 # ref object from #12316
type type
Foo = ref Bar Foo = ref Bar
@ -627,8 +596,45 @@ block:
discard "null".parseJson.to Foo discard "null".parseJson.to Foo
block: block:
# named array #12289 # named array #12289
type Vec = array[2, int] type Vec = array[2, int]
let arr = "[1,2]".parseJson.to Vec let arr = "[1,2]".parseJson.to Vec
doAssert arr == [1,2] doAssert arr == [1,2]
block:
# test error message in exception
type
MyType = object
otherMember: string
member: MySubType
MySubType = object
somethingElse: string
list: seq[MyData]
MyData = object
value: int
let jsonNode = parseJson("""
{
"otherMember": "otherValue",
"member": {
"somethingElse": "something",
"list": [{"value": 1}, {"value": 2}, {}]
}
}
""")
try:
let tmp = jsonNode.to(MyType)
doAssert false, "this should be unreachable"
except KeyError:
doAssert getCurrentExceptionMsg().contains ".member.list[2].value"
testJson()
static:
testJson()