diff options
-rw-r--r-- | lib/core/typeinfo.nim | 362 |
1 files changed, 274 insertions, 88 deletions
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index f78f671da..bf76b7bf6 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -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 - - TAny* = object {.pure.} + 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.} ## 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 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 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 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 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 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 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 readString*(x: TAny): string = return 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") |