diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index ecc0b5048..aaa507c09 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -213,6 +213,159 @@ PType::~PType() { } +//========================================================================== +// +// PType :: AddConversion +// +//========================================================================== + +bool PType::AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &)) +{ + // Make sure a conversion hasn't already been registered + for (unsigned i = 0; i < Conversions.Size(); ++i) + { + if (Conversions[i].TargetType == target) + return false; + } + Conversions.Push(Conversion(target, convertconst)); + return true; +} + +//========================================================================== +// +// PType :: FindConversion +// +// Returns <0 if there is no path to target. Otherwise, returns the distance +// to target and fills slots (if non-NULL) with the necessary conversions +// to get there. A result of 0 means this is the target. +// +//========================================================================== + +int PType::FindConversion(PType *target, const PType::Conversion **slots, int numslots) +{ + if (this == target) + { + return 0; + } + // The queue is implemented as a ring buffer + VisitQueue queue; + VisitedNodeSet visited; + + // Use a breadth-first search to find the shortest path to the target. + MarkPred(NULL, -1, -1); + queue.Push(this); + visited.Insert(this); + while (!queue.IsEmpty()) + { + PType *t = queue.Pop(); + if (t == target) + { // found it + if (slots != NULL) + { + if (t->Distance >= numslots) + { // Distance is too far for the output + return -2; + } + t->FillConversionPath(slots); + } + return t->Distance + 1; + } + for (unsigned i = 0; i < t->Conversions.Size(); ++i) + { + PType *succ = t->Conversions[i].TargetType; + if (!visited.Check(succ)) + { + succ->MarkPred(t, i, Distance + 1); + visited.Insert(succ); + queue.Push(succ); + } + } + } + return -1; +} + +//========================================================================== +// +// PType :: FillConversionPath +// +// Traces backwards from the target type to the original type and fills in +// the conversions necessary to get between them. slots must point to an +// array large enough to contain the entire path. +// +//========================================================================== + +void PType::FillConversionPath(const PType::Conversion **slots) +{ + for (PType *node = this; node->Distance >= 0; node = node->PredType) + { + assert(node->PredType != NULL); + slots[node->Distance] = &node->PredType->Conversions[node->PredConv]; + } +} + +//========================================================================== +// +// PType :: VisitQueue :: Push +// +//========================================================================== + +void PType::VisitQueue::Push(PType *type) +{ + Queue[In] = type; + Advance(In); + assert(!IsEmpty() && "Queue overflowed"); +} + +//========================================================================== +// +// PType :: VisitQueue :: Pop +// +//========================================================================== + +PType *PType::VisitQueue::Pop() +{ + if (IsEmpty()) + { + return NULL; + } + PType *node = Queue[Out]; + Advance(Out); + return node; +} + +//========================================================================== +// +// PType :: VisitedNodeSet :: Insert +// +//========================================================================== + +void PType::VisitedNodeSet::Insert(PType *node) +{ + assert(!Check(node) && "Node was already inserted"); + size_t buck = Hash(node) & (countof(Buckets) - 1); + node->VisitNext = Buckets[buck]; + Buckets[buck] = node; +} + +//========================================================================== +// +// PType :: VisitedNodeSet :: Check +// +//========================================================================== + +bool PType::VisitedNodeSet::Check(const PType *node) +{ + size_t buck = Hash(node) & (countof(Buckets) - 1); + for (const PType *probe = Buckets[buck]; probe != NULL; probe = probe->VisitNext) + { + if (probe == node) + { + return true; + } + } + return false; +} + //========================================================================== // // PType :: SetValue diff --git a/src/dobjtype.h b/src/dobjtype.h index 2861b1da2..a282d0790 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -113,6 +113,7 @@ private: // Map * * // Prototype *+ *+ +struct ZCC_ExprConstant; class PClassType; class PType : public DObject { @@ -128,6 +129,15 @@ public: typedef PClassType MetaClass; MetaClass *GetClass() const; + struct Conversion + { + Conversion(PType *target, void (*convert)(ZCC_ExprConstant *, class FSharedStringArena &)) + : TargetType(target), ConvertConstant(convert) {} + + PType *TargetType; + void (*ConvertConstant)(ZCC_ExprConstant *val, class FSharedStringArena &strdump); + }; + unsigned int Size; // this type's size unsigned int Align; // this type's preferred alignment PType *HashNext; // next type in this type table @@ -136,6 +146,10 @@ public: PType(unsigned int size, unsigned int align); virtual ~PType(); + bool AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &)); + + int FindConversion(PType *target, const Conversion **slots, int numslots); + // Sets the value of a variable of this type at (addr) virtual void SetValue(void *addr, int val); @@ -163,6 +177,54 @@ public: virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; static void StaticInit(); + +private: + // Stuff for type conversion searches + class VisitQueue + { + public: + VisitQueue() : In(0), Out(0) {} + void Push(PType *type); + PType *Pop(); + bool IsEmpty() { return In == Out; } + + private: + // This is a fixed-sized ring buffer. + PType *Queue[64]; + int In, Out; + + void Advance(int &ptr) + { + ptr = (ptr + 1) & (countof(Queue) - 1); + } + }; + + class VisitedNodeSet + { + public: + VisitedNodeSet() { memset(Buckets, 0, sizeof(Buckets)); } + void Insert(PType *node); + bool Check(const PType *node); + + private: + PType *Buckets[32]; + + size_t Hash(const PType *type) { return size_t(type) >> 4; } + }; + + TArray Conversions; + PType *PredType; + PType *VisitNext; + short PredConv; + short Distance; + + void MarkPred(PType *pred, int conv, int dist) + { + PredType = pred; + PredConv = conv; + Distance = dist; + } + void FillConversionPath(const Conversion **slots); }; // Not-really-a-type types --------------------------------------------------