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