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 15namespace v8 { 16namespace 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. 21RUNTIME_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 CHECK(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. 63RUNTIME_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 CHECK(script->value()->IsScript()); 71 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value())); 72 73 RETURN_RESULT_OR_FAILURE(isolate, 74 LiveEdit::GatherCompileInfo(script_handle, source)); 75} 76 77 78// Changes the source of the script to a new_source. 79// If old_script_name is provided (i.e. is a String), also creates a copy of 80// the script with its original source and sends notification to debugger. 81RUNTIME_FUNCTION(Runtime_LiveEditReplaceScript) { 82 HandleScope scope(isolate); 83 CHECK(isolate->debug()->live_edit_enabled()); 84 DCHECK(args.length() == 3); 85 CONVERT_ARG_CHECKED(JSValue, original_script_value, 0); 86 CONVERT_ARG_HANDLE_CHECKED(String, new_source, 1); 87 CONVERT_ARG_HANDLE_CHECKED(Object, old_script_name, 2); 88 89 CHECK(original_script_value->value()->IsScript()); 90 Handle<Script> original_script(Script::cast(original_script_value->value())); 91 92 Handle<Object> old_script = LiveEdit::ChangeScriptSource( 93 original_script, new_source, old_script_name); 94 95 if (old_script->IsScript()) { 96 Handle<Script> script_handle = Handle<Script>::cast(old_script); 97 return *Script::GetWrapper(script_handle); 98 } else { 99 return isolate->heap()->null_value(); 100 } 101} 102 103 104RUNTIME_FUNCTION(Runtime_LiveEditFunctionSourceUpdated) { 105 HandleScope scope(isolate); 106 CHECK(isolate->debug()->live_edit_enabled()); 107 DCHECK(args.length() == 1); 108 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 0); 109 CHECK(SharedInfoWrapper::IsInstance(shared_info)); 110 111 LiveEdit::FunctionSourceUpdated(shared_info); 112 return isolate->heap()->undefined_value(); 113} 114 115 116// Replaces code of SharedFunctionInfo with a new one. 117RUNTIME_FUNCTION(Runtime_LiveEditReplaceFunctionCode) { 118 HandleScope scope(isolate); 119 CHECK(isolate->debug()->live_edit_enabled()); 120 DCHECK(args.length() == 2); 121 CONVERT_ARG_HANDLE_CHECKED(JSArray, new_compile_info, 0); 122 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 1); 123 CHECK(SharedInfoWrapper::IsInstance(shared_info)); 124 125 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info); 126 return isolate->heap()->undefined_value(); 127} 128 129 130// Connects SharedFunctionInfo to another script. 131RUNTIME_FUNCTION(Runtime_LiveEditFunctionSetScript) { 132 HandleScope scope(isolate); 133 CHECK(isolate->debug()->live_edit_enabled()); 134 DCHECK(args.length() == 2); 135 CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0); 136 CONVERT_ARG_HANDLE_CHECKED(Object, script_object, 1); 137 138 if (function_object->IsJSValue()) { 139 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object); 140 if (script_object->IsJSValue()) { 141 CHECK(JSValue::cast(*script_object)->value()->IsScript()); 142 Script* script = Script::cast(JSValue::cast(*script_object)->value()); 143 script_object = Handle<Object>(script, isolate); 144 } 145 CHECK(function_wrapper->value()->IsSharedFunctionInfo()); 146 LiveEdit::SetFunctionScript(function_wrapper, script_object); 147 } else { 148 // Just ignore this. We may not have a SharedFunctionInfo for some functions 149 // and we check it in this function. 150 } 151 152 return isolate->heap()->undefined_value(); 153} 154 155 156// In a code of a parent function replaces original function as embedded object 157// with a substitution one. 158RUNTIME_FUNCTION(Runtime_LiveEditReplaceRefToNestedFunction) { 159 HandleScope scope(isolate); 160 CHECK(isolate->debug()->live_edit_enabled()); 161 DCHECK(args.length() == 3); 162 163 CONVERT_ARG_HANDLE_CHECKED(JSValue, parent_wrapper, 0); 164 CONVERT_ARG_HANDLE_CHECKED(JSValue, orig_wrapper, 1); 165 CONVERT_ARG_HANDLE_CHECKED(JSValue, subst_wrapper, 2); 166 CHECK(parent_wrapper->value()->IsSharedFunctionInfo()); 167 CHECK(orig_wrapper->value()->IsSharedFunctionInfo()); 168 CHECK(subst_wrapper->value()->IsSharedFunctionInfo()); 169 170 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper, 171 subst_wrapper); 172 return isolate->heap()->undefined_value(); 173} 174 175 176// Updates positions of a shared function info (first parameter) according 177// to script source change. Text change is described in second parameter as 178// array of groups of 3 numbers: 179// (change_begin, change_end, change_end_new_position). 180// Each group describes a change in text; groups are sorted by change_begin. 181RUNTIME_FUNCTION(Runtime_LiveEditPatchFunctionPositions) { 182 HandleScope scope(isolate); 183 CHECK(isolate->debug()->live_edit_enabled()); 184 DCHECK(args.length() == 2); 185 CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0); 186 CONVERT_ARG_HANDLE_CHECKED(JSArray, position_change_array, 1); 187 CHECK(SharedInfoWrapper::IsInstance(shared_array)); 188 189 LiveEdit::PatchFunctionPositions(shared_array, position_change_array); 190 return isolate->heap()->undefined_value(); 191} 192 193 194// For array of SharedFunctionInfo's (each wrapped in JSValue) 195// checks that none of them have activations on stacks (of any thread). 196// Returns array of the same length with corresponding results of 197// LiveEdit::FunctionPatchabilityStatus type. 198RUNTIME_FUNCTION(Runtime_LiveEditCheckAndDropActivations) { 199 HandleScope scope(isolate); 200 CHECK(isolate->debug()->live_edit_enabled()); 201 DCHECK(args.length() == 3); 202 CONVERT_ARG_HANDLE_CHECKED(JSArray, old_shared_array, 0); 203 CONVERT_ARG_HANDLE_CHECKED(JSArray, new_shared_array, 1); 204 CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 2); 205 USE(new_shared_array); 206 CHECK(old_shared_array->length()->IsSmi()); 207 CHECK(new_shared_array->length() == old_shared_array->length()); 208 CHECK(old_shared_array->HasFastElements()); 209 CHECK(new_shared_array->HasFastElements()); 210 int array_length = Smi::cast(old_shared_array->length())->value(); 211 for (int i = 0; i < array_length; i++) { 212 Handle<Object> old_element; 213 Handle<Object> new_element; 214 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 215 isolate, old_element, 216 JSReceiver::GetElement(isolate, old_shared_array, i)); 217 CHECK(old_element->IsJSValue() && 218 Handle<JSValue>::cast(old_element)->value()->IsSharedFunctionInfo()); 219 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 220 isolate, new_element, 221 JSReceiver::GetElement(isolate, new_shared_array, i)); 222 CHECK( 223 new_element->IsUndefined(isolate) || 224 (new_element->IsJSValue() && 225 Handle<JSValue>::cast(new_element)->value()->IsSharedFunctionInfo())); 226 } 227 228 return *LiveEdit::CheckAndDropActivations(old_shared_array, new_shared_array, 229 do_drop); 230} 231 232 233// Compares 2 strings line-by-line, then token-wise and returns diff in form 234// of JSArray of triplets (pos1, pos1_end, pos2_end) describing list 235// of diff chunks. 236RUNTIME_FUNCTION(Runtime_LiveEditCompareStrings) { 237 HandleScope scope(isolate); 238 CHECK(isolate->debug()->live_edit_enabled()); 239 DCHECK(args.length() == 2); 240 CONVERT_ARG_HANDLE_CHECKED(String, s1, 0); 241 CONVERT_ARG_HANDLE_CHECKED(String, s2, 1); 242 243 Handle<JSArray> result = LiveEdit::CompareStrings(s1, s2); 244 uint32_t array_length = 0; 245 CHECK(result->length()->ToArrayLength(&array_length)); 246 if (array_length > 0) { 247 isolate->debug()->feature_tracker()->Track(DebugFeatureTracker::kLiveEdit); 248 } 249 250 return *result; 251} 252 253 254// Restarts a call frame and completely drops all frames above. 255// Returns true if successful. Otherwise returns undefined or an error message. 256RUNTIME_FUNCTION(Runtime_LiveEditRestartFrame) { 257 HandleScope scope(isolate); 258 CHECK(isolate->debug()->live_edit_enabled()); 259 DCHECK(args.length() == 2); 260 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]); 261 CHECK(isolate->debug()->CheckExecutionState(break_id)); 262 263 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]); 264 Heap* heap = isolate->heap(); 265 266 // Find the relevant frame with the requested index. 267 StackFrame::Id id = isolate->debug()->break_frame_id(); 268 if (id == StackFrame::NO_ID) { 269 // If there are no JavaScript stack frames return undefined. 270 return heap->undefined_value(); 271 } 272 273 StackTraceFrameIterator it(isolate, id); 274 int inlined_jsframe_index = 275 DebugFrameHelper::FindIndexedNonNativeFrame(&it, index); 276 // Liveedit is not supported on Wasm. 277 if (inlined_jsframe_index == -1 || it.is_wasm()) { 278 return heap->undefined_value(); 279 } 280 // We don't really care what the inlined frame index is, since we are 281 // throwing away the entire frame anyways. 282 const char* error_message = LiveEdit::RestartFrame(it.javascript_frame()); 283 if (error_message) { 284 return *(isolate->factory()->InternalizeUtf8String(error_message)); 285 } 286 return heap->true_value(); 287} 288} // namespace internal 289} // namespace v8 290