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/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);
};