summary refs log tree commit diff stats
path: root/compiler/vtables.nim
blob: f57b59eaef9d166c74dff242ea356268f1976680 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types
import std/[algorithm, tables, intsets, assertions]



proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym =
#[
proc dispatch(x: Base, params: ...) =
  cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params)
]#
  var base = methods[0].ast[dispatcherPos].sym
  result = base
  var paramLen = base.typ.len
  var body = newNodeI(nkStmtList, base.info)

  var disp = newNodeI(nkIfStmt, base.info)

  var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
  let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
  let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ

  var nTyp = base.typ.n[1].sym.typ
  var dispatchObject = newSymNode(base.typ.n[1].sym)
  if nTyp.kind == tyObject:
    dispatchObject = newTree(nkAddr, dispatchObject)
  else:
    if g.config.backend != backendCpp: # TODO: maybe handle ptr?
      if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject:
        dispatchObject = newTree(nkDerefExpr, dispatchObject)

  var getVTableCall = newTree(nkCall,
    newSymNode(nimGetVTableSym),
    dispatchObject,
    newIntNode(nkIntLit, index)
  )
  getVTableCall.typ = base.typ
  var vTableCall = newNodeIT(nkCall, base.info, base.typ[0])
  var castNode = newTree(nkCast,
        newNodeIT(nkType, base.info, base.typ),
        getVTableCall)

  castNode.typ = base.typ
  vTableCall.add castNode
  for col in 1..<paramLen:
    let param = base.typ.n[col].sym
    vTableCall.add newSymNode(param)

  var ret: PNode
  if base.typ[0] != nil:
    var a = newNodeI(nkFastAsgn, base.info)
    a.add newSymNode(base.ast[resultPos].sym)
    a.add vTableCall
    ret = newNodeI(nkReturnStmt, base.info)
    ret.add a
  else:
    ret = vTableCall

  if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
    let ifBranch = newNodeI(nkElifBranch, base.info)
    let boolType = getSysType(g, unknownLineInfo, tyBool)
    var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil)
    let checkSelf = newNodeIT(nkCall, base.info, boolType)
    checkSelf.add newSymNode(isNil)
    checkSelf.add newSymNode(base.typ.n[1].sym)
    ifBranch.add checkSelf
    ifBranch.add newTree(nkCall,
        newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym))
    let elseBranch = newTree(nkElifBranch, ret)
    disp.add ifBranch
    disp.add elseBranch
  else:
    disp = ret

  body.add disp
  body.flags.incl nfTransf # should not be further transformed
  result.ast[bodyPos] = body

proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool =
  result = tfHasMeta in base.flags
  for i in s:
    if tfHasMeta in i.value.flags:
      result = true
      break

proc collectVTableDispatchers*(g: ModuleGraph) =
  var itemTable = initTable[ItemId, seq[LazySym]]()
  var rootTypeSeq = newSeq[PType]()
  var rootItemIdCount = initCountTable[ItemId]()
  for bucket in 0..<g.methods.len:
    var relevantCols = initIntSet()
    if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
    sortBucket(g.methods[bucket].methods, relevantCols)
    let base = g.methods[bucket].methods[^1]
    let baseType = base.typ[1].skipTypes(skipPtrs-{tyTypeDesc})
    if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
      let methodIndexLen = g.bucketTable[baseType.itemId]
      if baseType.itemId notin itemTable: # once is enough
        rootTypeSeq.add baseType
        itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)

        sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
          if x.depth >= y.depth: 1
          else: -1
          )

        for item in g.objectTree[baseType.itemId]:
          if item.value.itemId notin itemTable:
            itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)

      var mIndex = 0 # here is the correpsonding index
      if baseType.itemId notin rootItemIdCount:
        rootItemIdCount[baseType.itemId] = 1
      else:
        mIndex = rootItemIdCount[baseType.itemId]
        rootItemIdCount.inc(baseType.itemId)
      for idx in 0..<g.methods[bucket].methods.len:
        let obj = g.methods[bucket].methods[idx].typ[1].skipTypes(skipPtrs)
        itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
      g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex)
    else: # if the base object doesn't have this method
      g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen)

proc sortVTableDispatchers*(g: ModuleGraph) =
  var itemTable = initTable[ItemId, seq[LazySym]]()
  var rootTypeSeq = newSeq[ItemId]()
  var rootItemIdCount = initCountTable[ItemId]()
  for bucket in 0..<g.methods.len:
    var relevantCols = initIntSet()
    if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
    sortBucket(g.methods[bucket].methods, relevantCols)
    let base = g.methods[bucket].methods[^1]
    let baseType = base.typ[1].skipTypes(skipPtrs-{tyTypeDesc})
    if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
      let methodIndexLen = g.bucketTable[baseType.itemId]
      if baseType.itemId notin itemTable: # once is enough
        rootTypeSeq.add baseType.itemId
        itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)

        sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
          if x.depth >= y.depth: 1
          else: -1
          )

        for item in g.objectTree[baseType.itemId]:
          if item.value.itemId notin itemTable:
            itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)

      var mIndex = 0 # here is the correpsonding index
      if baseType.itemId notin rootItemIdCount:
        rootItemIdCount[baseType.itemId] = 1
      else:
        mIndex = rootItemIdCount[baseType.itemId]
        rootItemIdCount.inc(baseType.itemId)
      for idx in 0..<g.methods[bucket].methods.len:
        let obj = g.methods[bucket].methods[idx].typ[1].skipTypes(skipPtrs)
        itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])

  for baseType in rootTypeSeq:
    g.setMethodsPerType(baseType, itemTable[baseType])
    for item in g.objectTree[baseType]:
      let typ = item.value.skipTypes(skipPtrs)
      let idx = typ.itemId
      for mIndex in 0..<itemTable[idx].len:
        if itemTable[idx][mIndex].sym == nil:
          let parentIndex = typ[0].skipTypes(skipPtrs).itemId
          itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
      g.setMethodsPerType(idx, itemTable[idx])