1e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka//===--- VTTBuilder.cpp - C++ VTT layout builder --------------------------===// 2e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// 3e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// The LLVM Compiler Infrastructure 4e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// 5e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// This file is distributed under the University of Illinois Open Source 6e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// License. See LICENSE.TXT for details. 7e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// 8e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka//===----------------------------------------------------------------------===// 9e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// 10e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// This contains code dealing with generation of the layout of virtual table 11e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// tables (VTT). 12e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka// 13e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka//===----------------------------------------------------------------------===// 14e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 15e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include "clang/AST/VTTBuilder.h" 16e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include "clang/AST/ASTContext.h" 17d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenka#include "clang/AST/CXXInheritance.h" 18e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include "clang/AST/RecordLayout.h" 19e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include "clang/Basic/TargetInfo.h" 20e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include "llvm/Support/Format.h" 21e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include <algorithm> 22e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka#include <cstdio> 23e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 24e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkausing namespace clang; 25b93bb3538c55f173f94a4ee7510d9d1521d8f731Shishir Agrawal 26c9394399180abbc32d04f6a3652ce22d5931e0b8Shishir Agrawal#define DUMP_OVERRIDERS 0 274baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal 28e287feac673ff68565b766e0e463d105fa9cef9dAlex YakavenkaVTTBuilder::VTTBuilder(ASTContext &Ctx, 29e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *MostDerivedClass, 30e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka bool GenerateDefinition) 31e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka : Ctx(Ctx), MostDerivedClass(MostDerivedClass), 32e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka MostDerivedClassLayout(Ctx.getASTRecordLayout(MostDerivedClass)), 33e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka GenerateDefinition(GenerateDefinition) { 34e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Lay out this VTT. 35b93bb3538c55f173f94a4ee7510d9d1521d8f731Shishir Agrawal LayoutVTT(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 36ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville /*BaseIsVirtual=*/false); 374baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal} 38b93bb3538c55f173f94a4ee7510d9d1521d8f731Shishir Agrawal 39e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkavoid VTTBuilder::AddVTablePointer(BaseSubobject Base, uint64_t VTableIndex, 40e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *VTableClass) { 41d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenka // Store the vtable pointer index if we're generating the primary VTT. 42e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (VTableClass == MostDerivedClass) { 43e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka assert(!SecondaryVirtualPointerIndices.count(Base) && 44e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka "A virtual pointer index already exists for this base subobject!"); 45e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka SecondaryVirtualPointerIndices[Base] = VTTComponents.size(); 46d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenka } 47d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenka 48d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenka if (!GenerateDefinition) { 49e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VTTComponents.push_back(VTTComponent()); 50e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka return; 51e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka } 52e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 53e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VTTComponents.push_back(VTTComponent(VTableIndex, Base)); 54e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka} 55e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 56e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkavoid VTTBuilder::LayoutSecondaryVTTs(BaseSubobject Base) { 57e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *RD = Base.getBase(); 5805ef3b65972826780859b9acbd1fa9580d099832Alex Yakavenka 5905ef3b65972826780859b9acbd1fa9580d099832Alex Yakavenka for (const auto &I : RD->bases()) { 607f1a3f0ab65c144fde56e1246c5747b0c555034aShishir Agrawal // Don't layout virtual bases. 6105ef3b65972826780859b9acbd1fa9580d099832Alex Yakavenka if (I.isVirtual()) 62e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka continue; 63e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 64e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *BaseDecl = 65e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl()); 66cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville 67e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); 68e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka CharUnits BaseOffset = Base.getBaseOffset() + 69b93bb3538c55f173f94a4ee7510d9d1521d8f731Shishir Agrawal Layout.getBaseClassOffset(BaseDecl); 70b93bb3538c55f173f94a4ee7510d9d1521d8f731Shishir Agrawal 71e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Layout the VTT for this base. 72e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka LayoutVTT(BaseSubobject(BaseDecl, BaseOffset), /*BaseIsVirtual=*/false); 73e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka } 74e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka} 75e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 76e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkavoid 77e287feac673ff68565b766e0e463d105fa9cef9dAlex YakavenkaVTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base, 78e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka bool BaseIsMorallyVirtual, 79e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka uint64_t VTableIndex, 80e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *VTableClass, 81e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VisitedVirtualBasesSetTy &VBases) { 82e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *RD = Base.getBase(); 83e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 844baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal // We're not interested in bases that don't have virtual bases, and not 85e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // morally virtual bases. 86e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (!RD->getNumVBases() && !BaseIsMorallyVirtual) 87c9877fe39ad8f3641d16fd980404916da7f6ce70Shishir Agrawal return; 88e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 89e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka for (const auto &I : RD->bases()) { 90e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *BaseDecl = 91300cc7bd83f881abd0653a42f975223e0ab60cd4Shishir Agrawal cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl()); 92300cc7bd83f881abd0653a42f975223e0ab60cd4Shishir Agrawal 93e3ea8115b0d558f6c6b57b201885948f4309e5c0Shishir Agrawal // Itanium C++ ABI 2.6.2: 94e3ea8115b0d558f6c6b57b201885948f4309e5c0Shishir Agrawal // Secondary virtual pointers are present for all bases with either 95e3ea8115b0d558f6c6b57b201885948f4309e5c0Shishir Agrawal // virtual bases or virtual function declarations overridden along a 96c9877fe39ad8f3641d16fd980404916da7f6ce70Shishir Agrawal // virtual path. 97e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // 98a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville // If the base class is not dynamic, we don't want to add it, nor any 99a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville // of its base classes. 100e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (!BaseDecl->isDynamicClass()) 101e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka continue; 102e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 103e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka bool BaseDeclIsMorallyVirtual = BaseIsMorallyVirtual; 104e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka bool BaseDeclIsNonVirtualPrimaryBase = false; 105e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka CharUnits BaseOffset; 106a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville if (I.isVirtual()) { 107a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville // Ignore virtual bases that we've already visited. 108a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville if (!VBases.insert(BaseDecl)) 109a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville continue; 110a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville 111a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville BaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); 112a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville BaseDeclIsMorallyVirtual = true; 113a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville } else { 114a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); 115e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 116e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka BaseOffset = Base.getBaseOffset() + 117e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka Layout.getBaseClassOffset(BaseDecl); 118e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 119e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (!Layout.isPrimaryBaseVirtual() && 120e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka Layout.getPrimaryBase() == BaseDecl) 121e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka BaseDeclIsNonVirtualPrimaryBase = true; 122e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka } 123e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 124e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Itanium C++ ABI 2.6.2: 125e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Secondary virtual pointers: for each base class X which (a) has virtual 1264baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal // bases or is reachable along a virtual path from D, and (b) is not a 127e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // non-virtual primary base, the address of the virtual table for X-in-D 128e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // or an appropriate construction virtual table. 129e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (!BaseDeclIsNonVirtualPrimaryBase && 130e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka (BaseDecl->getNumVBases() || BaseDeclIsMorallyVirtual)) { 131e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Add the vtable pointer. 132e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka AddVTablePointer(BaseSubobject(BaseDecl, BaseOffset), VTableIndex, 133e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VTableClass); 134e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka } 135e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 136e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // And lay out the secondary virtual pointers for the base class. 137e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka LayoutSecondaryVirtualPointers(BaseSubobject(BaseDecl, BaseOffset), 138e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka BaseDeclIsMorallyVirtual, VTableIndex, 139e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VTableClass, VBases); 140e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka } 141e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka} 142e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 143e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkavoid 144e287feac673ff68565b766e0e463d105fa9cef9dAlex YakavenkaVTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base, 145e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka uint64_t VTableIndex) { 146e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VisitedVirtualBasesSetTy VBases; 147e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka LayoutSecondaryVirtualPointers(Base, /*BaseIsMorallyVirtual=*/false, 148e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VTableIndex, Base.getBase(), VBases); 149e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka} 150e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 151e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkavoid VTTBuilder::LayoutVirtualVTTs(const CXXRecordDecl *RD, 152e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VisitedVirtualBasesSetTy &VBases) { 153e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka for (const auto &I : RD->bases()) { 154e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *BaseDecl = 155e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl()); 156e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 157e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Check if this is a virtual base. 158e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (I.isVirtual()) { 159e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Check if we've seen this base before. 160e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (!VBases.insert(BaseDecl)) 161e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka continue; 162e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 163a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville CharUnits BaseOffset = 1644baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); 1654baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal 1664baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal LayoutVTT(BaseSubobject(BaseDecl, BaseOffset), /*BaseIsVirtual=*/true); 1674baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal } 168c9877fe39ad8f3641d16fd980404916da7f6ce70Shishir Agrawal 169c9877fe39ad8f3641d16fd980404916da7f6ce70Shishir Agrawal // We only need to layout virtual VTTs for this base if it actually has 1704baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal // virtual bases. 1714baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal if (BaseDecl->getNumVBases()) 1724baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal LayoutVirtualVTTs(BaseDecl, VBases); 1734baf17fd699249d1b387903b6db7328ad3f7b3e2Shishir Agrawal } 174e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka} 175e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 176e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenkavoid VTTBuilder::LayoutVTT(BaseSubobject Base, bool BaseIsVirtual) { 177e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka const CXXRecordDecl *RD = Base.getBase(); 178e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 179e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Itanium C++ ABI 2.6.2: 180e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // An array of virtual table addresses, called the VTT, is declared for 181e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // each class type that has indirect or direct virtual base classes. 182e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (RD->getNumVBases() == 0) 183e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka return; 184e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 185e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka bool IsPrimaryVTT = Base.getBase() == MostDerivedClass; 186e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 187e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka if (!IsPrimaryVTT) { 188e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Remember the sub-VTT index. 189e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka SubVTTIndicies[Base] = VTTComponents.size(); 190e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka } 191e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 192e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka uint64_t VTableIndex = VTTVTables.size(); 193e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka VTTVTables.push_back(VTTVTable(Base, BaseIsVirtual)); 194e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka 195e287feac673ff68565b766e0e463d105fa9cef9dAlex Yakavenka // Add the primary vtable pointer. 196a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville AddVTablePointer(Base, VTableIndex, RD); 197a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville 198a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville // Add the secondary VTTs. 199a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville LayoutSecondaryVTTs(Base); 200a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville 201a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville // Add the secondary virtual pointers. 202a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville LayoutSecondaryVirtualPointers(Base, VTableIndex); 203a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville 204a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville // If this is the primary VTT, we want to lay out virtual VTTs as well. 205a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville if (IsPrimaryVTT) { 206a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville VisitedVirtualBasesSetTy VBases; 207a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville LayoutVirtualVTTs(Base.getBase(), VBases); 208a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville } 209a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville} 210a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville