Revision: 7012
Author: [email protected]
Date: Wed Mar  2 01:16:05 2011
Log: Adding files for LiveObjectList implementation.

Patch by Mark Lam from Hewlett-Packard Development Company, LP

Review URL: http://codereview.chromium.org/6357005

http://code.google.com/p/v8/source/detail?r=7012

Modified:
 /branches/bleeding_edge/src/flag-definitions.h
 /branches/bleeding_edge/src/liveobjectlist-inl.h
 /branches/bleeding_edge/src/liveobjectlist.cc
 /branches/bleeding_edge/src/liveobjectlist.h

=======================================
--- /branches/bleeding_edge/src/flag-definitions.h      Tue Mar  1 04:26:19 2011
+++ /branches/bleeding_edge/src/flag-definitions.h      Wed Mar  2 01:16:05 2011
@@ -267,6 +267,12 @@
 // ic.cc
 DEFINE_bool(use_ic, true, "use inline caching")

+#ifdef LIVE_OBJECT_LIST
+// liveobjectlist.cc
+DEFINE_string(lol_workdir, NULL, "path for lol temp files")
+DEFINE_bool(verify_lol, false, "perform debugging verification for lol")
+#endif
+
 // macro-assembler-ia32.cc
 DEFINE_bool(native_code_counters, false,
             "generate extra code for manipulating stats counters")
=======================================
--- /branches/bleeding_edge/src/liveobjectlist-inl.h Thu Jan 20 00:11:53 2011 +++ /branches/bleeding_edge/src/liveobjectlist-inl.h Wed Mar 2 01:16:05 2011
@@ -32,5 +32,95 @@

 #include "liveobjectlist.h"

+namespace v8 {
+namespace internal {
+
+#ifdef LIVE_OBJECT_LIST
+
+void LiveObjectList::GCEpilogue() {
+  if (!NeedLOLProcessing()) return;
+  GCEpiloguePrivate();
+}
+
+
+void LiveObjectList::GCPrologue() {
+  if (!NeedLOLProcessing()) return;
+#ifdef VERIFY_LOL
+  if (FLAG_verify_lol) {
+    Verify();
+  }
+#endif
+}
+
+
+void LiveObjectList::IterateElements(ObjectVisitor* v) {
+  if (!NeedLOLProcessing()) return;
+  IterateElementsPrivate(v);
+}
+
+
+void LiveObjectList::ProcessNonLive(HeapObject *obj) {
+  // Only do work if we have at least one list to process.
+  if (last()) DoProcessNonLive(obj);
+}
+
+
+void LiveObjectList::UpdateReferencesForScavengeGC() {
+  if (LiveObjectList::NeedLOLProcessing()) {
+    UpdateLiveObjectListVisitor update_visitor;
+    LiveObjectList::IterateElements(&update_visitor);
+  }
+}
+
+
+LiveObjectList* LiveObjectList::FindLolForId(int id,
+                                             LiveObjectList* start_lol) {
+  if (id != 0) {
+    LiveObjectList* lol = start_lol;
+    while (lol != NULL) {
+      if (lol->id() == id) {
+        return lol;
+      }
+      lol = lol->prev_;
+    }
+  }
+  return NULL;
+}
+
+
+// Iterates the elements in every lol and returns the one that matches the
+// specified key.  If no matching element is found, then it returns NULL.
+template <typename T>
+inline LiveObjectList::Element*
+LiveObjectList::FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key) {
+  LiveObjectList *lol = last();
+  while (lol != NULL) {
+    Element* elements = lol->elements_;
+    for (int i = 0; i < lol->obj_count_; i++) {
+      Element* element = &elements[i];
+      if (GetValue(element) == key) {
+        return element;
+      }
+    }
+    lol = lol->prev_;
+  }
+  return NULL;
+}
+
+
+inline int LiveObjectList::GetElementId(LiveObjectList::Element* element) {
+  return element->id_;
+}
+
+
+inline HeapObject*
+LiveObjectList::GetElementObj(LiveObjectList::Element* element) {
+  return element->obj_;
+}
+
+#endif  // LIVE_OBJECT_LIST
+
+} }  // namespace v8::internal
+
 #endif  // V8_LIVEOBJECTLIST_INL_H_

=======================================
--- /branches/bleeding_edge/src/liveobjectlist.cc       Thu Jan 20 00:11:53 2011
+++ /branches/bleeding_edge/src/liveobjectlist.cc       Wed Mar  2 01:16:05 2011
@@ -37,7 +37,7 @@
 #include "heap.h"
 #include "inspector.h"
 #include "list-inl.h"
-#include "liveobjectlist.h"
+#include "liveobjectlist-inl.h"
 #include "string-stream.h"
 #include "top.h"
 #include "v8utils.h"
@@ -46,6 +46,2480 @@
 namespace internal {


+typedef int (*RawComparer)(const void*, const void*);
+
+
+#ifdef CHECK_ALL_OBJECT_TYPES
+
+#define DEBUG_LIVE_OBJECT_TYPES(v) \
+  v(Smi, "unexpected: Smi") \
+  \
+  v(CodeCache, "unexpected: CodeCache") \
+  v(BreakPointInfo, "unexpected: BreakPointInfo") \
+  v(DebugInfo, "unexpected: DebugInfo") \
+  v(TypeSwitchInfo, "unexpected: TypeSwitchInfo") \
+  v(SignatureInfo, "unexpected: SignatureInfo") \
+  v(Script, "unexpected: Script") \
+  v(ObjectTemplateInfo, "unexpected: ObjectTemplateInfo") \
+  v(FunctionTemplateInfo, "unexpected: FunctionTemplateInfo") \
+  v(CallHandlerInfo, "unexpected: CallHandlerInfo") \
+  v(InterceptorInfo, "unexpected: InterceptorInfo") \
+  v(AccessCheckInfo, "unexpected: AccessCheckInfo") \
+  v(AccessorInfo, "unexpected: AccessorInfo") \
+  v(ExternalTwoByteString, "unexpected: ExternalTwoByteString") \
+  v(ExternalAsciiString, "unexpected: ExternalAsciiString") \
+  v(ExternalString, "unexpected: ExternalString") \
+  v(SeqTwoByteString, "unexpected: SeqTwoByteString") \
+  v(SeqAsciiString, "unexpected: SeqAsciiString") \
+  v(SeqString, "unexpected: SeqString") \
+  v(JSFunctionResultCache, "unexpected: JSFunctionResultCache") \
+  v(GlobalContext, "unexpected: GlobalContext") \
+  v(MapCache, "unexpected: MapCache") \
+  v(CodeCacheHashTable, "unexpected: CodeCacheHashTable") \
+  v(CompilationCacheTable, "unexpected: CompilationCacheTable") \
+  v(SymbolTable, "unexpected: SymbolTable") \
+  v(Dictionary, "unexpected: Dictionary") \
+  v(HashTable, "unexpected: HashTable") \
+  v(DescriptorArray, "unexpected: DescriptorArray") \
+  v(ExternalFloatArray, "unexpected: ExternalFloatArray") \
+  v(ExternalUnsignedIntArray, "unexpected: ExternalUnsignedIntArray") \
+  v(ExternalIntArray, "unexpected: ExternalIntArray") \
+  v(ExternalUnsignedShortArray, "unexpected: ExternalUnsignedShortArray") \
+  v(ExternalShortArray, "unexpected: ExternalShortArray") \
+  v(ExternalUnsignedByteArray, "unexpected: ExternalUnsignedByteArray") \
+  v(ExternalByteArray, "unexpected: ExternalByteArray") \
+  v(JSValue, "unexpected: JSValue")
+
+#else
+#define DEBUG_LIVE_OBJECT_TYPES(v)
+#endif
+
+
+#define FOR_EACH_LIVE_OBJECT_TYPE(v) \
+  DEBUG_LIVE_OBJECT_TYPES(v) \
+  \
+  v(JSArray, "JSArray") \
+  v(JSRegExp, "JSRegExp") \
+  v(JSFunction, "JSFunction") \
+  v(JSGlobalObject, "JSGlobal") \
+  v(JSBuiltinsObject, "JSBuiltins") \
+  v(GlobalObject, "Global") \
+  v(JSGlobalProxy, "JSGlobalProxy") \
+  v(JSObject, "JSObject") \
+  \
+  v(Context, "meta: Context") \
+  v(ByteArray, "meta: ByteArray") \
+  v(PixelArray, "meta: PixelArray") \
+  v(ExternalArray, "meta: ExternalArray") \
+  v(FixedArray, "meta: FixedArray") \
+  v(String, "String") \
+  v(HeapNumber, "HeapNumber") \
+  \
+  v(Code, "meta: Code") \
+  v(Map, "meta: Map") \
+  v(Oddball, "Oddball") \
+  v(Proxy, "meta: Proxy") \
+  v(SharedFunctionInfo, "meta: SharedFunctionInfo") \
+  v(Struct, "meta: Struct") \
+  \
+  v(HeapObject, "HeapObject")
+
+
+enum /* LiveObjectType */ {
+#define DECLARE_OBJECT_TYPE_ENUM(type, name) kType##type,
+  FOR_EACH_LIVE_OBJECT_TYPE(DECLARE_OBJECT_TYPE_ENUM)
+  kInvalidLiveObjType,
+  kNumberOfTypes
+#undef DECLARE_OBJECT_TYPE_ENUM
+};
+
+
+LiveObjectType GetObjectType(HeapObject* heap_obj) {
+  // TODO(mlam): investigate usint Map::instance_type() instead.
+#define CHECK_FOR_OBJECT_TYPE(type, name) \
+  if (heap_obj->Is##type()) return kType##type;
+  FOR_EACH_LIVE_OBJECT_TYPE(CHECK_FOR_OBJECT_TYPE)
+#undef CHECK_FOR_OBJECT_TYPE
+
+  UNREACHABLE();
+  return kInvalidLiveObjType;
+}
+
+
+inline const char* GetObjectTypeDesc(LiveObjectType type) {
+  static const char* const name[kNumberOfTypes] = {
+  #define DEFINE_OBJECT_TYPE_NAME(type, name) name,
+    FOR_EACH_LIVE_OBJECT_TYPE(DEFINE_OBJECT_TYPE_NAME)
+    "invalid"
+  #undef DEFINE_OBJECT_TYPE_NAME
+  };
+  ASSERT(type < kNumberOfTypes);
+  return name[type];
+}
+
+
+const char* GetObjectTypeDesc(HeapObject* heap_obj) {
+  LiveObjectType type = GetObjectType(heap_obj);
+  return GetObjectTypeDesc(type);
+}
+
+
+bool IsOfType(LiveObjectType type, HeapObject *obj) {
+  // Note: there are types that are more general (e.g. JSObject) that would
+  // have passed the Is##type_() test for more specialized types (e.g.
+ // JSFunction). If we find a more specialized match but we're looking for
+  // the general type, then we should reject the ones that matches the
+  // specialized type.
+#define CHECK_OBJECT_TYPE(type_, name) \
+  if (obj->Is##type_()) return (type == kType##type_);
+
+  FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE)
+#undef CHECK_OBJECT_TYPE
+
+  return false;
+}
+
+
+const AllocationSpace kInvalidSpace = static_cast<AllocationSpace>(-1);
+
+static AllocationSpace FindSpaceFor(String* space_str) {
+  SmartPointer<char> s =
+      space_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+
+  const char* key_str = *s;
+  switch (key_str[0]) {
+    case 'c':
+      if (strcmp(key_str, "cell") == 0) return CELL_SPACE;
+      if (strcmp(key_str, "code") == 0) return CODE_SPACE;
+      break;
+    case 'l':
+      if (strcmp(key_str, "lo") == 0) return LO_SPACE;
+      break;
+    case 'm':
+      if (strcmp(key_str, "map") == 0) return MAP_SPACE;
+      break;
+    case 'n':
+      if (strcmp(key_str, "new") == 0) return NEW_SPACE;
+      break;
+    case 'o':
+      if (strcmp(key_str, "old-pointer") == 0) return OLD_POINTER_SPACE;
+      if (strcmp(key_str, "old-data") == 0) return OLD_DATA_SPACE;
+      break;
+  }
+  return kInvalidSpace;
+}
+
+
+static bool InSpace(AllocationSpace space, HeapObject *heap_obj) {
+  if (space != LO_SPACE) {
+    return Heap::InSpace(heap_obj, space);
+  }
+
+  // This is an optimization to speed up the check for an object in the LO
+  // space by exclusion because we know that all object pointers passed in
+  // here are guaranteed to be in the heap.  Hence, it is safe to infer
+  // using an exclusion test.
+  // Note: calling Heap::InSpace(heap_obj, LO_SPACE) is too slow for our
+  // filters.
+  int first_space = static_cast<int>(FIRST_SPACE);
+  int last_space = static_cast<int>(LO_SPACE);
+  for (int sp = first_space; sp < last_space; sp++) {
+    if (Heap::InSpace(heap_obj, static_cast<AllocationSpace>(sp))) {
+      return false;
+    }
+  }
+  SLOW_ASSERT(Heap::InSpace(heap_obj, LO_SPACE));
+  return true;
+}
+
+
+static LiveObjectType FindTypeFor(String* type_str) {
+  SmartPointer<char> s =
+      type_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+
+#define CHECK_OBJECT_TYPE(type_, name) { \
+    const char* type_desc = GetObjectTypeDesc(kType##type_); \
+    const char* key_str = *s; \
+    if (strstr(type_desc, key_str) != NULL) return kType##type_; \
+  }
+  FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE)
+#undef CHECK_OBJECT_TYPE
+
+  return kInvalidLiveObjType;
+}
+
+
+class LolFilter {
+ public:
+  explicit LolFilter(Handle<JSObject> filter_obj);
+
+  inline bool is_active() const { return is_active_; }
+  inline bool Matches(HeapObject* obj) {
+    return !is_active() || MatchesSlow(obj);
+  }
+
+ private:
+  void InitTypeFilter(Handle<JSObject> filter_obj);
+  void InitSpaceFilter(Handle<JSObject> filter_obj);
+  void InitPropertyFilter(Handle<JSObject> filter_obj);
+  bool MatchesSlow(HeapObject* obj);
+
+  bool is_active_;
+  LiveObjectType type_;
+  AllocationSpace space_;
+  Handle<String> prop_;
+};
+
+
+LolFilter::LolFilter(Handle<JSObject> filter_obj)
+    : is_active_(false),
+      type_(kInvalidLiveObjType),
+      space_(kInvalidSpace),
+      prop_() {
+  if (filter_obj.is_null()) return;
+
+  InitTypeFilter(filter_obj);
+  InitSpaceFilter(filter_obj);
+  InitPropertyFilter(filter_obj);
+}
+
+
+void LolFilter::InitTypeFilter(Handle<JSObject> filter_obj) {
+  Handle<String> type_sym = Factory::LookupAsciiSymbol("type");
+  MaybeObject* maybe_result = filter_obj->GetProperty(*type_sym);
+  Object* type_obj;
+  if (maybe_result->ToObject(&type_obj)) {
+    if (type_obj->IsString()) {
+      String* type_str = String::cast(type_obj);
+      type_ = FindTypeFor(type_str);
+      if (type_ != kInvalidLiveObjType) {
+        is_active_ = true;
+      }
+    }
+  }
+}
+
+
+void LolFilter::InitSpaceFilter(Handle<JSObject> filter_obj) {
+  Handle<String> space_sym = Factory::LookupAsciiSymbol("space");
+  MaybeObject* maybe_result = filter_obj->GetProperty(*space_sym);
+  Object* space_obj;
+  if (maybe_result->ToObject(&space_obj)) {
+    if (space_obj->IsString()) {
+      String* space_str = String::cast(space_obj);
+      space_ = FindSpaceFor(space_str);
+      if (space_ != kInvalidSpace) {
+        is_active_ = true;
+      }
+    }
+  }
+}
+
+
+void LolFilter::InitPropertyFilter(Handle<JSObject> filter_obj) {
+  Handle<String> prop_sym = Factory::LookupAsciiSymbol("prop");
+  MaybeObject* maybe_result = filter_obj->GetProperty(*prop_sym);
+  Object* prop_obj;
+  if (maybe_result->ToObject(&prop_obj)) {
+    if (prop_obj->IsString()) {
+      prop_ = Handle<String>(String::cast(prop_obj));
+      is_active_ = true;
+    }
+  }
+}
+
+
+bool LolFilter::MatchesSlow(HeapObject* obj) {
+  if ((type_ != kInvalidLiveObjType) && !IsOfType(type_, obj)) {
+    return false;  // Fail because obj is not of the type of interest.
+  }
+  if ((space_ != kInvalidSpace) && !InSpace(space_, obj)) {
+    return false;  // Fail because obj is not in the space of interest.
+  }
+  if (!prop_.is_null() && obj->IsJSObject()) {
+    LookupResult result;
+    obj->Lookup(*prop_, &result);
+    if (!result.IsProperty()) {
+ return false; // Fail because obj does not have the property of interest.
+    }
+  }
+  return true;
+}
+
+
+class LolIterator {
+ public:
+  LolIterator(LiveObjectList* older, LiveObjectList* newer)
+      : older_(older),
+        newer_(newer),
+        curr_(0),
+        elements_(0),
+        count_(0),
+        index_(0) { }
+
+  inline void Init() {
+    SetCurrent(newer_);
+ // If the elements_ list is empty, then move on to the next list as long
+    // as we're not at the last list (indicated by done()).
+    while ((elements_ == NULL) && !Done()) {
+      SetCurrent(curr_->prev_);
+    }
+  }
+
+  inline bool Done() const {
+    return (curr_ == older_);
+  }
+
+  // Object level iteration.
+  inline void Next() {
+    index_++;
+    if (index_ >= count_) {
+      // Iterate backwards until we get to the oldest list.
+      while (!Done()) {
+        SetCurrent(curr_->prev_);
+        // If we have elements to process, we're good to go.
+        if (elements_ != NULL) break;
+
+        // Else, we should advance to the next older list.
+      }
+    }
+  }
+
+  inline int Id() const {
+    return elements_[index_].id_;
+  }
+  inline HeapObject* Obj() const {
+    return elements_[index_].obj_;
+  }
+
+  inline int LolObjCount() const {
+    if (curr_ != NULL) return curr_->obj_count_;
+    return 0;
+  }
+
+ protected:
+  inline void SetCurrent(LiveObjectList* new_curr) {
+    curr_ = new_curr;
+    if (curr_ != NULL) {
+      elements_ = curr_->elements_;
+      count_ = curr_->obj_count_;
+      index_ = 0;
+    }
+  }
+
+  LiveObjectList* older_;
+  LiveObjectList* newer_;
+  LiveObjectList* curr_;
+  LiveObjectList::Element* elements_;
+  int count_;
+  int index_;
+};
+
+
+class LolForwardIterator : public LolIterator {
+ public:
+  LolForwardIterator(LiveObjectList* first, LiveObjectList* last)
+      : LolIterator(first, last) {
+  }
+
+  inline void Init() {
+    SetCurrent(older_);
+ // If the elements_ list is empty, then move on to the next list as long
+    // as we're not at the last list (indicated by Done()).
+    while ((elements_ == NULL) && !Done()) {
+      SetCurrent(curr_->next_);
+    }
+  }
+
+  inline bool Done() const {
+    return (curr_ == newer_);
+  }
+
+  // Object level iteration.
+  inline void Next() {
+    index_++;
+    if (index_ >= count_) {
+      // Done with current list.  Move on to the next.
+      while (!Done()) {  // If not at the last list already, ...
+        SetCurrent(curr_->next_);
+        // If we have elements to process, we're good to go.
+        if (elements_ != NULL) break;
+
+        // Else, we should advance to the next list.
+      }
+    }
+  }
+};
+
+
+// Minimizes the white space in a string.  Tabs and newlines are replaced
+// with a space where appropriate.
+static int CompactString(char* str) {
+  char* src = str;
+  char* dst = str;
+  char prev_ch = 0;
+  while (*dst != '\0') {
+    char ch = *src++;
+    // We will treat non-ascii chars as '?'.
+    if ((ch & 0x80) != 0) {
+      ch = '?';
+    }
+    // Compact contiguous whitespace chars into a single ' '.
+    if (isspace(ch)) {
+      if (prev_ch != ' ') *dst++ = ' ';
+      prev_ch = ' ';
+      continue;
+    }
+    *dst++ = ch;
+    prev_ch = ch;
+  }
+  return (dst - str);
+}
+
+
+// Generates a custom description based on the specific type of
+// object we're looking at.  We only generate specialized
+// descriptions where we can.  In all other cases, we emit the
+// generic info.
+static void GenerateObjectDesc(HeapObject* obj,
+                               char* buffer,
+                               int buffer_size) {
+  Vector<char> buffer_v(buffer, buffer_size);
+  ASSERT(obj != NULL);
+  if (obj->IsJSArray()) {
+    JSArray* jsarray = JSArray::cast(obj);
+    double length = jsarray->length()->Number();
+    OS::SNPrintF(buffer_v,
+                 "%p <%s> len %g",
+                 reinterpret_cast<void*>(obj),
+                 GetObjectTypeDesc(obj),
+                 length);
+
+  } else if (obj->IsString()) {
+    String *str = String::cast(obj);
+    // Only grab up to 160 chars in case they are double byte.
+    // We'll only dump 80 of them after we compact them.
+    const int kMaxCharToDump = 80;
+    const int kMaxBufferSize = kMaxCharToDump * 2;
+    SmartPointer<char> str_sp = str->ToCString(DISALLOW_NULLS,
+                                               ROBUST_STRING_TRAVERSAL,
+                                               0,
+                                               kMaxBufferSize);
+    char* str_cstr = *str_sp;
+    int length = CompactString(str_cstr);
+    OS::SNPrintF(buffer_v,
+                 "%p <%s> '%.80s%s'",
+                 reinterpret_cast<void*>(obj),
+                 GetObjectTypeDesc(obj),
+                 str_cstr,
+                 (length > kMaxCharToDump) ? "..." : "");
+
+  } else if (obj->IsJSFunction() || obj->IsSharedFunctionInfo()) {
+    SharedFunctionInfo* sinfo;
+    if (obj->IsJSFunction()) {
+      JSFunction* func = JSFunction::cast(obj);
+      sinfo = func->shared();
+    } else {
+      sinfo = SharedFunctionInfo::cast(obj);
+    }
+
+    String* name = sinfo->DebugName();
+    SmartPointer<char> name_sp =
+        name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+    char* name_cstr = *name_sp;
+
+    HeapStringAllocator string_allocator;
+    StringStream stream(&string_allocator);
+    sinfo->SourceCodePrint(&stream, 50);
+    SmartPointer<const char> source_sp = stream.ToCString();
+    const char* source_cstr = *source_sp;
+
+    OS::SNPrintF(buffer_v,
+                 "%p <%s> '%s' %s",
+                 reinterpret_cast<void*>(obj),
+                 GetObjectTypeDesc(obj),
+                 name_cstr,
+                 source_cstr);
+
+  } else if (obj->IsFixedArray()) {
+    FixedArray* fixed = FixedArray::cast(obj);
+
+    OS::SNPrintF(buffer_v,
+                 "%p <%s> len %d",
+                 reinterpret_cast<void*>(obj),
+                 GetObjectTypeDesc(obj),
+                 fixed->length());
+
+  } else {
+    OS::SNPrintF(buffer_v,
+                 "%p <%s>",
+                 reinterpret_cast<void*>(obj),
+                 GetObjectTypeDesc(obj));
+  }
+}
+
+
+// Utility function for filling in a line of detail in a verbose dump.
+static bool AddObjDetail(Handle<FixedArray> arr,
+                         int index,
+                         int obj_id,
+                         Handle<HeapObject> target,
+                         const char* desc_str,
+                         Handle<String> id_sym,
+                         Handle<String> desc_sym,
+                         Handle<String> size_sym,
+                         Handle<JSObject> detail,
+                         Handle<String> desc,
+                         Handle<Object> error) {
+  detail = Factory::NewJSObject(Top::object_function());
+  if (detail->IsFailure()) {
+    error = detail;
+    return false;
+  }
+
+  int size = 0;
+  char buffer[512];
+  if (desc_str == NULL) {
+    ASSERT(!target.is_null());
+    HeapObject* obj = *target;
+    GenerateObjectDesc(obj, buffer, sizeof(buffer));
+    desc_str = buffer;
+    size = obj->Size();
+  }
+  desc = Factory::NewStringFromAscii(CStrVector(desc_str));
+  if (desc->IsFailure()) {
+    error = desc;
+    return false;
+  }
+
+  { MaybeObject* maybe_result =
+          detail->SetProperty(*id_sym, Smi::FromInt(obj_id), NONE);
+    if (maybe_result->IsFailure()) return false;
+  }
+  { MaybeObject* maybe_result =
+          detail->SetProperty(*desc_sym, *desc, NONE);
+    if (maybe_result->IsFailure()) return false;
+  }
+  { MaybeObject* maybe_result =
+          detail->SetProperty(*size_sym, Smi::FromInt(size), NONE);
+    if (maybe_result->IsFailure()) return false;
+  }
+
+  arr->set(index, *detail);
+  return true;
+}
+
+
+class DumpWriter {
+ public:
+  virtual ~DumpWriter() {}
+
+  virtual void ComputeTotalCountAndSize(LolFilter* filter,
+                                        int* count,
+                                        int* size) = 0;
+  virtual bool Write(Handle<FixedArray> elements_arr,
+                     int start,
+                     int dump_limit,
+                     LolFilter* filter,
+                     Handle<Object> error) = 0;
+};
+
+
+class LolDumpWriter: public DumpWriter {
+ public:
+  LolDumpWriter(LiveObjectList* older, LiveObjectList* newer)
+      : older_(older), newer_(newer) {
+  }
+
+  void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
+    *count = 0;
+    *size = 0;
+
+    LolIterator it(older_, newer_);
+    for (it.Init(); !it.Done(); it.Next()) {
+      HeapObject* heap_obj = it.Obj();
+      if (!filter->Matches(heap_obj)) {
+        continue;
+      }
+
+      *size += heap_obj->Size();
+      (*count)++;
+    }
+  }
+
+  bool Write(Handle<FixedArray> elements_arr,
+             int start,
+             int dump_limit,
+             LolFilter* filter,
+             Handle<Object> error) {
+    // The lols are listed in latest to earliest.  We want to dump from
+    // earliest to latest.  So, compute the last element to start with.
+    int index = 0;
+    int count = 0;
+
+    // Prefetch some needed symbols.
+    Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
+    Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
+    Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
+
+    // Fill the array with the lol object details.
+    Handle<JSObject> detail;
+    Handle<String> desc;
+    Handle<HeapObject> target;
+
+    LiveObjectList* first_lol = (older_ != NULL) ?
+                                older_->next_ : LiveObjectList::first_;
+    LiveObjectList* last_lol = (newer_ != NULL) ? newer_->next_ : NULL;
+
+    LolForwardIterator it(first_lol, last_lol);
+    for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
+      HeapObject* heap_obj = it.Obj();
+
+      // Skip objects that have been filtered out.
+      if (!filter->Matches(heap_obj)) {
+        continue;
+      }
+
+      // Only report objects that are in the section of interest.
+      if (count >= start) {
+        target = Handle<HeapObject>(heap_obj);
+        bool success = AddObjDetail(elements_arr,
+                                    index++,
+                                    it.Id(),
+                                    target,
+                                    NULL,
+                                    id_sym,
+                                    desc_sym,
+                                    size_sym,
+                                    detail,
+                                    desc,
+                                    error);
+        if (!success) return false;
+      }
+      count++;
+    }
+    return true;
+  }
+
+ private:
+  LiveObjectList* older_;
+  LiveObjectList* newer_;
+};
+
+
+class RetainersDumpWriter: public DumpWriter {
+ public:
+  RetainersDumpWriter(Handle<HeapObject> target,
+                      Handle<JSObject> instance_filter,
+                      Handle<JSFunction> args_function)
+      : target_(target),
+        instance_filter_(instance_filter),
+        args_function_(args_function) {
+  }
+
+  void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
+    Handle<FixedArray> retainers_arr;
+    Handle<Object> error;
+
+    *size = -1;
+    LiveObjectList::GetRetainers(target_,
+                                 instance_filter_,
+                                 retainers_arr,
+                                 0,
+                                 Smi::kMaxValue,
+                                 count,
+                                 filter,
+                                 NULL,
+                                 *args_function_,
+                                 error);
+  }
+
+  bool Write(Handle<FixedArray> elements_arr,
+             int start,
+             int dump_limit,
+             LolFilter* filter,
+             Handle<Object> error) {
+    int dummy;
+    int count;
+
+    // Fill the retainer objects.
+    count = LiveObjectList::GetRetainers(target_,
+                                         instance_filter_,
+                                         elements_arr,
+                                         start,
+                                         dump_limit,
+                                         &dummy,
+                                         filter,
+                                         NULL,
+                                         *args_function_,
+                                         error);
+    if (count < 0) {
+        return false;
+    }
+    return true;
+  }
+
+ private:
+  Handle<HeapObject> target_;
+  Handle<JSObject> instance_filter_;
+  Handle<JSFunction> args_function_;
+};
+
+
+class LiveObjectSummary {
+ public:
+  explicit LiveObjectSummary(LolFilter* filter)
+      : total_count_(0),
+        total_size_(0),
+        found_root_(false),
+        found_weak_root_(false),
+        filter_(filter) {
+    memset(counts_, 0, sizeof(counts_[0]) * kNumberOfEntries);
+    memset(sizes_, 0, sizeof(sizes_[0]) * kNumberOfEntries);
+  }
+
+  void Add(HeapObject* heap_obj) {
+    int size = heap_obj->Size();
+    LiveObjectType type = GetObjectType(heap_obj);
+    ASSERT(type != kInvalidLiveObjType);
+    counts_[type]++;
+    sizes_[type] += size;
+    total_count_++;
+    total_size_ += size;
+  }
+
+  void set_found_root() { found_root_ = true; }
+  void set_found_weak_root() { found_weak_root_ = true; }
+
+  inline int Count(LiveObjectType type) {
+    return counts_[type];
+  }
+  inline int Size(LiveObjectType type) {
+    return sizes_[type];
+  }
+  inline int total_count() {
+    return total_count_;
+  }
+  inline int total_size() {
+    return total_size_;
+  }
+  inline bool found_root() {
+    return found_root_;
+  }
+  inline bool found_weak_root() {
+    return found_weak_root_;
+  }
+  int GetNumberOfEntries() {
+    int entries = 0;
+    for (int i = 0; i < kNumberOfEntries; i++) {
+      if (counts_[i]) entries++;
+    }
+    return entries;
+  }
+
+  inline LolFilter* filter() { return filter_; }
+
+  static const int kNumberOfEntries = kNumberOfTypes;
+
+ private:
+  int counts_[kNumberOfEntries];
+  int sizes_[kNumberOfEntries];
+  int total_count_;
+  int total_size_;
+  bool found_root_;
+  bool found_weak_root_;
+
+  LolFilter *filter_;
+};
+
+
+// Abstraction for a summary writer.
+class SummaryWriter {
+ public:
+  virtual ~SummaryWriter() {}
+  virtual void Write(LiveObjectSummary* summary) = 0;
+};
+
+
+// A summary writer for filling in a summary of lol lists and diffs.
+class LolSummaryWriter: public SummaryWriter {
+ public:
+  LolSummaryWriter(LiveObjectList *older_lol,
+                   LiveObjectList *newer_lol)
+      : older_(older_lol), newer_(newer_lol) {
+  }
+
+  void Write(LiveObjectSummary* summary) {
+    LolFilter* filter = summary->filter();
+
+    // Fill the summary with the lol object details.
+    LolIterator it(older_, newer_);
+    for (it.Init(); !it.Done(); it.Next()) {
+      HeapObject* heap_obj = it.Obj();
+      if (!filter->Matches(heap_obj)) {
+        continue;
+      }
+      summary->Add(heap_obj);
+    }
+  }
+
+ private:
+  LiveObjectList* older_;
+  LiveObjectList* newer_;
+};
+
+
+// A summary writer for filling in a retainers list.
+class RetainersSummaryWriter: public SummaryWriter {
+ public:
+  RetainersSummaryWriter(Handle<HeapObject> target,
+                         Handle<JSObject> instance_filter,
+                         Handle<JSFunction> args_function)
+      : target_(target),
+        instance_filter_(instance_filter),
+        args_function_(args_function) {
+  }
+
+  void Write(LiveObjectSummary* summary) {
+    Handle<FixedArray> retainers_arr;
+    Handle<Object> error;
+    int dummy_total_count;
+    LiveObjectList::GetRetainers(target_,
+                                 instance_filter_,
+                                 retainers_arr,
+                                 0,
+                                 Smi::kMaxValue,
+                                 &dummy_total_count,
+                                 summary->filter(),
+                                 summary,
+                                 *args_function_,
+                                 error);
+  }
+
+ private:
+  Handle<HeapObject> target_;
+  Handle<JSObject> instance_filter_;
+  Handle<JSFunction> args_function_;
+};
+
+
+uint32_t LiveObjectList::next_element_id_ = 1;
+int LiveObjectList::list_count_ = 0;
+int LiveObjectList::last_id_ = 0;
+LiveObjectList* LiveObjectList::first_ = NULL;
+LiveObjectList* LiveObjectList::last_ = NULL;
+
+
+LiveObjectList::LiveObjectList(LiveObjectList* prev, int capacity)
+    : prev_(prev),
+      next_(NULL),
+      capacity_(capacity),
+      obj_count_(0) {
+  elements_ = NewArray<Element>(capacity);
+  id_ = ++last_id_;
+
+  list_count_++;
+}
+
+
+LiveObjectList::~LiveObjectList() {
+  DeleteArray<Element>(elements_);
+  delete prev_;
+}
+
+
+int LiveObjectList::GetTotalObjCountAndSize(int* size_p) {
+  int size = 0;
+  int count = 0;
+  LiveObjectList *lol = this;
+  do {
+    // Only compute total size if requested i.e. when size_p is not null.
+    if (size_p != NULL) {
+      Element* elements = lol->elements_;
+      for (int i = 0; i < lol->obj_count_; i++) {
+        HeapObject* heap_obj = elements[i].obj_;
+        size += heap_obj->Size();
+      }
+    }
+    count += lol->obj_count_;
+    lol = lol->prev_;
+  } while (lol != NULL);
+
+  if (size_p != NULL) {
+    *size_p = size;
+  }
+  return count;
+}
+
+
+// Adds an object to the lol.
+// Returns true if successful, else returns false.
+bool LiveObjectList::Add(HeapObject* obj) {
+ // If the object is already accounted for in the prev list which we inherit
+  // from, then no need to add it to this list.
+  if ((prev() != NULL) && (prev()->Find(obj) != NULL)) {
+    return true;
+  }
+  ASSERT(obj_count_ <= capacity_);
+  if (obj_count_ == capacity_) {
+ // The heap must have grown and we have more objects than capacity to store
+    // them.
+    return false;  // Fail this addition.
+  }
+  Element& element = elements_[obj_count_++];
+  element.id_ = next_element_id_++;
+  element.obj_ = obj;
+  return true;
+}
+
+
+// Comparator used for sorting and searching the lol.
+int LiveObjectList::CompareElement(const Element* a, const Element* b) {
+  const HeapObject* obj1 = a->obj_;
+  const HeapObject* obj2 = b->obj_;
+ // For lol elements, it doesn't matter which comes first if 2 elements point + // to the same object (which gets culled later). Hence, we only care about
+  // the the greater than / less than relationships.
+  return (obj1 > obj2) ? 1 : (obj1 == obj2) ? 0 : -1;
+}
+
+
+// Looks for the specified object in the lol, and returns its element if found.
+LiveObjectList::Element* LiveObjectList::Find(HeapObject* obj) {
+  LiveObjectList* lol = this;
+  Element key;
+  Element* result = NULL;
+
+  key.obj_ = obj;
+  // Iterate through the chain of lol's to look for the object.
+  while ((result == NULL) && (lol != NULL)) {
+    result = reinterpret_cast<Element*>(
+        bsearch(&key, lol->elements_, lol->obj_count_,
+                sizeof(Element),
+                reinterpret_cast<RawComparer>(CompareElement)));
+    lol = lol->prev_;
+  }
+  return result;
+}
+
+
+// "Nullifies" (convert the HeapObject* into an SMI) so that it will get cleaned
+// up in the GCEpilogue, while preserving the sort order of the lol.
+// NOTE: the lols need to be already sorted before NullifyMostRecent() is
+// called.
+void LiveObjectList::NullifyMostRecent(HeapObject* obj) {
+  LiveObjectList* lol = last();
+  Element key;
+  Element* result = NULL;
+
+  key.obj_ = obj;
+  // Iterate through the chain of lol's to look for the object.
+  while (lol != NULL) {
+    result = reinterpret_cast<Element*>(
+        bsearch(&key, lol->elements_, lol->obj_count_,
+                sizeof(Element),
+                reinterpret_cast<RawComparer>(CompareElement)));
+    if (result != NULL) {
+ // Since there may be more than one (we are nullifying dup's after all), + // find the first in the current lol, and nullify that. The lol should
+      // be sorted already to make this easy (see the use of SortAll()).
+      int i = result - lol->elements_;
+
+ // NOTE: we sort the lol in increasing order. So, if an object has been + // "nullified" (its lowest bit will be cleared to make it look like an + // SMI), it would/should show up before the equivalent dups that have not + // yet been "nullified". Hence, we should be searching backwards for the + // first occurence of a matching object and nullify that instance. This
+      // will ensure that we preserve the expected sorting order.
+      for (i--; i > 0; i--) {
***The diff for this file has been truncated for email.***
=======================================
--- /branches/bleeding_edge/src/liveobjectlist.h        Thu Jan 20 00:11:53 2011
+++ /branches/bleeding_edge/src/liveobjectlist.h        Wed Mar  2 01:16:05 2011
@@ -40,67 +40,277 @@

 #ifdef LIVE_OBJECT_LIST

-
-// Temporary stubbed out LiveObjectList implementation.
+#ifdef DEBUG
+// The following symbol when defined enables thorough verification of lol data. +// FLAG_verify_lol will also need to set to true to enable the verification.
+#define VERIFY_LOL
+#endif
+
+
+typedef int LiveObjectType;
+class LolFilter;
+class LiveObjectSummary;
+class DumpWriter;
+class SummaryWriter;
+
+
+// The LiveObjectList is both a mechanism for tracking a live capture of
+// objects in the JS heap, as well as is the data structure which represents +// each of those captures. Unlike a snapshot, the lol is live. For example,
+// if an object in a captured lol dies and is collected by the GC, the lol
+// will reflect that the object is no longer available.  The term
+// LiveObjectList (and lol) is used to describe both the mechanism and the
+// data structure depending on context of use.
+//
+// In captured lols, objects are tracked using their address and an object id. +// The object id is unique. Once assigned to an object, the object id can never +// be assigned to another object. That is unless all captured lols are deleted +// which allows the user to start over with a fresh set of lols and object ids. +// The uniqueness of the object ids allows the user to track specific objects
+// and inspect its longevity while debugging JS code in execution.
+//
+// The lol comes with utility functions to capture, dump, summarize, and diff
+// captured lols amongst other functionality.  These functionality are
+// accessible via the v8 debugger interface.
 class LiveObjectList {
  public:
-  inline static void GCEpilogue() {}
-  inline static void GCPrologue() {}
-  inline static void IterateElements(ObjectVisitor* v) {}
-  inline static void ProcessNonLive(HeapObject *obj) {}
-  inline static void UpdateReferencesForScavengeGC() {}
-
-  static MaybeObject* Capture() { return Heap::undefined_value(); }
-  static bool Delete(int id) { return false; }
+  inline static void GCEpilogue();
+  inline static void GCPrologue();
+  inline static void IterateElements(ObjectVisitor* v);
+  inline static void ProcessNonLive(HeapObject *obj);
+  inline static void UpdateReferencesForScavengeGC();
+
+ // Note: LOLs can be listed by calling Dump(0, <lol id>), and 2 LOLs can be + // compared/diff'ed using Dump(<lol id1>, <lol id2>, ...). This will yield
+  // a verbose dump of all the objects in the resultant lists.
+  //   Similarly, a summarized result of a LOL listing or a diff can be
+  // attained using the Summarize(0, <lol id>) and Summarize(<lol id1,
+  // <lol id2>, ...) respectively.
+
+  static MaybeObject* Capture();
+  static bool Delete(int id);
   static MaybeObject* Dump(int id1,
                            int id2,
                            int start_idx,
                            int dump_limit,
-                           Handle<JSObject> filter_obj) {
-    return Heap::undefined_value();
-  }
-  static MaybeObject* Info(int start_idx, int dump_limit) {
-    return Heap::undefined_value();
-  }
-  static MaybeObject* Summarize(int id1,
-                                int id2,
-                                Handle<JSObject> filter_obj) {
-    return Heap::undefined_value();
-  }
-
-  static void Reset() {}
-  static Object* GetObj(int obj_id) { return Heap::undefined_value(); }
-  static Object* GetObjId(Handle<String> address) {
-    return Heap::undefined_value();
-  }
+                           Handle<JSObject> filter_obj);
+  static MaybeObject* Info(int start_idx, int dump_limit);
+ static MaybeObject* Summarize(int id1, int id2, Handle<JSObject> filter_obj);
+
+  static void Reset();
+  static Object* GetObj(int obj_id);
+  static int GetObjId(Object* obj);
+  static Object* GetObjId(Handle<String> address);
   static MaybeObject* GetObjRetainers(int obj_id,
                                       Handle<JSObject> instance_filter,
                                       bool verbose,
                                       int start,
                                       int count,
-                                      Handle<JSObject> filter_obj) {
-    return Heap::undefined_value();
-  }
+                                      Handle<JSObject> filter_obj);

   static Object* GetPath(int obj_id1,
                          int obj_id2,
-                         Handle<JSObject> instance_filter) {
-    return Heap::undefined_value();
-  }
-  static Object* PrintObj(int obj_id) { return Heap::undefined_value(); }
+                         Handle<JSObject> instance_filter);
+  static Object* PrintObj(int obj_id);
+
+ private:
+
+  struct Element {
+    int id_;
+    HeapObject* obj_;
+  };
+
+  explicit LiveObjectList(LiveObjectList* prev, int capacity);
+  ~LiveObjectList();
+
+  static void GCEpiloguePrivate();
+  static void IterateElementsPrivate(ObjectVisitor* v);
+
+  static void DoProcessNonLive(HeapObject *obj);
+
+  static int CompareElement(const Element* a, const Element* b);
+
+  static Object* GetPathPrivate(HeapObject* obj1, HeapObject* obj2);
+
+  static int GetRetainers(Handle<HeapObject> target,
+                          Handle<JSObject> instance_filter,
+                          Handle<FixedArray> retainers_arr,
+                          int start,
+                          int dump_limit,
+                          int* total_count,
+                          LolFilter* filter,
+                          LiveObjectSummary *summary,
+                          JSFunction* arguments_function,
+                          Handle<Object> error);
+
+  static MaybeObject* DumpPrivate(DumpWriter* writer,
+                                  int start,
+                                  int dump_limit,
+                                  LolFilter* filter);
+  static MaybeObject* SummarizePrivate(SummaryWriter* writer,
+                                       LolFilter* filter,
+                                       bool is_tracking_roots);
+
+  static bool NeedLOLProcessing() { return (last() != NULL); }
+  static void NullifyNonLivePointer(HeapObject **p) {
+ // Mask out the low bit that marks this as a heap object. We'll use this
+    // cleared bit as an indicator that this pointer needs to be collected.
+    //
+    // Meanwhile, we still preserve its approximate value so that we don't
+    // have to resort the elements list all the time.
+    //
+    // Note: Doing so also makes this HeapObject* look like an SMI.  Hence,
+    // GC pointer updater will ignore it when it gets scanned.
+    *p = reinterpret_cast<HeapObject*>((*p)->address());
+  }
+
+  LiveObjectList* prev() { return prev_; }
+  LiveObjectList* next() { return next_; }
+  int id() { return id_; }
+
+  static int list_count() { return list_count_; }
+  static LiveObjectList* last() { return last_; }
+
+ inline static LiveObjectList* FindLolForId(int id, LiveObjectList* start_lol);
+  int TotalObjCount() { return GetTotalObjCountAndSize(NULL); }
+  int GetTotalObjCountAndSize(int* size_p);
+
+  bool Add(HeapObject* obj);
+  Element* Find(HeapObject* obj);
+  static void NullifyMostRecent(HeapObject* obj);
+  void Sort();
+  static void SortAll();
+
+  static void PurgeDuplicates();  // Only to be called by GCEpilogue.
+
+#ifdef VERIFY_LOL
+  static void Verify(bool match_heap_exactly = false);
+  static void VerifyNotInFromSpace();
+#endif
+
+ // Iterates the elements in every lol and returns the one that matches the
+  // specified key.  If no matching element is found, then it returns NULL.
+  template <typename T>
+  inline static LiveObjectList::Element*
+      FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key);
+
+  inline static int GetElementId(Element* element);
+  inline static HeapObject* GetElementObj(Element* element);
+
+  // Instance fields.
+  LiveObjectList* prev_;
+  LiveObjectList* next_;
+  int id_;
+  int capacity_;
+  int obj_count_;
+  Element *elements_;
+
+  // Statics for managing all the lists.
+  static uint32_t next_element_id_;
+  static int list_count_;
+  static int last_id_;
+  static LiveObjectList* first_;
+  static LiveObjectList* last_;
+
+  friend class LolIterator;
+  friend class LolForwardIterator;
+  friend class LolDumpWriter;
+  friend class RetainersDumpWriter;
+  friend class RetainersSummaryWriter;
+  friend class UpdateLiveObjectListVisitor;
 };


+// Helper class for updating the LiveObjectList HeapObject pointers.
+class UpdateLiveObjectListVisitor: public ObjectVisitor {
+ public:
+
+  void VisitPointer(Object** p) { UpdatePointer(p); }
+
+  void VisitPointers(Object** start, Object** end) {
+    // Copy all HeapObject pointers in [start, end).
+    for (Object** p = start; p < end; p++) UpdatePointer(p);
+  }
+
+ private:
+  // Based on Heap::ScavengeObject() but only does forwarding of pointers
+  // to live new space objects, and not actually keep them alive.
+  void UpdatePointer(Object** p) {
+    Object* object = *p;
+    if (!Heap::InNewSpace(object)) return;
+
+    HeapObject* heap_obj = HeapObject::cast(object);
+    ASSERT(Heap::InFromSpace(heap_obj));
+
+    // We use the first word (where the map pointer usually is) of a heap
+    // object to record the forwarding pointer.  A forwarding pointer can
+    // point to an old space, the code space, or the to space of the new
+    // generation.
+    MapWord first_word = heap_obj->map_word();
+
+ // If the first word is a forwarding address, the object has already been
+    // copied.
+    if (first_word.IsForwardingAddress()) {
+      *p = first_word.ToForwardingAddress();
+      return;
+
+    // Else, it's a dead object.
+    } else {
+ LiveObjectList::NullifyNonLivePointer(reinterpret_cast<HeapObject**>(p));
+    }
+  }
+};
+
+
 #else  // !LIVE_OBJECT_LIST


 class LiveObjectList {
  public:
-  static void GCEpilogue() {}
-  static void GCPrologue() {}
-  static void IterateElements(ObjectVisitor* v) {}
-  static void ProcessNonLive(HeapObject *obj) {}
-  static void UpdateReferencesForScavengeGC() {}
+  inline static void GCEpilogue() {}
+  inline static void GCPrologue() {}
+  inline static void IterateElements(ObjectVisitor* v) {}
+  inline static void ProcessNonLive(HeapObject* obj) {}
+  inline static void UpdateReferencesForScavengeGC() {}
+
+  inline static MaybeObject* Capture() { return Heap::undefined_value(); }
+  inline static bool Delete(int id) { return false; }
+  inline static MaybeObject* Dump(int id1,
+                                  int id2,
+                                  int start_idx,
+                                  int dump_limit,
+                                  Handle<JSObject> filter_obj) {
+    return Heap::undefined_value();
+  }
+  inline static MaybeObject* Info(int start_idx, int dump_limit) {
+    return Heap::undefined_value();
+  }
+  inline static MaybeObject* Summarize(int id1,
+                                       int id2,
+                                       Handle<JSObject> filter_obj) {
+    return Heap::undefined_value();
+  }
+
+  inline static void Reset() {}
+ inline static Object* GetObj(int obj_id) { return Heap::undefined_value(); }
+  inline static Object* GetObjId(Handle<String> address) {
+    return Heap::undefined_value();
+  }
+  inline static MaybeObject* GetObjRetainers(int obj_id,
+ Handle<JSObject> instance_filter,
+                                             bool verbose,
+                                             int start,
+                                             int count,
+                                             Handle<JSObject> filter_obj) {
+    return Heap::undefined_value();
+  }
+
+  inline static Object* GetPath(int obj_id1,
+                                int obj_id2,
+                                Handle<JSObject> instance_filter) {
+    return Heap::undefined_value();
+  }
+ inline static Object* PrintObj(int obj_id) { return Heap::undefined_value(); }
 };


--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to