Index: src/liveedit.cc |
diff --git a/src/liveedit.cc b/src/liveedit.cc |
index 01badc0384196d16588707e3bc9fff47c544b6d7..fc6bb052252f1a74b432d8aad6b8a0c71c84e995 100644 |
--- a/src/liveedit.cc |
+++ b/src/liveedit.cc |
@@ -35,27 +35,491 @@ |
#include "global-handles.h" |
#include "debug.h" |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ |
namespace v8 { |
namespace internal { |
+static Handle<JSFunction> LiveEditMakeFunction(bool is_global, |
+ Compiler::ValidationState validate, |
+ Handle<Script> script, |
+ Handle<Context> context, |
+ v8::Extension* extension, |
+ ScriptDataImpl* pre_data) { |
+ const bool is_eval = false; |
+ PostponeInterruptsScope postpone; |
+ |
+ ASSERT(!i::Top::global_context().is_null()); |
+ script->set_context_data((*i::Top::global_context())->data()); |
+ |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ // no eval or json support |
+ // Do not notify debugger (maybe later) |
+ //Debugger::OnBeforeCompile(script); |
+#endif |
+ |
+ // Only allow non-global compiles for eval. |
+ ASSERT(is_eval || is_global); |
+ |
+ // Build AST. |
+ FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data); |
+ |
+ // Check for parse errors. |
+ if (lit == NULL) { |
+ ASSERT(Top::has_pending_exception()); |
+ return Handle<JSFunction>::null(); |
+ } |
+ |
+ // Compile the code. |
+ CompilationInfo info(lit, script, is_eval); |
+ Handle<Code> code = MakeCodeForLiveEdit(context, &info); |
+ |
+ // Check for stack-overflow exceptions. |
+ if (code.is_null()) { |
+ Top::StackOverflow(); |
+ return Handle<JSFunction>::null(); |
+ } |
+ |
+ const bool support_profiler_in_live_edit = false; |
+ if (support_profiler_in_live_edit) { |
+ #if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT |
+ // Log the code generation for the script. Check explicit whether logging is |
+ // to avoid allocating when not required. |
+ if (Logger::is_logging() || OProfileAgent::is_enabled()) { |
+ if (script->name()->IsString()) { |
+ SmartPointer<char> data = |
+ String::cast(script->name())->ToCString(DISALLOW_NULLS); |
+ LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG, |
+ *code, *data)); |
+ OProfileAgent::CreateNativeCodeRegion(*data, |
+ code->instruction_start(), |
+ code->instruction_size()); |
+ } else { |
+ LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG, |
+ *code, "")); |
+ OProfileAgent::CreateNativeCodeRegion(is_eval ? "Eval" : "Script", |
+ code->instruction_start(), |
+ code->instruction_size()); |
+ } |
+ } |
+ #endif |
+ } |
+ |
+ // Allocate function. |
+ Handle<JSFunction> fun = |
+ Factory::NewFunctionBoilerplate(lit->name(), |
+ lit->materialized_literal_count(), |
+ code); |
+ |
+ ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position()); |
+ Compiler::SetFunctionInfo(fun, lit, true, script); |
+ |
+ // Hint to the runtime system used when allocating space for initial |
+ // property space by setting the expected number of properties for |
+ // the instances of the function. |
+ SetExpectedNofPropertiesFromEstimate(fun, lit->expected_property_count()); |
+ |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ // Do not notify debugger (maybe later) |
+ // Debugger::OnAfterCompile(script, fun); |
+#endif |
+ |
+ return fun; |
+} |
+ |
+static StaticResource<SafeStringInputBuffer> safe_string_input_buffer; |
+ |
+class UnusedReferenceChecker { |
+ public: |
+ UnusedReferenceChecker() : head_(NULL) { |
+ } |
+ |
+ ~UnusedReferenceChecker() { |
+ for (Item* item = head_; item != NULL; item = item->next) { |
+ GlobalHandles::Destroy(item->ref); |
+ } |
+ } |
+ |
+ void Add(Object* obj) { |
+ Item* item = new Item(); |
+ item->next = head_; |
+ head_ = item; |
+ Object** ref = GlobalHandles::Create(obj).location(); |
+ GlobalHandles::MakeWeak(ref, item, &Callback); |
+ item->ref = ref; |
+ item->collected = false; |
+ } |
+ |
+ void Check() { |
+ Heap::CollectAllGarbage(false); |
+ for (Item* item = head_; item != NULL; item = item->next) { |
+ if (!item->collected) { |
+ const int kSize = 10; |
+ Handle<FixedArray> array = Factory::NewFixedArray(kSize); |
+ int found = FindReferences(*item->ref, 0, *array, array->length()); |
+ printf("Item "); |
+ (*item->ref)->ShortPrint(); |
+ printf(" still referenced by %d item(s):\n", found); |
+ for (int i = 0; i < found; i++) { |
+ if (i >= array->length()) { |
+ printf("... there's more.\n"); |
+ break; |
+ } |
+ array->get(i)->ShortPrint(); |
+ printf("\n"); |
+ } |
+ } |
+ ASSERT(item->collected); |
+ } |
+ |
+ } |
+ |
+ static void Callback(Persistent<Value> object, void* parameter) { |
+ Item* item = reinterpret_cast<Item*>(parameter); |
+ item->collected = true; |
+ } |
+ |
+ private: |
+ struct Item : public ZoneObject { |
+ Object** ref; |
+ bool collected; |
+ Item* next; |
+ }; |
+ Item* head_; |
+ |
+ class VisitorImpl : public ObjectVisitor { |
+ public: |
+ VisitorImpl(Object* target) : target_(target), found_(false) {} |
+ |
+ bool HasFound() { |
+ return found_; |
+ } |
+ |
+ protected: |
+ void VisitPointers(Object** start, Object** end) { |
+ for (Object** pp = start; pp < end; pp++) { |
+ if (*pp == target_) { |
+ found_ = true; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ private: |
+ Object* target_; |
+ bool found_; |
+ }; |
+ |
+ static int FindReferences(Object* target, int max_references, |
+ FixedArray* instances, int instances_size) { |
+ NoHandleAllocation ha; |
+ AssertNoAllocation no_alloc; |
+ |
+ // Iterate the heap. |
+ int count = 0; |
+ HeapIterator iterator; |
+ HeapObject* heap_obj = NULL; |
+ while (((heap_obj = iterator.next()) != NULL) && |
+ (max_references == 0 || count < max_references)) { |
+ |
+ // Skip context extension objects and argument arrays as these are |
+ // checked in the context of functions using them. |
+ HeapObject* obj = heap_obj; |
+ |
+ VisitorImpl visitor(target); |
+ obj->Iterate(&visitor); |
+ if (visitor.HasFound()) { |
+ if (instances != NULL && count < instances_size) { |
+ instances->set(count, obj); |
+ } |
+ count++; |
+ } |
+ } |
+ |
+ // Return the number of referencing objects found. |
+ return count; |
+ } |
+}; |
+ |
+void CompileAndAnalyzeScript(Handle<Script> script) { |
+ Extension* extension = NULL; |
+ Handle<String> source = Handle<String>(String::cast(script->source())); |
+ ScriptDataImpl* pre_data = NULL; |
+ if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) { |
+ Access<SafeStringInputBuffer> buf(&safe_string_input_buffer); |
+ buf->Reset(source.location()); |
+ pre_data = PreParse(source, buf.value(), extension); |
+ } |
+ |
+ // Compile the function and add it to the cache. |
+ Handle<JSFunction> res = LiveEditMakeFunction(true, |
+ Compiler::DONT_VALIDATE_JSON, |
+ script, |
+ Handle<Context>::null(), |
+ NULL, |
+ pre_data); |
+ // Compilation. |
+ ASSERT(res->IsJSFunction()); |
+} |
+ |
+// Let us be careful with double pointers. |
+// Here expected input types are explicit. |
+template<typename FROM, typename TO> |
+TO** DoublePointerNarrowingCast(FROM** from) { |
+ TO* t1 = NULL; |
+ FROM* t2 = t1; |
+ USE(t2); |
+ return reinterpret_cast<TO**>(from); |
+} |
+ |
+template<typename FROM, typename TO> |
+TO** DoublePointerWideningCast(FROM** from) { |
+ FROM* t1 = NULL; |
+ TO* t2 = t1; |
+ USE(t2); |
+ return reinterpret_cast<TO**>(from); |
+} |
+ |
+// For a function (i.e. closure: code + data) describes expectations that |
+// code has about its data. If new version of code has the same expectations |
+// as the old version has, all live closure instances may be safely patched |
+// with a new code. |
+// TODO(peter.rybin): make sure this description is accurate enough. |
+class CodeInfo { |
+ private: |
+ struct ScopeSlotInfo { |
+ String** var_name; |
+ int index; |
+ ScopeSlotInfo() : var_name(NULL) {} |
+ }; |
+ |
+ Vector<ScopeSlotInfo> slots; |
+ int num_parameters; |
+ |
+ public: |
+ void RecordNumParameters(int num_parameters) { |
+ this->num_parameters = num_parameters; |
+ } |
+ // Stores the scope info in some internal format. |
+ void RecordScopes(Scope* scope) { |
+ // Saves some description of scope. It stores name and indexes of |
+ // variables in the whole scope chain. Null-named slots delimit |
+ // scopes of this chain. |
+ Scope* outer_scope = scope->outer_scope(); |
+ if (outer_scope == NULL) { |
+ return; |
+ } |
+ ZoneList<ScopeSlotInfo> slots(10); |
+ do { |
+ ZoneList<Variable*> list(10); |
+ outer_scope->CollectUsedVariables(&list); |
+ int j = 0; |
+ for (int i = 0; i < list.length(); i++) { |
+ Variable* var1 = list[i]; |
+ Slot* slot = var1->slot(); |
+ if (slot != NULL && slot->type() == Slot::CONTEXT) { |
+ if (j != i) { |
+ list[j] = var1; |
+ } |
+ j++; |
+ } |
+ } |
+ |
+ // Sort it. |
+ // TODO(peter.rybin): next time try bubble sort. |
+ for (int k = 1; k < j; k++) { |
+ int l = k; |
+ for (int m = k + 1; m < j; m++) { |
+ if (list[l]->slot()->index() > list[m]->slot()->index()) { |
+ l = m; |
+ } |
+ } |
+ list[k] = list[l]; |
+ } |
+ for (int i = 0; i < j; i++) { |
+ ScopeSlotInfo info; |
+ info.var_name = DoublePointerNarrowingCast<Object,String>( |
+ GlobalHandles::Create(*list[i]->name()).location()); |
+ info.index = list[i]->slot()->index(); |
+ slots.Add(info); |
+ } |
+ slots.Add(ScopeSlotInfo()); |
+ outer_scope = outer_scope->outer_scope(); |
+ } while (outer_scope != NULL); |
+ this->slots = slots.ToVector(); |
+ } |
+ |
+ // Compares itself with other info. When they are equal it means that |
+ // different code versions have the same expectations about their data. |
+ bool CompareTo(CodeInfo* info2) { |
+ CodeInfo* info1 = this; |
+ if (info1->num_parameters != info2->num_parameters) { |
+ return false; |
+ } |
+ if (info1->slots.length() != info2->slots.length()) { |
+ return false; |
+ } |
+ for (int i = 0; i < info1->slots.length(); i++) { |
+ ScopeSlotInfo* slot1 = &info1->slots[i]; |
+ ScopeSlotInfo* slot2 = &info2->slots[i]; |
+ if (slot1->index != slot2->index) { |
+ return false; |
+ } |
+ if (slot1->var_name == NULL) { |
+ if (slot2->var_name != NULL) { |
+ return false; |
+ } |
+ } else { |
+ if (!(*slot1->var_name)->Equals(*slot2->var_name)) { |
+ return false; |
+ } |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ void DisposeHandles() { |
+ for (int j = 0; j < slots.length(); j++) { |
+ if (slots[j].var_name != NULL) { |
+ // TODO does it have to be so brutal? |
+ GlobalHandles::Destroy(DoublePointerWideningCast<String,Object>( |
+ slots[j].var_name)); |
+ } |
+ } |
+ } |
+ void Print() { |
+ for (int i = 0; i < slots.length(); i++) { |
+ if (slots[i].var_name == NULL) { |
+ printf(" | "); |
+ } else { |
+ SmartPointer<char> str = (*slots[i].var_name)->ToCString(); |
+ printf(" %d/%s", slots[i].index,*str); |
+ } |
+ } |
+ printf(" params %d", num_parameters); |
+ } |
+}; |
+ |
+// Saved information about a function. It is stored in array. It points |
+// to other items in this array by their indexes. |
+struct FunctionInfo { |
+ int outer_index; |
+ int next_sibling_index; |
+ CodeInfo code_info; |
+ int start_pos; |
+ int end_pos; |
+ Code** function_code; |
+ |
+ FunctionInfo() : function_code(NULL) {} |
+}; |
class FunctionInfoListener { |
public: |
+ FunctionInfoListener(): function_infos_(10), |
+ current_pos_(-1), last_sibling_pos_(-1) {} |
+ |
+ Vector<FunctionInfo> GetFunctionInfos() { |
+ Vector<FunctionInfo> temp = function_infos_.ToVector(); |
+ // new index -> old index |
+ |
+ ZoneList<int> old_index_map_builder(temp.length()); |
+ for (int i = 0; i < temp.length(); i++) { |
+ old_index_map_builder.Add(i); |
+ } |
+ Vector<int> old_index_map = old_index_map_builder.ToVector(); |
+ for (int i = 0; i < temp.length(); i++) { |
+ int k = i; |
+ for (int j = i + 1; j < temp.length(); j++) { |
+ if (temp[k].start_pos > temp[j].start_pos) { |
+ k = j; |
+ } |
+ } |
+ if (k != i) { |
+ FunctionInfo temp_info = temp[k]; |
+ int temp_index = old_index_map[k]; |
+ temp[k] = temp[i]; |
+ old_index_map[k] = temp_index; |
+ temp[i] = temp_info; |
+ old_index_map[i] = temp_index; |
+ } |
+ } |
+ int index = 0; |
+ ResetIndexes(temp, &index, -1, -1, old_index_map); |
+ ASSERT(index == temp.length()); |
+ |
+ return temp; |
+ } |
+ |
+ static void DisposeData(Vector<FunctionInfo> data) { |
+ for (int i = 0; i < data.length(); i++) { |
+ FunctionInfo* info = &data[i]; |
+ CodeInfo* code_info = &info->code_info; |
+ code_info->DisposeHandles(); |
+ if (info->function_code != NULL) { |
+ // TODO does it have to be so brutal? |
+ GlobalHandles::Destroy(DoublePointerWideningCast<Code,Object>(info->function_code)); |
+ } |
+ } |
+ } |
+ |
+ friend class LiveEditFunctionTracker; |
+ |
+ private: |
+ void ResetIndexes(Vector<FunctionInfo> array, int* current_index, |
+ int new_parent_index, |
+ int old_parent_index, |
+ Vector<int> old_index_map) { |
+ int previous_sibling = -1; |
+ while (*current_index < array.length() && array[*current_index].outer_index == old_parent_index) { |
+ int index = *current_index; |
+ array[index].outer_index = new_parent_index; |
+ if (previous_sibling != -1) { |
+ array[previous_sibling].next_sibling_index = index; |
+ } |
+ previous_sibling = index; |
+ (*current_index)++; |
+ ResetIndexes(array, current_index, index, old_index_map[index], old_index_map); |
+ } |
+ if (previous_sibling != -1) { |
+ array[previous_sibling].next_sibling_index = -1; |
+ } |
+ } |
+ |
void FunctionStarted(FunctionLiteral* fun) { |
- // Implementation follows. |
+ // printf("Start '%s' %d %d\n", *fun->name()->ToCString(), fun->start_position(), fun->end_position()); |
+ FunctionInfo info; |
+ info.code_info.RecordNumParameters(fun->num_parameters()); |
+ info.start_pos = fun->start_position(); |
+ info.end_pos = fun->end_position(); |
+ info.outer_index = current_pos_; |
+ info.next_sibling_index = last_sibling_pos_; |
+ last_sibling_pos_ = -1; |
+ current_pos_ = function_infos_.length(); |
+ function_infos_.Add(info); |
+ if (info.outer_index != -1) { |
+ FunctionInfo& outer_info = function_infos_[info.outer_index]; |
+ ASSERT(info.start_pos >= outer_info.start_pos); |
+ ASSERT(info.end_pos <= outer_info.end_pos); |
+ } |
} |
void FunctionDone() { |
- // Implementation follows. |
+ last_sibling_pos_ = current_pos_; |
+ current_pos_ = function_infos_[current_pos_].outer_index; |
+ // printf("Done\n"); |
} |
void FunctionScope(Scope* scope){ |
- // Implementation follows. |
+ function_infos_[current_pos_].code_info.RecordScopes(scope); |
} |
void FunctionCode(Handle<Code> function_code) { |
- // Implementation follows. |
+ function_infos_[current_pos_].function_code = DoublePointerNarrowingCast<Object, Code>(GlobalHandles::Create(*function_code).location()); |
} |
+ |
+ ZoneList<FunctionInfo> function_infos_; |
+ int current_pos_; |
+ int last_sibling_pos_; |
}; |
static FunctionInfoListener* active_function_info_listener = NULL; |
@@ -84,4 +548,271 @@ bool LiveEditFunctionTracker::IsActive() { |
return active_function_info_listener != NULL; |
} |
+class PosTranslator { |
+ public: |
+ PosTranslator(int start, int old_len, int new_len) |
+ : start_(start), old_len_(old_len), new_len_(new_len) {} |
+ int Translate(int pos) { |
+ if (pos <= start_) { |
+ return pos; |
+ } |
+ if (pos >= start_ + old_len_) { |
+ return pos + new_len_ - old_len_; |
+ } |
+ return -1; |
+ } |
+ |
+ private: |
+ int start_; |
+ int old_len_; |
+ int new_len_; |
+}; |
+ |
+ |
+// Chooses the innermost function that covers the change region. |
+// TODO(peter.rybin): function should cover change region by its body region, |
+// not by its entire text |
+int ChooseChangedFunction(Vector<FunctionInfo> fun_infos, |
+ int change_pos, int change_len) { |
+ |
+ // First condition: function should start before the change region. |
+ // Function #0 (whole-script function) always does, but we want |
+ // the one, that comes last in this list. |
+ int index = 0; |
+ while (index + 1 < fun_infos.length() && |
+ fun_infos[index + 1].start_pos <= change_pos) { |
+ index++; |
+ } |
+ // Now we have stand at the last function that begins before the change |
+ // region. The function that covers entire change region is either |
+ // this function or enclosing one. |
+ for (; fun_infos[index].end_pos < change_pos + change_len; |
+ index = fun_infos[index].outer_index) { |
+ ASSERT(index != -1); |
+ } |
+ return index; |
+} |
+ |
+void PatchCode(FunctionInfo* new_info, |
+ Handle<SharedFunctionInfo> shared_info, |
+ UnusedReferenceChecker* ref_checker) { |
+ if (shared_info.is_null()) { |
+ // Function not found. Probably it's normal. |
+ // TODO explain why it's normal. |
+ return; |
+ } |
+ ref_checker->Add(shared_info->code()); |
+ shared_info->set_code(*new_info->function_code, UPDATE_WRITE_BARRIER); |
+ shared_info->set_start_position(new_info->start_pos); |
+ shared_info->set_end_position(new_info->end_pos); |
+ // TODO: update breakpoints, original code, constructor stub |
+} |
+ |
+void PatchPositions(FunctionInfo* new_info, |
+ Handle<SharedFunctionInfo> shared_info, |
+ PosTranslator* pos_translator) { |
+ if (shared_info.is_null()) { |
+ // Function not found. Probably it's normal. |
+ // TODO explain why it's normal. |
+ return; |
+ } |
+ int new_start_pos = pos_translator->Translate( |
+ shared_info->start_position()); |
+ int new_end_pos = pos_translator->Translate(shared_info->end_position()); |
+ ASSERT(new_start_pos == new_info->start_pos); |
+ ASSERT(new_end_pos == new_info->end_pos); |
+ shared_info->set_start_position(new_start_pos); |
+ shared_info->set_end_position(new_end_pos); |
+ |
+ // TODO |
+ // breakpoints, rinfos, shared function positions |
+} |
+ |
+void LinkToOldScript(FunctionInfo* old_info, Handle<Script> old_script, |
+ Handle<SharedFunctionInfo> shared_info) { |
+ if (shared_info.is_null()) { |
+ // Function not found. Probably it's normal. |
+ // TODO explain why it's normal. |
+ return; |
+ } |
+ shared_info->set_script(*old_script); |
+} |
+ |
+// Helps to find SharedFunctionInfo based on its source positions. |
+class FunctionListHelper { |
+ public: |
+ FunctionListHelper(Handle<FixedArray> shared_functions, |
+ int shared_functions_len, |
+ Vector<FunctionInfo> info_list) |
+ : shared_functions_(shared_functions), |
+ shared_functions_len_(shared_functions_len), |
+ info_list_(info_list) { |
+ } |
+ |
+ Handle<SharedFunctionInfo> Get(int index) { |
+ FunctionInfo* old_info = &info_list_[index]; |
+ for (int i = 0; i < shared_functions_len_; i++) { |
+ SharedFunctionInfo* info = |
+ SharedFunctionInfo::cast(shared_functions_->get(i)); |
+ if (info->start_position() == old_info->start_pos && |
+ info->end_position() == old_info->end_pos) { |
+ return Handle<SharedFunctionInfo>(info); |
+ } |
+ } |
+ { |
+ printf("Failed to find positions %d %d in\n:", old_info->start_pos, old_info->end_pos); |
+ for (int i = 0; i < shared_functions_len_; i++) { |
+ SharedFunctionInfo* info = |
+ SharedFunctionInfo::cast(shared_functions_->get(i)); |
+ printf(" %d %d\n", info->start_position(), info->end_position()); |
+ } |
+ } |
+ return Handle<SharedFunctionInfo>(); |
+ } |
+ private: |
+ Handle<FixedArray> shared_functions_; |
+ int shared_functions_len_; |
+ Vector<FunctionInfo> info_list_; |
+}; |
+ |
+void ChangeScriptLive(Handle<Script> original_script, |
+ Handle<String> original_source, |
+ Handle<String> new_source, |
+ Handle<FixedArray> function_shared_info, |
+ int function_shared_info_len, |
+ int change_pos, int change_len_old, int change_len_new) { |
+ |
+ printf("Old script:\n"); |
+ original_source->Print(); |
+ printf("\n"); |
+ printf("New script:\n"); |
+ new_source->Print(); |
+ printf("\n"); |
+ |
+ { |
+ CompilationZoneScope zone_scope(DELETE_ON_EXIT); |
+ |
+ Vector<FunctionInfo> old_infos; |
+ Vector<FunctionInfo> new_infos; |
+ { |
+ FunctionInfoListener old_listener; |
+ active_function_info_listener = &old_listener; |
+ CompileAndAnalyzeScript(original_script); |
+ old_infos = old_listener.GetFunctionInfos(); |
+ active_function_info_listener = NULL; |
+ //TODO: check compilation errors |
+ } |
+ |
+ // Temporary change source string. |
+ original_script->set_source(*new_source); |
+ { |
+ FunctionInfoListener new_listener; |
+ active_function_info_listener = &new_listener; |
+ CompileAndAnalyzeScript(original_script); |
+ new_infos = new_listener.GetFunctionInfos(); |
+ active_function_info_listener = NULL; |
+ //TODO: check compilation errors |
+ } |
+ // Change source string back, we'll change it permanently later. |
+ original_script->set_source(*original_source); |
+ |
+ int function_being_changed; |
+ { |
+ int old_func_index = |
+ ChooseChangedFunction(old_infos, change_pos, change_len_old); |
+ int new_func_index = |
+ ChooseChangedFunction(new_infos, change_pos, change_len_new); |
+ // Old and new functions should have the same indexes in their lists |
+ // up to old_func_index/new_func_index |
+ ASSERT(old_func_index == new_func_index); |
+ USE(new_func_index); |
+ function_being_changed = old_func_index; |
+ } |
+ printf("Changed function is %d %d (index %d)\n", |
+ new_infos[function_being_changed].start_pos, |
+ new_infos[function_being_changed].end_pos, |
+ function_being_changed); |
+ |
+ // Go to outer functions until code expectations are equal |
+ while (!old_infos[function_being_changed].code_info.CompareTo( |
+ &new_infos[function_being_changed].code_info)) { |
+ |
+ ASSERT(old_infos[function_being_changed].outer_index == |
+ new_infos[function_being_changed].outer_index); |
+ printf("Scope has changed from "); |
+ old_infos[function_being_changed].code_info.Print(); |
+ printf(" to "); |
+ new_infos[function_being_changed].code_info.Print(); |
+ printf("\n"); |
+ function_being_changed = old_infos[function_being_changed].outer_index; |
+ ASSERT(function_being_changed != -1); |
+ printf("function has been chosen: %d %d (index %d)\n", |
+ new_infos[function_being_changed].start_pos, |
+ new_infos[function_being_changed].end_pos, |
+ function_being_changed); |
+ } |
+ |
+ // TODO(peter.rybin): Check frames on stack. |
+ // Committing all changes. |
+ original_script->set_source(*new_source); |
+ |
+ Handle<Script> old_script = Factory::NewScript(original_source); |
+ old_script->set_name(original_script->name()); |
+ old_script->set_line_offset(original_script->line_offset()); |
+ old_script->set_column_offset(original_script->column_offset()); |
+ old_script->set_data(original_script->data()); |
+ old_script->set_type(original_script->type()); |
+ old_script->set_context_data(original_script->context_data()); |
+ old_script->set_compilation_type(original_script->compilation_type()); |
+ |
+ Debugger::OnAfterCompile(old_script, Handle<JSFunction>(), true); |
+ |
+ |
+ FunctionListHelper old_list_helper(function_shared_info, |
+ function_shared_info_len, old_infos); |
+ |
+ UnusedReferenceChecker ref_checker; |
+ |
+ PatchCode(&new_infos[function_being_changed], |
+ old_list_helper.Get(function_being_changed), |
+ &ref_checker); |
+ |
+ PosTranslator translator(change_pos, change_len_old, change_len_new); |
+ for (int i = 0; i < function_being_changed; i++) { |
+ PatchPositions(&new_infos[i], old_list_helper.Get(i), &translator); |
+ } |
+ |
+ // We are jumping to the sibling of our function (in both lists). |
+ // From this index all following items in the lists are our siblings |
+ // or their inner functions. |
+ int old_next_sibling = old_infos[function_being_changed].next_sibling_index; |
+ int new_next_sibling = new_infos[function_being_changed].next_sibling_index; |
+ |
+ if (old_next_sibling == -1) { |
+ ASSERT(new_next_sibling == -1); |
+ } else { |
+ ASSERT(old_infos.length() - old_next_sibling == |
+ new_infos.length() - new_next_sibling); |
+ |
+ for (int i = old_next_sibling, j = new_next_sibling; |
+ i < old_infos.length(); i++, j++) { |
+ PatchPositions(&new_infos[j], old_list_helper.Get(i), &translator); |
+ } |
+ } |
+ |
+ // All functions before next_sibling index are inner functions of the |
+ // function that we are patching. We leave those functions unpatched. |
+ for (int i = function_being_changed + 1; i < old_next_sibling; i++) { |
+ LinkToOldScript(&old_infos[i], old_script, old_list_helper.Get(i)); |
+ } |
+ |
+ FunctionInfoListener::DisposeData(old_infos); |
+ FunctionInfoListener::DisposeData(new_infos); |
+ |
+ ref_checker.Check(); |
+ } |
+} |
+ |
} } // namespace v8::internal |
+ |
+#endif // ENABLE_DEBUGGER_SUPPORT |