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