vtables.nim 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types
  2. import std/[algorithm, tables, intsets, assertions]
  3. proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym =
  4. #[
  5. proc dispatch(x: Base, params: ...) =
  6. cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params)
  7. ]#
  8. var base = methods[0].ast[dispatcherPos].sym
  9. result = base
  10. var paramLen = base.typ.signatureLen
  11. var body = newNodeI(nkStmtList, base.info)
  12. var disp = newNodeI(nkIfStmt, base.info)
  13. var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
  14. let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
  15. let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
  16. var nTyp = base.typ.n[1].sym.typ
  17. var dispatchObject = newSymNode(base.typ.n[1].sym)
  18. if nTyp.kind == tyObject:
  19. dispatchObject = newTree(nkAddr, dispatchObject)
  20. else:
  21. if g.config.backend != backendCpp: # TODO: maybe handle ptr?
  22. if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject:
  23. dispatchObject = newTree(nkDerefExpr, dispatchObject)
  24. var getVTableCall = newTree(nkCall,
  25. newSymNode(nimGetVTableSym),
  26. dispatchObject,
  27. newIntNode(nkIntLit, index)
  28. )
  29. getVTableCall.typ = base.typ
  30. var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
  31. var castNode = newTree(nkCast,
  32. newNodeIT(nkType, base.info, base.typ),
  33. getVTableCall)
  34. castNode.typ = base.typ
  35. vTableCall.add castNode
  36. for col in 1..<paramLen:
  37. let param = base.typ.n[col].sym
  38. vTableCall.add newSymNode(param)
  39. var ret: PNode
  40. if base.typ.returnType != nil:
  41. var a = newNodeI(nkFastAsgn, base.info)
  42. a.add newSymNode(base.ast[resultPos].sym)
  43. a.add vTableCall
  44. ret = newNodeI(nkReturnStmt, base.info)
  45. ret.add a
  46. else:
  47. ret = vTableCall
  48. if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
  49. let ifBranch = newNodeI(nkElifBranch, base.info)
  50. let boolType = getSysType(g, unknownLineInfo, tyBool)
  51. var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil)
  52. let checkSelf = newNodeIT(nkCall, base.info, boolType)
  53. checkSelf.add newSymNode(isNil)
  54. checkSelf.add newSymNode(base.typ.n[1].sym)
  55. ifBranch.add checkSelf
  56. ifBranch.add newTree(nkCall,
  57. newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym))
  58. let elseBranch = newTree(nkElifBranch, ret)
  59. disp.add ifBranch
  60. disp.add elseBranch
  61. else:
  62. disp = ret
  63. body.add disp
  64. body.flags.incl nfTransf # should not be further transformed
  65. result.ast[bodyPos] = body
  66. proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool =
  67. result = tfHasMeta in base.flags
  68. for i in s:
  69. if tfHasMeta in i.value.flags:
  70. result = true
  71. break
  72. proc collectVTableDispatchers*(g: ModuleGraph) =
  73. var itemTable = initTable[ItemId, seq[LazySym]]()
  74. var rootTypeSeq = newSeq[PType]()
  75. var rootItemIdCount = initCountTable[ItemId]()
  76. for bucket in 0..<g.methods.len:
  77. var relevantCols = initIntSet()
  78. if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
  79. sortBucket(g.methods[bucket].methods, relevantCols)
  80. let base = g.methods[bucket].methods[^1]
  81. let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc})
  82. if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
  83. let methodIndexLen = g.bucketTable[baseType.itemId]
  84. if baseType.itemId notin itemTable: # once is enough
  85. rootTypeSeq.add baseType
  86. itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
  87. sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
  88. if x.depth >= y.depth: 1
  89. else: -1
  90. )
  91. for item in g.objectTree[baseType.itemId]:
  92. if item.value.itemId notin itemTable:
  93. itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
  94. var mIndex = 0 # here is the correpsonding index
  95. if baseType.itemId notin rootItemIdCount:
  96. rootItemIdCount[baseType.itemId] = 1
  97. else:
  98. mIndex = rootItemIdCount[baseType.itemId]
  99. rootItemIdCount.inc(baseType.itemId)
  100. for idx in 0..<g.methods[bucket].methods.len:
  101. let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
  102. itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
  103. g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex)
  104. else: # if the base object doesn't have this method
  105. g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen)
  106. proc sortVTableDispatchers*(g: ModuleGraph) =
  107. var itemTable = initTable[ItemId, seq[LazySym]]()
  108. var rootTypeSeq = newSeq[ItemId]()
  109. var rootItemIdCount = initCountTable[ItemId]()
  110. for bucket in 0..<g.methods.len:
  111. var relevantCols = initIntSet()
  112. if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
  113. sortBucket(g.methods[bucket].methods, relevantCols)
  114. let base = g.methods[bucket].methods[^1]
  115. let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc})
  116. if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
  117. let methodIndexLen = g.bucketTable[baseType.itemId]
  118. if baseType.itemId notin itemTable: # once is enough
  119. rootTypeSeq.add baseType.itemId
  120. itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
  121. sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
  122. if x.depth >= y.depth: 1
  123. else: -1
  124. )
  125. for item in g.objectTree[baseType.itemId]:
  126. if item.value.itemId notin itemTable:
  127. itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
  128. var mIndex = 0 # here is the correpsonding index
  129. if baseType.itemId notin rootItemIdCount:
  130. rootItemIdCount[baseType.itemId] = 1
  131. else:
  132. mIndex = rootItemIdCount[baseType.itemId]
  133. rootItemIdCount.inc(baseType.itemId)
  134. for idx in 0..<g.methods[bucket].methods.len:
  135. let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
  136. itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
  137. for baseType in rootTypeSeq:
  138. g.setMethodsPerType(baseType, itemTable[baseType])
  139. for item in g.objectTree[baseType]:
  140. let typ = item.value.skipTypes(skipPtrs)
  141. let idx = typ.itemId
  142. for mIndex in 0..<itemTable[idx].len:
  143. if itemTable[idx][mIndex].sym == nil:
  144. let parentIndex = typ.baseClass.skipTypes(skipPtrs).itemId
  145. itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
  146. g.setMethodsPerType(idx, itemTable[idx])