summary refs log tree commit diff stats
path: root/compiler/vmmarshal.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/vmmarshal.nim')
-rw-r--r--compiler/vmmarshal.nim315
1 files changed, 315 insertions, 0 deletions
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
new file mode 100644
index 000000000..0e67ededa
--- /dev/null
+++ b/compiler/vmmarshal.nim
@@ -0,0 +1,315 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements marshaling for the VM.
+
+import ast, astalgo, idents, types, msgs,
+  options, lineinfos
+
+import std/[streams, json, intsets, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
+
+proc ptrToInt(x: PNode): int {.inline.} =
+  result = cast[int](x) # don't skip alignment
+
+proc getField(n: PNode; position: int): PSym =
+  case n.kind
+  of nkRecList:
+    result = nil
+    for i in 0..<n.len:
+      result = getField(n[i], position)
+      if result != nil: return
+  of nkRecCase:
+    result = getField(n[0], position)
+    if result != nil: return
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        result = getField(lastSon(n[i]), position)
+        if result != nil: return
+      else: discard
+  of nkSym:
+    if n.sym.position == position: result = n.sym
+    else: result = nil
+  else: result = nil
+
+proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; conf: ConfigRef)
+
+proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet; conf: ConfigRef) =
+  assert x.kind == nkObjConstr
+  let start = 1
+  for i in start..<x.len:
+    if i > start: s.add(", ")
+    var it = x[i]
+    if it.kind == nkExprColonExpr:
+      if it[0].kind == nkSym:
+        let field = it[0].sym
+        s.add(escapeJson(field.name.s))
+        s.add(": ")
+        storeAny(s, field.typ, it[1], stored, conf)
+    elif typ.n != nil:
+      let field = getField(typ.n, i)
+      s.add(escapeJson(field.name.s))
+      s.add(": ")
+      storeAny(s, field.typ, it, stored, conf)
+
+proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
+              conf: ConfigRef) =
+  case t.kind
+  of tyNone: assert false
+  of tyBool: s.add($(a.intVal != 0))
+  of tyChar:
+    let ch = char(a.intVal)
+    if ch < '\128':
+      s.add(escapeJson($ch))
+    else:
+      s.add($int(ch))
+  of tyArray, tySequence:
+    if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
+    else:
+      s.add("[")
+      for i in 0..<a.len:
+        if i > 0: s.add(", ")
+        storeAny(s, t.elemType, a[i], stored, conf)
+      s.add("]")
+  of tyTuple:
+    s.add("{")
+    for i, ti in t.ikids:
+      if i > 0: s.add(", ")
+      s.add("\"Field" & $i)
+      s.add("\": ")
+      storeAny(s, ti, a[i].skipColon, stored, conf)
+    s.add("}")
+  of tyObject:
+    s.add("{")
+    storeObj(s, t, a, stored, conf)
+    s.add("}")
+  of tySet:
+    s.add("[")
+    for i in 0..<a.len:
+      if i > 0: s.add(", ")
+      if a[i].kind == nkRange:
+        var x = copyNode(a[i][0])
+        storeAny(s, t.elementType, x, stored, conf)
+        inc x.intVal
+        while x.intVal <= a[i][1].intVal:
+          s.add(", ")
+          storeAny(s, t.elementType, x, stored, conf)
+          inc x.intVal
+      else:
+        storeAny(s, t.elementType, a[i], stored, conf)
+    s.add("]")
+  of tyRange, tyGenericInst, tyAlias, tySink:
+    storeAny(s, t.skipModifier, a, stored, conf)
+  of tyEnum:
+    # we need a slow linear search because of enums with holes:
+    for e in items(t.n):
+      if e.sym.position == a.intVal:
+        s.add e.sym.name.s.escapeJson
+        break
+  of tyPtr, tyRef:
+    var x = a
+    if isNil(x) or x.kind == nkNilLit: s.add("null")
+    elif stored.containsOrIncl(x.ptrToInt):
+      # already stored, so we simply write out the pointer as an int:
+      s.add($x.ptrToInt)
+    else:
+      # else as a [value, key] pair:
+      # (reversed order for convenient x[0] access!)
+      s.add("[")
+      s.add($x.ptrToInt)
+      s.add(", ")
+      storeAny(s, t.elementType, a, stored, conf)
+      s.add("]")
+  of tyString, tyCstring:
+    if a.kind == nkNilLit: s.add("null")
+    else: s.add(escapeJson(a.strVal))
+  of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
+  of tyFloat..tyFloat128: s.add($a.floatVal)
+  else:
+    internalError conf, a.info, "cannot marshal at compile-time " & t.typeToString
+
+proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
+  var stored = initIntSet()
+  storeAny(s, t, a, stored, conf)
+
+proc loadAny(p: var JsonParser, t: PType,
+             tab: var Table[BiggestInt, PNode];
+             cache: IdentCache;
+             conf: ConfigRef;
+             idgen: IdGenerator): PNode =
+  case t.kind
+  of tyNone:
+    result = nil
+    assert false
+  of tyBool:
+    case p.kind
+    of jsonFalse: result = newIntNode(nkIntLit, 0)
+    of jsonTrue: result = newIntNode(nkIntLit, 1)
+    else: raiseParseErr(p, "'true' or 'false' expected for a bool")
+    next(p)
+  of tyChar:
+    if p.kind == jsonString:
+      var x = p.str
+      result = nil
+      if x.len == 1:
+        result = newIntNode(nkIntLit, ord(x[0]))
+        next(p)
+        return
+    elif p.kind == jsonInt:
+      result = newIntNode(nkIntLit, getInt(p))
+      next(p)
+      return
+    else:
+      result = nil
+    raiseParseErr(p, "string of length 1 expected for a char")
+  of tyEnum:
+    result = nil
+    if p.kind == jsonString:
+      for e in items(t.n):
+        if e.sym.name.s == p.str:
+          result = newIntNode(nkIntLit, e.sym.position)
+          next(p)
+          return
+    raiseParseErr(p, "string expected for an enum")
+  of tyArray:
+    if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
+    next(p)
+    result = newNode(nkBracket)
+    while p.kind != jsonArrayEnd and p.kind != jsonEof:
+      result.add loadAny(p, t.elemType, tab, cache, conf, idgen)
+    if p.kind == jsonArrayEnd: next(p)
+    else: raiseParseErr(p, "']' end of array expected")
+  of tySequence:
+    case p.kind
+    of jsonNull:
+      result = newNode(nkNilLit)
+      next(p)
+    of jsonArrayStart:
+      next(p)
+      result = newNode(nkBracket)
+      while p.kind != jsonArrayEnd and p.kind != jsonEof:
+        result.add loadAny(p, t.elemType, tab, cache, conf, idgen)
+      if p.kind == jsonArrayEnd: next(p)
+      else: raiseParseErr(p, "")
+    else:
+      result = nil
+      raiseParseErr(p, "'[' expected for a seq")
+  of tyTuple:
+    if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
+    next(p)
+    result = newNode(nkTupleConstr)
+    var i = 0
+    let tupleLen = t.kidsLen
+    while p.kind != jsonObjectEnd and p.kind != jsonEof:
+      if p.kind != jsonString:
+        raiseParseErr(p, "string expected for a field name")
+      next(p)
+      if i >= tupleLen:
+        raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
+      result.add loadAny(p, t[i], tab, cache, conf, idgen)
+      inc i
+    if p.kind == jsonObjectEnd: next(p)
+    else: raiseParseErr(p, "'}' end of object expected")
+  of tyObject:
+    if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
+    next(p)
+    result = newNode(nkObjConstr)
+    result.sons = @[newNode(nkEmpty)]
+    while p.kind != jsonObjectEnd and p.kind != jsonEof:
+      if p.kind != jsonString:
+        raiseParseErr(p, "string expected for a field name")
+      let ident = getIdent(cache, p.str)
+      let field = lookupInRecord(t.n, ident)
+      if field.isNil:
+        raiseParseErr(p, "unknown field for object of type " & typeToString(t))
+      next(p)
+      let pos = field.position + 1
+      if pos >= result.len:
+        setLen(result.sons, pos + 1)
+      let fieldNode = newNode(nkExprColonExpr)
+      fieldNode.add newSymNode(newSym(skField, ident, idgen, nil, unknownLineInfo))
+      fieldNode.add loadAny(p, field.typ, tab, cache, conf, idgen)
+      result[pos] = fieldNode
+    if p.kind == jsonObjectEnd: next(p)
+    else: raiseParseErr(p, "'}' end of object expected")
+  of tySet:
+    if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
+    next(p)
+    result = newNode(nkCurly)
+    while p.kind != jsonArrayEnd and p.kind != jsonEof:
+      result.add loadAny(p, t.elementType, tab, cache, conf, idgen)
+    if p.kind == jsonArrayEnd: next(p)
+    else: raiseParseErr(p, "']' end of array expected")
+  of tyPtr, tyRef:
+    case p.kind
+    of jsonNull:
+      result = newNode(nkNilLit)
+      next(p)
+    of jsonInt:
+      result = tab.getOrDefault(p.getInt)
+      if result.isNil:
+        raiseParseErr(p, "cannot load object with address " & $p.getInt)
+      next(p)
+    of jsonArrayStart:
+      result = nil
+      next(p)
+      if p.kind == jsonInt:
+        let idx = p.getInt
+        next(p)
+        result = loadAny(p, t.elementType, tab, cache, conf, idgen)
+        tab[idx] = result
+      else: raiseParseErr(p, "index for ref type expected")
+      if p.kind == jsonArrayEnd: next(p)
+      else: raiseParseErr(p, "']' end of ref-address pair expected")
+    else:
+      result = nil
+      raiseParseErr(p, "int for pointer type expected")
+  of tyString, tyCstring:
+    case p.kind
+    of jsonNull:
+      result = newNode(nkNilLit)
+      next(p)
+    of jsonString:
+      result = newStrNode(nkStrLit, p.str)
+      next(p)
+    else:
+      result = nil
+      raiseParseErr(p, "string expected")
+  of tyInt..tyInt64, tyUInt..tyUInt64:
+    if p.kind == jsonInt:
+      result = newIntNode(nkIntLit, getInt(p))
+      next(p)
+      return
+    else:
+      result = nil
+    raiseParseErr(p, "int expected")
+  of tyFloat..tyFloat128:
+    if p.kind == jsonFloat:
+      result = newFloatNode(nkFloatLit, getFloat(p))
+      next(p)
+      return
+    else:
+      result = nil
+    raiseParseErr(p, "float expected")
+  of tyRange, tyGenericInst, tyAlias, tySink:
+    result = loadAny(p, t.skipModifier, tab, cache, conf, idgen)
+  else:
+    result = nil
+    internalError conf, "cannot marshal at compile-time " & t.typeToString
+
+proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode =
+  var tab = initTable[BiggestInt, PNode]()
+  var p: JsonParser = default(JsonParser)
+  open(p, newStringStream(s), "unknown file")
+  next(p)
+  result = loadAny(p, t, tab, cache, conf, idgen)
+  close(p)