Chromium Code Reviews| Index: src/debug.cc |
| =================================================================== |
| --- src/debug.cc (revision 1984) |
| +++ src/debug.cc (working copy) |
| @@ -425,6 +425,7 @@ |
| bool Debug::has_break_points_ = false; |
| +ScriptCache* Debug::script_cache_ = NULL; |
| DebugInfoListNode* Debug::debug_info_list_ = NULL; |
| @@ -486,6 +487,99 @@ |
| Code* Debug::debug_break_return_ = NULL; |
| +void ScriptCache::Add(Handle<Script> script) { |
| + // Create an entry in the hash map for the script. |
| + int id = Smi::cast(script->id())->value(); |
| + HashMap::Entry* entry = |
| + HashMap::Lookup(reinterpret_cast<void*>(id), Hash(id), true); |
| + if (entry->value != NULL) { |
| + ASSERT(*script == *reinterpret_cast<Script**>(entry->value)); |
| + return; |
| + } |
| + |
| + // Globalize the script object, make it weak and use the location of the |
| + // global handle as the value in the hash map. |
| + Handle<Script> script_ = |
| + Handle<Script>::cast((GlobalHandles::Create(*script))); |
| + GlobalHandles::MakeWeak(reinterpret_cast<Object**>(script_.location()), |
| + this, ScriptCache::HandleWeakScript); |
| + entry->value = script_.location(); |
| +} |
| + |
| + |
| +Handle<FixedArray> ScriptCache::GetScripts() { |
| + Handle<FixedArray> instances = Factory::NewFixedArray(occupancy()); |
| + int count = 0; |
| + for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) { |
| + ASSERT(entry->value != NULL); |
| + if (entry->value != NULL) { |
| + instances->set(count, *reinterpret_cast<Script**>(entry->value)); |
| + count++; |
| + } |
| + } |
| + return instances; |
| +} |
| + |
| + |
| +void ScriptCache::ProcessCollectedScripts() { |
| + for (int i = 0; i < collected_scripts_.length(); i++) { |
| + Debugger::OnScriptCollected(collected_scripts_[i]); |
| + } |
| + collected_scripts_.Clear(); |
| +} |
| + |
| + |
| +void ScriptCache::Clear() { |
| + // Iterate the script cache to get rid of all the weak handles. |
| + for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) { |
| + ASSERT(entry != NULL); |
| + Object** location = reinterpret_cast<Object**>(entry->value); |
| + ASSERT((*location)->IsScript()); |
| + GlobalHandles::ClearWeakness(location); |
| + GlobalHandles::Destroy(location); |
| + } |
| + // Clear the content of the hash map. |
| + HashMap::Clear(); |
| +} |
| + |
| + |
| +void ScriptCache::HandleWeakScript(v8::Persistent<v8::Value> obj, void* data) { |
| + ScriptCache* script_cache = reinterpret_cast<ScriptCache*>(data); |
| + // Find the location of the global handle. |
| + Script** location = |
| + reinterpret_cast<Script**>(Utils::OpenHandle(*obj).location()); |
| + ASSERT((*location)->IsScript()); |
| + |
| + HandleScope scope; |
|
Mads Ager (chromium)
2009/05/18 12:57:00
Why do you need to create this handle to the scrip
Søren Thygesen Gjesse
2009/05/18 13:13:17
I don't - unused code.
|
| + Handle<Script> script(location); |
| + |
| + // Remove the entry from the cache. |
| + int id = Smi::cast((*location)->id())->value(); |
| + script_cache->Remove(reinterpret_cast<void*>(id), Hash(id)); |
| + script_cache->collected_scripts_.Add(id); |
| + |
| + // Clear the weak handle. |
| + obj.Dispose(); |
| + obj.Clear(); |
| +} |
| + |
| + |
| +void Debug::Setup(bool create_heap_objects) { |
| + ThreadInit(); |
| + if (create_heap_objects) { |
| + // Get code to handle entry to debug break on return. |
| + debug_break_return_entry_ = |
| + Builtins::builtin(Builtins::Return_DebugBreakEntry); |
| + ASSERT(debug_break_return_entry_->IsCode()); |
| + |
| + // Get code to handle debug break on return. |
| + debug_break_return_ = |
| + Builtins::builtin(Builtins::Return_DebugBreak); |
| + ASSERT(debug_break_return_->IsCode()); |
| + } |
| +} |
| + |
| + |
| void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data) { |
| DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data); |
| RemoveDebugInfo(node->debug_info()); |
| @@ -512,22 +606,6 @@ |
| } |
| -void Debug::Setup(bool create_heap_objects) { |
| - ThreadInit(); |
| - if (create_heap_objects) { |
| - // Get code to handle entry to debug break on return. |
| - debug_break_return_entry_ = |
| - Builtins::builtin(Builtins::Return_DebugBreakEntry); |
| - ASSERT(debug_break_return_entry_->IsCode()); |
| - |
| - // Get code to handle debug break on return. |
| - debug_break_return_ = |
| - Builtins::builtin(Builtins::Return_DebugBreak); |
| - ASSERT(debug_break_return_->IsCode()); |
| - } |
| -} |
| - |
| - |
| bool Debug::CompileDebuggerScript(int index) { |
| HandleScope scope; |
| @@ -627,6 +705,7 @@ |
| // Debugger loaded. |
| debug_context_ = Handle<Context>::cast(GlobalHandles::Create(*context)); |
| + |
| return true; |
| } |
| @@ -637,6 +716,9 @@ |
| return; |
| } |
| + // Clear the script cache. |
| + DestroyScriptCache(); |
| + |
| // Clear debugger context global handle. |
| GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_context_.location())); |
| debug_context_ = Handle<Context>(); |
| @@ -1414,6 +1496,94 @@ |
| } |
| +// If an object given is an external string, check that the underlying |
| +// resource is accessible. For other kinds of objects, always return true. |
| +static bool IsExternalStringValid(Object* str) { |
| + if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) { |
| + return true; |
| + } |
| + if (String::cast(str)->IsAsciiRepresentation()) { |
| + return ExternalAsciiString::cast(str)->resource() != NULL; |
| + } else if (String::cast(str)->IsTwoByteRepresentation()) { |
| + return ExternalTwoByteString::cast(str)->resource() != NULL; |
| + } else { |
| + return true; |
| + } |
| +} |
| + |
| + |
| +void Debug::CreateScriptCache() { |
| + HandleScope scope; |
| + |
| + // Perform two GCs to get rid of all unreferenced scripts. The first GC gets |
| + // rid of all the cached script wrappers and the second gets rid of the |
| + // scripts which is no longer referenced. |
| + Heap::CollectAllGarbage(); |
| + Heap::CollectAllGarbage(); |
| + |
| + ASSERT(script_cache_ == NULL); |
| + script_cache_ = new ScriptCache(); |
| + |
| + // Scan heap for Script objects. |
| + int count = 0; |
| + HeapIterator iterator; |
| + while (iterator.has_next()) { |
| + HeapObject* obj = iterator.next(); |
| + ASSERT(obj != NULL); |
| + if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) { |
| + script_cache_->Add(Handle<Script>(Script::cast(obj))); |
| + count++; |
| + } |
| + } |
| +} |
| + |
| + |
| +void Debug::DestroyScriptCache() { |
| + // Get rid of the script cache if it was created. |
| + if (script_cache_ != NULL) { |
| + delete script_cache_; |
| + script_cache_ = NULL; |
| + } |
| +} |
| + |
| + |
| +void Debug::AddScriptToScriptCache(Handle<Script> script) { |
| + if (script_cache_ != NULL) { |
| + script_cache_->Add(script); |
| + } |
| +} |
| + |
| + |
| +Handle<FixedArray> Debug::GetLoadedScripts() { |
| + // Create and fill the script cache when the loaded scripts is requested for |
| + // the first time. |
| + if (script_cache_ == NULL) { |
| + CreateScriptCache(); |
| + } |
| + |
| + // If the script cache is not active just return an empty array. |
| + ASSERT(script_cache_ != NULL); |
| + if (script_cache_ == NULL) { |
| + Factory::NewFixedArray(0); |
| + } |
| + |
| + // Perform GC to get unreferenced scripts evicted from the cache before before |
|
Mads Ager (chromium)
2009/05/18 12:57:00
before before -> before
Søren Thygesen Gjesse
2009/05/18 13:13:17
Done.
|
| + // returning the content. |
| + Heap::CollectAllGarbage(); |
| + |
| + // Get the scripts from the cache. |
| + return script_cache_->GetScripts(); |
| +} |
| + |
| + |
| +void Debug::AfterGarbageCollection() { |
| + // Generate events for collected scripts. |
| + if (script_cache_ != NULL) { |
| + script_cache_->ProcessCollectedScripts(); |
| + } |
| +} |
| + |
| + |
| Mutex* Debugger::debugger_access_ = OS::CreateMutex(); |
| Handle<Object> Debugger::event_listener_ = Handle<Object>(); |
| Handle<Object> Debugger::event_listener_data_ = Handle<Object>(); |
| @@ -1518,6 +1688,21 @@ |
| } |
| +Handle<Object> Debugger::MakeScriptCollectedEvent(int id, |
| + bool* caught_exception) { |
| + // Create the script collected event object. |
| + Handle<Object> exec_state = MakeExecutionState(caught_exception); |
| + Handle<Object> id_object = Handle<Smi>(Smi::FromInt(id)); |
| + const int argc = 2; |
| + Object** argv[argc] = { exec_state.location(), id_object.location() }; |
| + |
| + return MakeJSObject(CStrVector("MakeScriptCollectedEvent"), |
| + argc, |
| + argv, |
| + caught_exception); |
| +} |
| + |
| + |
| void Debugger::OnException(Handle<Object> exception, bool uncaught) { |
| HandleScope scope; |
| @@ -1624,12 +1809,15 @@ |
| void Debugger::OnAfterCompile(Handle<Script> script, Handle<JSFunction> fun) { |
| HandleScope scope; |
| - // No compile events while compiling natives. |
| - if (compiling_natives()) return; |
| + // Add the newly compiled script to the script cache. |
| + Debug::AddScriptToScriptCache(script); |
| // No more to do if not debugging. |
| if (!IsDebuggerActive()) return; |
| + // No compile events while compiling natives. |
| + if (compiling_natives()) return; |
| + |
| // Store whether in debugger before entering debugger. |
| bool in_debugger = Debug::InDebugger(); |
| @@ -1708,6 +1896,33 @@ |
| } |
| +void Debugger::OnScriptCollected(int id) { |
| + HandleScope scope; |
| + |
| + // No more to do if not debugging. |
| + if (!IsDebuggerActive()) return; |
| + if (!Debugger::EventActive(v8::ScriptCollected)) return; |
| + |
| + // Enter the debugger. |
| + EnterDebugger debugger; |
| + if (debugger.FailedToEnter()) return; |
| + |
| + // Create the script collected state object. |
| + bool caught_exception = false; |
| + Handle<Object> event_data = MakeScriptCollectedEvent(id, |
| + &caught_exception); |
| + // Bail out and don't call debugger if exception. |
| + if (caught_exception) { |
| + return; |
| + } |
| + |
| + // Process debug event. |
| + ProcessDebugEvent(v8::ScriptCollected, |
| + Handle<JSObject>::cast(event_data), |
| + true); |
| +} |
| + |
| + |
| void Debugger::ProcessDebugEvent(v8::DebugEvent event, |
| Handle<JSObject> event_data, |
| bool auto_continue) { |
| @@ -1756,9 +1971,6 @@ |
| } |
| } |
| } |
| - |
| - // Clear the mirror cache. |
| - Debug::ClearMirrorCache(); |
| } |
| @@ -1798,6 +2010,9 @@ |
| case v8::AfterCompile: |
| sendEventMessage = true; |
| break; |
| + case v8::ScriptCollected: |
| + sendEventMessage = true; |
| + break; |
| case v8::NewFunction: |
| break; |
| default: |