Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Unified Diff: src/liveedit.cc

Issue 546125: A brutal approach to V8 script liveedit (Closed)
Patch Set: merge Created 10 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/liveedit.h ('k') | src/runtime.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/liveedit.h ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698