Provide user-friendly access to natively bound function parameters
authoryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 10 Feb 2012 13:54:20 +0000 (13:54 +0000)
committeryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 10 Feb 2012 13:54:20 +0000 (13:54 +0000)
For a closure created using native Function.prototype.bind() the most important information for the heap profiler user is bound function, receiver and arguments. This change adds shortcuts for those fields.
Review URL: https://chromiumcodereview.appspot.com/9382002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10675 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/profile-generator.cc
src/profile-generator.h
test/cctest/test-heap-profiler.cc

index a204a90..27ca11f 100644 (file)
@@ -1775,9 +1775,11 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
   } else if (object->IsJSFunction()) {
     JSFunction* func = JSFunction::cast(object);
     SharedFunctionInfo* shared = func->shared();
+    const char* name = shared->bound() ? "native_bind" :
+        collection_->names()->GetName(String::cast(shared->name()));
     return AddEntry(object,
                     HeapEntry::kClosure,
-                    collection_->names()->GetName(String::cast(shared->name())),
+                    name,
                     children_count,
                     retainers_count);
   } else if (object->IsJSRegExp()) {
@@ -2011,19 +2013,22 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
               heap_->prototype_symbol(), js_fun->prototype());
         }
       }
+      SharedFunctionInfo* shared_info = js_fun->shared();
+      // JSFunction has either bindings or literals and never both.
+      bool bound = shared_info->bound();
+      TagObject(js_fun->literals_or_bindings(),
+                bound ? "(function bindings)" : "(function literals)");
       SetInternalReference(js_fun, entry,
-                           "shared", js_fun->shared(),
+                           bound ? "bindings" : "literals",
+                           js_fun->literals_or_bindings(),
+                           JSFunction::kLiteralsOffset);
+      SetInternalReference(js_fun, entry,
+                           "shared", shared_info,
                            JSFunction::kSharedFunctionInfoOffset);
       TagObject(js_fun->unchecked_context(), "(context)");
       SetInternalReference(js_fun, entry,
                            "context", js_fun->unchecked_context(),
                            JSFunction::kContextOffset);
-      TagObject(js_fun->literals_or_bindings(),
-                "(function literals_or_bindings)");
-      SetInternalReference(js_fun, entry,
-                           "literals_or_bindings",
-                           js_fun->literals_or_bindings(),
-                           JSFunction::kLiteralsOffset);
       for (int i = JSFunction::kNonWeakFieldsEndOffset;
            i < JSFunction::kSize;
            i += kPointerSize) {
@@ -2126,17 +2131,6 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
     SetInternalReference(obj, entry,
                          "line_ends", script->line_ends(),
                          Script::kLineEndsOffset);
-  } else if (obj->IsDescriptorArray()) {
-    DescriptorArray* desc_array = DescriptorArray::cast(obj);
-    if (desc_array->length() > DescriptorArray::kContentArrayIndex) {
-      Object* content_array =
-          desc_array->get(DescriptorArray::kContentArrayIndex);
-      TagObject(content_array, "(map descriptor content)");
-      SetInternalReference(obj, entry,
-                           "content", content_array,
-                           FixedArray::OffsetOfElementAt(
-                               DescriptorArray::kContentArrayIndex));
-    }
   } else if (obj->IsCodeCache()) {
     CodeCache* code_cache = CodeCache::cast(obj);
     TagObject(code_cache->default_cache(), "(default code cache)");
@@ -2162,11 +2156,27 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
 
 void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
                                               HeapEntry* entry) {
-  if (js_obj->IsJSFunction()) {
-    JSFunction* func = JSFunction::cast(js_obj);
-    Context* context = func->context();
-    ScopeInfo* scope_info = context->closure()->shared()->scope_info();
-
+  if (!js_obj->IsJSFunction()) return;
+
+  JSFunction* func = JSFunction::cast(js_obj);
+  Context* context = func->context();
+  ScopeInfo* scope_info = context->closure()->shared()->scope_info();
+
+  if (func->shared()->bound()) {
+    FixedArray* bindings = func->function_bindings();
+    SetNativeBindReference(js_obj, entry, "bound_this",
+                           bindings->get(JSFunction::kBoundThisIndex));
+    SetNativeBindReference(js_obj, entry, "bound_function",
+                           bindings->get(JSFunction::kBoundFunctionIndex));
+    for (int i = JSFunction::kBoundArgumentsStartIndex;
+         i < bindings->length(); i++) {
+      const char* reference_name = collection_->names()->GetFormatted(
+          "bound_argument_%d",
+          i - JSFunction::kBoundArgumentsStartIndex);
+      SetNativeBindReference(js_obj, entry, reference_name,
+                             bindings->get(i));
+    }
+  } else {
     // Add context allocated locals.
     int context_locals = scope_info->ContextLocalCount();
     for (int i = 0; i < context_locals; ++i) {
@@ -2444,6 +2454,22 @@ void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
 }
 
 
+void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
+                                            HeapEntry* parent_entry,
+                                            const char* reference_name,
+                                            Object* child_obj) {
+  HeapEntry* child_entry = GetEntry(child_obj);
+  if (child_entry != NULL) {
+    filler_->SetNamedReference(HeapGraphEdge::kShortcut,
+                               parent_obj,
+                               parent_entry,
+                               reference_name,
+                               child_obj,
+                               child_entry);
+  }
+}
+
+
 void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
                                          HeapEntry* parent_entry,
                                          int index,
index a0dea58..a24f9a9 100644 (file)
@@ -964,6 +964,10 @@ class V8HeapExplorer : public HeapEntriesAllocator {
                            HeapEntry* parent,
                            String* reference_name,
                            Object* child);
+  void SetNativeBindReference(HeapObject* parent_obj,
+                              HeapEntry* parent,
+                              const char* reference_name,
+                              Object* child);
   void SetElementReference(HeapObject* parent_obj,
                            HeapEntry* parent,
                            int index,
index f57477e..bf7e91b 100644 (file)
@@ -147,6 +147,43 @@ TEST(HeapSnapshotObjectSizes) {
 }
 
 
+TEST(BoundFunctionInSnapshot) {
+  v8::HandleScope scope;
+  LocalContext env;
+  CompileRun(
+      "function myFunction(a, b) { this.a = a; this.b = b; }\n"
+      "function AAAAA() {}\n"
+      "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
+  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+  const v8::HeapGraphNode* f =
+      GetProperty(global, v8::HeapGraphEdge::kShortcut, "boundFunction");
+  CHECK(f);
+  CHECK_EQ(v8::String::New("native_bind"), f->GetName());
+  const v8::HeapGraphNode* bindings =
+      GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
+  CHECK_NE(NULL, bindings);
+  CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
+  CHECK_EQ(4, bindings->GetChildrenCount());
+
+  const v8::HeapGraphNode* bound_this = GetProperty(
+      f, v8::HeapGraphEdge::kShortcut, "bound_this");
+  CHECK(bound_this);
+  CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
+
+  const v8::HeapGraphNode* bound_function = GetProperty(
+      f, v8::HeapGraphEdge::kShortcut, "bound_function");
+  CHECK(bound_function);
+  CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
+
+  const v8::HeapGraphNode* bound_argument = GetProperty(
+      f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
+  CHECK(bound_argument);
+  CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
+}
+
+
 TEST(HeapSnapshotEntryChildren) {
   v8::HandleScope scope;
   LocalContext env;