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   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.
     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 
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    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