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/ast/ast-traversal-visitor.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.
     36 //
     37 // The primary interest of the Tracker is to record function scope structures
     38 // in order to analyze whether function code may be safely patched (with new
     39 // code successfully reading existing data from function scopes). The Tracker
     40 // also collects compiled function codes.
     41 class LiveEditFunctionTracker
     42     : public AstTraversalVisitor<LiveEditFunctionTracker> {
     43  public:
     44   // Traverses the entire AST, and records information about all
     45   // FunctionLiterals for further use by LiveEdit code patching. The collected
     46   // information is returned as a serialized array.
     47   static Handle<JSArray> Collect(FunctionLiteral* node, Handle<Script> script,
     48                                  Zone* zone, Isolate* isolate);
     49 
     50  protected:
     51   friend AstTraversalVisitor<LiveEditFunctionTracker>;
     52   void VisitFunctionLiteral(FunctionLiteral* node);
     53 
     54  private:
     55   LiveEditFunctionTracker(Handle<Script> script, Zone* zone, Isolate* isolate);
     56 
     57   void FunctionStarted(FunctionLiteral* fun);
     58   void FunctionDone(Handle<SharedFunctionInfo> shared, Scope* scope);
     59   Handle<Object> SerializeFunctionScope(Scope* scope);
     60 
     61   Handle<Script> script_;
     62   Zone* zone_;
     63   Isolate* isolate_;
     64 
     65   Handle<JSArray> result_;
     66   int len_;
     67   int current_parent_index_;
     68 
     69   DISALLOW_COPY_AND_ASSIGN(LiveEditFunctionTracker);
     70 };
     71 
     72 
     73 class LiveEdit : AllStatic {
     74  public:
     75   static void InitializeThreadLocal(Debug* debug);
     76 
     77   static bool SetAfterBreakTarget(Debug* debug);
     78 
     79   MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
     80       Handle<Script> script,
     81       Handle<String> source);
     82 
     83   static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
     84                                   Handle<JSArray> shared_info_array);
     85 
     86   static void FunctionSourceUpdated(Handle<JSArray> shared_info_array);
     87 
     88   // Updates script field in FunctionSharedInfo.
     89   static void SetFunctionScript(Handle<JSValue> function_wrapper,
     90                                 Handle<Object> script_handle);
     91 
     92   static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
     93                                      Handle<JSArray> position_change_array);
     94 
     95   // For a script updates its source field. If old_script_name is provided
     96   // (i.e. is a String), also creates a copy of the script with its original
     97   // source and sends notification to debugger.
     98   static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
     99                                            Handle<String> new_source,
    100                                            Handle<Object> old_script_name);
    101 
    102   // In a code of a parent function replaces original function as embedded
    103   // object with a substitution one.
    104   static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
    105                                          Handle<JSValue> orig_function_shared,
    106                                          Handle<JSValue> subst_function_shared);
    107 
    108   // Find open generator activations, and set corresponding "result" elements to
    109   // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
    110   static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
    111                                    Handle<FixedArray> result, int len);
    112 
    113   // Checks listed functions on stack and return array with corresponding
    114   // FunctionPatchabilityStatus statuses; extra array element may
    115   // contain general error message. Modifies the current stack and
    116   // has restart the lowest found frames and drops all other frames above
    117   // if possible and if do_drop is true.
    118   static Handle<JSArray> CheckAndDropActivations(
    119       Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
    120       bool do_drop);
    121 
    122   // Restarts the call frame and completely drops all frames above it.
    123   // Return error message or NULL.
    124   static const char* RestartFrame(JavaScriptFrame* frame);
    125 
    126   // A copy of this is in liveedit.js.
    127   enum FunctionPatchabilityStatus {
    128     FUNCTION_AVAILABLE_FOR_PATCH = 1,
    129     FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
    130     FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
    131     FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
    132     FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
    133     FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
    134     FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7,
    135     FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8
    136   };
    137 
    138   // Compares 2 strings line-by-line, then token-wise and returns diff in form
    139   // of array of triplets (pos1, pos1_end, pos2_end) describing list
    140   // of diff chunks.
    141   static Handle<JSArray> CompareStrings(Handle<String> s1,
    142                                         Handle<String> s2);
    143 
    144   // Architecture-specific constant.
    145   static const bool kFrameDropperSupported;
    146 
    147   /**
    148    * Defines layout of a stack frame that supports padding. This is a regular
    149    * internal frame that has a flexible stack structure. LiveEdit can shift
    150    * its lower part up the stack, taking up the 'padding' space when additional
    151    * stack memory is required.
    152    * Such frame is expected immediately above the topmost JavaScript frame.
    153    *
    154    * Stack Layout:
    155    *   --- Top
    156    *   LiveEdit routine frames
    157    *   ---
    158    *   C frames of debug handler
    159    *   ---
    160    *   ...
    161    *   ---
    162    *      An internal frame that has n padding words:
    163    *      - any number of words as needed by code -- upper part of frame
    164    *      - padding size: a Smi storing n -- current size of padding
    165    *      - padding: n words filled with kPaddingValue in form of Smi
    166    *      - 3 context/type words of a regular InternalFrame
    167    *      - fp
    168    *   ---
    169    *      Topmost JavaScript frame
    170    *   ---
    171    *   ...
    172    *   --- Bottom
    173    */
    174   // A number of words that should be reserved on stack for the LiveEdit use.
    175   // Stored on stack in form of Smi.
    176   static const int kFramePaddingInitialSize = 1;
    177   // A value that padding words are filled with (in form of Smi). Going
    178   // bottom-top, the first word not having this value is a counter word.
    179   static const int kFramePaddingValue = kFramePaddingInitialSize + 1;
    180 };
    181 
    182 
    183 // A general-purpose comparator between 2 arrays.
    184 class Comparator {
    185  public:
    186   // Holds 2 arrays of some elements allowing to compare any pair of
    187   // element from the first array and element from the second array.
    188   class Input {
    189    public:
    190     virtual int GetLength1() = 0;
    191     virtual int GetLength2() = 0;
    192     virtual bool Equals(int index1, int index2) = 0;
    193 
    194    protected:
    195     virtual ~Input() {}
    196   };
    197 
    198   // Receives compare result as a series of chunks.
    199   class Output {
    200    public:
    201     // Puts another chunk in result list. Note that technically speaking
    202     // only 3 arguments actually needed with 4th being derivable.
    203     virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
    204 
    205    protected:
    206     virtual ~Output() {}
    207   };
    208 
    209   // Finds the difference between 2 arrays of elements.
    210   static void CalculateDifference(Input* input,
    211                                   Output* result_writer);
    212 };
    213 
    214 
    215 
    216 // Simple helper class that creates more or less typed structures over
    217 // JSArray object. This is an adhoc method of passing structures from C++
    218 // to JavaScript.
    219 template<typename S>
    220 class JSArrayBasedStruct {
    221  public:
    222   static S Create(Isolate* isolate) {
    223     Factory* factory = isolate->factory();
    224     Handle<JSArray> array = factory->NewJSArray(S::kSize_);
    225     return S(array);
    226   }
    227 
    228   static S cast(Object* object) {
    229     JSArray* array = JSArray::cast(object);
    230     Handle<JSArray> array_handle(array);
    231     return S(array_handle);
    232   }
    233 
    234   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
    235   }
    236 
    237   Handle<JSArray> GetJSArray() {
    238     return array_;
    239   }
    240 
    241   Isolate* isolate() const {
    242     return array_->GetIsolate();
    243   }
    244 
    245  protected:
    246   void SetField(int field_position, Handle<Object> value) {
    247     Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
    248         .Assert();
    249   }
    250 
    251   void SetSmiValueField(int field_position, int value) {
    252     SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
    253   }
    254 
    255   Handle<Object> GetField(int field_position) {
    256     return JSReceiver::GetElement(isolate(), array_, field_position)
    257         .ToHandleChecked();
    258   }
    259 
    260   int GetSmiValueField(int field_position) {
    261     Handle<Object> res = GetField(field_position);
    262     return Handle<Smi>::cast(res)->value();
    263   }
    264 
    265  private:
    266   Handle<JSArray> array_;
    267 };
    268 
    269 
    270 // Represents some function compilation details. This structure will be used
    271 // from JavaScript. It contains Code object, which is kept wrapped
    272 // into a BlindReference for sanitizing reasons.
    273 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
    274  public:
    275   explicit FunctionInfoWrapper(Handle<JSArray> array)
    276       : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
    277   }
    278 
    279   void SetInitialProperties(Handle<String> name, int start_position,
    280                             int end_position, int param_num, int literal_count,
    281                             int parent_index);
    282 
    283   void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
    284     this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
    285   }
    286 
    287   void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
    288 
    289   Handle<SharedFunctionInfo> GetSharedFunctionInfo();
    290 
    291   int GetLiteralCount() {
    292     return this->GetSmiValueField(kLiteralNumOffset_);
    293   }
    294 
    295   int GetParentIndex() {
    296     return this->GetSmiValueField(kParentIndexOffset_);
    297   }
    298 
    299   int GetStartPosition() {
    300     return this->GetSmiValueField(kStartPositionOffset_);
    301   }
    302 
    303   int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
    304 
    305  private:
    306   static const int kFunctionNameOffset_ = 0;
    307   static const int kStartPositionOffset_ = 1;
    308   static const int kEndPositionOffset_ = 2;
    309   static const int kParamNumOffset_ = 3;
    310   static const int kFunctionScopeInfoOffset_ = 4;
    311   static const int kParentIndexOffset_ = 5;
    312   static const int kSharedFunctionInfoOffset_ = 6;
    313   static const int kLiteralNumOffset_ = 7;
    314   static const int kSize_ = 8;
    315 
    316   friend class JSArrayBasedStruct<FunctionInfoWrapper>;
    317 };
    318 
    319 
    320 // Wraps SharedFunctionInfo along with some of its fields for passing it
    321 // back to JavaScript. SharedFunctionInfo object itself is additionally
    322 // wrapped into BlindReference for sanitizing reasons.
    323 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
    324  public:
    325   static bool IsInstance(Handle<JSArray> array) {
    326     if (array->length() != Smi::FromInt(kSize_)) return false;
    327     Handle<Object> element(
    328         JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
    329             .ToHandleChecked());
    330     if (!element->IsJSValue()) return false;
    331     return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
    332   }
    333 
    334   explicit SharedInfoWrapper(Handle<JSArray> array)
    335       : JSArrayBasedStruct<SharedInfoWrapper>(array) {
    336   }
    337 
    338   void SetProperties(Handle<String> name,
    339                      int start_position,
    340                      int end_position,
    341                      Handle<SharedFunctionInfo> info);
    342 
    343   Handle<SharedFunctionInfo> GetInfo();
    344 
    345  private:
    346   static const int kFunctionNameOffset_ = 0;
    347   static const int kStartPositionOffset_ = 1;
    348   static const int kEndPositionOffset_ = 2;
    349   static const int kSharedInfoOffset_ = 3;
    350   static const int kSize_ = 4;
    351 
    352   friend class JSArrayBasedStruct<SharedInfoWrapper>;
    353 };
    354 
    355 }  // namespace internal
    356 }  // namespace v8
    357 
    358 #endif /* V8_DEBUG_LIVEEDIT_H_ */
    359