lots of changes and additions to typeinfo.nim

This commit is contained in:
Araq
2011-06-12 00:41:53 +02:00
parent 165302cda4
commit 8887684aaa

View File

@@ -1,36 +1,59 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2011 Dominik Picheta, Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements an interface to Nimrod's runtime type information.
## Note that even though ``TAny`` and its operations hide the nasty low level
## details from its clients, it remains inherently unsafe!
# XXX raw pointer needs to be exposed somehow?
include "system/hti.nim"
type
TType* = enum # This mirrors the TNimKind type in hti.nim
TNNone, TNBool, TNChar,
TNEmpty, TNArrayConstr, TNNil, TNExpr, TNStmt, TNTypeDesc,
TNGenericInvokation, # ``T[a, b]`` for types to invoke
TNGenericBody, # ``T[a, b, body]`` last parameter is the body
TNGenericInst, # ``T[a, b, realInstance]`` instantiated generic type
TNGenericParam, # ``a`` in the example
TNDistinct, # distinct type
TNEnum,
TNOrdinal,
TNArray,
TNObject,
TNTuple,
TNSet,
TNRange,
TNPtr, TNRef,
TNVar,
TNSequence,
TNProc,
TNPointer, TNOpenArray,
TNString, TNCString, TNForward,
TNInt, TNInt8, TNInt16, TNInt32, TNInt64,
TNFloat, TNFloat32, TNFloat64, TNFloat128,
TNPureObject # signals that object has no `n_type` field
TAnyKind* = enum ## what kind of ``any`` it is
akNone = 0, ## invalid any
akBool = 1, ## any represents a ``bool``
akChar = 2, ## any represents a ``char``
akEnum = 14, ## any represents an enum
akArray = 16, ## any represents an array
akObject = 17, ## any represents an object
akTuple = 18, ## any represents a tuple
akSet = 19, ## any represents a set
akRange = 20, ## any represents a range
akPtr = 21, ## any represents a ptr
akRef = 22, ## any represents a ref
akSequence = 24, ## any represents a sequence
akProc = 25, ## any represens a proc
akPointer = 26, ## any represens a pointer
akString = 28, ## any represens a string
akCString = 29, ## any represens a cstring
akInt = 31, ## any represens an int
akInt8 = 32, ## any represens an int8
akInt16 = 33, ## any represens an int16
akInt32 = 34, ## any represens an int32
akInt64 = 35, ## any represens an int64
akFloat = 36, ## any represens a float
akFloat32 = 37, ## any represens a float32
akFloat64 = 38, ## any represens a float64
akFloat128 = 39, ## any represens a float128
akPureObject = 40 ## any represens an object has no `type` field
TAny* = object {.pure.}
TAny* = object {.pure.} ## can represent any nimrod value; NOTE: the wrapped
## value can be modified with its wrapper! This means
## that ``TAny`` keeps a non-traced pointer to its
## wrapped value and MUST not live longer than its
## wrapped value.
value: pointer
rawType: PNimType
ppointer = ptr pointer
pbyteArray = ptr array[0.. 0xffff, byte]
TGenSeq {.pure.} = object
len, space: int
@@ -65,75 +88,85 @@ proc newAny(value: pointer, rawType: PNimType): TAny =
result.value = value
result.rawType = rawType
proc toAny*[T](x: var T): TAny =
var k = getTypeInfo(x)
return newAny(addr(x), cast[PNimType](k))
proc toAny*[T](x: var T): TAny {.inline.} =
## constructs a ``TAny`` object from `x`. This captures `x`'s address, so
## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure
## that the wrapper DOES NOT live longer than `x`!
result.value = addr(x)
result.rawType = cast[PNimType](getTypeInfo(x))
proc getType*(x: TAny): TType = return TType(x.rawType.kind)
proc getKind*(x: TAny): TAnyKind {.inline.} =
## get the type kind
result = TAnyKind(ord(x.rawType.kind))
proc skipRange(x: PNimType): PNimType {.inline.} =
result = x
if result.kind == tyRange: result = result.base
template `+!!`(a, b: expr): expr = cast[pointer](cast[TAddress](a) + b)
proc `[]`*(x: TAny, i: int): TAny =
assert getType(x) in {TNArray, TNSequence}
if x.getType == TNArray:
## accessor for an any `x` that represents an array or a sequence.
case x.rawType.kind
of tyArray:
var bs = x.rawType.base.size
if i >% (x.rawType.size div bs - 1):
raise newException(EInvalidIndex, "Index out of bounds.")
return newAny(cast[pointer](cast[TAddress](x.value) + i*bs),
x.rawType.base)
elif x.getType == TNSequence:
raise newException(EInvalidIndex, "index out of bounds")
return newAny(x.value +!! i*bs, x.rawType.base)
of tySequence:
var s = cast[ppointer](x.value)[]
var bs = x.rawType.base.size
if i >% (cast[PGenSeq](s).len-1):
raise newException(EInvalidIndex, "Index out of bounds.")
return newAny(cast[pointer](cast[TAddress](s) + GenericSeqSize+i*bs),
x.rawType.base)
raise newException(EInvalidIndex, "index out of bounds")
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
else: assert false
proc `[]=`*(x: TAny, i: int, y: TAny) =
assert getType(x) in {TNArray, TNSequence}
if x.getType == TNArray:
## accessor for an any `x` that represents an array or a sequence.
case x.rawType.kind
of tyArray:
var bs = x.rawType.base.size
if i >% (x.rawType.size div bs - 1):
raise newException(EInvalidIndex, "Index out of bounds.")
genericAssign(cast[pointer](cast[TAddress](x.value) + i*bs),
y.value, y.rawType)
elif x.getType == TNSequence:
raise newException(EInvalidIndex, "index out of bounds")
assert y.rawType == x.rawType.base
genericAssign(x.value +!! i*bs, y.value, y.rawType)
of tySequence:
var s = cast[ppointer](x.value)[]
var bs = x.rawType.base.size
if i >% (cast[PGenSeq](s).len-1):
raise newException(EInvalidIndex, "Index out of bounds.")
genericAssign(cast[pointer](cast[TAddress](s) + GenericSeqSize+i*bs),
y.value, y.rawType)
raise newException(EInvalidIndex, "index out of bounds")
assert y.rawType == x.rawType.base
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
else: assert false
proc len*(x: TAny): int =
assert getType(x) in {TNArray, TNSequence}
if x.getType == TNArray:
var bs = x.rawType.base.size
return (x.rawType.size div bs)
elif x.getType == TNSequence:
var s = cast[ppointer](x.value)[]
return cast[PGenSeq](s).len
## len for an any `x` that represents an array or a sequence.
case x.rawType.kind
of tyArray: result = x.rawType.size div x.rawType.base.size
of tySequence: result = cast[PGenSeq](cast[ppointer](x.value)[]).len
else: assert false
proc fieldsAux(p: pointer, n: ptr TNimNode,
ret: var seq[tuple[name: cstring, any: TAny]]) =
ret: var seq[tuple[name: cstring, any: TAny]]) =
case n.kind
of nkNone: assert(false)
of nkSlot:
var tup = (n.name,
newAny(cast[pointer](cast[TAddress](p) + n.offset), n.typ))
ret.add(tup)
ret.add((n.name, newAny(p +!! n.offset, n.typ)))
assert ret[ret.len()-1][0] != nil
of nkList:
for i in 0..n.len-1:
fieldsAux(p, n.sons[i], ret)
for i in 0..n.len-1: fieldsAux(p, n.sons[i], ret)
of nkCase:
var m = selectBranch(p, n)
ret.add((n.name, newAny(cast[pointer](cast[TAddress](p) + n.offset), n.typ)))
ret.add((n.name, newAny(p +!! n.offset, n.typ)))
if m != nil: fieldsAux(p, m, ret)
iterator fields*(x: TAny): tuple[name: string, any: TAny] =
assert getType(x) in {TNTuple, TNPureObject, TNObject}
## iterates over every active field of the any `x` that represents an object
## or a tuple.
assert x.rawType.kind in {tyTuple, tyPureObject, tyObject}
var p = x.value
var t = x.rawType
if x.getType == TNObject: t = cast[ptr PNimType](x.value)[]
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
var n = t.node
var ret: seq[tuple[name: cstring, any: TAny]] = @[]
fieldsAux(p, n, ret)
@@ -141,27 +174,172 @@ iterator fields*(x: TAny): tuple[name: string, any: TAny] =
yield ($name, any)
proc `[]`*(x: TAny): TAny =
assert getType(x) in {TNRef, TNPtr}
var p = cast[ppointer](x.value)[]
if p == nil:
result.value = nil
result.rawType = nil
return
## dereference operation for the any `x` that represents a ptr or a ref.
assert x.rawtype.kind in {tyRef, tyPtr}
result.value = cast[ppointer](x.value)[]
result.rawType = x.rawType.base
proc `[]=`*(x, y: TAny) =
## dereference operation for the any `x` that represents a ptr or a ref.
assert x.rawtype.kind in {tyRef, tyPtr}
assert y.rawType == x.rawType.base
genericAssign(cast[ppointer](x.value)[], y.value, y.rawType)
proc getInt*(x: TAny): int =
## retrieve the int value out of `x`. `x` needs to represent an int.
assert skipRange(x.rawtype).kind == tyInt
result = cast[ptr int](x.value)[]
proc getInt8*(x: TAny): int8 =
## retrieve the int8 value out of `x`. `x` needs to represent an int8.
assert skipRange(x.rawtype).kind == tyInt8
result = cast[ptr int8](x.value)[]
proc getInt16*(x: TAny): int16 =
## retrieve the int16 value out of `x`. `x` needs to represent an int16.
assert skipRange(x.rawtype).kind == tyInt16
result = cast[ptr int16](x.value)[]
proc getInt32*(x: TAny): int32 =
## retrieve the int32 value out of `x`. `x` needs to represent an int32.
assert skipRange(x.rawtype).kind == tyInt32
result = cast[ptr int32](x.value)[]
proc getInt64*(x: TAny): int64 =
## retrieve the int64 value out of `x`. `x` needs to represent an int64.
assert skipRange(x.rawtype).kind == tyInt64
result = cast[ptr int64](x.value)[]
proc getBiggestInt*(x: TAny): biggestInt =
## retrieve the integer value out of `x`. `x` needs to represent
## some integer, a bool, a char or an enum. The value might be
## sign-extended to ``biggestInt``.
var t = skipRange(x.rawtype)
case t.kind
of tyInt: result = biggestInt(cast[ptr int](x.value)[])
of tyInt8: result = biggestInt(cast[ptr int8](x.value)[])
of tyInt16: result = biggestInt(cast[ptr int16](x.value)[])
of tyInt32: result = biggestInt(cast[ptr int32](x.value)[])
of tyInt64: result = biggestInt(cast[ptr int64](x.value)[])
of tyBool: result = biggestInt(cast[ptr bool](x.value)[])
of tyChar: result = biggestInt(cast[ptr char](x.value)[])
of tyEnum:
case t.size
of 1: result = ze64(cast[ptr int8](x.value)[])
of 2: result = ze64(cast[ptr int16](x.value)[])
of 4: result = biggestInt(cast[ptr int32](x.value)[])
of 8: result = biggestInt(cast[ptr int64](x.value)[])
else: assert false
else: assert false
proc getChar*(x: TAny): char =
## retrieve the char value out of `x`. `x` needs to represent a char.
var t = skipRange(x.rawtype)
assert t.kind == tyChar
result = cast[ptr char](x.value)[]
proc getBool*(x: TAny): bool =
## retrieve the bool value out of `x`. `x` needs to represent a bool.
var t = skipRange(x.rawtype)
assert t.kind == tyBool
result = cast[ptr bool](x.value)[]
proc getEnumField*(x: TAny): string =
## gets the enum field name as a string. `x` needs to represent an enum.
var typ = skipRange(x.rawtype)
assert typ.kind == tyEnum
var e = int(getBiggestInt(x))
if ntfEnumHole notin typ.flags:
if e <% typ.node.len:
return $typ.node.sons[e].name
else:
result.value = p
result.rawType = x.rawType.base
# ugh we need a slow linear search:
var n = typ.node
var s = n.sons
for i in 0 .. n.len-1:
if s[i].offset == e: return $s[i].name
result = $e & " (invalid data!)"
proc readInt*(x: TAny): int = return cast[ptr int](x.value)[]
proc readInt8*(x: TAny): int8 = return cast[ptr int8](x.value)[]
proc readInt16*(x: TAny): int16 = return cast[ptr int16](x.value)[]
proc readInt32*(x: TAny): int32 = return cast[ptr int32](x.value)[]
proc readInt64*(x: TAny): int64 = return cast[ptr int64](x.value)[]
proc getFloat*(x: TAny): float =
## retrieve the float value out of `x`. `x` needs to represent an float.
assert skipRange(x.rawtype).kind == tyFloat
result = cast[ptr float](x.value)[]
proc readFloat*(x: TAny): float = return cast[ptr float](x.value)[]
proc readFloat32*(x: TAny): float32 = return cast[ptr float32](x.value)[]
proc readFloat64*(x: TAny): float64 = return cast[ptr float64](x.value)[]
proc getFloat32*(x: TAny): float32 =
## retrieve the float32 value out of `x`. `x` needs to represent an float32.
assert skipRange(x.rawtype).kind == tyFloat64
result = cast[ptr float32](x.value)[]
proc getFloat64*(x: TAny): float64 =
## retrieve the float64 value out of `x`. `x` needs to represent an float64.
assert skipRange(x.rawtype).kind == tyFloat64
result = cast[ptr float64](x.value)[]
proc readString*(x: TAny): string = return cast[ptr string](x.value)[]
proc getBiggestFloat*(x: TAny): biggestFloat =
## retrieve the float value out of `x`. `x` needs to represent
## some float. The value is extended to ``biggestFloat``.
case skipRange(x.rawtype).kind
of tyFloat: result = biggestFloat(cast[ptr Float](x.value)[])
of tyFloat32: result = biggestFloat(cast[ptr Float32](x.value)[])
of tyFloat64: result = biggestFloat(cast[ptr Float64](x.value)[])
else: assert false
proc getString*(x: TAny): string =
## retrieve the string value out of `x`. `x` needs to represent a string.
assert x.rawtype.kind == tyString
result = cast[ptr string](x.value)[]
proc assign*(x, y: TAny) =
## copies the value of `y` to `x`. The assignment operator for ``TAny``
## does NOT do this; it performs a shallow copy instead!
assert y.rawType == x.rawType
genericAssign(x.value, y.value, y.rawType)
iterator elements*(x: TAny): int =
## iterates over every element of `x` that represents a Nimrod bitset.
assert x.rawType.kind == tySet
var typ = x.rawtype
var p = x.value
# "typ.slots.len" field is for sets the "first" field
var u: int64
case typ.size
of 1: u = ze64(cast[ptr int8](p)[])
of 2: u = ze64(cast[ptr int16](p)[])
of 4: u = ze64(cast[ptr int32](p)[])
of 8: u = cast[ptr int64](p)[]
else:
var a = cast[pbyteArray](p)
for i in 0 .. typ.size*8-1:
if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0:
yield i+typ.node.len
if typ.size <= 8:
for i in 0..sizeof(int64)*8-1:
if (u and (1'i64 shl int64(i))) != 0'i64:
yield i+typ.node.len
proc inclSetElement*(x: TAny, elem: int) =
## includes an element `elem` in `x`. `x` needs to represent a Nimrod bitset.
assert x.rawType.kind == tySet
var typ = x.rawtype
var p = x.value
# "typ.slots.len" field is for sets the "first" field
var e = elem - typ.node.len
case typ.size
of 1:
var a = cast[ptr int8](p)
a[] = a[] or (1'i8 shl int8(e))
of 2:
var a = cast[ptr int16](p)
a[] = a[] or (1'i16 shl int16(e))
of 4:
var a = cast[ptr int32](p)
a[] = a[] or (1'i32 shl int32(e))
of 8:
var a = cast[ptr int64](p)
a[] = a[] or (1'i64 shl e)
else:
var a = cast[pbyteArray](p)
a[e div 8] = toU8(a[e div 8] or (1 shl (e mod 8)))
when isMainModule:
type
@@ -180,25 +358,33 @@ when isMainModule:
var x = toAny(test)
var y = 78
x[4] = toAny(y)
echo cast[ptr int](x[2].value)[]
assert cast[ptr int](x[2].value)[] == 2
var test2: tuple[name: string, s: int] = ("test", 56)
var x2 = toAny(test2)
var i = 0
for n, a in fields(x2):
echo("Name = ", n)
echo("Any type = ", a.getType)
case i
of 0: assert n == "name" and $a.getKind == "akString"
of 1: assert n == "s" and $a.getKind == "akInt"
else: assert false
inc i
var test3: TestObj
test3.test = 42
test3.test2 = blah2
var x3 = toAny(test3)
i = 0
for n, a in fields(x3):
echo("Name = ", n)
echo("Any type = ", a.getType)
case i
of 0: assert n == "test" and $a.getKind == "akInt"
of 1: assert n == "asd" and $a.getKind == "akInt"
of 2: assert n == "test2" and $a.getKind == "akEnum"
else: assert false
inc i
var test4: ref string
new(test4)
test4[] = "test"
var x4 = toAny(test4)
echo x4[].getType()
assert($x4[].getKind() == "akString")