OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/v8.h" |
| 6 |
| 7 #include "src/arguments.h" |
| 8 #include "src/debug.h" |
| 9 #include "src/liveedit.h" |
| 10 #include "src/runtime/runtime.h" |
| 11 #include "src/runtime/runtime-utils.h" |
| 12 |
| 13 namespace v8 { |
| 14 namespace internal { |
| 15 |
| 16 |
| 17 static int FindSharedFunctionInfosForScript(HeapIterator* iterator, |
| 18 Script* script, |
| 19 FixedArray* buffer) { |
| 20 DisallowHeapAllocation no_allocation; |
| 21 int counter = 0; |
| 22 int buffer_size = buffer->length(); |
| 23 for (HeapObject* obj = iterator->next(); obj != NULL; |
| 24 obj = iterator->next()) { |
| 25 DCHECK(obj != NULL); |
| 26 if (!obj->IsSharedFunctionInfo()) { |
| 27 continue; |
| 28 } |
| 29 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj); |
| 30 if (shared->script() != script) { |
| 31 continue; |
| 32 } |
| 33 if (counter < buffer_size) { |
| 34 buffer->set(counter, shared); |
| 35 } |
| 36 counter++; |
| 37 } |
| 38 return counter; |
| 39 } |
| 40 |
| 41 |
| 42 // For a script finds all SharedFunctionInfo's in the heap that points |
| 43 // to this script. Returns JSArray of SharedFunctionInfo wrapped |
| 44 // in OpaqueReferences. |
| 45 RUNTIME_FUNCTION(Runtime_LiveEditFindSharedFunctionInfosForScript) { |
| 46 HandleScope scope(isolate); |
| 47 CHECK(isolate->debug()->live_edit_enabled()); |
| 48 DCHECK(args.length() == 1); |
| 49 CONVERT_ARG_CHECKED(JSValue, script_value, 0); |
| 50 |
| 51 RUNTIME_ASSERT(script_value->value()->IsScript()); |
| 52 Handle<Script> script = Handle<Script>(Script::cast(script_value->value())); |
| 53 |
| 54 const int kBufferSize = 32; |
| 55 |
| 56 Handle<FixedArray> array; |
| 57 array = isolate->factory()->NewFixedArray(kBufferSize); |
| 58 int number; |
| 59 Heap* heap = isolate->heap(); |
| 60 { |
| 61 HeapIterator heap_iterator(heap); |
| 62 Script* scr = *script; |
| 63 FixedArray* arr = *array; |
| 64 number = FindSharedFunctionInfosForScript(&heap_iterator, scr, arr); |
| 65 } |
| 66 if (number > kBufferSize) { |
| 67 array = isolate->factory()->NewFixedArray(number); |
| 68 HeapIterator heap_iterator(heap); |
| 69 Script* scr = *script; |
| 70 FixedArray* arr = *array; |
| 71 FindSharedFunctionInfosForScript(&heap_iterator, scr, arr); |
| 72 } |
| 73 |
| 74 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(array); |
| 75 result->set_length(Smi::FromInt(number)); |
| 76 |
| 77 LiveEdit::WrapSharedFunctionInfos(result); |
| 78 |
| 79 return *result; |
| 80 } |
| 81 |
| 82 |
| 83 // For a script calculates compilation information about all its functions. |
| 84 // The script source is explicitly specified by the second argument. |
| 85 // The source of the actual script is not used, however it is important that |
| 86 // all generated code keeps references to this particular instance of script. |
| 87 // Returns a JSArray of compilation infos. The array is ordered so that |
| 88 // each function with all its descendant is always stored in a continues range |
| 89 // with the function itself going first. The root function is a script function. |
| 90 RUNTIME_FUNCTION(Runtime_LiveEditGatherCompileInfo) { |
| 91 HandleScope scope(isolate); |
| 92 CHECK(isolate->debug()->live_edit_enabled()); |
| 93 DCHECK(args.length() == 2); |
| 94 CONVERT_ARG_CHECKED(JSValue, script, 0); |
| 95 CONVERT_ARG_HANDLE_CHECKED(String, source, 1); |
| 96 |
| 97 RUNTIME_ASSERT(script->value()->IsScript()); |
| 98 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value())); |
| 99 |
| 100 Handle<JSArray> result; |
| 101 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| 102 isolate, result, LiveEdit::GatherCompileInfo(script_handle, source)); |
| 103 return *result; |
| 104 } |
| 105 |
| 106 |
| 107 // Changes the source of the script to a new_source. |
| 108 // If old_script_name is provided (i.e. is a String), also creates a copy of |
| 109 // the script with its original source and sends notification to debugger. |
| 110 RUNTIME_FUNCTION(Runtime_LiveEditReplaceScript) { |
| 111 HandleScope scope(isolate); |
| 112 CHECK(isolate->debug()->live_edit_enabled()); |
| 113 DCHECK(args.length() == 3); |
| 114 CONVERT_ARG_CHECKED(JSValue, original_script_value, 0); |
| 115 CONVERT_ARG_HANDLE_CHECKED(String, new_source, 1); |
| 116 CONVERT_ARG_HANDLE_CHECKED(Object, old_script_name, 2); |
| 117 |
| 118 RUNTIME_ASSERT(original_script_value->value()->IsScript()); |
| 119 Handle<Script> original_script(Script::cast(original_script_value->value())); |
| 120 |
| 121 Handle<Object> old_script = LiveEdit::ChangeScriptSource( |
| 122 original_script, new_source, old_script_name); |
| 123 |
| 124 if (old_script->IsScript()) { |
| 125 Handle<Script> script_handle = Handle<Script>::cast(old_script); |
| 126 return *Script::GetWrapper(script_handle); |
| 127 } else { |
| 128 return isolate->heap()->null_value(); |
| 129 } |
| 130 } |
| 131 |
| 132 |
| 133 RUNTIME_FUNCTION(Runtime_LiveEditFunctionSourceUpdated) { |
| 134 HandleScope scope(isolate); |
| 135 CHECK(isolate->debug()->live_edit_enabled()); |
| 136 DCHECK(args.length() == 1); |
| 137 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 0); |
| 138 RUNTIME_ASSERT(SharedInfoWrapper::IsInstance(shared_info)); |
| 139 |
| 140 LiveEdit::FunctionSourceUpdated(shared_info); |
| 141 return isolate->heap()->undefined_value(); |
| 142 } |
| 143 |
| 144 |
| 145 // Replaces code of SharedFunctionInfo with a new one. |
| 146 RUNTIME_FUNCTION(Runtime_LiveEditReplaceFunctionCode) { |
| 147 HandleScope scope(isolate); |
| 148 CHECK(isolate->debug()->live_edit_enabled()); |
| 149 DCHECK(args.length() == 2); |
| 150 CONVERT_ARG_HANDLE_CHECKED(JSArray, new_compile_info, 0); |
| 151 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 1); |
| 152 RUNTIME_ASSERT(SharedInfoWrapper::IsInstance(shared_info)); |
| 153 |
| 154 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info); |
| 155 return isolate->heap()->undefined_value(); |
| 156 } |
| 157 |
| 158 |
| 159 // Connects SharedFunctionInfo to another script. |
| 160 RUNTIME_FUNCTION(Runtime_LiveEditFunctionSetScript) { |
| 161 HandleScope scope(isolate); |
| 162 CHECK(isolate->debug()->live_edit_enabled()); |
| 163 DCHECK(args.length() == 2); |
| 164 CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0); |
| 165 CONVERT_ARG_HANDLE_CHECKED(Object, script_object, 1); |
| 166 |
| 167 if (function_object->IsJSValue()) { |
| 168 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object); |
| 169 if (script_object->IsJSValue()) { |
| 170 RUNTIME_ASSERT(JSValue::cast(*script_object)->value()->IsScript()); |
| 171 Script* script = Script::cast(JSValue::cast(*script_object)->value()); |
| 172 script_object = Handle<Object>(script, isolate); |
| 173 } |
| 174 RUNTIME_ASSERT(function_wrapper->value()->IsSharedFunctionInfo()); |
| 175 LiveEdit::SetFunctionScript(function_wrapper, script_object); |
| 176 } else { |
| 177 // Just ignore this. We may not have a SharedFunctionInfo for some functions |
| 178 // and we check it in this function. |
| 179 } |
| 180 |
| 181 return isolate->heap()->undefined_value(); |
| 182 } |
| 183 |
| 184 |
| 185 // In a code of a parent function replaces original function as embedded object |
| 186 // with a substitution one. |
| 187 RUNTIME_FUNCTION(Runtime_LiveEditReplaceRefToNestedFunction) { |
| 188 HandleScope scope(isolate); |
| 189 CHECK(isolate->debug()->live_edit_enabled()); |
| 190 DCHECK(args.length() == 3); |
| 191 |
| 192 CONVERT_ARG_HANDLE_CHECKED(JSValue, parent_wrapper, 0); |
| 193 CONVERT_ARG_HANDLE_CHECKED(JSValue, orig_wrapper, 1); |
| 194 CONVERT_ARG_HANDLE_CHECKED(JSValue, subst_wrapper, 2); |
| 195 RUNTIME_ASSERT(parent_wrapper->value()->IsSharedFunctionInfo()); |
| 196 RUNTIME_ASSERT(orig_wrapper->value()->IsSharedFunctionInfo()); |
| 197 RUNTIME_ASSERT(subst_wrapper->value()->IsSharedFunctionInfo()); |
| 198 |
| 199 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper, |
| 200 subst_wrapper); |
| 201 return isolate->heap()->undefined_value(); |
| 202 } |
| 203 |
| 204 |
| 205 // Updates positions of a shared function info (first parameter) according |
| 206 // to script source change. Text change is described in second parameter as |
| 207 // array of groups of 3 numbers: |
| 208 // (change_begin, change_end, change_end_new_position). |
| 209 // Each group describes a change in text; groups are sorted by change_begin. |
| 210 RUNTIME_FUNCTION(Runtime_LiveEditPatchFunctionPositions) { |
| 211 HandleScope scope(isolate); |
| 212 CHECK(isolate->debug()->live_edit_enabled()); |
| 213 DCHECK(args.length() == 2); |
| 214 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0); |
| 215 CONVERT_ARG_HANDLE_CHECKED(JSArray, position_change_array, 1); |
| 216 RUNTIME_ASSERT(SharedInfoWrapper::IsInstance(shared_array)) |
| 217 |
| 218 LiveEdit::PatchFunctionPositions(shared_array, position_change_array); |
| 219 return isolate->heap()->undefined_value(); |
| 220 } |
| 221 |
| 222 |
| 223 // For array of SharedFunctionInfo's (each wrapped in JSValue) |
| 224 // checks that none of them have activations on stacks (of any thread). |
| 225 // Returns array of the same length with corresponding results of |
| 226 // LiveEdit::FunctionPatchabilityStatus type. |
| 227 RUNTIME_FUNCTION(Runtime_LiveEditCheckAndDropActivations) { |
| 228 HandleScope scope(isolate); |
| 229 CHECK(isolate->debug()->live_edit_enabled()); |
| 230 DCHECK(args.length() == 2); |
| 231 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0); |
| 232 CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 1); |
| 233 RUNTIME_ASSERT(shared_array->length()->IsSmi()); |
| 234 RUNTIME_ASSERT(shared_array->HasFastElements()) |
| 235 int array_length = Smi::cast(shared_array->length())->value(); |
| 236 for (int i = 0; i < array_length; i++) { |
| 237 Handle<Object> element = |
| 238 Object::GetElement(isolate, shared_array, i).ToHandleChecked(); |
| 239 RUNTIME_ASSERT( |
| 240 element->IsJSValue() && |
| 241 Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo()); |
| 242 } |
| 243 |
| 244 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop); |
| 245 } |
| 246 |
| 247 |
| 248 // Compares 2 strings line-by-line, then token-wise and returns diff in form |
| 249 // of JSArray of triplets (pos1, pos1_end, pos2_end) describing list |
| 250 // of diff chunks. |
| 251 RUNTIME_FUNCTION(Runtime_LiveEditCompareStrings) { |
| 252 HandleScope scope(isolate); |
| 253 CHECK(isolate->debug()->live_edit_enabled()); |
| 254 DCHECK(args.length() == 2); |
| 255 CONVERT_ARG_HANDLE_CHECKED(String, s1, 0); |
| 256 CONVERT_ARG_HANDLE_CHECKED(String, s2, 1); |
| 257 |
| 258 return *LiveEdit::CompareStrings(s1, s2); |
| 259 } |
| 260 |
| 261 |
| 262 // Restarts a call frame and completely drops all frames above. |
| 263 // Returns true if successful. Otherwise returns undefined or an error message. |
| 264 RUNTIME_FUNCTION(Runtime_LiveEditRestartFrame) { |
| 265 HandleScope scope(isolate); |
| 266 CHECK(isolate->debug()->live_edit_enabled()); |
| 267 DCHECK(args.length() == 2); |
| 268 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]); |
| 269 RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id)); |
| 270 |
| 271 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]); |
| 272 Heap* heap = isolate->heap(); |
| 273 |
| 274 // Find the relevant frame with the requested index. |
| 275 StackFrame::Id id = isolate->debug()->break_frame_id(); |
| 276 if (id == StackFrame::NO_ID) { |
| 277 // If there are no JavaScript stack frames return undefined. |
| 278 return heap->undefined_value(); |
| 279 } |
| 280 |
| 281 JavaScriptFrameIterator it(isolate, id); |
| 282 int inlined_jsframe_index = FindIndexedNonNativeFrame(&it, index); |
| 283 if (inlined_jsframe_index == -1) return heap->undefined_value(); |
| 284 // We don't really care what the inlined frame index is, since we are |
| 285 // throwing away the entire frame anyways. |
| 286 const char* error_message = LiveEdit::RestartFrame(it.frame()); |
| 287 if (error_message) { |
| 288 return *(isolate->factory()->InternalizeUtf8String(error_message)); |
| 289 } |
| 290 return heap->true_value(); |
| 291 } |
| 292 } |
| 293 } // namespace v8::internal |
OLD | NEW |