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:
~Input()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:
~Output()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:
Create(Isolate * isolate)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 
cast(Object * object)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 
JSArrayBasedStruct(Handle<JSArray> array)234   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
235   }
236 
GetJSArray()237   Handle<JSArray> GetJSArray() {
238     return array_;
239   }
240 
isolate()241   Isolate* isolate() const {
242     return array_->GetIsolate();
243   }
244 
245  protected:
SetField(int field_position,Handle<Object> value)246   void SetField(int field_position, Handle<Object> value) {
247     Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
248         .Assert();
249   }
250 
SetSmiValueField(int field_position,int value)251   void SetSmiValueField(int field_position, int value) {
252     SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
253   }
254 
GetField(int field_position)255   Handle<Object> GetField(int field_position) {
256     return JSReceiver::GetElement(isolate(), array_, field_position)
257         .ToHandleChecked();
258   }
259 
GetSmiValueField(int field_position)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:
FunctionInfoWrapper(Handle<JSArray> array)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 
SetFunctionScopeInfo(Handle<Object> scope_info_array)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 
GetLiteralCount()291   int GetLiteralCount() {
292     return this->GetSmiValueField(kLiteralNumOffset_);
293   }
294 
GetParentIndex()295   int GetParentIndex() {
296     return this->GetSmiValueField(kParentIndexOffset_);
297   }
298 
GetStartPosition()299   int GetStartPosition() {
300     return this->GetSmiValueField(kStartPositionOffset_);
301   }
302 
GetEndPosition()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:
IsInstance(Handle<JSArray> array)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 
SharedInfoWrapper(Handle<JSArray> array)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