blob: b453d154592f235c4f14be702d44a7ef4b3d868c [file] [log] [blame]
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001// 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
13namespace v8 {
14namespace internal {
15
16
17static 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.
45RUNTIME_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.
90RUNTIME_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.
110RUNTIME_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
133RUNTIME_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.
146RUNTIME_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.
160RUNTIME_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.
187RUNTIME_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.
210RUNTIME_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.
227RUNTIME_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.
251RUNTIME_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.
264RUNTIME_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 = Runtime::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