Changed ValueObject to use a dedicated ChildrenManager class to store its children, instead of an std::vector
This solves an issue where a ValueObject was getting a wrong children count (usually, a huge value) and trying to resize the vector of children to fit that many ValueObject*

Added a loop detection algorithm to the synthetic children provider for std::list

Added a few more checks to the synthetic children provider for std::vector

Both std::list and std::vector's synthetic children providers now cache the count of children instead of recomputing it every time
std::map has a field that stores the count, so there is little need to cache it on our side


git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@152371 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/examples/synthetic/gnu_libstdcpp.py b/examples/synthetic/gnu_libstdcpp.py
index 3265f55..a80b7ba 100644
--- a/examples/synthetic/gnu_libstdcpp.py
+++ b/examples/synthetic/gnu_libstdcpp.py
@@ -12,7 +12,36 @@
 		self.valobj = valobj
 		self.update()
 
+	def next_node(self,node):
+		return node.GetChildMemberWithName('_M_next')
+
+	def is_valid(self,node):
+		return self.value(self.next_node(node)) != self.node_address
+
+	def value(self,node):
+		return node.GetValueAsUnsigned()
+
+	# Floyd's cyle-finding algorithm
+	# try to detect if this list has a loop
+	def has_loop(self):
+		slow = self.next
+		fast1 = self.next
+		fast2 = self.next
+		while self.is_valid(slow):
+			slow_value = self.value(slow)
+			fast1 = self.next_node(fast2)
+			fast2 = self.next_node(fast1)
+			if self.value(fast1) == slow_value or self.value(fast2) == slow_value:
+				return True
+			slow = self.next_node(slow)
+		return False
+
 	def num_children(self):
+		if self.count == None:
+			self.count = self.num_children_impl()
+		return self.count
+
+	def num_children_impl(self):
 		try:
 			next_val = self.next.GetValueAsUnsigned(0)
 			prev_val = self.prev.GetValueAsUnsigned(0)
@@ -23,6 +52,8 @@
 				return 0
 			if next_val == prev_val:
 				return 1
+			if self.has_loop():
+				return 0
 			size = 2
 			current = self.next
 			while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address:
@@ -70,6 +101,7 @@
 			self.prev = node.GetChildMemberWithName('_M_prev')
 			self.data_type = self.extract_type()
 			self.data_size = self.data_type.GetByteSize()
+			self.count = None
 		except:
 			pass
 
@@ -80,6 +112,16 @@
 		self.update()
 
 	def num_children(self):
+		if self.count == None:
+			self.count = self.num_children_impl()
+		return self.count
+
+	def is_valid_pointer(ptr,process):
+		error = lldb.SBError()
+		process.ReadMemory(ptr,1,error)
+		return False if error.Fail() else True
+
+	def num_children_impl(self):
 		try:
 			start_val = self.start.GetValueAsUnsigned(0)
 			finish_val = self.finish.GetValueAsUnsigned(0)
@@ -101,7 +143,14 @@
 			if finish_val > end_val:
 				return 0
 
-			num_children = (finish_val-start_val)/self.data_size
+			# if we have a struct (or other data type that the compiler pads to native word size)
+			# this check might fail, unless the sizeof() we get is itself incremented to take the
+			# padding bytes into account - on current clang it looks like this is the case
+			num_children = (finish_val-start_val)
+			if (num_children % self.data_size) != 0:
+				return 0
+			else:
+				num_children = num_children/self.data_size
 			return num_children
 		except:
 			return 0;
@@ -131,6 +180,11 @@
 			self.end = impl.GetChildMemberWithName('_M_end_of_storage')
 			self.data_type = self.start.GetType().GetPointeeType()
 			self.data_size = self.data_type.GetByteSize()
+			# if any of these objects is invalid, it means there is no point in trying to fetch anything
+			if self.start.IsValid() and self.finish.IsValid() and self.end.IsValid() and self.data_type.IsValid():
+				self.count = None
+			else:
+				self.count = 0
 		except:
 			pass