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