Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / v8 / src / runtime / runtime-liveedit.cc
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/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
13 namespace v8 {
14 namespace internal {
15
16
17 static 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.
45 RUNTIME_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.
90 RUNTIME_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.
110 RUNTIME_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
133 RUNTIME_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.
146 RUNTIME_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.
160 RUNTIME_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.
187 RUNTIME_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.
210 RUNTIME_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.
227 RUNTIME_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.
251 RUNTIME_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.
264 RUNTIME_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