Add name inference for anonymous functions to facilitate debugging and profiling...
authormikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 14 Apr 2009 00:51:59 +0000 (00:51 +0000)
committermikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 14 Apr 2009 00:51:59 +0000 (00:51 +0000)
Currently function name inference is wired with AST optimization pass to avoid introducing another pass over AST. A better solution would be to rewrite AST visitors so they can be naturally combined together in a single pass, as their current implementation doesn't allow it.

For examples of cases where function names can be inferred, see the tests file.

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

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

22 files changed:
src/SConscript
src/ast.h
src/codegen-arm.h
src/codegen-ia32.h
src/codegen.cc
src/compiler.cc
src/func-name-inferrer.cc [new file with mode: 0644]
src/func-name-inferrer.h [new file with mode: 0644]
src/heap.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/prettyprinter.cc
src/rewriter.cc
src/runtime.cc
src/runtime.h
test/cctest/SConscript
test/cctest/test-func-name-inference.cc [new file with mode: 0644]
tools/v8.xcodeproj/project.pbxproj
tools/visual_studio/v8_base.vcproj
tools/visual_studio/v8_base_arm.vcproj
tools/visual_studio/v8_cctest.vcproj

index 9b7ff02e5d0395b4bf175fdc644fea1d60df7527..97fb7d6caafb74dd5605bf9b275444fcaa4fc7ae 100644 (file)
@@ -40,7 +40,8 @@ SOURCES = {
     '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',
index 5dfd21d9e4b98d3b53750f461c3df9afbb298ab4..b496816f7e42b7bb9dfb0bc4d47442bf24be8713 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1223,7 +1223,8 @@ class FunctionLiteral: public Expression {
         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 @@ class FunctionLiteral: public Expression {
   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 @@ class FunctionLiteral: public Expression {
   bool is_expression_;
   int loop_nesting_;
   int function_token_position_;
+  Handle<String> inferred_name_;
 #ifdef DEBUG
   bool already_compiled_;
 #endif
index 54c5e882edb160379bf81a96913971faa5e41dbb..b072f75ed98a3f20a0dc0d8d054d84fa3ccbdef1 100644 (file)
@@ -160,7 +160,8 @@ class CodeGenerator: public AstVisitor {
                               int end_position,
                               bool is_expression,
                               bool is_toplevel,
-                              Handle<Script> script);
+                              Handle<Script> script,
+                              Handle<String> inferred_name);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
index 165f87717fc8a9c362330460d0e4ae882fdf397e..24a57a074d4efdeb31fe4ebb7b088674f654cb15 100644 (file)
@@ -299,7 +299,8 @@ class CodeGenerator: public AstVisitor {
                               int end_position,
                               bool is_expression,
                               bool is_toplevel,
-                              Handle<Script> script);
+                              Handle<Script> script,
+                              Handle<String> inferred_name);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
index c8f69c734c5447576abf9a1db2ed137c44bfb7c6..a3c55d4a65ba5eb3d7887c2599308a9c0c2f75f6 100644 (file)
@@ -230,7 +230,8 @@ void CodeGenerator::SetFunctionInfo(Handle<JSFunction> fun,
                                     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 @@ void CodeGenerator::SetFunctionInfo(Handle<JSFunction> fun,
   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 @@ Handle<JSFunction> CodeGenerator::BuildBoilerplate(FunctionLiteral* node) {
   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);
index ced094c0f2290db3df029f79f9342aa2d666595c..63fed4a1b6a11a43e3eccccc1735cde286de8930 100644 (file)
@@ -152,7 +152,8 @@ static Handle<JSFunction> MakeFunction(bool is_global,
   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 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared,
   // 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/func-name-inferrer.cc b/src/func-name-inferrer.cc
new file mode 100644 (file)
index 0000000..ef0c7db
--- /dev/null
@@ -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 (file)
index 0000000..6e2bf91
--- /dev/null
@@ -0,0 +1,125 @@
+// 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:
+  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_
index 26be5a4684fbdba00d6f012af75bacd07baeb029..8427f963df3baeba3ed3c8982e93d9486d4fd233 100644 (file)
@@ -1384,6 +1384,7 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) {
   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;
 }
 
index 762bb63638a9213e1f9f26f0def24cf505887725..653ee0dcc7913e9f2c5a085ecea74006e75141b3 100644 (file)
@@ -2077,6 +2077,7 @@ ACCESSORS(SharedFunctionInfo, function_data, Object,
 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)
index f9f869d406ae121c0100b1c5dce54d414e189d35..23bad8a683abce72f84e44338e7bbaad5943a539 100644 (file)
@@ -4641,7 +4641,7 @@ void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
 void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
   IteratePointers(v, kNameOffset, kCodeOffset + kPointerSize);
   IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
-  IteratePointer(v, kDebugInfoOffset);
+  IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
 }
 
 
index 63480eb08e1e3bec4d44cffdf2963b31f8c7a9f3..0e178081e506b5e14ccbf07dbcdf8e9270ea7617 100644 (file)
@@ -2665,6 +2665,13 @@ class SharedFunctionInfo: public HeapObject {
   // [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 +2731,8 @@ class SharedFunctionInfo: public HeapObject {
   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.
index 7f8c5678068e7a702da90302a3fe91f69b072591..5cfc62c1090dd64729c162678ad1340a36f67fe4 100644 (file)
@@ -709,6 +709,7 @@ const char* AstPrinter::PrintProgram(FunctionLiteral* program) {
   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::VisitDebuggerStatement(DebuggerStatement* node) {
 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
index 1aa24aa4c4172461170a71e392aaf0f0e1962167..6641f2615a9afc3ef3f490d02c98329dc16e2f26 100644 (file)
@@ -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 @@ namespace v8 { namespace internal {
 
 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 @@ class AstOptimizer: public AstVisitor {
   // 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::VisitDebuggerStatement(DebuggerStatement* node) {
 
 
 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 @@ void AstOptimizer::VisitVariableProxy(VariableProxy* node) {
     } 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 @@ void AstOptimizer::VisitLiteral(Literal* node) {
   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::VisitArrayLiteral(ArrayLiteral* node) {
   }
 }
 
-
 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::VisitCatchExtensionObject(CatchExtensionObject* node) {
 
 
 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::VisitCallNew(CallNew* node) {
 
 
 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 @@ bool Rewriter::Optimize(FunctionLiteral* function) {
   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;
index 5962b2ab1e3bb1f8dfa699ed94d4ba7435d4ccc1..2896ff3841cfa4634e561081df102e3aff5ae142 100644 (file)
@@ -3391,6 +3391,13 @@ static Object* Runtime_StringToUpperCase(Arguments args) {
 }
 
 
+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* Runtime_SetFunctionBreakPoint(Arguments args) {
 }
 
 
-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 @@ static Object* Runtime_SetScriptBreakPoint(Arguments args) {
   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
index 486ffb364aff03f8f91ad776f787220d1bf8f3e5..657e5c5d978eb2baf73780e9b67916f89c0a0c69 100644 (file)
@@ -355,6 +355,8 @@ class Runtime : public AllStatic {
 
   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 @@ class Runtime : public AllStatic {
 
   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);
 };
index 091768f0cffa77ac34ed89eca9606116ef0053ae..f66f72b0eb2510f2f2cd7ccf0c92687db9af84c3 100644 (file)
@@ -42,6 +42,7 @@ SOURCES = {
     '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 (file)
index 0000000..5058629
--- /dev/null
@@ -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", "");
+}
index df84554dc350e915542da480401ad4eaffc3666f..eba4ec771a02f96f8ab8067f7c2f8c0481c7623b 100644 (file)
                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 */
                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 */
                                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 */,
                                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 */,
                                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 */,
index 81e1f09fd224c562790f2343efa624b924ad6163..776e628d0f2c0ae0dc4de4053434b826e31a0d38 100644 (file)
                                RelativePath="..\..\src\frames.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\src\func-name-inferrer.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\src\func-name-inferrer.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\src\global-handles.cc"
                                >
index a03306deee6140b6196776625a4876c20a4d73b2..a91d63acee67fc892cc5e413ef47b9507b42657d 100644 (file)
                                RelativePath="..\..\src\frames.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\src\func-name-inferrer.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\src\func-name-inferrer.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\src\global-handles.cc"
                                >
index 859e4458588a9e20034cabd65118ffc87d6b5f3e..5d49d2487dbd17379496bcce57ec01e5bdf9f27a 100644 (file)
                        RelativePath="..\..\test\cctest\test-flags.cc"
                        >
                </File>
+               <File
+                       RelativePath="..\..\test\cctest\test-func-name-inference.cc"
+                       >
+               </File>
                <File
                        RelativePath="..\..\test\cctest\test-hashmap.cc"
                        >