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