| 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
|
|
|