Home | History | Annotate | Download | only in debug
      1 // Copyright 2012 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 #ifndef V8_DEBUG_LIVEEDIT_H_
      6 #define V8_DEBUG_LIVEEDIT_H_
      7 
      8 
      9 // Live Edit feature implementation.
     10 // User should be able to change script on already running VM. This feature
     11 // matches hot swap features in other frameworks.
     12 //
     13 // The basic use-case is when user spots some mistake in function body
     14 // from debugger and wishes to change the algorithm without restart.
     15 //
     16 // A single change always has a form of a simple replacement (in pseudo-code):
     17 //   script.source[positions, positions+length] = new_string;
     18 // Implementation first determines, which function's body includes this
     19 // change area. Then both old and new versions of script are fully compiled
     20 // in order to analyze, whether the function changed its outer scope
     21 // expectations (or number of parameters). If it didn't, function's code is
     22 // patched with a newly compiled code. If it did change, enclosing function
     23 // gets patched. All inner functions are left untouched, whatever happened
     24 // to them in a new script version. However, new version of code will
     25 // instantiate newly compiled functions.
     26 
     27 
     28 #include "src/allocation.h"
     29 #include "src/compiler.h"
     30 
     31 namespace v8 {
     32 namespace internal {
     33 
     34 // This class collects some specific information on structure of functions
     35 // in a particular script. It gets called from compiler all the time, but
     36 // actually records any data only when liveedit operation is in process;
     37 // in any other time this class is very cheap.
     38 //
     39 // The primary interest of the Tracker is to record function scope structures
     40 // in order to analyze whether function code maybe safely patched (with new
     41 // code successfully reading existing data from function scopes). The Tracker
     42 // also collects compiled function codes.
     43 class LiveEditFunctionTracker {
     44  public:
     45   explicit LiveEditFunctionTracker(Isolate* isolate, FunctionLiteral* fun);
     46   ~LiveEditFunctionTracker();
     47   void RecordFunctionInfo(Handle<SharedFunctionInfo> info,
     48                           FunctionLiteral* lit, Zone* zone);
     49   void RecordRootFunctionInfo(Handle<Code> code);
     50 
     51   static bool IsActive(Isolate* isolate);
     52 
     53  private:
     54   Isolate* isolate_;
     55 };
     56 
     57 
     58 class LiveEdit : AllStatic {
     59  public:
     60   // Describes how exactly a frame has been dropped from stack.
     61   enum FrameDropMode {
     62     // No frame has been dropped.
     63     FRAMES_UNTOUCHED,
     64     // The top JS frame had been calling debug break slot stub. Patch the
     65     // address this stub jumps to in the end.
     66     FRAME_DROPPED_IN_DEBUG_SLOT_CALL,
     67     // The top JS frame had been calling some C++ function. The return address
     68     // gets patched automatically.
     69     FRAME_DROPPED_IN_DIRECT_CALL,
     70     FRAME_DROPPED_IN_RETURN_CALL,
     71     CURRENTLY_SET_MODE
     72   };
     73 
     74   static void InitializeThreadLocal(Debug* debug);
     75 
     76   static bool SetAfterBreakTarget(Debug* debug);
     77 
     78   MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
     79       Handle<Script> script,
     80       Handle<String> source);
     81 
     82   static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
     83                                   Handle<JSArray> shared_info_array);
     84 
     85   static void FunctionSourceUpdated(Handle<JSArray> shared_info_array);
     86 
     87   // Updates script field in FunctionSharedInfo.
     88   static void SetFunctionScript(Handle<JSValue> function_wrapper,
     89                                 Handle<Object> script_handle);
     90 
     91   static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
     92                                      Handle<JSArray> position_change_array);
     93 
     94   // For a script updates its source field. If old_script_name is provided
     95   // (i.e. is a String), also creates a copy of the script with its original
     96   // source and sends notification to debugger.
     97   static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
     98                                            Handle<String> new_source,
     99                                            Handle<Object> old_script_name);
    100 
    101   // In a code of a parent function replaces original function as embedded
    102   // object with a substitution one.
    103   static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
    104                                          Handle<JSValue> orig_function_shared,
    105                                          Handle<JSValue> subst_function_shared);
    106 
    107   // Find open generator activations, and set corresponding "result" elements to
    108   // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
    109   static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
    110                                    Handle<FixedArray> result, int len);
    111 
    112   // Checks listed functions on stack and return array with corresponding
    113   // FunctionPatchabilityStatus statuses; extra array element may
    114   // contain general error message. Modifies the current stack and
    115   // has restart the lowest found frames and drops all other frames above
    116   // if possible and if do_drop is true.
    117   static Handle<JSArray> CheckAndDropActivations(
    118       Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
    119       bool do_drop);
    120 
    121   // Restarts the call frame and completely drops all frames above it.
    122   // Return error message or NULL.
    123   static const char* RestartFrame(JavaScriptFrame* frame);
    124 
    125   // A copy of this is in liveedit.js.
    126   enum FunctionPatchabilityStatus {
    127     FUNCTION_AVAILABLE_FOR_PATCH = 1,
    128     FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
    129     FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
    130     FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
    131     FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
    132     FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
    133     FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7,
    134     FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8
    135   };
    136 
    137   // Compares 2 strings line-by-line, then token-wise and returns diff in form
    138   // of array of triplets (pos1, pos1_end, pos2_end) describing list
    139   // of diff chunks.
    140   static Handle<JSArray> CompareStrings(Handle<String> s1,
    141                                         Handle<String> s2);
    142 
    143   // Architecture-specific constant.
    144   static const bool kFrameDropperSupported;
    145 
    146   /**
    147    * Defines layout of a stack frame that supports padding. This is a regular
    148    * internal frame that has a flexible stack structure. LiveEdit can shift
    149    * its lower part up the stack, taking up the 'padding' space when additional
    150    * stack memory is required.
    151    * Such frame is expected immediately above the topmost JavaScript frame.
    152    *
    153    * Stack Layout:
    154    *   --- Top
    155    *   LiveEdit routine frames
    156    *   ---
    157    *   C frames of debug handler
    158    *   ---
    159    *   ...
    160    *   ---
    161    *      An internal frame that has n padding words:
    162    *      - any number of words as needed by code -- upper part of frame
    163    *      - padding size: a Smi storing n -- current size of padding
    164    *      - padding: n words filled with kPaddingValue in form of Smi
    165    *      - 3 context/type words of a regular InternalFrame
    166    *      - fp
    167    *   ---
    168    *      Topmost JavaScript frame
    169    *   ---
    170    *   ...
    171    *   --- Bottom
    172    */
    173   // A size of frame base including fp. Padding words starts right above
    174   // the base.
    175   static const int kFrameDropperFrameSize =
    176       4 + StandardFrameConstants::kCPSlotCount;
    177   // A number of words that should be reserved on stack for the LiveEdit use.
    178   // Stored on stack in form of Smi.
    179   static const int kFramePaddingInitialSize = 1;
    180   // A value that padding words are filled with (in form of Smi). Going
    181   // bottom-top, the first word not having this value is a counter word.
    182   static const int kFramePaddingValue = kFramePaddingInitialSize + 1;
    183 };
    184 
    185 
    186 // A general-purpose comparator between 2 arrays.
    187 class Comparator {
    188  public:
    189   // Holds 2 arrays of some elements allowing to compare any pair of
    190   // element from the first array and element from the second array.
    191   class Input {
    192    public:
    193     virtual int GetLength1() = 0;
    194     virtual int GetLength2() = 0;
    195     virtual bool Equals(int index1, int index2) = 0;
    196 
    197    protected:
    198     virtual ~Input() {}
    199   };
    200 
    201   // Receives compare result as a series of chunks.
    202   class Output {
    203    public:
    204     // Puts another chunk in result list. Note that technically speaking
    205     // only 3 arguments actually needed with 4th being derivable.
    206     virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
    207 
    208    protected:
    209     virtual ~Output() {}
    210   };
    211 
    212   // Finds the difference between 2 arrays of elements.
    213   static void CalculateDifference(Input* input,
    214                                   Output* result_writer);
    215 };
    216 
    217 
    218 
    219 // Simple helper class that creates more or less typed structures over
    220 // JSArray object. This is an adhoc method of passing structures from C++
    221 // to JavaScript.
    222 template<typename S>
    223 class JSArrayBasedStruct {
    224  public:
    225   static S Create(Isolate* isolate) {
    226     Factory* factory = isolate->factory();
    227     Handle<JSArray> array = factory->NewJSArray(S::kSize_);
    228     return S(array);
    229   }
    230 
    231   static S cast(Object* object) {
    232     JSArray* array = JSArray::cast(object);
    233     Handle<JSArray> array_handle(array);
    234     return S(array_handle);
    235   }
    236 
    237   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
    238   }
    239 
    240   Handle<JSArray> GetJSArray() {
    241     return array_;
    242   }
    243 
    244   Isolate* isolate() const {
    245     return array_->GetIsolate();
    246   }
    247 
    248  protected:
    249   void SetField(int field_position, Handle<Object> value) {
    250     Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
    251         .Assert();
    252   }
    253 
    254   void SetSmiValueField(int field_position, int value) {
    255     SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
    256   }
    257 
    258   Handle<Object> GetField(int field_position) {
    259     return Object::GetElement(
    260         isolate(), array_, field_position).ToHandleChecked();
    261   }
    262 
    263   int GetSmiValueField(int field_position) {
    264     Handle<Object> res = GetField(field_position);
    265     return Handle<Smi>::cast(res)->value();
    266   }
    267 
    268  private:
    269   Handle<JSArray> array_;
    270 };
    271 
    272 
    273 // Represents some function compilation details. This structure will be used
    274 // from JavaScript. It contains Code object, which is kept wrapped
    275 // into a BlindReference for sanitizing reasons.
    276 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
    277  public:
    278   explicit FunctionInfoWrapper(Handle<JSArray> array)
    279       : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
    280   }
    281 
    282   void SetInitialProperties(Handle<String> name, int start_position,
    283                             int end_position, int param_num, int literal_count,
    284                             int parent_index);
    285 
    286   void SetFunctionCode(Handle<Code> function_code,
    287                        Handle<HeapObject> code_scope_info);
    288 
    289   void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
    290     this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
    291   }
    292 
    293   void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
    294 
    295   int GetLiteralCount() {
    296     return this->GetSmiValueField(kLiteralNumOffset_);
    297   }
    298 
    299   int GetParentIndex() {
    300     return this->GetSmiValueField(kParentIndexOffset_);
    301   }
    302 
    303   Handle<Code> GetFunctionCode();
    304 
    305   MaybeHandle<TypeFeedbackVector> GetFeedbackVector();
    306 
    307   Handle<Object> GetCodeScopeInfo();
    308 
    309   int GetStartPosition() {
    310     return this->GetSmiValueField(kStartPositionOffset_);
    311   }
    312 
    313   int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
    314 
    315  private:
    316   static const int kFunctionNameOffset_ = 0;
    317   static const int kStartPositionOffset_ = 1;
    318   static const int kEndPositionOffset_ = 2;
    319   static const int kParamNumOffset_ = 3;
    320   static const int kCodeOffset_ = 4;
    321   static const int kCodeScopeInfoOffset_ = 5;
    322   static const int kFunctionScopeInfoOffset_ = 6;
    323   static const int kParentIndexOffset_ = 7;
    324   static const int kSharedFunctionInfoOffset_ = 8;
    325   static const int kLiteralNumOffset_ = 9;
    326   static const int kSize_ = 10;
    327 
    328   friend class JSArrayBasedStruct<FunctionInfoWrapper>;
    329 };
    330 
    331 
    332 // Wraps SharedFunctionInfo along with some of its fields for passing it
    333 // back to JavaScript. SharedFunctionInfo object itself is additionally
    334 // wrapped into BlindReference for sanitizing reasons.
    335 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
    336  public:
    337   static bool IsInstance(Handle<JSArray> array) {
    338     if (array->length() != Smi::FromInt(kSize_)) return false;
    339     Handle<Object> element(
    340         Object::GetElement(array->GetIsolate(),
    341                            array,
    342                            kSharedInfoOffset_).ToHandleChecked());
    343     if (!element->IsJSValue()) return false;
    344     return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
    345   }
    346 
    347   explicit SharedInfoWrapper(Handle<JSArray> array)
    348       : JSArrayBasedStruct<SharedInfoWrapper>(array) {
    349   }
    350 
    351   void SetProperties(Handle<String> name,
    352                      int start_position,
    353                      int end_position,
    354                      Handle<SharedFunctionInfo> info);
    355 
    356   Handle<SharedFunctionInfo> GetInfo();
    357 
    358  private:
    359   static const int kFunctionNameOffset_ = 0;
    360   static const int kStartPositionOffset_ = 1;
    361   static const int kEndPositionOffset_ = 2;
    362   static const int kSharedInfoOffset_ = 3;
    363   static const int kSize_ = 4;
    364 
    365   friend class JSArrayBasedStruct<SharedInfoWrapper>;
    366 };
    367 
    368 }  // namespace internal
    369 }  // namespace v8
    370 
    371 #endif /* V8_DEBUG_LIVEEDIT_H_ */
    372