OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 17 matching lines...) Expand all Loading... |
28 | 28 |
29 #include "v8.h" | 29 #include "v8.h" |
30 | 30 |
31 #include "liveedit.h" | 31 #include "liveedit.h" |
32 #include "compiler.h" | 32 #include "compiler.h" |
33 #include "oprofile-agent.h" | 33 #include "oprofile-agent.h" |
34 #include "scopes.h" | 34 #include "scopes.h" |
35 #include "global-handles.h" | 35 #include "global-handles.h" |
36 #include "debug.h" | 36 #include "debug.h" |
37 | 37 |
| 38 #ifdef ENABLE_DEBUGGER_SUPPORT |
| 39 |
38 namespace v8 { | 40 namespace v8 { |
39 namespace internal { | 41 namespace internal { |
40 | 42 |
| 43 static Handle<JSFunction> LiveEditMakeFunction(bool is_global, |
| 44 Compiler::ValidationState validate, |
| 45 Handle<Script> script, |
| 46 Handle<Context> context, |
| 47 v8::Extension* extension, |
| 48 ScriptDataImpl* pre_data) { |
| 49 const bool is_eval = false; |
| 50 PostponeInterruptsScope postpone; |
| 51 |
| 52 ASSERT(!i::Top::global_context().is_null()); |
| 53 script->set_context_data((*i::Top::global_context())->data()); |
| 54 |
| 55 #ifdef ENABLE_DEBUGGER_SUPPORT |
| 56 // no eval or json support |
| 57 // Do not notify debugger (maybe later) |
| 58 //Debugger::OnBeforeCompile(script); |
| 59 #endif |
| 60 |
| 61 // Only allow non-global compiles for eval. |
| 62 ASSERT(is_eval || is_global); |
| 63 |
| 64 // Build AST. |
| 65 FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data); |
| 66 |
| 67 // Check for parse errors. |
| 68 if (lit == NULL) { |
| 69 ASSERT(Top::has_pending_exception()); |
| 70 return Handle<JSFunction>::null(); |
| 71 } |
| 72 |
| 73 // Compile the code. |
| 74 CompilationInfo info(lit, script, is_eval); |
| 75 Handle<Code> code = MakeCodeForLiveEdit(context, &info); |
| 76 |
| 77 // Check for stack-overflow exceptions. |
| 78 if (code.is_null()) { |
| 79 Top::StackOverflow(); |
| 80 return Handle<JSFunction>::null(); |
| 81 } |
| 82 |
| 83 const bool support_profiler_in_live_edit = false; |
| 84 if (support_profiler_in_live_edit) { |
| 85 #if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT |
| 86 // Log the code generation for the script. Check explicit whether logging is |
| 87 // to avoid allocating when not required. |
| 88 if (Logger::is_logging() || OProfileAgent::is_enabled()) { |
| 89 if (script->name()->IsString()) { |
| 90 SmartPointer<char> data = |
| 91 String::cast(script->name())->ToCString(DISALLOW_NULLS); |
| 92 LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG, |
| 93 *code, *data)); |
| 94 OProfileAgent::CreateNativeCodeRegion(*data, |
| 95 code->instruction_start(), |
| 96 code->instruction_size()); |
| 97 } else { |
| 98 LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG, |
| 99 *code, "")); |
| 100 OProfileAgent::CreateNativeCodeRegion(is_eval ? "Eval" : "Script", |
| 101 code->instruction_start(), |
| 102 code->instruction_size()); |
| 103 } |
| 104 } |
| 105 #endif |
| 106 } |
| 107 |
| 108 // Allocate function. |
| 109 Handle<JSFunction> fun = |
| 110 Factory::NewFunctionBoilerplate(lit->name(), |
| 111 lit->materialized_literal_count(), |
| 112 code); |
| 113 |
| 114 ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position()); |
| 115 Compiler::SetFunctionInfo(fun, lit, true, script); |
| 116 |
| 117 // Hint to the runtime system used when allocating space for initial |
| 118 // property space by setting the expected number of properties for |
| 119 // the instances of the function. |
| 120 SetExpectedNofPropertiesFromEstimate(fun, lit->expected_property_count()); |
| 121 |
| 122 #ifdef ENABLE_DEBUGGER_SUPPORT |
| 123 // Do not notify debugger (maybe later) |
| 124 // Debugger::OnAfterCompile(script, fun); |
| 125 #endif |
| 126 |
| 127 return fun; |
| 128 } |
| 129 |
| 130 static StaticResource<SafeStringInputBuffer> safe_string_input_buffer; |
| 131 |
| 132 class UnusedReferenceChecker { |
| 133 public: |
| 134 UnusedReferenceChecker() : head_(NULL) { |
| 135 } |
| 136 |
| 137 ~UnusedReferenceChecker() { |
| 138 for (Item* item = head_; item != NULL; item = item->next) { |
| 139 GlobalHandles::Destroy(item->ref); |
| 140 } |
| 141 } |
| 142 |
| 143 void Add(Object* obj) { |
| 144 Item* item = new Item(); |
| 145 item->next = head_; |
| 146 head_ = item; |
| 147 Object** ref = GlobalHandles::Create(obj).location(); |
| 148 GlobalHandles::MakeWeak(ref, item, &Callback); |
| 149 item->ref = ref; |
| 150 item->collected = false; |
| 151 } |
| 152 |
| 153 void Check() { |
| 154 Heap::CollectAllGarbage(false); |
| 155 for (Item* item = head_; item != NULL; item = item->next) { |
| 156 if (!item->collected) { |
| 157 const int kSize = 10; |
| 158 Handle<FixedArray> array = Factory::NewFixedArray(kSize); |
| 159 int found = FindReferences(*item->ref, 0, *array, array->length()); |
| 160 printf("Item "); |
| 161 (*item->ref)->ShortPrint(); |
| 162 printf(" still referenced by %d item(s):\n", found); |
| 163 for (int i = 0; i < found; i++) { |
| 164 if (i >= array->length()) { |
| 165 printf("... there's more.\n"); |
| 166 break; |
| 167 } |
| 168 array->get(i)->ShortPrint(); |
| 169 printf("\n"); |
| 170 } |
| 171 } |
| 172 ASSERT(item->collected); |
| 173 } |
| 174 |
| 175 } |
| 176 |
| 177 static void Callback(Persistent<Value> object, void* parameter) { |
| 178 Item* item = reinterpret_cast<Item*>(parameter); |
| 179 item->collected = true; |
| 180 } |
| 181 |
| 182 private: |
| 183 struct Item : public ZoneObject { |
| 184 Object** ref; |
| 185 bool collected; |
| 186 Item* next; |
| 187 }; |
| 188 Item* head_; |
| 189 |
| 190 class VisitorImpl : public ObjectVisitor { |
| 191 public: |
| 192 VisitorImpl(Object* target) : target_(target), found_(false) {} |
| 193 |
| 194 bool HasFound() { |
| 195 return found_; |
| 196 } |
| 197 |
| 198 protected: |
| 199 void VisitPointers(Object** start, Object** end) { |
| 200 for (Object** pp = start; pp < end; pp++) { |
| 201 if (*pp == target_) { |
| 202 found_ = true; |
| 203 break; |
| 204 } |
| 205 } |
| 206 } |
| 207 |
| 208 private: |
| 209 Object* target_; |
| 210 bool found_; |
| 211 }; |
| 212 |
| 213 static int FindReferences(Object* target, int max_references, |
| 214 FixedArray* instances, int instances_size) { |
| 215 NoHandleAllocation ha; |
| 216 AssertNoAllocation no_alloc; |
| 217 |
| 218 // Iterate the heap. |
| 219 int count = 0; |
| 220 HeapIterator iterator; |
| 221 HeapObject* heap_obj = NULL; |
| 222 while (((heap_obj = iterator.next()) != NULL) && |
| 223 (max_references == 0 || count < max_references)) { |
| 224 |
| 225 // Skip context extension objects and argument arrays as these are |
| 226 // checked in the context of functions using them. |
| 227 HeapObject* obj = heap_obj; |
| 228 |
| 229 VisitorImpl visitor(target); |
| 230 obj->Iterate(&visitor); |
| 231 if (visitor.HasFound()) { |
| 232 if (instances != NULL && count < instances_size) { |
| 233 instances->set(count, obj); |
| 234 } |
| 235 count++; |
| 236 } |
| 237 } |
| 238 |
| 239 // Return the number of referencing objects found. |
| 240 return count; |
| 241 } |
| 242 }; |
| 243 |
| 244 void CompileAndAnalyzeScript(Handle<Script> script) { |
| 245 Extension* extension = NULL; |
| 246 Handle<String> source = Handle<String>(String::cast(script->source())); |
| 247 ScriptDataImpl* pre_data = NULL; |
| 248 if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) { |
| 249 Access<SafeStringInputBuffer> buf(&safe_string_input_buffer); |
| 250 buf->Reset(source.location()); |
| 251 pre_data = PreParse(source, buf.value(), extension); |
| 252 } |
| 253 |
| 254 // Compile the function and add it to the cache. |
| 255 Handle<JSFunction> res = LiveEditMakeFunction(true, |
| 256 Compiler::DONT_VALIDATE_JSON, |
| 257 script, |
| 258 Handle<Context>::null(), |
| 259 NULL, |
| 260 pre_data); |
| 261 // Compilation. |
| 262 ASSERT(res->IsJSFunction()); |
| 263 } |
| 264 |
| 265 // Let us be careful with double pointers. |
| 266 // Here expected input types are explicit. |
| 267 template<typename FROM, typename TO> |
| 268 TO** DoublePointerNarrowingCast(FROM** from) { |
| 269 TO* t1 = NULL; |
| 270 FROM* t2 = t1; |
| 271 USE(t2); |
| 272 return reinterpret_cast<TO**>(from); |
| 273 } |
| 274 |
| 275 template<typename FROM, typename TO> |
| 276 TO** DoublePointerWideningCast(FROM** from) { |
| 277 FROM* t1 = NULL; |
| 278 TO* t2 = t1; |
| 279 USE(t2); |
| 280 return reinterpret_cast<TO**>(from); |
| 281 } |
| 282 |
| 283 // For a function (i.e. closure: code + data) describes expectations that |
| 284 // code has about its data. If new version of code has the same expectations |
| 285 // as the old version has, all live closure instances may be safely patched |
| 286 // with a new code. |
| 287 // TODO(peter.rybin): make sure this description is accurate enough. |
| 288 class CodeInfo { |
| 289 private: |
| 290 struct ScopeSlotInfo { |
| 291 String** var_name; |
| 292 int index; |
| 293 ScopeSlotInfo() : var_name(NULL) {} |
| 294 }; |
| 295 |
| 296 Vector<ScopeSlotInfo> slots; |
| 297 int num_parameters; |
| 298 |
| 299 public: |
| 300 void RecordNumParameters(int num_parameters) { |
| 301 this->num_parameters = num_parameters; |
| 302 } |
| 303 // Stores the scope info in some internal format. |
| 304 void RecordScopes(Scope* scope) { |
| 305 // Saves some description of scope. It stores name and indexes of |
| 306 // variables in the whole scope chain. Null-named slots delimit |
| 307 // scopes of this chain. |
| 308 Scope* outer_scope = scope->outer_scope(); |
| 309 if (outer_scope == NULL) { |
| 310 return; |
| 311 } |
| 312 ZoneList<ScopeSlotInfo> slots(10); |
| 313 do { |
| 314 ZoneList<Variable*> list(10); |
| 315 outer_scope->CollectUsedVariables(&list); |
| 316 int j = 0; |
| 317 for (int i = 0; i < list.length(); i++) { |
| 318 Variable* var1 = list[i]; |
| 319 Slot* slot = var1->slot(); |
| 320 if (slot != NULL && slot->type() == Slot::CONTEXT) { |
| 321 if (j != i) { |
| 322 list[j] = var1; |
| 323 } |
| 324 j++; |
| 325 } |
| 326 } |
| 327 |
| 328 // Sort it. |
| 329 // TODO(peter.rybin): next time try bubble sort. |
| 330 for (int k = 1; k < j; k++) { |
| 331 int l = k; |
| 332 for (int m = k + 1; m < j; m++) { |
| 333 if (list[l]->slot()->index() > list[m]->slot()->index()) { |
| 334 l = m; |
| 335 } |
| 336 } |
| 337 list[k] = list[l]; |
| 338 } |
| 339 for (int i = 0; i < j; i++) { |
| 340 ScopeSlotInfo info; |
| 341 info.var_name = DoublePointerNarrowingCast<Object,String>( |
| 342 GlobalHandles::Create(*list[i]->name()).location()); |
| 343 info.index = list[i]->slot()->index(); |
| 344 slots.Add(info); |
| 345 } |
| 346 slots.Add(ScopeSlotInfo()); |
| 347 outer_scope = outer_scope->outer_scope(); |
| 348 } while (outer_scope != NULL); |
| 349 this->slots = slots.ToVector(); |
| 350 } |
| 351 |
| 352 // Compares itself with other info. When they are equal it means that |
| 353 // different code versions have the same expectations about their data. |
| 354 bool CompareTo(CodeInfo* info2) { |
| 355 CodeInfo* info1 = this; |
| 356 if (info1->num_parameters != info2->num_parameters) { |
| 357 return false; |
| 358 } |
| 359 if (info1->slots.length() != info2->slots.length()) { |
| 360 return false; |
| 361 } |
| 362 for (int i = 0; i < info1->slots.length(); i++) { |
| 363 ScopeSlotInfo* slot1 = &info1->slots[i]; |
| 364 ScopeSlotInfo* slot2 = &info2->slots[i]; |
| 365 if (slot1->index != slot2->index) { |
| 366 return false; |
| 367 } |
| 368 if (slot1->var_name == NULL) { |
| 369 if (slot2->var_name != NULL) { |
| 370 return false; |
| 371 } |
| 372 } else { |
| 373 if (!(*slot1->var_name)->Equals(*slot2->var_name)) { |
| 374 return false; |
| 375 } |
| 376 } |
| 377 } |
| 378 return true; |
| 379 } |
| 380 |
| 381 void DisposeHandles() { |
| 382 for (int j = 0; j < slots.length(); j++) { |
| 383 if (slots[j].var_name != NULL) { |
| 384 // TODO does it have to be so brutal? |
| 385 GlobalHandles::Destroy(DoublePointerWideningCast<String,Object>( |
| 386 slots[j].var_name)); |
| 387 } |
| 388 } |
| 389 } |
| 390 void Print() { |
| 391 for (int i = 0; i < slots.length(); i++) { |
| 392 if (slots[i].var_name == NULL) { |
| 393 printf(" | "); |
| 394 } else { |
| 395 SmartPointer<char> str = (*slots[i].var_name)->ToCString(); |
| 396 printf(" %d/%s", slots[i].index,*str); |
| 397 } |
| 398 } |
| 399 printf(" params %d", num_parameters); |
| 400 } |
| 401 }; |
| 402 |
| 403 // Saved information about a function. It is stored in array. It points |
| 404 // to other items in this array by their indexes. |
| 405 struct FunctionInfo { |
| 406 int outer_index; |
| 407 int next_sibling_index; |
| 408 CodeInfo code_info; |
| 409 int start_pos; |
| 410 int end_pos; |
| 411 Code** function_code; |
| 412 |
| 413 FunctionInfo() : function_code(NULL) {} |
| 414 }; |
41 | 415 |
42 class FunctionInfoListener { | 416 class FunctionInfoListener { |
43 public: | 417 public: |
| 418 FunctionInfoListener(): function_infos_(10), |
| 419 current_pos_(-1), last_sibling_pos_(-1) {} |
| 420 |
| 421 Vector<FunctionInfo> GetFunctionInfos() { |
| 422 Vector<FunctionInfo> temp = function_infos_.ToVector(); |
| 423 // new index -> old index |
| 424 |
| 425 ZoneList<int> old_index_map_builder(temp.length()); |
| 426 for (int i = 0; i < temp.length(); i++) { |
| 427 old_index_map_builder.Add(i); |
| 428 } |
| 429 Vector<int> old_index_map = old_index_map_builder.ToVector(); |
| 430 for (int i = 0; i < temp.length(); i++) { |
| 431 int k = i; |
| 432 for (int j = i + 1; j < temp.length(); j++) { |
| 433 if (temp[k].start_pos > temp[j].start_pos) { |
| 434 k = j; |
| 435 } |
| 436 } |
| 437 if (k != i) { |
| 438 FunctionInfo temp_info = temp[k]; |
| 439 int temp_index = old_index_map[k]; |
| 440 temp[k] = temp[i]; |
| 441 old_index_map[k] = temp_index; |
| 442 temp[i] = temp_info; |
| 443 old_index_map[i] = temp_index; |
| 444 } |
| 445 } |
| 446 int index = 0; |
| 447 ResetIndexes(temp, &index, -1, -1, old_index_map); |
| 448 ASSERT(index == temp.length()); |
| 449 |
| 450 return temp; |
| 451 } |
| 452 |
| 453 static void DisposeData(Vector<FunctionInfo> data) { |
| 454 for (int i = 0; i < data.length(); i++) { |
| 455 FunctionInfo* info = &data[i]; |
| 456 CodeInfo* code_info = &info->code_info; |
| 457 code_info->DisposeHandles(); |
| 458 if (info->function_code != NULL) { |
| 459 // TODO does it have to be so brutal? |
| 460 GlobalHandles::Destroy(DoublePointerWideningCast<Code,Object>(info->func
tion_code)); |
| 461 } |
| 462 } |
| 463 } |
| 464 |
| 465 friend class LiveEditFunctionTracker; |
| 466 |
| 467 private: |
| 468 void ResetIndexes(Vector<FunctionInfo> array, int* current_index, |
| 469 int new_parent_index, |
| 470 int old_parent_index, |
| 471 Vector<int> old_index_map) { |
| 472 int previous_sibling = -1; |
| 473 while (*current_index < array.length() && array[*current_index].outer_index
== old_parent_index) { |
| 474 int index = *current_index; |
| 475 array[index].outer_index = new_parent_index; |
| 476 if (previous_sibling != -1) { |
| 477 array[previous_sibling].next_sibling_index = index; |
| 478 } |
| 479 previous_sibling = index; |
| 480 (*current_index)++; |
| 481 ResetIndexes(array, current_index, index, old_index_map[index], old_index_
map); |
| 482 } |
| 483 if (previous_sibling != -1) { |
| 484 array[previous_sibling].next_sibling_index = -1; |
| 485 } |
| 486 } |
| 487 |
44 void FunctionStarted(FunctionLiteral* fun) { | 488 void FunctionStarted(FunctionLiteral* fun) { |
45 // Implementation follows. | 489 // printf("Start '%s' %d %d\n", *fun->name()->ToCString(), fun->start_positi
on(), fun->end_position()); |
| 490 FunctionInfo info; |
| 491 info.code_info.RecordNumParameters(fun->num_parameters()); |
| 492 info.start_pos = fun->start_position(); |
| 493 info.end_pos = fun->end_position(); |
| 494 info.outer_index = current_pos_; |
| 495 info.next_sibling_index = last_sibling_pos_; |
| 496 last_sibling_pos_ = -1; |
| 497 current_pos_ = function_infos_.length(); |
| 498 function_infos_.Add(info); |
| 499 if (info.outer_index != -1) { |
| 500 FunctionInfo& outer_info = function_infos_[info.outer_index]; |
| 501 ASSERT(info.start_pos >= outer_info.start_pos); |
| 502 ASSERT(info.end_pos <= outer_info.end_pos); |
| 503 } |
46 } | 504 } |
47 | 505 |
48 void FunctionDone() { | 506 void FunctionDone() { |
49 // Implementation follows. | 507 last_sibling_pos_ = current_pos_; |
| 508 current_pos_ = function_infos_[current_pos_].outer_index; |
| 509 // printf("Done\n"); |
50 } | 510 } |
51 | 511 |
52 void FunctionScope(Scope* scope){ | 512 void FunctionScope(Scope* scope){ |
53 // Implementation follows. | 513 function_infos_[current_pos_].code_info.RecordScopes(scope); |
54 } | 514 } |
55 | 515 |
56 void FunctionCode(Handle<Code> function_code) { | 516 void FunctionCode(Handle<Code> function_code) { |
57 // Implementation follows. | 517 function_infos_[current_pos_].function_code = DoublePointerNarrowingCast<Obj
ect, Code>(GlobalHandles::Create(*function_code).location()); |
58 } | 518 } |
| 519 |
| 520 ZoneList<FunctionInfo> function_infos_; |
| 521 int current_pos_; |
| 522 int last_sibling_pos_; |
59 }; | 523 }; |
60 | 524 |
61 static FunctionInfoListener* active_function_info_listener = NULL; | 525 static FunctionInfoListener* active_function_info_listener = NULL; |
62 | 526 |
63 LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) { | 527 LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) { |
64 if (active_function_info_listener != NULL) { | 528 if (active_function_info_listener != NULL) { |
65 active_function_info_listener->FunctionStarted(fun); | 529 active_function_info_listener->FunctionStarted(fun); |
66 } | 530 } |
67 } | 531 } |
68 LiveEditFunctionTracker::~LiveEditFunctionTracker() { | 532 LiveEditFunctionTracker::~LiveEditFunctionTracker() { |
69 if (active_function_info_listener != NULL) { | 533 if (active_function_info_listener != NULL) { |
70 active_function_info_listener->FunctionDone(); | 534 active_function_info_listener->FunctionDone(); |
71 } | 535 } |
72 } | 536 } |
73 void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) { | 537 void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) { |
74 if (active_function_info_listener != NULL) { | 538 if (active_function_info_listener != NULL) { |
75 active_function_info_listener->FunctionCode(code); | 539 active_function_info_listener->FunctionCode(code); |
76 } | 540 } |
77 } | 541 } |
78 void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) { | 542 void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) { |
79 if (active_function_info_listener != NULL) { | 543 if (active_function_info_listener != NULL) { |
80 active_function_info_listener->FunctionScope(scope); | 544 active_function_info_listener->FunctionScope(scope); |
81 } | 545 } |
82 } | 546 } |
83 bool LiveEditFunctionTracker::IsActive() { | 547 bool LiveEditFunctionTracker::IsActive() { |
84 return active_function_info_listener != NULL; | 548 return active_function_info_listener != NULL; |
85 } | 549 } |
86 | 550 |
| 551 class PosTranslator { |
| 552 public: |
| 553 PosTranslator(int start, int old_len, int new_len) |
| 554 : start_(start), old_len_(old_len), new_len_(new_len) {} |
| 555 int Translate(int pos) { |
| 556 if (pos <= start_) { |
| 557 return pos; |
| 558 } |
| 559 if (pos >= start_ + old_len_) { |
| 560 return pos + new_len_ - old_len_; |
| 561 } |
| 562 return -1; |
| 563 } |
| 564 |
| 565 private: |
| 566 int start_; |
| 567 int old_len_; |
| 568 int new_len_; |
| 569 }; |
| 570 |
| 571 |
| 572 // Chooses the innermost function that covers the change region. |
| 573 // TODO(peter.rybin): function should cover change region by its body region, |
| 574 // not by its entire text |
| 575 int ChooseChangedFunction(Vector<FunctionInfo> fun_infos, |
| 576 int change_pos, int change_len) { |
| 577 |
| 578 // First condition: function should start before the change region. |
| 579 // Function #0 (whole-script function) always does, but we want |
| 580 // the one, that comes last in this list. |
| 581 int index = 0; |
| 582 while (index + 1 < fun_infos.length() && |
| 583 fun_infos[index + 1].start_pos <= change_pos) { |
| 584 index++; |
| 585 } |
| 586 // Now we have stand at the last function that begins before the change |
| 587 // region. The function that covers entire change region is either |
| 588 // this function or enclosing one. |
| 589 for (; fun_infos[index].end_pos < change_pos + change_len; |
| 590 index = fun_infos[index].outer_index) { |
| 591 ASSERT(index != -1); |
| 592 } |
| 593 return index; |
| 594 } |
| 595 |
| 596 void PatchCode(FunctionInfo* new_info, |
| 597 Handle<SharedFunctionInfo> shared_info, |
| 598 UnusedReferenceChecker* ref_checker) { |
| 599 if (shared_info.is_null()) { |
| 600 // Function not found. Probably it's normal. |
| 601 // TODO explain why it's normal. |
| 602 return; |
| 603 } |
| 604 ref_checker->Add(shared_info->code()); |
| 605 shared_info->set_code(*new_info->function_code, UPDATE_WRITE_BARRIER); |
| 606 shared_info->set_start_position(new_info->start_pos); |
| 607 shared_info->set_end_position(new_info->end_pos); |
| 608 // TODO: update breakpoints, original code, constructor stub |
| 609 } |
| 610 |
| 611 void PatchPositions(FunctionInfo* new_info, |
| 612 Handle<SharedFunctionInfo> shared_info, |
| 613 PosTranslator* pos_translator) { |
| 614 if (shared_info.is_null()) { |
| 615 // Function not found. Probably it's normal. |
| 616 // TODO explain why it's normal. |
| 617 return; |
| 618 } |
| 619 int new_start_pos = pos_translator->Translate( |
| 620 shared_info->start_position()); |
| 621 int new_end_pos = pos_translator->Translate(shared_info->end_position()); |
| 622 ASSERT(new_start_pos == new_info->start_pos); |
| 623 ASSERT(new_end_pos == new_info->end_pos); |
| 624 shared_info->set_start_position(new_start_pos); |
| 625 shared_info->set_end_position(new_end_pos); |
| 626 |
| 627 // TODO |
| 628 // breakpoints, rinfos, shared function positions |
| 629 } |
| 630 |
| 631 void LinkToOldScript(FunctionInfo* old_info, Handle<Script> old_script, |
| 632 Handle<SharedFunctionInfo> shared_info) { |
| 633 if (shared_info.is_null()) { |
| 634 // Function not found. Probably it's normal. |
| 635 // TODO explain why it's normal. |
| 636 return; |
| 637 } |
| 638 shared_info->set_script(*old_script); |
| 639 } |
| 640 |
| 641 // Helps to find SharedFunctionInfo based on its source positions. |
| 642 class FunctionListHelper { |
| 643 public: |
| 644 FunctionListHelper(Handle<FixedArray> shared_functions, |
| 645 int shared_functions_len, |
| 646 Vector<FunctionInfo> info_list) |
| 647 : shared_functions_(shared_functions), |
| 648 shared_functions_len_(shared_functions_len), |
| 649 info_list_(info_list) { |
| 650 } |
| 651 |
| 652 Handle<SharedFunctionInfo> Get(int index) { |
| 653 FunctionInfo* old_info = &info_list_[index]; |
| 654 for (int i = 0; i < shared_functions_len_; i++) { |
| 655 SharedFunctionInfo* info = |
| 656 SharedFunctionInfo::cast(shared_functions_->get(i)); |
| 657 if (info->start_position() == old_info->start_pos && |
| 658 info->end_position() == old_info->end_pos) { |
| 659 return Handle<SharedFunctionInfo>(info); |
| 660 } |
| 661 } |
| 662 { |
| 663 printf("Failed to find positions %d %d in\n:", old_info->start_pos, old_in
fo->end_pos); |
| 664 for (int i = 0; i < shared_functions_len_; i++) { |
| 665 SharedFunctionInfo* info = |
| 666 SharedFunctionInfo::cast(shared_functions_->get(i)); |
| 667 printf(" %d %d\n", info->start_position(), info->end_position()); |
| 668 } |
| 669 } |
| 670 return Handle<SharedFunctionInfo>(); |
| 671 } |
| 672 private: |
| 673 Handle<FixedArray> shared_functions_; |
| 674 int shared_functions_len_; |
| 675 Vector<FunctionInfo> info_list_; |
| 676 }; |
| 677 |
| 678 void ChangeScriptLive(Handle<Script> original_script, |
| 679 Handle<String> original_source, |
| 680 Handle<String> new_source, |
| 681 Handle<FixedArray> function_shared_info, |
| 682 int function_shared_info_len, |
| 683 int change_pos, int change_len_old, int change_len_new) { |
| 684 |
| 685 printf("Old script:\n"); |
| 686 original_source->Print(); |
| 687 printf("\n"); |
| 688 printf("New script:\n"); |
| 689 new_source->Print(); |
| 690 printf("\n"); |
| 691 |
| 692 { |
| 693 CompilationZoneScope zone_scope(DELETE_ON_EXIT); |
| 694 |
| 695 Vector<FunctionInfo> old_infos; |
| 696 Vector<FunctionInfo> new_infos; |
| 697 { |
| 698 FunctionInfoListener old_listener; |
| 699 active_function_info_listener = &old_listener; |
| 700 CompileAndAnalyzeScript(original_script); |
| 701 old_infos = old_listener.GetFunctionInfos(); |
| 702 active_function_info_listener = NULL; |
| 703 //TODO: check compilation errors |
| 704 } |
| 705 |
| 706 // Temporary change source string. |
| 707 original_script->set_source(*new_source); |
| 708 { |
| 709 FunctionInfoListener new_listener; |
| 710 active_function_info_listener = &new_listener; |
| 711 CompileAndAnalyzeScript(original_script); |
| 712 new_infos = new_listener.GetFunctionInfos(); |
| 713 active_function_info_listener = NULL; |
| 714 //TODO: check compilation errors |
| 715 } |
| 716 // Change source string back, we'll change it permanently later. |
| 717 original_script->set_source(*original_source); |
| 718 |
| 719 int function_being_changed; |
| 720 { |
| 721 int old_func_index = |
| 722 ChooseChangedFunction(old_infos, change_pos, change_len_old); |
| 723 int new_func_index = |
| 724 ChooseChangedFunction(new_infos, change_pos, change_len_new); |
| 725 // Old and new functions should have the same indexes in their lists |
| 726 // up to old_func_index/new_func_index |
| 727 ASSERT(old_func_index == new_func_index); |
| 728 USE(new_func_index); |
| 729 function_being_changed = old_func_index; |
| 730 } |
| 731 printf("Changed function is %d %d (index %d)\n", |
| 732 new_infos[function_being_changed].start_pos, |
| 733 new_infos[function_being_changed].end_pos, |
| 734 function_being_changed); |
| 735 |
| 736 // Go to outer functions until code expectations are equal |
| 737 while (!old_infos[function_being_changed].code_info.CompareTo( |
| 738 &new_infos[function_being_changed].code_info)) { |
| 739 |
| 740 ASSERT(old_infos[function_being_changed].outer_index == |
| 741 new_infos[function_being_changed].outer_index); |
| 742 printf("Scope has changed from "); |
| 743 old_infos[function_being_changed].code_info.Print(); |
| 744 printf(" to "); |
| 745 new_infos[function_being_changed].code_info.Print(); |
| 746 printf("\n"); |
| 747 function_being_changed = old_infos[function_being_changed].outer_index; |
| 748 ASSERT(function_being_changed != -1); |
| 749 printf("function has been chosen: %d %d (index %d)\n", |
| 750 new_infos[function_being_changed].start_pos, |
| 751 new_infos[function_being_changed].end_pos, |
| 752 function_being_changed); |
| 753 } |
| 754 |
| 755 // TODO(peter.rybin): Check frames on stack. |
| 756 // Committing all changes. |
| 757 original_script->set_source(*new_source); |
| 758 |
| 759 Handle<Script> old_script = Factory::NewScript(original_source); |
| 760 old_script->set_name(original_script->name()); |
| 761 old_script->set_line_offset(original_script->line_offset()); |
| 762 old_script->set_column_offset(original_script->column_offset()); |
| 763 old_script->set_data(original_script->data()); |
| 764 old_script->set_type(original_script->type()); |
| 765 old_script->set_context_data(original_script->context_data()); |
| 766 old_script->set_compilation_type(original_script->compilation_type()); |
| 767 |
| 768 Debugger::OnAfterCompile(old_script, Handle<JSFunction>(), true); |
| 769 |
| 770 |
| 771 FunctionListHelper old_list_helper(function_shared_info, |
| 772 function_shared_info_len, old_infos); |
| 773 |
| 774 UnusedReferenceChecker ref_checker; |
| 775 |
| 776 PatchCode(&new_infos[function_being_changed], |
| 777 old_list_helper.Get(function_being_changed), |
| 778 &ref_checker); |
| 779 |
| 780 PosTranslator translator(change_pos, change_len_old, change_len_new); |
| 781 for (int i = 0; i < function_being_changed; i++) { |
| 782 PatchPositions(&new_infos[i], old_list_helper.Get(i), &translator); |
| 783 } |
| 784 |
| 785 // We are jumping to the sibling of our function (in both lists). |
| 786 // From this index all following items in the lists are our siblings |
| 787 // or their inner functions. |
| 788 int old_next_sibling = old_infos[function_being_changed].next_sibling_index; |
| 789 int new_next_sibling = new_infos[function_being_changed].next_sibling_index; |
| 790 |
| 791 if (old_next_sibling == -1) { |
| 792 ASSERT(new_next_sibling == -1); |
| 793 } else { |
| 794 ASSERT(old_infos.length() - old_next_sibling == |
| 795 new_infos.length() - new_next_sibling); |
| 796 |
| 797 for (int i = old_next_sibling, j = new_next_sibling; |
| 798 i < old_infos.length(); i++, j++) { |
| 799 PatchPositions(&new_infos[j], old_list_helper.Get(i), &translator); |
| 800 } |
| 801 } |
| 802 |
| 803 // All functions before next_sibling index are inner functions of the |
| 804 // function that we are patching. We leave those functions unpatched. |
| 805 for (int i = function_being_changed + 1; i < old_next_sibling; i++) { |
| 806 LinkToOldScript(&old_infos[i], old_script, old_list_helper.Get(i)); |
| 807 } |
| 808 |
| 809 FunctionInfoListener::DisposeData(old_infos); |
| 810 FunctionInfoListener::DisposeData(new_infos); |
| 811 |
| 812 ref_checker.Check(); |
| 813 } |
| 814 } |
| 815 |
87 } } // namespace v8::internal | 816 } } // namespace v8::internal |
| 817 |
| 818 #endif // ENABLE_DEBUGGER_SUPPORT |
OLD | NEW |