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