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