Home | History | Annotate | Download | only in runtime
      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 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.
     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   RETURN_RESULT_OR_FAILURE(isolate,
     74                            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 RUNTIME_FUNCTION(Runtime_LiveEditReplaceScript) {
     82   HandleScope scope(isolate);
     83   CHECK(isolate->debug()->live_edit_enabled());
     84   DCHECK(args.length() == 3);
     85   CONVERT_ARG_CHECKED(JSValue, original_script_value, 0);
     86   CONVERT_ARG_HANDLE_CHECKED(String, new_source, 1);
     87   CONVERT_ARG_HANDLE_CHECKED(Object, old_script_name, 2);
     88 
     89   RUNTIME_ASSERT(original_script_value->value()->IsScript());
     90   Handle<Script> original_script(Script::cast(original_script_value->value()));
     91 
     92   Handle<Object> old_script = LiveEdit::ChangeScriptSource(
     93       original_script, new_source, old_script_name);
     94 
     95   if (old_script->IsScript()) {
     96     Handle<Script> script_handle = Handle<Script>::cast(old_script);
     97     return *Script::GetWrapper(script_handle);
     98   } else {
     99     return isolate->heap()->null_value();
    100   }
    101 }
    102 
    103 
    104 RUNTIME_FUNCTION(Runtime_LiveEditFunctionSourceUpdated) {
    105   HandleScope scope(isolate);
    106   CHECK(isolate->debug()->live_edit_enabled());
    107   DCHECK(args.length() == 1);
    108   CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 0);
    109   RUNTIME_ASSERT(SharedInfoWrapper::IsInstance(shared_info));
    110 
    111   LiveEdit::FunctionSourceUpdated(shared_info);
    112   return isolate->heap()->undefined_value();
    113 }
    114 
    115 
    116 // Replaces code of SharedFunctionInfo with a new one.
    117 RUNTIME_FUNCTION(Runtime_LiveEditReplaceFunctionCode) {
    118   HandleScope scope(isolate);
    119   CHECK(isolate->debug()->live_edit_enabled());
    120   DCHECK(args.length() == 2);
    121   CONVERT_ARG_HANDLE_CHECKED(JSArray, new_compile_info, 0);
    122   CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 1);
    123   RUNTIME_ASSERT(SharedInfoWrapper::IsInstance(shared_info));
    124 
    125   LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
    126   return isolate->heap()->undefined_value();
    127 }
    128 
    129 
    130 // Connects SharedFunctionInfo to another script.
    131 RUNTIME_FUNCTION(Runtime_LiveEditFunctionSetScript) {
    132   HandleScope scope(isolate);
    133   CHECK(isolate->debug()->live_edit_enabled());
    134   DCHECK(args.length() == 2);
    135   CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
    136   CONVERT_ARG_HANDLE_CHECKED(Object, script_object, 1);
    137 
    138   if (function_object->IsJSValue()) {
    139     Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
    140     if (script_object->IsJSValue()) {
    141       RUNTIME_ASSERT(JSValue::cast(*script_object)->value()->IsScript());
    142       Script* script = Script::cast(JSValue::cast(*script_object)->value());
    143       script_object = Handle<Object>(script, isolate);
    144     }
    145     RUNTIME_ASSERT(function_wrapper->value()->IsSharedFunctionInfo());
    146     LiveEdit::SetFunctionScript(function_wrapper, script_object);
    147   } else {
    148     // Just ignore this. We may not have a SharedFunctionInfo for some functions
    149     // and we check it in this function.
    150   }
    151 
    152   return isolate->heap()->undefined_value();
    153 }
    154 
    155 
    156 // In a code of a parent function replaces original function as embedded object
    157 // with a substitution one.
    158 RUNTIME_FUNCTION(Runtime_LiveEditReplaceRefToNestedFunction) {
    159   HandleScope scope(isolate);
    160   CHECK(isolate->debug()->live_edit_enabled());
    161   DCHECK(args.length() == 3);
    162 
    163   CONVERT_ARG_HANDLE_CHECKED(JSValue, parent_wrapper, 0);
    164   CONVERT_ARG_HANDLE_CHECKED(JSValue, orig_wrapper, 1);
    165   CONVERT_ARG_HANDLE_CHECKED(JSValue, subst_wrapper, 2);
    166   RUNTIME_ASSERT(parent_wrapper->value()->IsSharedFunctionInfo());
    167   RUNTIME_ASSERT(orig_wrapper->value()->IsSharedFunctionInfo());
    168   RUNTIME_ASSERT(subst_wrapper->value()->IsSharedFunctionInfo());
    169 
    170   LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
    171                                        subst_wrapper);
    172   return isolate->heap()->undefined_value();
    173 }
    174 
    175 
    176 // Updates positions of a shared function info (first parameter) according
    177 // to script source change. Text change is described in second parameter as
    178 // array of groups of 3 numbers:
    179 // (change_begin, change_end, change_end_new_position).
    180 // Each group describes a change in text; groups are sorted by change_begin.
    181 RUNTIME_FUNCTION(Runtime_LiveEditPatchFunctionPositions) {
    182   HandleScope scope(isolate);
    183   CHECK(isolate->debug()->live_edit_enabled());
    184   DCHECK(args.length() == 2);
    185   CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0);
    186   CONVERT_ARG_HANDLE_CHECKED(JSArray, position_change_array, 1);
    187   RUNTIME_ASSERT(SharedInfoWrapper::IsInstance(shared_array));
    188 
    189   LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
    190   return isolate->heap()->undefined_value();
    191 }
    192 
    193 
    194 // For array of SharedFunctionInfo's (each wrapped in JSValue)
    195 // checks that none of them have activations on stacks (of any thread).
    196 // Returns array of the same length with corresponding results of
    197 // LiveEdit::FunctionPatchabilityStatus type.
    198 RUNTIME_FUNCTION(Runtime_LiveEditCheckAndDropActivations) {
    199   HandleScope scope(isolate);
    200   CHECK(isolate->debug()->live_edit_enabled());
    201   DCHECK(args.length() == 3);
    202   CONVERT_ARG_HANDLE_CHECKED(JSArray, old_shared_array, 0);
    203   CONVERT_ARG_HANDLE_CHECKED(JSArray, new_shared_array, 1);
    204   CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 2);
    205   USE(new_shared_array);
    206   RUNTIME_ASSERT(old_shared_array->length()->IsSmi());
    207   RUNTIME_ASSERT(new_shared_array->length() == old_shared_array->length());
    208   RUNTIME_ASSERT(old_shared_array->HasFastElements());
    209   RUNTIME_ASSERT(new_shared_array->HasFastElements());
    210   int array_length = Smi::cast(old_shared_array->length())->value();
    211   for (int i = 0; i < array_length; i++) {
    212     Handle<Object> old_element;
    213     Handle<Object> new_element;
    214     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    215         isolate, old_element,
    216         JSReceiver::GetElement(isolate, old_shared_array, i));
    217     RUNTIME_ASSERT(
    218         old_element->IsJSValue() &&
    219         Handle<JSValue>::cast(old_element)->value()->IsSharedFunctionInfo());
    220     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    221         isolate, new_element,
    222         JSReceiver::GetElement(isolate, new_shared_array, i));
    223     RUNTIME_ASSERT(
    224         new_element->IsUndefined(isolate) ||
    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.
    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 = 0;
    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.
    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