vtables.nim 6.6 KB

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