summary refs log tree commit diff stats
path: root/compiler/vtables.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/vtables.nim')
-rw-r--r--compiler/vtables.nim167
1 files changed, 167 insertions, 0 deletions
diff --git a/compiler/vtables.nim b/compiler/vtables.nim
new file mode 100644
index 000000000..928c64dd5
--- /dev/null
+++ b/compiler/vtables.nim
@@ -0,0 +1,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.signatureLen
+  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.returnType)
+  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.returnType != 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.firstParamType.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.firstParamType.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.firstParamType.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.firstParamType.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.baseClass.skipTypes(skipPtrs).itemId
+          itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
+      g.setMethodsPerType(idx, itemTable[idx])