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