diff options
-rwxr-xr-x | compiler/ast.nim | 2 | ||||
-rwxr-xr-x | compiler/ccgexprs.nim | 5 | ||||
-rw-r--r-- | lib/core/typeinfo.nim | 195 | ||||
-rwxr-xr-x | lib/system.nim | 1 | ||||
-rwxr-xr-x | lib/system/hti.nim | 10 |
5 files changed, 210 insertions, 3 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index d1c10168a..673dae5e9 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -352,7 +352,7 @@ type mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, - mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError + mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mGetTypeInfo type PNode* = ref TNode diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2cb99d3a9..7447dbb6c 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1052,6 +1052,10 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", [addrLoc(a), genTypeInfo(p.module, t)])) +proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = + var t = skipTypes(e.sons[1].typ, abstractVarRange) + putIntoDest(p, d, e.typ, genTypeInfo(p.module, t)) + proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc InitLocExpr(p, n.sons[1], a) @@ -1378,6 +1382,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mShrI..mXor: binaryArith(p, e, d, op) of mAddi..mModi64: binaryArithOverflow(p, e, d, op) of mRepr: genRepr(p, e, d) + of mGetTypeInfo: genGetTypeInfo(p, e, d) of mSwap: genSwap(p, e, d) of mUnaryLt: if not (optOverflowCheck in p.Options): unaryExpr(p, e, d, "$1 - 1") diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim new file mode 100644 index 000000000..d6415d6dc --- /dev/null +++ b/lib/core/typeinfo.nim @@ -0,0 +1,195 @@ +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.} + value: pointer + rawType: PNimType + + ppointer = ptr pointer + + TGenSeq {.pure.} = object + len, space: int + PGenSeq = ptr TGenSeq + +const + GenericSeqSize = (2 * sizeof(int)) + +proc genericAssign(dest, src: Pointer, mt: PNimType) {.importc.} + +proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = + assert(n.kind == nkCase) + var d: int + var a = cast[TAddress](aa) + case n.typ.size + of 1: d = ze(cast[ptr int8](a +% n.offset)[]) + of 2: d = ze(cast[ptr int16](a +% n.offset)[]) + of 4: d = int(cast[ptr int32](a +% n.offset)[]) + else: assert(false) + return d + +proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode = + var discr = getDiscriminant(aa, n) + if discr <% n.len: + result = n.sons[discr] + if result == nil: result = n.sons[n.len] + # n.sons[n.len] contains the ``else`` part (but may be nil) + else: + result = n.sons[n.len] + +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 getType*(x: TAny): TType = return TType(x.rawType.kind) + +proc `[]`*(x: TAny, i: int): TAny = + assert getType(x) in {TNArray, TNSequence} + if x.getType == TNArray: + 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: + 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) + +proc `[]=`*(x: TAny, i: int, y: TAny) = + assert getType(x) in {TNArray, TNSequence} + if x.getType == TNArray: + 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: + 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) + +proc fieldsAux(p: pointer, n: ptr TNimNode, + 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) + assert ret[ret.len()-1][0] != nil + of nkList: + 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))) + if m != nil: fieldsAux(p, m, ret) + +iterator fields*(x: TAny): tuple[name: string, any: TAny] = + assert getType(x) in {TNTuple, TNPureObject, TNObject} + var p = x.value + var t = x.rawType + if x.getType == TNObject: t = cast[ptr PNimType](x.value)[] + var n = t.node + var ret: seq[tuple[name: cstring, any: TAny]] = @[] + fieldsAux(p, n, ret) + for name, any in items(ret): + 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 + else: + result.value = p + result.rawType = x.rawType.base + +proc readInt*(x: TAny): int = cast[ptr int](x.value)[] +proc readInt8*(x: TAny): int8 = cast[ptr int8](x.value)[] +proc readInt16*(x: TAny): int16 = cast[ptr int16](x.value)[] +proc readInt32*(x: TAny): int32 = cast[ptr int32](x.value)[] +proc readInt64*(x: TAny): int64 = cast[ptr int64](x.value)[] + +proc readFloat*(x: TAny): float = cast[ptr float](x.value)[] +proc readFloat32*(x: TAny): float32 = cast[ptr float32](x.value)[] +proc readFloat64*(x: TAny): float64 = cast[ptr float64](x.value)[] + +proc readString*(x: TAny): string = cast[ptr string](x.value)[] + +when isMainModule: + type + TE = enum + blah, blah2 + + TestObj = object + test, asd: int + case test2: TE + of blah: + help: string + else: + nil + + var test = @[0,1,2,3,4] + var x = toAny(test) + var y = 78 + x[4] = toAny(y) + echo cast[ptr int](x[2].value)[] + + var test2: tuple[name: string, s: int] = ("test", 56) + var x2 = toAny(test2) + for n, a in fields(x2): + echo("Name = ", n) + echo("Any type = ", a.getType) + + var test3: TestObj + test3.test = 42 + test3.test2 = blah2 + var x3 = toAny(test3) + for n, a in fields(x3): + echo("Name = ", n) + echo("Any type = ", a.getType) + + + var test4: ref string + new(test4) + test4[] = "test" + var x4 = toAny(test4) + echo x4[].getType() diff --git a/lib/system.nim b/lib/system.nim index 5250a7ae7..e348bafea 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1889,3 +1889,4 @@ proc `[]=`*[T](s: var seq[T], x: TSlice[int], b: openArray[T]) = else: raise newException(EOutOfRange, "differing lengths for slice assignment") +proc getTypeInfo*[T](x: T): pointer {.magic: "ToAny".} diff --git a/lib/system/hti.nim b/lib/system/hti.nim index c6ea0bc44..c5b6967ef 100755 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -7,6 +7,12 @@ # distribution, for details about the copyright. # +when defined(NimString): + # we are in system module: + {.pragma: codegenType, compilerproc.} +else: + {.pragma: codegenType.} + type # This should be he same as ast.TTypeKind # many enum fields are not used at runtime TNimKind = enum @@ -35,7 +41,7 @@ type # This should be he same as ast.TTypeKind tyPureObject # signals that object has no `n_type` field TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase - TNimNode {.compilerproc, final.} = object + TNimNode {.codegenType, final.} = object kind: TNimNodeKind offset: int typ: ptr TNimType @@ -48,7 +54,7 @@ type # This should be he same as ast.TTypeKind ntfAcyclic = 1, # type cannot form a cycle ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow # version - TNimType {.compilerproc, final.} = object + TNimType {.codegenType, final.} = object size: int kind: TNimKind flags: set[TNimTypeFlag] |