Skip to content

Commit

Permalink
Extent json.to testing to VM, add workrounds for VM bugs. (nim-lang#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
krux02 authored and Araq committed Oct 28, 2019
1 parent a2ad7d4 commit 5ed99f8
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 168 deletions.
177 changes: 127 additions & 50 deletions lib/pure/json.nim
Original file line number Diff line number Diff line change
Expand Up @@ -966,30 +966,38 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
]
raise newException(JsonKindError, msg)


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

# forward declare all initFromJson

proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string)
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string)
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string)
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: string)
proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string)
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string)
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var 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: var 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: var string)
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string)
proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string)

# 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)
# since strings don't have a nil state anymore, this mapping of
# JNull to the default string is questionable. `none(string)` and
Expand All @@ -999,91 +1007,115 @@ when defined(nimFixedForwardGeneric):
else:
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)
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

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)
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)
if jsonNode.kind == JFloat:
dst = T(jsonNode.fnum)
else:
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)
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)
dst.setLen jsonNode.len
let orignalJsonPathLen = jsonPath.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)
let originalJsonPathLen = jsonPath.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]()
verifyJsonKind(jsonNode, {JObject}, jsonPath)
let originalJsonPathLen = jsonPath.len
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]()
verifyJsonKind(jsonNode, {JObject}, jsonPath)
let originalJsonPathLen = jsonPath.len
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:
dst = nil
else:
dst = new(ref T)
dst = new(T)
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:
dst = some(default(T))
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 typImpl = getTypeImpl(dst)
let baseTyp = typImpl[0]

result = quote do:
initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`)
when nimvm:
# workaround #12282
var tmp: `baseTyp`
initFromJson( tmp, `jsonNode`, `jsonPath`)
`dst` = `typInst`(tmp)
else:
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)

proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void =
if typeExpr.kind == nnkTupleConstr:
error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)

proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath: NimNode, depth: int): void {.compileTime.} =
if depth > 150:
error("recursion limit reached", typeNode)
proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode): void {.compileTime.} =
case typeNode.kind
of nnkEmpty:
discard
of nnkRecList, nnkTupleTy:
for it in typeNode:
foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, depth + 1)
foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen)

of nnkIdentDefs:
typeNode.expectLen 3
let fieldSym = typeNode[0]
let fieldNameLit = newLit(fieldSym.strVal)
let fieldPathLit = newLit("." & fieldSym.strVal)
let fieldType = typeNode[1]

# Detecting incompatiple tuple types in `assignObjectImpl` only
Expand All @@ -1092,16 +1124,30 @@ when defined(nimFixedForwardGeneric):
detectIncompatibleType(fieldType, fieldSym)

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:
let kindSym = typeNode[0][0]
let kindNameLit = newLit(kindSym.strVal)
let kindPathLit = newLit("." & kindSym.strVal)
let kindType = typeNode[0][1]
let kindOffsetLit = newLit(uint(getOffset(kindSym)))
dst.add quote do:
var kindTmp: `kindType`
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath` & "." & `kindNameLit`)
jsonPath.add `kindPathLit`
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`)
jsonPath.setLen `originalJsonPathLen`
when defined js:
`tmpSym`.`kindSym` = kindTmp
else:
Expand All @@ -1112,14 +1158,14 @@ when defined(nimFixedForwardGeneric):
((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
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:
let ofBranch = newNimNode(typeNode.kind)
for i in 0 ..< typeNode.len-1:
ofBranch.add copyNimTree(typeNode[i])
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
ofBranch.add dstInner
dst[^1].expectKind nnkCaseStmt
Expand All @@ -1133,26 +1179,29 @@ when defined(nimFixedForwardGeneric):
var impl = getTypeImpl(base)
while impl.kind in {nnkRefTy, nnkPtrTy}:
impl = getTypeImpl(impl[0])
foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, depth + 1)
foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
let body = typeNode[2]
foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, depth + 1)
foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen)

else:
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 originalJsonPathLen = genSym(nskLet, "originalJsonPathLen")
result = newStmtList()
result.add quote do:
let `originalJsonPathLen` = len(`jsonPath`)
if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
# both, `dst` and `typeSym` don't have good lineinfo. But nothing
# else is available here.
detectIncompatibleType(typeSym, dst)
foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, 0)
foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen)
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)

proc to*[T](node: JsonNode, t: typedesc[T]): T =
Expand Down Expand Up @@ -1191,7 +1240,8 @@ when defined(nimFixedForwardGeneric):
## doAssert data.person.age == 21
## doAssert data.list == @[1, 2, 3, 4]

initFromJson(result, node, "")
var jsonPath = ""
initFromJson(result, node, jsonPath)

when false:
import os
Expand Down Expand Up @@ -1424,3 +1474,30 @@ when isMainModule:
res.add($x)
res.add " "
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)
10 changes: 8 additions & 2 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3872,6 +3872,12 @@ elif defined(JS):
proc deallocShared(p: 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):
include "system/jssys"
include "system/reprjs"
Expand Down Expand Up @@ -4326,9 +4332,9 @@ proc addQuoted*[T](s: var string, x: T) =
s.addEscapedChar(x)
s.add("'")
# prevent temporary string allocation
elif T is SomeSignedInt and not defined(JS):
elif T is SomeSignedInt:
s.addInt(x)
elif T is SomeFloat and not defined(JS):
elif T is SomeFloat:
s.addFloat(x)
elif compiles(s.add(x)):
s.add(x)
Expand Down
Loading

0 comments on commit 5ed99f8

Please sign in to comment.