Made the stack traversal code in the profiler robust by avoiding to look into the heap.

Added name inferencing for anonymous functions to facilitate debugging and profiling.

Re-enabled stats timers in the developer shell (d8).

Fixed issue 303 by avoiding to shortcut cons-symbols.


git-svn-id: http://v8.googlecode.com/svn/trunk@1702 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 888c140..aeef611 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2009-04-14: Version 1.1.9
+
+        Made the stack traversal code in the profiler robust by avoiding
+        to look into the heap.
+
+        Added name inferencing for anonymous functions to facilitate
+        debugging and profiling.
+
+        Re-enabled stats timers in the developer shell (d8).
+
+        Fixed issue 303 by avoiding to shortcut cons-symbols.
+
+
 2009-04-11: Version 1.1.8
 
         Changed test-debug/ThreadedDebugging to be non-flaky (issue 96).
diff --git a/src/SConscript b/src/SConscript
index 9b7ff02..97fb7d6 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -40,7 +40,8 @@
     'codegen.cc', 'compilation-cache.cc', 'compiler.cc', 'contexts.cc',
     'conversions.cc', 'counters.cc', 'dateparser.cc', 'debug.cc',
     'debug-agent.cc', 'disassembler.cc', 'execution.cc', 'factory.cc',
-    'flags.cc', 'frames.cc', 'global-handles.cc', 'handles.cc', 'hashmap.cc',
+    'flags.cc', 'frames.cc', 'func-name-inferrer.cc',
+    'global-handles.cc', 'handles.cc', 'hashmap.cc',
     'heap.cc', 'ic.cc', 'interpreter-irregexp.cc', 'jsregexp.cc',
     'jump-target.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc',
     'oprofile-agent.cc', 'parser.cc', 'property.cc', 'regexp-macro-assembler.cc',
diff --git a/src/api.cc b/src/api.cc
index da674b2..eaee198 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -2373,7 +2373,7 @@
 
 
 const char* v8::V8::GetVersion() {
-  return "1.1.8";
+  return "1.1.9";
 }
 
 
diff --git a/src/ast.h b/src/ast.h
index 5dfd21d..b496816 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1223,7 +1223,8 @@
         end_position_(end_position),
         is_expression_(is_expression),
         loop_nesting_(0),
-        function_token_position_(RelocInfo::kNoPosition) {
+        function_token_position_(RelocInfo::kNoPosition),
+        inferred_name_(Heap::empty_string()) {
 #ifdef DEBUG
     already_compiled_ = false;
 #endif
@@ -1253,6 +1254,11 @@
   bool loop_nesting() const { return loop_nesting_; }
   void set_loop_nesting(int nesting) { loop_nesting_ = nesting; }
 
+  Handle<String> inferred_name() const  { return inferred_name_; }
+  void set_inferred_name(Handle<String> inferred_name) {
+    inferred_name_ = inferred_name;
+  }
+
 #ifdef DEBUG
   void mark_as_compiled() {
     ASSERT(!already_compiled_);
@@ -1273,6 +1279,7 @@
   bool is_expression_;
   int loop_nesting_;
   int function_token_position_;
+  Handle<String> inferred_name_;
 #ifdef DEBUG
   bool already_compiled_;
 #endif
diff --git a/src/codegen-arm.h b/src/codegen-arm.h
index 54c5e88..b072f75 100644
--- a/src/codegen-arm.h
+++ b/src/codegen-arm.h
@@ -160,7 +160,8 @@
                               int end_position,
                               bool is_expression,
                               bool is_toplevel,
-                              Handle<Script> script);
+                              Handle<Script> script,
+                              Handle<String> inferred_name);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
diff --git a/src/codegen-ia32.h b/src/codegen-ia32.h
index 165f877..24a57a0 100644
--- a/src/codegen-ia32.h
+++ b/src/codegen-ia32.h
@@ -299,7 +299,8 @@
                               int end_position,
                               bool is_expression,
                               bool is_toplevel,
-                              Handle<Script> script);
+                              Handle<Script> script,
+                              Handle<String> inferred_name);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
diff --git a/src/codegen.cc b/src/codegen.cc
index c8f69c7..a3c55d4 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -230,7 +230,8 @@
                                     int end_position,
                                     bool is_expression,
                                     bool is_toplevel,
-                                    Handle<Script> script) {
+                                    Handle<Script> script,
+                                    Handle<String> inferred_name) {
   fun->shared()->set_length(length);
   fun->shared()->set_formal_parameter_count(length);
   fun->shared()->set_script(*script);
@@ -239,6 +240,7 @@
   fun->shared()->set_end_position(end_position);
   fun->shared()->set_is_expression(is_expression);
   fun->shared()->set_is_toplevel(is_toplevel);
+  fun->shared()->set_inferred_name(*inferred_name);
 }
 
 
@@ -299,7 +301,8 @@
   CodeGenerator::SetFunctionInfo(function, node->num_parameters(),
                                  node->function_token_position(),
                                  node->start_position(), node->end_position(),
-                                 node->is_expression(), false, script_);
+                                 node->is_expression(), false, script_,
+                                 node->inferred_name());
 
   // Notify debugger that a new function has been added.
   Debugger::OnNewFunction(function);
diff --git a/src/compiler.cc b/src/compiler.cc
index ced094c..63fed4a 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -152,7 +152,8 @@
   CodeGenerator::SetFunctionInfo(fun, lit->scope()->num_parameters(),
                                  RelocInfo::kNoPosition,
                                  lit->start_position(), lit->end_position(),
-                                 lit->is_expression(), true, script);
+                                 lit->is_expression(), true, script,
+                                 lit->inferred_name());
 
   // Hint to the runtime system used when allocating space for initial
   // property space by setting the expected number of properties for
@@ -316,20 +317,22 @@
   // name and line number. Check explicit whether logging is enabled as finding
   // the line number is not for free.
   if (Logger::is_enabled() || OProfileAgent::is_enabled()) {
+    Handle<String> func_name(lit->name()->length() > 0 ?
+                             *lit->name() : shared->inferred_name());
     if (script->name()->IsString()) {
       int line_num = GetScriptLineNumber(script, start_position);
       if (line_num > 0) {
         line_num += script->line_offset()->value() + 1;
       }
-      LOG(CodeCreateEvent("LazyCompile", *code, *lit->name(),
+      LOG(CodeCreateEvent("LazyCompile", *code, *func_name,
                           String::cast(script->name()), line_num));
-      OProfileAgent::CreateNativeCodeRegion(*lit->name(),
+      OProfileAgent::CreateNativeCodeRegion(*func_name,
                                             String::cast(script->name()),
                                             line_num, code->address(),
                                             code->ExecutableSize());
     } else {
-      LOG(CodeCreateEvent("LazyCompile", *code, *lit->name()));
-      OProfileAgent::CreateNativeCodeRegion(*lit->name(), code->address(),
+      LOG(CodeCreateEvent("LazyCompile", *code, *func_name));
+      OProfileAgent::CreateNativeCodeRegion(*func_name, code->address(),
                                             code->ExecutableSize());
     }
   }
diff --git a/src/d8.cc b/src/d8.cc
index 934bcbd..9648168 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -268,12 +268,19 @@
 }
 
 
-int32_t* Counter::Bind(const char* name) {
+int32_t* Counter::Bind(const char* name, bool is_histogram) {
   int i;
   for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
     name_[i] = static_cast<char>(name[i]);
   name_[i] = '\0';
-  return &counter_;
+  is_histogram_ = is_histogram;
+  return ptr();
+}
+
+
+void Counter::AddSample(int32_t sample) {
+  count_++;
+  sample_total_ += sample;
 }
 
 
@@ -302,6 +309,8 @@
   }
   counters_ = static_cast<CounterCollection*>(memory);
   V8::SetCounterFunction(LookupCounter);
+  V8::SetCreateHistogramFunction(CreateHistogram);
+  V8::SetAddHistogramSampleFunction(AddHistogramSample);
 }
 
 
@@ -316,15 +325,44 @@
 }
 
 
-int* Shell::LookupCounter(const char* name) {
+Counter* Shell::GetCounter(const char* name, bool is_histogram) {
   Counter* counter = counter_map_->Lookup(name);
+
+  if (counter == NULL) {
+    counter = counters_->GetNextCounter();
+    if (counter != NULL) {
+      counter_map_->Set(name, counter);
+      counter->Bind(name, is_histogram);
+    }
+  } else {
+    ASSERT(counter->is_histogram() == is_histogram);
+  }
+  return counter;
+}
+
+
+int* Shell::LookupCounter(const char* name) {
+  Counter* counter = GetCounter(name, false);
+
   if (counter != NULL) {
     return counter->ptr();
+  } else {
+    return NULL;
   }
-  Counter* result = counters_->GetNextCounter();
-  if (result == NULL) return NULL;
-  counter_map_->Set(name, result);
-  return result->Bind(name);
+}
+
+
+void* Shell::CreateHistogram(const char* name,
+                             int min,
+                             int max,
+                             size_t buckets) {
+  return GetCounter(name, true);
+}
+
+
+void Shell::AddHistogramSample(void* histogram, int sample) {
+  Counter* counter = reinterpret_cast<Counter*>(histogram);
+  counter->AddSample(sample);
 }
 
 
@@ -333,8 +371,12 @@
   // Set up counters
   if (i::FLAG_map_counters != NULL)
     MapCounters(i::FLAG_map_counters);
-  if (i::FLAG_dump_counters)
+  if (i::FLAG_dump_counters) {
     V8::SetCounterFunction(LookupCounter);
+    V8::SetCreateHistogramFunction(CreateHistogram);
+    V8::SetAddHistogramSampleFunction(AddHistogramSample);
+  }
+
   // Initialize the global objects
   HandleScope scope;
   Handle<ObjectTemplate> global_template = ObjectTemplate::New();
@@ -406,7 +448,14 @@
     ::printf("+----------------------------------------+-------------+\n");
     for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
       Counter* counter = i.CurrentValue();
-      ::printf("| %-38s | %11i |\n", i.CurrentKey(), counter->value());
+      if (counter->is_histogram()) {
+        ::printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count());
+        ::printf("| t:%-36s | %11i |\n",
+                 i.CurrentKey(),
+                 counter->sample_total());
+      } else {
+        ::printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count());
+      }
     }
     ::printf("+----------------------------------------+-------------+\n");
   }
diff --git a/src/d8.h b/src/d8.h
index 54ae612..342a0d2 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -42,11 +42,16 @@
 class Counter {
  public:
   static const int kMaxNameSize = 64;
-  int32_t* Bind(const char* name);
-  int32_t* ptr() { return &counter_; }
-  int32_t value() { return counter_; }
+  int32_t* Bind(const char* name, bool histogram);
+  int32_t* ptr() { return &count_; }
+  int32_t count() { return count_; }
+  int32_t sample_total() { return sample_total_; }
+  bool is_histogram() { return is_histogram_; }
+  void AddSample(int32_t sample);
  private:
-  int32_t counter_;
+  int32_t count_;
+  int32_t sample_total_;
+  bool is_histogram_;
   uint8_t name_[kMaxNameSize];
 };
 
@@ -116,6 +121,11 @@
   static void Initialize();
   static void OnExit();
   static int* LookupCounter(const char* name);
+  static void* CreateHistogram(const char* name,
+                               int min,
+                               int max,
+                               size_t buckets);
+  static void AddHistogramSample(void* histogram, int sample);
   static void MapCounters(const char* name);
   static Handle<String> ReadFile(const char* name);
   static void RunShell();
@@ -179,6 +189,7 @@
   static CounterCollection local_counters_;
   static CounterCollection* counters_;
   static i::OS::MemoryMappedFile* counters_file_;
+  static Counter* GetCounter(const char* name, bool is_histogram);
 };
 
 
diff --git a/src/frames-arm.cc b/src/frames-arm.cc
index fe850a8..121fb75 100644
--- a/src/frames-arm.cc
+++ b/src/frames-arm.cc
@@ -80,7 +80,7 @@
 
 Address JavaScriptFrame::GetCallerStackPointer() const {
   int arguments;
-  if (Heap::gc_state() != Heap::NOT_IN_GC) {
+  if (Heap::gc_state() != Heap::NOT_IN_GC || disable_heap_access_) {
     // The arguments for cooked frames are traversed as if they were
     // expression stack elements of the calling frame. The reason for
     // this rather strange decision is that we cannot access the
diff --git a/src/frames-ia32.cc b/src/frames-ia32.cc
index 2b24777..1bc62ec 100644
--- a/src/frames-ia32.cc
+++ b/src/frames-ia32.cc
@@ -78,7 +78,7 @@
 
 Address JavaScriptFrame::GetCallerStackPointer() const {
   int arguments;
-  if (Heap::gc_state() != Heap::NOT_IN_GC) {
+  if (Heap::gc_state() != Heap::NOT_IN_GC || disable_heap_access_) {
     // The arguments for cooked frames are traversed as if they were
     // expression stack elements of the calling frame. The reason for
     // this rather strange decision is that we cannot access the
diff --git a/src/frames-inl.h b/src/frames-inl.h
index 07c8e4e..cb03e2f 100644
--- a/src/frames-inl.h
+++ b/src/frames-inl.h
@@ -169,19 +169,6 @@
 }
 
 
-inline bool JavaScriptFrame::is_at_function() const {
-  Object* result = function_slot_object();
-  // Verify that frame points at correct JS function object.
-  // We are verifying that function object address and
-  // the underlying map object address are valid, and that
-  // function is really a function.
-  return Heap::Contains(reinterpret_cast<Address>(result)) &&
-      result->IsHeapObject() &&
-      Heap::Contains(HeapObject::cast(result)->map()) &&
-      result->IsJSFunction();
-}
-
-
 inline Object* JavaScriptFrame::function() const {
   Object* result = function_slot_object();
   ASSERT(result->IsJSFunction());
diff --git a/src/frames.cc b/src/frames.cc
index a7da25a..88c723d 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -86,6 +86,7 @@
   if (use_top || fp != NULL) {
     Reset();
   }
+  JavaScriptFrame_.DisableHeapAccess();
 }
 
 #undef INITIALIZE_SINGLETON
@@ -208,7 +209,9 @@
   StackFrame* last_frame = iterator_.frame();
   Address last_sp = last_frame->sp(), last_fp = last_frame->fp();
   // Before advancing to the next stack frame, perform pointer validity tests
-  iteration_done_ = !IsValidFrame(last_frame) || !IsValidCaller(last_frame);
+  iteration_done_ = !IsValidFrame(last_frame) ||
+      !CanIterateHandles(last_frame, iterator_.handler()) ||
+      !IsValidCaller(last_frame);
   if (iteration_done_) return;
 
   iterator_.Advance();
@@ -219,12 +222,17 @@
 }
 
 
+bool SafeStackFrameIterator::CanIterateHandles(StackFrame* frame,
+                                               StackHandler* handler) {
+  // If StackIterator iterates over StackHandles, verify that
+  // StackHandlerIterator can be instantiated (see StackHandlerIterator
+  // constructor.)
+  return !is_valid_top_ || (frame->sp() <= handler->address());
+}
+
+
 bool SafeStackFrameIterator::IsValidFrame(StackFrame* frame) const {
-  return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp()) &&
-      // JavaScriptFrame uses function shared info to advance, hence it must
-      // point to a valid function object.
-      (!frame->is_java_script() ||
-       reinterpret_cast<JavaScriptFrame*>(frame)->is_at_function());
+  return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp());
 }
 
 
@@ -270,7 +278,7 @@
 SafeStackTraceFrameIterator::SafeStackTraceFrameIterator(
     Address fp, Address sp, Address low_bound, Address high_bound) :
     SafeJavaScriptFrameIterator(fp, sp, low_bound, high_bound) {
-  if (!done() && !frame()->is_at_function()) Advance();
+  if (!done() && !frame()->is_java_script()) Advance();
 }
 
 
@@ -278,7 +286,7 @@
   while (true) {
     SafeJavaScriptFrameIterator::Advance();
     if (done()) return;
-    if (frame()->is_at_function()) return;
+    if (frame()->is_java_script()) return;
   }
 }
 #endif
diff --git a/src/frames.h b/src/frames.h
index 78d8e72..8ab4be9 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -373,7 +373,6 @@
   virtual Type type() const { return JAVA_SCRIPT; }
 
   // Accessors.
-  inline bool is_at_function() const;
   inline Object* function() const;
   inline Object* receiver() const;
   inline void set_receiver(Object* value);
@@ -414,11 +413,19 @@
 
  protected:
   explicit JavaScriptFrame(StackFrameIterator* iterator)
-      : StandardFrame(iterator) { }
+      : StandardFrame(iterator), disable_heap_access_(false) { }
 
   virtual Address GetCallerStackPointer() const;
 
+  // When this mode is enabled it is not allowed to access heap objects.
+  // This is a special mode used when gathering stack samples in profiler.
+  // A shortcoming is that caller's SP value will be calculated incorrectly
+  // (see GetCallerStackPointer implementation), but it is not used for stack
+  // sampling.
+  void DisableHeapAccess() { disable_heap_access_ = true; }
+
  private:
+  bool disable_heap_access_;
   inline Object* function_slot_object() const;
 
   friend class StackFrameIterator;
@@ -638,6 +645,7 @@
   bool IsValidStackAddress(Address addr) const {
     return IsWithinBounds(low_bound_, high_bound_, addr);
   }
+  bool CanIterateHandles(StackFrame* frame, StackHandler* handler);
   bool IsValidFrame(StackFrame* frame) const;
   bool IsValidCaller(StackFrame* frame);
 
diff --git a/src/func-name-inferrer.cc b/src/func-name-inferrer.cc
new file mode 100644
index 0000000..ef0c7db
--- /dev/null
+++ b/src/func-name-inferrer.cc
@@ -0,0 +1,74 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "ast.h"
+#include "func-name-inferrer.h"
+
+namespace v8 { namespace internal {
+
+
+void FuncNameInferrer::PushEnclosingName(Handle<String> name) {
+  // Enclosing name is a name of a constructor function. To check
+  // that it is really a constructor, we check that it is not empty
+  // and starts with a capital letter.
+  if (name->length() > 0 && Runtime::IsUpperCaseChar(name->Get(0))) {
+    names_stack_.Add(name);
+  }
+}
+
+
+Handle<String> FuncNameInferrer::MakeNameFromStack() {
+  if (names_stack_.is_empty()) {
+    return Factory::empty_string();
+  } else {
+    return MakeNameFromStackHelper(1, names_stack_.at(0));
+  }
+}
+
+
+Handle<String> FuncNameInferrer::MakeNameFromStackHelper(int pos,
+                                                         Handle<String> prev) {
+  if (pos >= names_stack_.length()) {
+    return prev;
+  } else {
+    Handle<String> curr = Factory::NewConsString(dot_, names_stack_.at(pos));
+    return MakeNameFromStackHelper(pos + 1, Factory::NewConsString(prev, curr));
+  }
+}
+
+
+void FuncNameInferrer::MaybeInferFunctionName() {
+  if (func_to_infer_ != NULL) {
+    func_to_infer_->set_inferred_name(MakeNameFromStack());
+    func_to_infer_ = NULL;
+  }
+}
+
+
+} }  // namespace v8::internal
diff --git a/src/func-name-inferrer.h b/src/func-name-inferrer.h
new file mode 100644
index 0000000..4e49285
--- /dev/null
+++ b/src/func-name-inferrer.h
@@ -0,0 +1,126 @@
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_FUNC_NAME_INFERRER_H_
+#define V8_FUNC_NAME_INFERRER_H_
+
+namespace v8 { namespace internal {
+
+// FuncNameInferrer is a stateful class that is used to perform name
+// inference for anonymous functions during static analysis of source code.
+// Inference is performed in cases when an anonymous function is assigned
+// to a variable or a property (see test-func-name-inference.cc for examples.)
+
+// The basic idea is that during AST traversal LHSs of expressions are
+// always visited before RHSs. Thus, during visiting the LHS, a name can be
+// collected, and during visiting the RHS, a function literal can be collected.
+// Inference is performed while leaving the assignment node.
+
+class FuncNameInferrer BASE_EMBEDDED {
+ public:
+  FuncNameInferrer() :
+      entries_stack_(10),
+      names_stack_(5),
+      func_to_infer_(NULL),
+      dot_(Factory::NewStringFromAscii(CStrVector("."))) {
+  }
+
+  bool IsOpen() const { return !entries_stack_.is_empty(); }
+
+  void PushEnclosingName(Handle<String> name);
+
+  void Enter() {
+    entries_stack_.Add(names_stack_.length());
+  }
+
+  void Leave() {
+    ASSERT(IsOpen());
+    names_stack_.Rewind(entries_stack_.RemoveLast());
+  }
+
+  void PushName(Handle<String> name) {
+    if (IsOpen()) {
+      names_stack_.Add(name);
+    }
+  }
+
+  void SetFuncToInfer(FunctionLiteral* func_to_infer) {
+    if (IsOpen()) {
+      ASSERT(func_to_infer_ == NULL);
+      func_to_infer_ = func_to_infer;
+    }
+  }
+
+  void InferAndLeave() {
+    ASSERT(IsOpen());
+    MaybeInferFunctionName();
+    Leave();
+  }
+
+ private:
+  Handle<String> MakeNameFromStack();
+  Handle<String> MakeNameFromStackHelper(int pos, Handle<String> prev);
+  void MaybeInferFunctionName();
+
+  List<int> entries_stack_;
+  List<Handle<String> > names_stack_;
+  FunctionLiteral* func_to_infer_;
+  Handle<String> dot_;
+
+  DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer);
+};
+
+
+// A wrapper class that automatically calls InferAndLeave when
+// leaving scope.
+class ScopedFuncNameInferrer BASE_EMBEDDED {
+ public:
+  explicit ScopedFuncNameInferrer(FuncNameInferrer* inferrer) :
+      inferrer_(inferrer),
+      is_entered_(false) {}
+  ~ScopedFuncNameInferrer() {
+    if (is_entered_) {
+      inferrer_->InferAndLeave();
+    }
+  }
+
+  void Enter() {
+    inferrer_->Enter();
+    is_entered_ = true;
+  }
+
+ private:
+  FuncNameInferrer* inferrer_;
+  bool is_entered_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedFuncNameInferrer);
+};
+
+
+} }  // namespace v8::internal
+
+#endif  // V8_FUNC_NAME_INFERRER_H_
diff --git a/src/heap.cc b/src/heap.cc
index 26be5a4..de12d3f 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -374,9 +374,34 @@
 }
 
 
+static void VerifySymbolTable() {
+#ifdef DEBUG
+  // Helper class for verifying the symbol table.
+  class SymbolTableVerifier : public ObjectVisitor {
+   public:
+    SymbolTableVerifier() { }
+    void VisitPointers(Object** start, Object** end) {
+      // Visit all HeapObject pointers in [start, end).
+      for (Object** p = start; p < end; p++) {
+        if ((*p)->IsHeapObject()) {
+          // Check that the symbol is actually a symbol.
+          ASSERT((*p)->IsNull() || (*p)->IsUndefined() || (*p)->IsSymbol());
+        }
+      }
+    }
+  };
+
+  SymbolTableVerifier verifier;
+  SymbolTable* symbol_table = SymbolTable::cast(Heap::symbol_table());
+  symbol_table->IterateElements(&verifier);
+#endif  // DEBUG
+}
+
+
 void Heap::PerformGarbageCollection(AllocationSpace space,
                                     GarbageCollector collector,
                                     GCTracer* tracer) {
+  VerifySymbolTable();
   if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) {
     ASSERT(!allocation_allowed_);
     global_gc_prologue_callback_();
@@ -421,6 +446,7 @@
     ASSERT(!allocation_allowed_);
     global_gc_epilogue_callback_();
   }
+  VerifySymbolTable();
 }
 
 
@@ -813,12 +839,12 @@
 
 
 static inline bool IsShortcutCandidate(HeapObject* object, Map* map) {
-  // A ConsString object with Heap::empty_string() as the right side
-  // is a candidate for being shortcut by the scavenger.
+  STATIC_ASSERT(kNotStringTag != 0 && kSymbolTag != 0);
   ASSERT(object->map() == map);
-  if (map->instance_type() >= FIRST_NONSTRING_TYPE) return false;
-  return (StringShape(map).representation_tag() == kConsStringTag) &&
-         (ConsString::cast(object)->unchecked_second() == Heap::empty_string());
+  InstanceType type = map->instance_type();
+  if ((type & kShortcutTypeMask) != kShortcutTypeTag) return false;
+  ASSERT(object->IsString() && !object->IsSymbol());
+  return ConsString::cast(object)->unchecked_second() == Heap::empty_string();
 }
 
 
@@ -1384,6 +1410,7 @@
   share->set_script(undefined_value());
   share->set_start_position_and_type(0);
   share->set_debug_info(undefined_value());
+  share->set_inferred_name(empty_string());
   return result;
 }
 
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 740864c..6e7a8b0 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -96,24 +96,6 @@
 }
 
 
-#ifdef DEBUG
-// Helper class for verifying the symbol table.
-class SymbolTableVerifier : public ObjectVisitor {
- public:
-  SymbolTableVerifier() { }
-  void VisitPointers(Object** start, Object** end) {
-    // Visit all HeapObject pointers in [start, end).
-    for (Object** p = start; p < end; p++) {
-      if ((*p)->IsHeapObject()) {
-        // Check that the symbol is actually a symbol.
-        ASSERT((*p)->IsNull() || (*p)->IsUndefined() || (*p)->IsSymbol());
-      }
-    }
-  }
-};
-#endif  // DEBUG
-
-
 void MarkCompactCollector::Prepare(GCTracer* tracer) {
   // Rather than passing the tracer around we stash it in a static member
   // variable.
@@ -166,10 +148,6 @@
   }
 
 #ifdef DEBUG
-  SymbolTable* symbol_table = SymbolTable::cast(Heap::symbol_table());
-  SymbolTableVerifier v;
-  symbol_table->IterateElements(&v);
-
   live_bytes_ = 0;
   live_young_objects_ = 0;
   live_old_pointer_objects_ = 0;
@@ -242,13 +220,7 @@
   MapWord map_word = object->map_word();
   map_word.ClearMark();
   InstanceType type = map_word.ToMap()->instance_type();
-  if (type >= FIRST_NONSTRING_TYPE || (type & kIsSymbolMask) != 0) {
-    return object;
-  }
-
-  StringRepresentationTag rep =
-      static_cast<StringRepresentationTag>(type & kStringRepresentationMask);
-  if (rep != kConsStringTag) return object;
+  if ((type & kShortcutTypeMask) != kShortcutTypeTag) return object;
 
   Object* second = reinterpret_cast<ConsString*>(object)->unchecked_second();
   if (reinterpret_cast<String*>(second) != Heap::empty_string()) return object;
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 762bb63..653ee0d 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2077,6 +2077,7 @@
 ACCESSORS(SharedFunctionInfo, lazy_load_data, Object, kLazyLoadDataOffset)
 ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
 ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
+ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
 
 BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype,
                kHiddenPrototypeBit)
diff --git a/src/objects.cc b/src/objects.cc
index f9f869d..fc7d9e3 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -679,7 +679,7 @@
     SmartPointer<uc16> smart_chars = this->ToWideCString();
     ASSERT(memcmp(*smart_chars,
                   resource->data(),
-                  resource->length()*sizeof(**smart_chars)) == 0);
+                  resource->length() * sizeof(**smart_chars)) == 0);
   }
 #endif  // DEBUG
 
@@ -4641,7 +4641,7 @@
 void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
   IteratePointers(v, kNameOffset, kCodeOffset + kPointerSize);
   IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
-  IteratePointer(v, kDebugInfoOffset);
+  IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
 }
 
 
@@ -6304,7 +6304,7 @@
     if (StringShape(string_).IsCons()) {
       ConsString* cons_string = ConsString::cast(string_);
       cons_string->TryFlatten();
-      if (cons_string->second() == Heap::empty_string()) {
+      if (cons_string->second()->length() == 0) {
         string_ = cons_string->first();
       }
     }
@@ -6312,6 +6312,7 @@
     Map* map = Heap::SymbolMapForString(string_);
     if (map != NULL) {
       string_->set_map(map);
+      ASSERT(string_->IsSymbol());
       return string_;
     }
     // Otherwise allocate a new symbol.
diff --git a/src/objects.h b/src/objects.h
index 63480eb..ade282b 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -441,6 +441,19 @@
   kExternalStringTag = 0x3
 };
 
+
+// A ConsString with an empty string as the right side is a candidate
+// for being shortcut by the garbage collector unless it is a
+// symbol. It's not common to have non-flat symbols, so we do not
+// shortcut them thereby avoiding turning symbols into strings. See
+// heap.cc and mark-compact.cc.
+const uint32_t kShortcutTypeMask =
+    kIsNotStringMask |
+    kIsSymbolMask |
+    kStringRepresentationMask;
+const uint32_t kShortcutTypeTag = kConsStringTag;
+
+
 enum InstanceType {
   SHORT_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kSeqStringTag,
   MEDIUM_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kSeqStringTag,
@@ -2665,6 +2678,13 @@
   // [debug info]: Debug information.
   DECL_ACCESSORS(debug_info, Object)
 
+  // [inferred name]: Name inferred from variable or property
+  // assignment of this function. Used to facilitate debugging and
+  // profiling of JavaScript code written in OO style, where almost
+  // all functions are anonymous but are assigned to object
+  // properties.
+  DECL_ACCESSORS(inferred_name, String)
+
   // Position of the 'function' token in the script source.
   inline int function_token_position();
   inline void set_function_token_position(int function_token_position);
@@ -2724,7 +2744,8 @@
   static const int kEndPositionOffset = kStartPositionAndTypeOffset + kIntSize;
   static const int kFunctionTokenPositionOffset = kEndPositionOffset + kIntSize;
   static const int kDebugInfoOffset = kFunctionTokenPositionOffset + kIntSize;
-  static const int kSize = kDebugInfoOffset + kPointerSize;
+  static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
+  static const int kSize = kInferredNameOffset + kPointerSize;
 
  private:
   // Bit positions in length_and_flg.
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index 7f8c567..5cfc62c 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -709,6 +709,7 @@
   Init();
   { IndentedScope indent("FUNC");
     PrintLiteralIndented("NAME", program->name(), true);
+    PrintLiteralIndented("INFERRED NAME", program->inferred_name(), true);
     PrintParameters(program->scope());
     PrintDeclarations(program->scope()->declarations());
     PrintStatements(program->body());
@@ -885,6 +886,7 @@
 void AstPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
   IndentedScope indent("FUNC LITERAL");
   PrintLiteralIndented("NAME", node->name(), false);
+  PrintLiteralIndented("INFERRED NAME", node->inferred_name(), false);
   PrintParameters(node->scope());
   // We don't want to see the function literal in this case: it
   // will be printed via PrintProgram when the code for it is
diff --git a/src/rewriter.cc b/src/rewriter.cc
index 1aa24aa..6641f26 100644
--- a/src/rewriter.cc
+++ b/src/rewriter.cc
@@ -28,6 +28,7 @@
 #include "v8.h"
 
 #include "ast.h"
+#include "func-name-inferrer.h"
 #include "scopes.h"
 #include "rewriter.h"
 
@@ -36,7 +37,9 @@
 
 class AstOptimizer: public AstVisitor {
  public:
-  explicit AstOptimizer() {
+  explicit AstOptimizer() {}
+  explicit AstOptimizer(Handle<String> enclosing_name) {
+    func_name_inferrer_.PushEnclosingName(enclosing_name);
   }
 
   void Optimize(ZoneList<Statement*>* statements);
@@ -45,6 +48,8 @@
   // Used for loop condition analysis.  Cleared before visiting a loop
   // condition, set when a function literal is visited.
   bool has_function_literal_;
+  // Helper object for function name inferring.
+  FuncNameInferrer func_name_inferrer_;
 
   // Helpers
   void OptimizeArguments(ZoneList<Expression*>* arguments);
@@ -185,8 +190,12 @@
 
 
 void AstOptimizer::VisitFunctionLiteral(FunctionLiteral* node) {
-  USE(node);
   has_function_literal_ = true;
+
+  if (node->name()->length() == 0) {
+    // Anonymous function.
+    func_name_inferrer_.SetFuncToInfer(node);
+  }
 }
 
 
@@ -216,6 +225,11 @@
     } else if (node->type()->IsLikelySmi()) {
       var->type()->SetAsLikelySmi();
     }
+
+    if (!var->is_this() &&
+        !Heap::result_symbol()->Equals(*var->name())) {
+      func_name_inferrer_.PushName(var->name());
+    }
   }
 }
 
@@ -224,6 +238,11 @@
   Handle<Object> literal = node->handle();
   if (literal->IsSmi()) {
     node->type()->SetAsLikelySmi();
+  } else if (literal->IsString()) {
+    Handle<String> lit_str(Handle<String>::cast(literal));
+    if (!Heap::prototype_symbol()->Equals(*lit_str)) {
+      func_name_inferrer_.PushName(lit_str);
+    }
   }
 }
 
@@ -239,9 +258,10 @@
   }
 }
 
-
 void AstOptimizer::VisitObjectLiteral(ObjectLiteral* node) {
   for (int i = 0; i < node->properties()->length(); i++) {
+    ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_);
+    scoped_fni.Enter();
     Visit(node->properties()->at(i)->key());
     Visit(node->properties()->at(i)->value());
   }
@@ -255,11 +275,17 @@
 
 
 void AstOptimizer::VisitAssignment(Assignment* node) {
+  ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_);
   switch (node->op()) {
     case Token::INIT_VAR:
     case Token::INIT_CONST:
     case Token::ASSIGN:
       // No type can be infered from the general assignment.
+
+      if (node->value()->AsFunctionLiteral() != NULL ||
+          node->value()->AsObjectLiteral() != NULL) {
+        scoped_fni.Enter();
+      }
       break;
     case Token::ASSIGN_BIT_OR:
     case Token::ASSIGN_BIT_XOR:
@@ -368,6 +394,12 @@
 
 
 void AstOptimizer::VisitCallRuntime(CallRuntime* node) {
+  ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_);
+  if (Factory::InitializeVarGlobal_symbol()->Equals(*node->name()) &&
+      node->arguments()->length() >= 2 &&
+      node->arguments()->at(1)->AsFunctionLiteral() != NULL) {
+      scoped_fni.Enter();
+  }
   OptimizeArguments(node->arguments());
 }
 
@@ -794,13 +826,10 @@
   ZoneList<Statement*>* body = function->body();
 
   if (FLAG_optimize_ast && !body->is_empty()) {
-    Scope* scope = function->scope();
-    if (!scope->is_global_scope()) {
-      AstOptimizer optimizer;
-      optimizer.Optimize(body);
-      if (optimizer.HasStackOverflow()) {
-        return false;
-      }
+    AstOptimizer optimizer(function->name());
+    optimizer.Optimize(body);
+    if (optimizer.HasStackOverflow()) {
+      return false;
     }
   }
   return true;
diff --git a/src/runtime.cc b/src/runtime.cc
index 5962b2a..2896ff3 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -3391,6 +3391,13 @@
 }
 
 
+bool Runtime::IsUpperCaseChar(uint16_t ch) {
+  unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
+  int char_length = to_upper_mapping.get(ch, 0, chars);
+  return char_length == 0;
+}
+
+
 static Object* Runtime_NumberToString(Arguments args) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 1);
@@ -6061,8 +6068,8 @@
 }
 
 
-static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
-                                              int position) {
+Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
+                                                int position) {
   // Iterate the heap looking for SharedFunctionInfo generated from the
   // script. The inner most SharedFunctionInfo containing the source position
   // for the requested break point is found.
@@ -6159,7 +6166,8 @@
   RUNTIME_ASSERT(wrapper->value()->IsScript());
   Handle<Script> script(Script::cast(wrapper->value()));
 
-  Object* result = FindSharedFunctionInfoInScript(script, source_position);
+  Object* result = Runtime::FindSharedFunctionInfoInScript(
+      script, source_position);
   if (!result->IsUndefined()) {
     Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
     // Find position within function. The script position might be before the
diff --git a/src/runtime.h b/src/runtime.h
index 486ffb3..657e5c5 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -355,6 +355,8 @@
 
   static int StringMatch(Handle<String> sub, Handle<String> pat, int index);
 
+  static bool IsUpperCaseChar(uint16_t ch);
+
   // TODO(1240886): The following three methods are *not* handle safe,
   // but accept handle arguments. This seems fragile.
 
@@ -369,6 +371,10 @@
 
   static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key);
 
+  // This function is used in FunctionNameUsing* tests.
+  static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
+                                                int position);
+
   // Helper functions used stubs.
   static void PerformGC(Object* result);
 };
diff --git a/test/cctest/SConscript b/test/cctest/SConscript
index 091768f..f66f72b 100644
--- a/test/cctest/SConscript
+++ b/test/cctest/SConscript
@@ -42,6 +42,7 @@
     'test-debug.cc',
     'test-decls.cc',
     'test-flags.cc',
+    'test-func-name-inference.cc',
     'test-hashmap.cc',
     'test-heap.cc',
     'test-list.cc',
diff --git a/test/cctest/test-func-name-inference.cc b/test/cctest/test-func-name-inference.cc
new file mode 100644
index 0000000..5058629
--- /dev/null
+++ b/test/cctest/test-func-name-inference.cc
@@ -0,0 +1,223 @@
+// Copyright 2007-2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "api.h"
+#include "runtime.h"
+#include "cctest.h"
+
+
+using ::v8::internal::Handle;
+using ::v8::internal::JSFunction;
+using ::v8::internal::Object;
+using ::v8::internal::Script;
+using ::v8::internal::SharedFunctionInfo;
+using ::v8::internal::String;
+
+namespace i = ::v8::internal;
+
+
+static v8::Persistent<v8::Context> env;
+
+
+static void InitializeVM() {
+  if (env.IsEmpty()) {
+    v8::HandleScope scope;
+    env = v8::Context::New();
+  }
+  v8::HandleScope scope;
+  env->Enter();
+}
+
+
+static void CheckFunctionName(v8::Handle<v8::Script> script,
+                              const char* func_pos_src,
+                              const char* ref_inferred_name) {
+  // Get script source.
+  Handle<JSFunction> fun = v8::Utils::OpenHandle(*script);
+  Handle<Script> i_script(Script::cast(fun->shared()->script()));
+  CHECK(i_script->source()->IsString());
+  Handle<String> script_src(String::cast(i_script->source()));
+
+  // Find the position of a given func source substring in the source.
+  Handle<String> func_pos_str =
+      i::Factory::NewStringFromAscii(i::CStrVector(func_pos_src));
+  int func_pos = i::Runtime::StringMatch(script_src, func_pos_str, 0);
+  CHECK_NE(0, func_pos);
+
+  // Obtain SharedFunctionInfo for the function.
+  Object* shared_func_info_ptr =
+      i::Runtime::FindSharedFunctionInfoInScript(i_script, func_pos);
+  CHECK(shared_func_info_ptr != i::Heap::undefined_value());
+  Handle<SharedFunctionInfo> shared_func_info(
+      SharedFunctionInfo::cast(shared_func_info_ptr));
+
+  // Verify inferred function name.
+  i::SmartPointer<char> inferred_name =
+      shared_func_info->inferred_name()->ToCString();
+  CHECK_EQ(ref_inferred_name, *inferred_name);
+}
+
+
+static v8::Handle<v8::Script> Compile(const char* src) {
+  return v8::Script::Compile(v8::String::New(src));
+}
+
+
+TEST(GlobalProperty) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "fun1 = function() { return 1; }\n"
+      "fun2 = function() { return 2; }\n");
+  CheckFunctionName(script, "return 1", "fun1");
+  CheckFunctionName(script, "return 2", "fun2");
+}
+
+
+TEST(GlobalVar) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "var fun1 = function() { return 1; }\n"
+      "var fun2 = function() { return 2; }\n");
+  CheckFunctionName(script, "return 1", "fun1");
+  CheckFunctionName(script, "return 2", "fun2");
+}
+
+
+TEST(LocalVar) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function outer() {\n"
+      "  var fun1 = function() { return 1; }\n"
+      "  var fun2 = function() { return 2; }\n"
+      "}");
+  CheckFunctionName(script, "return 1", "fun1");
+  CheckFunctionName(script, "return 2", "fun2");
+}
+
+
+TEST(InConstructor) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function MyClass() {\n"
+      "  this.method1 = function() { return 1; }\n"
+      "  this.method2 = function() { return 2; }\n"
+      "}");
+  CheckFunctionName(script, "return 1", "MyClass.method1");
+  CheckFunctionName(script, "return 2", "MyClass.method2");
+}
+
+
+TEST(Factory) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function createMyObj() {\n"
+      "  var obj = {};\n"
+      "  obj.method1 = function() { return 1; }\n"
+      "  obj.method2 = function() { return 2; }\n"
+      "  return obj;\n"
+      "}");
+  CheckFunctionName(script, "return 1", "obj.method1");
+  CheckFunctionName(script, "return 2", "obj.method2");
+}
+
+
+TEST(Static) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function MyClass() {}\n"
+      "MyClass.static1 = function() { return 1; }\n"
+      "MyClass.static2 = function() { return 2; }\n"
+      "MyClass.MyInnerClass = {}\n"
+      "MyClass.MyInnerClass.static3 = function() { return 3; }\n"
+      "MyClass.MyInnerClass.static4 = function() { return 4; }");
+  CheckFunctionName(script, "return 1", "MyClass.static1");
+  CheckFunctionName(script, "return 2", "MyClass.static2");
+  CheckFunctionName(script, "return 3", "MyClass.MyInnerClass.static3");
+  CheckFunctionName(script, "return 4", "MyClass.MyInnerClass.static4");
+}
+
+
+TEST(Prototype) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function MyClass() {}\n"
+      "MyClass.prototype.method1 = function() { return 1; }\n"
+      "MyClass.prototype.method2 = function() { return 2; }\n"
+      "MyClass.MyInnerClass = function() {}\n"
+      "MyClass.MyInnerClass.prototype.method3 = function() { return 3; }\n"
+      "MyClass.MyInnerClass.prototype.method4 = function() { return 4; }");
+  CheckFunctionName(script, "return 1", "MyClass.method1");
+  CheckFunctionName(script, "return 2", "MyClass.method2");
+  CheckFunctionName(script, "return 3", "MyClass.MyInnerClass.method3");
+  CheckFunctionName(script, "return 4", "MyClass.MyInnerClass.method4");
+}
+
+
+TEST(ObjectLiteral) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function MyClass() {}\n"
+      "MyClass.prototype = {\n"
+      "  method1: function() { return 1; },\n"
+      "  method2: function() { return 2; } }");
+  CheckFunctionName(script, "return 1", "MyClass.method1");
+  CheckFunctionName(script, "return 2", "MyClass.method2");
+}
+
+
+TEST(AsParameter) {
+  InitializeVM();
+  v8::HandleScope scope;
+
+  v8::Handle<v8::Script> script = Compile(
+      "function f1(a) { return a(); }\n"
+      "function f2(a, b) { return a() + b(); }\n"
+      "var result1 = f1(function() { return 1; })\n"
+      "var result2 = f2(function() { return 2; }, function() { return 3; })");
+  // Can't infer names here.
+  CheckFunctionName(script, "return 1", "");
+  CheckFunctionName(script, "return 2", "");
+  CheckFunctionName(script, "return 3", "");
+}
diff --git a/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj
index df84554..eba4ec7 100644
--- a/tools/v8.xcodeproj/project.pbxproj
+++ b/tools/v8.xcodeproj/project.pbxproj
@@ -203,6 +203,8 @@
 		89F23C9F0E78D604006B2466 /* simulator-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF17D0E719B8F00D62E90 /* simulator-arm.cc */; };
 		89F23CA00E78D609006B2466 /* stub-cache-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF18A0E719B8F00D62E90 /* stub-cache-arm.cc */; };
 		89FB0E3A0F8E533F00B04B3C /* d8-posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89FB0E360F8E531900B04B3C /* d8-posix.cc */; };
+		9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; };
+		9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; };
 		9FC86ABD0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; };
 		9FC86ABE0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; };
 /* End PBXBuildFile section */
@@ -520,6 +522,8 @@
 		89F23C950E78D5B6006B2466 /* v8_shell-arm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "v8_shell-arm"; sourceTree = BUILT_PRODUCTS_DIR; };
 		89FB0E360F8E531900B04B3C /* d8-posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-posix.cc"; path = "../src/d8-posix.cc"; sourceTree = "<group>"; };
 		89FB0E370F8E531900B04B3C /* d8-windows.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-windows.cc"; path = "../src/d8-windows.cc"; sourceTree = "<group>"; };
+		9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "func-name-inferrer.cc"; sourceTree = "<group>"; };
+		9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "func-name-inferrer.h"; sourceTree = "<group>"; };
 		9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "oprofile-agent.cc"; sourceTree = "<group>"; };
 		9FC86ABC0F5FEDAC00F22668 /* oprofile-agent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "oprofile-agent.h"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -692,6 +696,8 @@
 				897FF13B0E719B8F00D62E90 /* frames-inl.h */,
 				897FF13C0E719B8F00D62E90 /* frames.cc */,
 				897FF13D0E719B8F00D62E90 /* frames.h */,
+				9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */,
+				9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */,
 				897FF13E0E719B8F00D62E90 /* global-handles.cc */,
 				897FF13F0E719B8F00D62E90 /* global-handles.h */,
 				897FF1400E719B8F00D62E90 /* globals.h */,
@@ -1117,6 +1123,7 @@
 				89A88E050E71A65D0043BA31 /* flags.cc in Sources */,
 				89A88E060E71A6600043BA31 /* frames-ia32.cc in Sources */,
 				89A88E070E71A6610043BA31 /* frames.cc in Sources */,
+				9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */,
 				89A88E080E71A6620043BA31 /* global-handles.cc in Sources */,
 				89A88E090E71A6640043BA31 /* handles.cc in Sources */,
 				89A88E0A0E71A6650043BA31 /* hashmap.cc in Sources */,
@@ -1217,6 +1224,7 @@
 				89F23C580E78D5B2006B2466 /* flags.cc in Sources */,
 				89F23C9C0E78D5F1006B2466 /* frames-arm.cc in Sources */,
 				89F23C5A0E78D5B2006B2466 /* frames.cc in Sources */,
+				9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */,
 				89F23C5B0E78D5B2006B2466 /* global-handles.cc in Sources */,
 				89F23C5C0E78D5B2006B2466 /* handles.cc in Sources */,
 				89F23C5D0E78D5B2006B2466 /* hashmap.cc in Sources */,
diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj
index 81e1f09..776e628 100644
--- a/tools/visual_studio/v8_base.vcproj
+++ b/tools/visual_studio/v8_base.vcproj
@@ -417,6 +417,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\src\func-name-inferrer.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\func-name-inferrer.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\src\global-handles.cc"
 				>
 			</File>
diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj
index a03306d..a91d63a 100644
--- a/tools/visual_studio/v8_base_arm.vcproj
+++ b/tools/visual_studio/v8_base_arm.vcproj
@@ -413,6 +413,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\src\func-name-inferrer.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\func-name-inferrer.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\src\global-handles.cc"
 				>
 			</File>
diff --git a/tools/visual_studio/v8_cctest.vcproj b/tools/visual_studio/v8_cctest.vcproj
index 859e445..5d49d24 100644
--- a/tools/visual_studio/v8_cctest.vcproj
+++ b/tools/visual_studio/v8_cctest.vcproj
@@ -186,6 +186,10 @@
 			>
 		</File>
 		<File
+			RelativePath="..\..\test\cctest\test-func-name-inference.cc"
+			>
+		</File>
+		<File
 			RelativePath="..\..\test\cctest\test-hashmap.cc"
 			>
 		</File>