# # # The Nim Compiler # (c) Copyright 2017 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from cgen.nim # ------------------------- Name Mangling -------------------------------- import sighashes from lowerings import createObj proc genProcHeader(m: BModule, prc: PSym): Rope proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range case w.id of ccgKeywordsLow..ccgKeywordsHigh, nimKeywordsLow..nimKeywordsHigh, ord(wInline): return true else: return false proc mangleField(m: BModule; name: PIdent): string = result = mangle(name.s) # fields are tricky to get right and thanks to generic types producing # duplicates we can end up mangling the same field multiple times. However # if we do so, the 'cppDefines' table might be modified in the meantime # meaning we produce inconsistent field names (see bug #5404). # Hence we do not check for ``m.g.config.cppDefines.contains(result)`` here # anymore: if isKeyword(name): result.add "_0" when false: proc hashOwner(s: PSym): SigHash = var m = s while m.kind != skModule: m = m.owner let p = m.owner assert p.kind == skPackage result = gDebugInfo.register(p.name.s, m.name.s) proc mangleName(m: BModule; s: PSym): Rope = result = s.loc.r if result == nil: result = s.name.s.mangle.rope add(result, idOrSig(s, m.module.name.s.mangle, m.sigConflicts)) s.loc.r = result writeMangledName(m.ndi, s, m.config) proc mangleParamName(m: BModule; s: PSym): Rope = ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc. ## Fortunately C's scoping rules are sane enough so that that doesn't ## cause any trouble. result = s.loc.r if result == nil: var res = s.name.s.mangle if isKeyword(s.name) or m.g.config.cppDefines.contains(res): res.add "_0" result = res.rope s.loc.r = result writeMangledName(m.ndi, s, m.config) proc mangleLocalName(p: BProc; s: PSym): Rope = assert s.kind in skLocalVars+{skTemp} #assert sfGlobal notin s.flags result = s.loc.r if result == nil: var key = s.name.s.mangle shallow(key) let counter = p.sigConflicts.getOrDefault(key) result = key.rope if s.kind == skTemp: # speed up conflict search for temps (these are quite common): if counter != 0: result.add "_" & rope(counter+1) elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key): result.add "_" & rope(counter+1) p.sigConflicts.inc(key) s.loc.r = result if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config) proc scopeMangledParam(p: BProc; param: PSym) = ## parameter generation only takes BModule, not a BProc, so we have to ## remember these parameter names are already in scope to be able to ## generate unique identifiers reliably (consider that ``var a = a`` is ## even an idiom in Nim). var key = param.name.s.mangle shallow(key) p.sigConflicts.inc(key) const irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation, tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred} proc typeName(typ: PType): Rope = let typ = typ.skipTypes(irrelevantForBackend) result = if typ.sym != nil and typ.kind in {tyObject, tyEnum}: rope($typ.kind & '_' & typ.sym.name.s.mangle) else: rope($typ.kind) proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = var t = typ while true: if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: return t.sym.loc.r if t.kind in irrelevantForBackend: t = t.lastSon else: break let typ = if typ.kind in {tyAlias, tySink}: typ.lastSon else: typ if typ.loc.r == nil: typ.loc.r = typ.typeName & $sig else: when defined(debugSigHashes): # check consistency: assert
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: package ranger.container</title>
</head><body bgcolor="#f0f0f8">

<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="ranger.html"><font color="#ffffff">ranger</font></a>.container</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/hut/ranger/ranger/container/__init__.py">/home/hut/ranger/ranger/container/__init__.py</a></font></td></tr></table>
    <p><tt>This&nbsp;package&nbsp;includes&nbsp;container-objects&nbsp;which&nbsp;are<br>
used&nbsp;to&nbsp;manage&nbsp;stored&nbsp;data</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
    
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="ranger.container.bookmarks.html">bookmarks</a><br>
<a href="ranger.container.commandlist.html">commandlist</a><br>
</td><td width="25%" valign=top><a href="ranger.container.environment.html">environment</a><br>
<a href="ranger.container.history.html">history</a><br>
</td><td width="25%" valign=top><a href="ranger.container.keybuffer.html">keybuffer</a><br>
<a href="ranger.container.tags.html">tags</a><br>
</td><td width="25%" valign=top></td></tr></table></td></tr></table>
</body></html>
let desc = getRecordFields(m, typ, check) if desc == nil and not hasField: addf(result, "char dummy;$n", []) else: add(result, desc) add(result, "};\L") if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: result.add "#pragma pack(pop)\L" proc getTupleDesc(m: BModule, typ: PType, name: Rope, check: var IntSet): Rope = result = "$1 $2 {$n" % [structOrUnion(typ), name] var desc: Rope = nil for i in countup(0, sonsLen(typ) - 1): addf(desc, "$1 Field$2;$n", [getTypeDescAux(m, typ.sons[i], check), rope(i)]) if desc == nil: add(result, "char dummy;\L") else: add(result, desc) add(result, "};\L") proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = # A helper proc for handling cppimport patterns, involving numeric # placeholders for generic types (e.g. '0, '**2, etc). # pre: the cursor must be placed at the ' symbol # post: the cursor will be placed after the final digit # false will returned if the input is not recognized as a placeholder inc cursor let begin = cursor while pat[cursor] == '*': inc cursor if pat[cursor] in Digits: outIdx = pat[cursor].ord - '0'.ord outStars = cursor - begin inc cursor return true else: return false proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = # Make sure the index refers to one of the generic params of the type. # XXX: we should catch this earlier and report it as a semantic error. if idx >= typ.len: doAssert false, "invalid apostrophe type parameter index" result = typ.sons[idx] for i in 1..stars: if result != nil and result.len > 0: result = if result.kind == tyGenericInst: result.sons[1] else: result.elemType proc getSeqPayloadType(m: BModule; t: PType): Rope = result = getTypeForward(m, t, hashType(t)) & "_Content" when false: var check = initIntSet() # XXX remove this duplication: appcg(m, m.s[cfsSeqTypes], "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE]; };$n", [getTypeDescAux(m, t.sons[0], check), result]) proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # returns only the type's name var t = origTyp.skipTypes(irrelevantForBackend) if containsOrIncl(check, t.id): if not (isImportedCppType(origTyp) or isImportedCppType(t)): internalError(m.config, "cannot generate C type for: " & typeToString(origTyp)) # XXX: this BUG is hard to fix -> we need to introduce helper structs, # but determining when this needs to be done is hard. We should split # C type generation into an analysis and a code generation phase somehow. if t.sym != nil: useHeader(m, t.sym) if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym) let sig = hashType(origTyp) result = getTypePre(m, t, sig) if result != nil: excl(check, t.id) return case t.kind of tyRef, tyOptAsRef, tyPtr, tyVar, tyLent: var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and compileToCpp(m): "&" else: "*" var et = origTyp.skipTypes(abstractInst).lastSon var etB = et.skipTypes(abstractInst) if mapType(m.config, t) == ctPtrToArray: if etB.kind == tySet: et = getSysType(m.g.graph, unknownLineInfo(), tyUInt8) else: et = elemType(etB) etB = et.skipTypes(abstractInst) star[0] = '*' case etB.kind of tyObject, tyTuple: if isImportedCppType(etB) and et.kind == tyGenericInst: result = getTypeDescAux(m, et, check) & star else: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) result = name & star m.typeCache[sig] = result of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) result = name & seqStar(m) & star m.typeCache[sig] = result pushType(m, et) else: # else we have a strong dependency :-( result = getTypeDescAux(m, et, check) & star m.typeCache[sig] = result of tyOpenArray, tyVarargs: result = getTypeDescWeak(m, t.sons[0], check) & "*" m.typeCache[sig] = result of tyEnum: result = cacheGetType(m.typeCache, sig) if result == nil: result = getTypeName(m, origTyp, sig) if not (isImportedCppType(t) or (sfImportc in t.sym.flags and t.sym.magic == mNone)): m.typeCache[sig] = result var size: int if firstOrd(m.config, t) < 0: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) size = 4 else: size = int(getSize(m.config, t)) case size of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result]) of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result]) of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) else: internalError(m.config, t.sym.info, "getTypeDescAux: enum") when false: let owner = hashOwner(t.sym) if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner): var vals: seq[(string, int)] = @[] for i in countup(0, t.n.len - 1): assert(t.n.sons[i].kind == nkSym) let field = t.n.sons[i].sym vals.add((field.name.s, field.position.int)) gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id, name: t.sym.name.s, values: vals)) of tyProc: result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result var rettype, desc: Rope genProcParams(m, t, rettype, desc, check, true, true) if not isImportedType(t): if t.callConv != ccClosure: # procedure vars may need a closure! addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", [rope(CallingConvToStr[t.callConv]), rettype, result, desc]) else: addf(m.s[cfsTypes], "typedef struct {$n" & "N_NIMCALL_PTR($2, ClP_0) $3;$n" & "void* ClE_0;$n} $1;$n", [result, rettype, desc]) of tySequence: # we cannot use getTypeForward here because then t would be associated # with the name of the struct, not with the pointer to the struct: result = cacheGetType(m.forwTypeCache, sig) if result == nil: result = getTypeName(m, origTyp, sig) if not isImportedType(t): addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(t), result]) m.forwTypeCache[sig] = result assert(cacheGetType(m.typeCache, sig) == nil) m.typeCache[sig] = result & seqStar(m) if not isImportedType(t): if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: const cppSeq = "struct $2 : #TGenericSeq {$n" cSeq = "struct $2 {$n" & " #TGenericSeq Sup;$n" if m.config.selectedGC == gcDestructors: appcg(m, m.s[cfsTypes], "typedef struct{ NI cap;void* allocator;$1 data[SEQ_DECL_SIZE];}$2_Content;$n" & "struct $2 {$n" & " NI len; $2_Content* p;$n" & "};$n", [getTypeDescAux(m, t.sons[0], check), result]) else: appcg(m, m.s[cfsSeqTypes], (if m.compileToCpp: cppSeq else: cSeq) & " $1 data[SEQ_DECL_SIZE];$n" & "};$n", [getTypeDescAux(m, t.sons[0], check), result]) elif m.config.selectedGC == gcDestructors: internalError(m.config, "cannot map the empty seq type to a C type") else: result = rope("TGenericSeq") add(result, seqStar(m)) of tyUncheckedArray: result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result if not isImportedType(t): let foo = getTypeDescAux(m, t.sons[0], check) addf(m.s[cfsTypes], "typedef $1 $2[1];$n", [foo, result]) of tyArray: var n: BiggestInt = lengthOrd(m.config, t) if n <= 0: n = 1 # make an array of at least one element result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result if not isImportedType(t): let foo = getTypeDescAux(m, t.sons[1], check) addf(m.s[cfsTypes], "typedef $1 $2[$3];$n", [foo, result, rope(n)]) else: addAbiCheck(m, t, result) of tyObject, tyTuple: if isImportedCppType(t) and origTyp.kind == tyGenericInst: let cppName = getTypeName(m, t, sig) var i = 0 var chunkStart = 0 template addResultType(ty: untyped) = if ty == nil or ty.kind == tyVoid: result.add(~"void") elif ty.kind == tyStatic: internalAssert m.config, ty.n != nil result.add ty.n.renderTree else: result.add getTypeDescAux(m, ty, check) while i < cppName.data.len: if cppName.data[i] == '\'': var chunkEnd = i-1 var idx, stars: int if scanCppGenericSlot(cppName.data, i, idx, stars): result.add cppName.data.substr(chunkStart, chunkEnd) chunkStart = i let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars) addResultType(typeInSlot) else: inc i if chunkStart != 0: result.add cppName.data.substr(chunkStart) else: result = cppName & "<" for i in 1 .. origTyp.len-2: if i > 1: result.add(" COMMA ") addResultType(origTyp.sons[i]) result.add("> ") # always call for sideeffects: assert t.kind != tyTuple discard getRecordDesc(m, t, result, check) # The resulting type will include commas and these won't play well # with the C macros for defining procs such as N_NIMCALL. We must # create a typedef for the type and use it in the proc signature: let typedefName = ~"TY" & $sig addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName]) m.typeCache[sig] = typedefName result = typedefName else: when false: if t.sym != nil and t.sym.name.s == "KeyValuePair": if t == origTyp: echo "wtf: came here" writeStackTrace() quit 1 result = cacheGetType(m.forwTypeCache, sig) if result == nil: when false: if t.sym != nil and t.sym.name.s == "KeyValuePair": # or {sfImportc, sfExportc} * t.sym.flags == {}: if t.loc.r != nil: echo t.kind, " ", hashType t echo origTyp.kind, " ", sig assert t.loc.r == nil result = getTypeName(m, origTyp, sig) m.forwTypeCache[sig] = result if not isImportedType(t): addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(t), result]) assert m.forwTypeCache[sig] == result m.typeCache[sig] = result # always call for sideeffects: if not incompleteType(t): let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check) else: getTupleDesc(m, t, result, check) if not isImportedType(t): add(m.s[cfsTypes], recdesc) elif tfIncompleteStruct notin t.flags: addAbiCheck(m, t, result) of tySet: result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon) m.typeCache[sig] = result if not isImportedType(t): let s = int(getSize(m.config, t)) case s of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)]) else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", [result, rope(getSize(m.config, t))]) of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyUserTypeClass, tyUserTypeClassInst, tyInferred: result = getTypeDescAux(m, lastSon(t), check) else: internalError(m.config, "getTypeDescAux(" & $t.kind & ')') result = nil # fixes bug #145: excl(check, t.id) proc getTypeDesc(m: BModule, typ: PType): Rope = var check = initIntSet() result = getTypeDescAux(m, typ, check) type TClosureTypeKind = enum ## In C closures are mapped to 3 different things. clHalf, ## fn(args) type without the trailing 'void* env' parameter clHalfWithEnv, ## fn(args, void* env) type with trailing 'void* env' parameter clFull ## struct {fn(args, void* env), env} proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope = assert t.kind == tyProc var check = initIntSet() result = getTempName(m) var rettype, desc: Rope genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf) if not isImportedType(t): if t.callConv != ccClosure or kind != clFull: addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", [rope(CallingConvToStr[t.callConv]), rettype, result, desc]) else: addf(m.s[cfsTypes], "typedef struct {$n" & "N_NIMCALL_PTR($2, ClP_0) $3;$n" & "void* ClE_0;$n} $1;$n", [result, rettype, desc]) proc finishTypeDescriptions(m: BModule) = var i = 0 while i < len(m.typeStack): discard getTypeDesc(m, m.typeStack[i]) inc(i) template cgDeclFrmt*(s: PSym): string = s.constraint.strVal proc genProcHeader(m: BModule, prc: PSym): Rope = var rettype, params: Rope genCLineDir(result, prc.info, m.config) # using static is needed for inline procs if lfExportLib in prc.loc.flags: if isHeaderFile in m.flags: result.add "N_LIB_IMPORT " else: result.add "N_LIB_EXPORT " elif prc.typ.callConv == ccInline: result.add "static " elif {sfImportc, sfExportc} * prc.flags == {}: result.add "N_LIB_PRIVATE " var check = initIntSet() fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown) genProcParams(m, prc.typ, rettype, params, check) # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! if prc.constraint.isNil: addf(result, "$1($2, $3)$4", [rope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, params]) else: result = prc.cgDeclFrmt % [rettype, prc.loc.r, params] # ------------------ type info generation ------------------------------------- proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope proc getNimNode(m: BModule): Rope = result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)] inc(m.typeNodes) proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope; info: TLineInfo) = var nimtypeKind: int #allocMemTI(m, typ, name) if isObjLackingTypeField(typ): nimtypeKind = ord(tyPureObject) else: nimtypeKind = ord(typ.kind) var size: Rope if tfIncompleteStruct in typ.flags: size = rope"void*" else: size = getTypeDesc(m, origType) addf(m.s[cfsTypeInit3], "$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n", [name, size, rope(nimtypeKind), base]) # compute type flags for GC optimization var flags = 0 if not containsGarbageCollectedRef(typ): flags = flags or 1 if not canFormAcycle(typ): flags = flags or 2 #else MessageOut("can contain a cycle: " & typeToString(typ)) if flags != 0: addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)]) discard cgsym(m, "TNimType") if isDefined(m.config, "nimTypeNames"): var typename = typeToString(if origType.typeInst != nil: origType.typeInst else: origType, preferName) if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil: typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info addf(m.s[cfsTypeInit3], "$1.name = $2;$n", [name, makeCstring typename]) discard cgsym(m, "nimTypeRoot") addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n", [name]) addf(m.s[cfsVars], "TNimType $1;$n", [name]) proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = var base: Rope if sonsLen(typ) > 0 and typ.lastSon != nil: var x = typ.lastSon if typ.kind == tyObject: x = x.skipTypes(skipPtrs) if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x): base = rope("0") else: base = genTypeInfo(m, x, info) else: base = rope("0") genTypeInfoAuxBase(m, typ, origType, name, base, info) proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = # bugfix: we need to search the type that contains the discriminator: var objtype = objtype while lookupInRecord(objtype.n, d.name) == nil: objtype = objtype.sons[0] if objtype.sym == nil: internalError(m.config, d.info, "anonymous obj with discriminator") result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)] proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope = discard cgsym(m, "TNimNode") var tmp = discriminatorTableName(m, objtype, d) result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)] proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; info: TLineInfo) = case n.kind of nkRecList: var L = sonsLen(n) if L == 1: genObjectFields(m, typ, origType, n.sons[0], expr, info) elif L > 0: var tmp = getTempName(m) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)]) for i in countup(0, L-1): var tmp2 = getNimNode(m) addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2]) genObjectFields(m, typ, origType, n.sons[i], tmp2, info) addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", [expr, rope(L), tmp]) else: addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, rope(L)]) of nkRecCase: assert(n.sons[0].kind == nkSym) var field = n.sons[0].sym var tmp = discriminatorTableName(m, typ, field) var L = lengthOrd(m.config, field.typ) assert L > 0 if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & "$1.len = $7;$n", [expr, getTypeDesc(m, origType), field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s), tmp, rope(L)]) addf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, rope(L+1)]) for i in countup(1, sonsLen(n)-1): var b = n.sons[i] # branch var tmp2 = getNimNode(m) genObjectFields(m, typ, origType, lastSon(b), tmp2, info) case b.kind of nkOfBranch: if sonsLen(b) < 2: internalError(m.config, b.info, "genObjectFields; nkOfBranch broken") for j in countup(0, sonsLen(b) - 2): if b.sons[j].kind == nkRange: var x = int(getOrdValue(b.sons[j].sons[0])) var y = int(getOrdValue(b.sons[j].sons[1])) while x <= y: addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(x), tmp2]) inc(x) else: addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(getOrdValue(b.sons[j])), tmp2]) of nkElse: addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(L), tmp2]) else: internalError(m.config, n.info, "genObjectFields(nkRecCase)") of nkSym: var field = n.sym if field.bitsize == 0: if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n", [expr, getTypeDesc(m, origType), field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)]) else: internalError(m.config, n.info, "genObjectFields") proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = if typ.kind == tyObject: if incompleteType(typ): localError(m.config, info, "request for RTTI generation for incomplete object: " & typeToString(typ)) genTypeInfoAux(m, typ, origType, name, info) else: genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info) var tmp = getNimNode(m) if not isImportedType(typ): genObjectFields(m, typ, origType, typ.n, tmp, info) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) var t = typ.sons[0] while t != nil: t = t.skipTypes(skipPtrs) t.flags.incl tfObjHasKids t = t.sons[0] proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info) var expr = getNimNode(m) var length = sonsLen(typ) if length > 0: var tmp = getTempName(m) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(length)]) for i in countup(0, length - 1): var a = typ.sons[i] var tmp2 = getNimNode(m) addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2]) addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" & "$1.name = \"Field$3\";$n", [tmp2, getTypeDesc(m, origType), rope(i), genTypeInfo(m, a, info)]) addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", [expr, rope(length), tmp]) else: addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, rope(length)]) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr]) proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = # Type information for enumerations is quite heavy, so we do some # optimizations here: The ``typ`` field is never set, as it is redundant # anyway. We generate a cstring array and a loop over it. Exceptional # positions will be reset after the loop. genTypeInfoAux(m, typ, typ, name, info) var nodePtrs = getTempName(m) var length = sonsLen(typ.n) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [nodePtrs, rope(length)]) var enumNames, specialCases: Rope var firstNimNode = m.typeNodes var hasHoles = false for i in countup(0, length - 1): assert(typ.n.sons[i].kind == nkSym) var field = typ.n.sons[i].sym var elemNode = getNimNode(m) if field.ast == nil: # no explicit string literal for the enum field, so use field.name: add(enumNames, makeCString(field.name.s)) else: add(enumNames, makeCString(field.ast.strVal)) if i < length - 1: add(enumNames, ", \L") if field.position != i or tfEnumHasHoles in typ.flags: addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)]) hasHoles = true var enumArray = getTempName(m) var counter = getTempName(m) addf(m.s[cfsTypeInit1], "NI $1;$n", [counter]) addf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n", [enumArray, rope(length), enumNames]) addf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" & "$3[$1+$4].kind = 1;$n" & "$3[$1+$4].offset = $1;$n" & "$3[$1+$4].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter, rope(length), m.typeNodesName, rope(firstNimNode), enumArray, nodePtrs]) add(m.s[cfsTypeInit3], specialCases) addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4.node = &$1;$n", [getNimNode(m), rope(length), nodePtrs, name]) if hasHoles: # 1 << 2 is {ntfEnumHole} addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name]) proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = assert(typ.sons[0] != nil) genTypeInfoAux(m, typ, typ, name, info) var tmp = getNimNode(m) addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", [tmp, rope(firstOrd(m.config, typ)), name]) proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info) proc fakeClosureType(m: BModule; owner: PSym): PType = # we generate the same RTTI as for a tuple[pointer, ref tuple[]] result = newType(tyTuple, owner) result.rawAddSon(newType(tyPointer, owner)) var r = newType(tyRef, owner) let obj = createObj(m.g.graph, owner, owner.info, final=false) r.rawAddSon(obj) result.rawAddSon(r) include ccgtrav proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = genProc(m, s) addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", [result, s.loc.r]) proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) let sig = hashType(origType) result = m.typeInfoMarker.getOrDefault(sig) if result != nil: return "(&".rope & result & ")".rope result = m.g.typeInfoMarker.getOrDefault(sig) if result != nil: discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") addf(m.s[cfsVars], "extern TNimType $1;$n", [result]) # also store in local type section: m.typeInfoMarker[sig] = result return "(&".rope & result & ")".rope result = "NTI$1_" % [rope($sig)] m.typeInfoMarker[sig] = result let owner = t.skipTypes(typedescPtrs).owner.getModule if owner != m.module: # make sure the type info is created in the owner module assert m.g.modules[owner.position] != nil discard genTypeInfo(m.g.modules[owner.position], origType, info) # reference the type info as extern here discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") addf(m.s[cfsVars], "extern TNimType $1;$n", [result]) return "(&".rope & result & ")".rope m.g.typeInfoMarker[sig] = result case t.kind of tyEmpty, tyVoid: result = rope"0" of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent: genTypeInfoAuxBase(m, t, t, result, rope"0", info) of tyStatic: if t.n != nil: result = genTypeInfo(m, lastSon t, info) else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') of tyUserTypeClasses: internalAssert m.config, t.isResolvedUserTypeClass return genTypeInfo(m, t.lastSon, info) of tyProc: if t.callConv != ccClosure: genTypeInfoAuxBase(m, t, t, result, rope"0", info) else: let x = fakeClosureType(m, t.owner) genTupleInfo(m, x, x, result, info) of tySequence: genTypeInfoAux(m, t, t, result, info) if m.config.selectedGC != gcDestructors: if m.config.selectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, origType, sig) addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) of tyRef, tyOptAsRef: genTypeInfoAux(m, t, t, result, info) if m.config.selectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, origType, sig) addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info) of tyArray: genArrayInfo(m, t, result, info) of tySet: genSetInfo(m, t, result, info) of tyEnum: genEnumInfo(m, t, result, info) of tyObject: genObjectInfo(m, t, origType, result, info) of tyTuple: # if t.n != nil: genObjectInfo(m, t, result) # else: # BUGFIX: use consistently RTTI without proper field names; otherwise # results are not deterministic! genTupleInfo(m, t, origType, result, info) else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') if t.deepCopy != nil: genDeepCopyProc(m, t.deepCopy, result) elif origType.deepCopy != nil: genDeepCopyProc(m, origType.deepCopy, result) result = "(&".rope & result & ")".rope proc genTypeSection(m: BModule, n: PNode) = discard