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   MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
     78       Handle<Script> script,
     79       Handle<String> source);
     80 
     81   static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
     82                                   Handle<JSArray> shared_info_array);
     83 
     84   static void FixupScript(Handle<Script> script, int max_function_literal_id);
     85 
     86   static void FunctionSourceUpdated(Handle<JSArray> shared_info_array,
     87                                     int new_function_literal_id);
     88 
     89   // Updates script field in FunctionSharedInfo.
     90   static void SetFunctionScript(Handle<JSValue> function_wrapper,
     91                                 Handle<Object> script_handle);
     92 
     93   static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
     94                                      Handle<JSArray> position_change_array);
     95 
     96   // For a script updates its source field. If old_script_name is provided
     97   // (i.e. is a String), also creates a copy of the script with its original
     98   // source and sends notification to debugger.
     99   static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
    100                                            Handle<String> new_source,
    101                                            Handle<Object> old_script_name);
    102 
    103   // In a code of a parent function replaces original function as embedded
    104   // object with a substitution one.
    105   static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
    106                                          Handle<JSValue> orig_function_shared,
    107                                          Handle<JSValue> subst_function_shared);
    108 
    109   // Find open generator activations, and set corresponding "result" elements to
    110   // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
    111   static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
    112                                    Handle<FixedArray> result, int len);
    113 
    114   // Checks listed functions on stack and return array with corresponding
    115   // FunctionPatchabilityStatus statuses; extra array element may
    116   // contain general error message. Modifies the current stack and
    117   // has restart the lowest found frames and drops all other frames above
    118   // if possible and if do_drop is true.
    119   static Handle<JSArray> CheckAndDropActivations(
    120       Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
    121       bool do_drop);
    122 
    123   // Restarts the call frame and completely drops all frames above it.
    124   // Return error message or NULL.
    125   static const char* RestartFrame(JavaScriptFrame* frame);
    126 
    127   // A copy of this is in liveedit.js.
    128   enum FunctionPatchabilityStatus {
    129     FUNCTION_AVAILABLE_FOR_PATCH = 1,
    130     FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
    131     FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
    132     FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
    133     FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
    134     FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
    135     FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7,
    136     FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8
    137   };
    138 
    139   // Compares 2 strings line-by-line, then token-wise and returns diff in form
    140   // of array of triplets (pos1, pos1_end, pos2_end) describing list
    141   // of diff chunks.
    142   static Handle<JSArray> CompareStrings(Handle<String> s1,
    143                                         Handle<String> s2);
    144 
    145   // Architecture-specific constant.
    146   static const bool kFrameDropperSupported;
    147 };
    148 
    149 
    150 // A general-purpose comparator between 2 arrays.
    151 class Comparator {
    152  public:
    153   // Holds 2 arrays of some elements allowing to compare any pair of
    154   // element from the first array and element from the second array.
    155   class Input {
    156    public:
    157     virtual int GetLength1() = 0;
    158     virtual int GetLength2() = 0;
    159     virtual bool Equals(int index1, int index2) = 0;
    160 
    161    protected:
    162     virtual ~Input() {}
    163   };
    164 
    165   // Receives compare result as a series of chunks.
    166   class Output {
    167    public:
    168     // Puts another chunk in result list. Note that technically speaking
    169     // only 3 arguments actually needed with 4th being derivable.
    170     virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
    171 
    172    protected:
    173     virtual ~Output() {}
    174   };
    175 
    176   // Finds the difference between 2 arrays of elements.
    177   static void CalculateDifference(Input* input,
    178                                   Output* result_writer);
    179 };
    180 
    181 
    182 
    183 // Simple helper class that creates more or less typed structures over
    184 // JSArray object. This is an adhoc method of passing structures from C++
    185 // to JavaScript.
    186 template<typename S>
    187 class JSArrayBasedStruct {
    188  public:
    189   static S Create(Isolate* isolate) {
    190     Factory* factory = isolate->factory();
    191     Handle<JSArray> array = factory->NewJSArray(S::kSize_);
    192     return S(array);
    193   }
    194 
    195   static S cast(Object* object) {
    196     JSArray* array = JSArray::cast(object);
    197     Handle<JSArray> array_handle(array);
    198     return S(array_handle);
    199   }
    200 
    201   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
    202   }
    203 
    204   Handle<JSArray> GetJSArray() {
    205     return array_;
    206   }
    207 
    208   Isolate* isolate() const {
    209     return array_->GetIsolate();
    210   }
    211 
    212  protected:
    213   void SetField(int field_position, Handle<Object> value) {
    214     Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
    215         .Assert();
    216   }
    217 
    218   void SetSmiValueField(int field_position, int value) {
    219     SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
    220   }
    221 
    222   Handle<Object> GetField(int field_position) {
    223     return JSReceiver::GetElement(isolate(), array_, field_position)
    224         .ToHandleChecked();
    225   }
    226 
    227   int GetSmiValueField(int field_position) {
    228     Handle<Object> res = GetField(field_position);
    229     return Handle<Smi>::cast(res)->value();
    230   }
    231 
    232  private:
    233   Handle<JSArray> array_;
    234 };
    235 
    236 
    237 // Represents some function compilation details. This structure will be used
    238 // from JavaScript. It contains Code object, which is kept wrapped
    239 // into a BlindReference for sanitizing reasons.
    240 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
    241  public:
    242   explicit FunctionInfoWrapper(Handle<JSArray> array)
    243       : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
    244   }
    245 
    246   void SetInitialProperties(Handle<String> name, int start_position,
    247                             int end_position, int param_num, int parent_index,
    248                             int function_literal_id);
    249 
    250   void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
    251     this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
    252   }
    253 
    254   void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
    255 
    256   Handle<SharedFunctionInfo> GetSharedFunctionInfo();
    257 
    258   int GetParentIndex() {
    259     return this->GetSmiValueField(kParentIndexOffset_);
    260   }
    261 
    262   int GetStartPosition() {
    263     return this->GetSmiValueField(kStartPositionOffset_);
    264   }
    265 
    266   int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
    267 
    268  private:
    269   static const int kFunctionNameOffset_ = 0;
    270   static const int kStartPositionOffset_ = 1;
    271   static const int kEndPositionOffset_ = 2;
    272   static const int kParamNumOffset_ = 3;
    273   static const int kFunctionScopeInfoOffset_ = 4;
    274   static const int kParentIndexOffset_ = 5;
    275   static const int kSharedFunctionInfoOffset_ = 6;
    276   static const int kFunctionLiteralIdOffset_ = 7;
    277   static const int kSize_ = 8;
    278 
    279   friend class JSArrayBasedStruct<FunctionInfoWrapper>;
    280 };
    281 
    282 
    283 // Wraps SharedFunctionInfo along with some of its fields for passing it
    284 // back to JavaScript. SharedFunctionInfo object itself is additionally
    285 // wrapped into BlindReference for sanitizing reasons.
    286 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
    287  public:
    288   static bool IsInstance(Handle<JSArray> array) {
    289     if (array->length() != Smi::FromInt(kSize_)) return false;
    290     Handle<Object> element(
    291         JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
    292             .ToHandleChecked());
    293     if (!element->IsJSValue()) return false;
    294     return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
    295   }
    296 
    297   explicit SharedInfoWrapper(Handle<JSArray> array)
    298       : JSArrayBasedStruct<SharedInfoWrapper>(array) {
    299   }
    300 
    301   void SetProperties(Handle<String> name,
    302                      int start_position,
    303                      int end_position,
    304                      Handle<SharedFunctionInfo> info);
    305 
    306   Handle<SharedFunctionInfo> GetInfo();
    307 
    308  private:
    309   static const int kFunctionNameOffset_ = 0;
    310   static const int kStartPositionOffset_ = 1;
    311   static const int kEndPositionOffset_ = 2;
    312   static const int kSharedInfoOffset_ = 3;
    313   static const int kSize_ = 4;
    314 
    315   friend class JSArrayBasedStruct<SharedInfoWrapper>;
    316 };
    317 
    318 }  // namespace internal
    319 }  // namespace v8
    320 
    321 #endif /* V8_DEBUG_LIVEEDIT_H_ */
    322