• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
~LiveEditFunctionTracker()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:
~Input()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:
~Output()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:
Create(Isolate * isolate)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 
cast(Object * object)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 
JSArrayBasedStruct(Handle<JSArray> array)246   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
247   }
248 
GetJSArray()249   Handle<JSArray> GetJSArray() {
250     return array_;
251   }
252 
isolate()253   Isolate* isolate() const {
254     return array_->GetIsolate();
255   }
256 
257  protected:
SetField(int field_position,Handle<Object> value)258   void SetField(int field_position, Handle<Object> value) {
259     Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
260         .Assert();
261   }
262 
SetSmiValueField(int field_position,int value)263   void SetSmiValueField(int field_position, int value) {
264     SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
265   }
266 
GetField(int field_position)267   Handle<Object> GetField(int field_position) {
268     return JSReceiver::GetElement(isolate(), array_, field_position)
269         .ToHandleChecked();
270   }
271 
GetSmiValueField(int field_position)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:
FunctionInfoWrapper(Handle<JSArray> array)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 
SetFunctionScopeInfo(Handle<Object> scope_info_array)298   void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
299     this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
300   }
301 
302   void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
303 
GetLiteralCount()304   int GetLiteralCount() {
305     return this->GetSmiValueField(kLiteralNumOffset_);
306   }
307 
GetParentIndex()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 
GetStartPosition()318   int GetStartPosition() {
319     return this->GetSmiValueField(kStartPositionOffset_);
320   }
321 
GetEndPosition()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:
IsInstance(Handle<JSArray> array)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 
SharedInfoWrapper(Handle<JSArray> array)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