• 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/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:
~Input()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:
~Output()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:
Create(Isolate * isolate)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 
cast(Object * object)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 
JSArrayBasedStruct(Handle<JSArray> array)201   explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
202   }
203 
GetJSArray()204   Handle<JSArray> GetJSArray() {
205     return array_;
206   }
207 
isolate()208   Isolate* isolate() const {
209     return array_->GetIsolate();
210   }
211 
212  protected:
SetField(int field_position,Handle<Object> value)213   void SetField(int field_position, Handle<Object> value) {
214     Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
215         .Assert();
216   }
217 
SetSmiValueField(int field_position,int value)218   void SetSmiValueField(int field_position, int value) {
219     SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
220   }
221 
GetField(int field_position)222   Handle<Object> GetField(int field_position) {
223     return JSReceiver::GetElement(isolate(), array_, field_position)
224         .ToHandleChecked();
225   }
226 
GetSmiValueField(int field_position)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:
FunctionInfoWrapper(Handle<JSArray> array)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 
SetFunctionScopeInfo(Handle<Object> scope_info_array)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 
GetParentIndex()258   int GetParentIndex() {
259     return this->GetSmiValueField(kParentIndexOffset_);
260   }
261 
GetStartPosition()262   int GetStartPosition() {
263     return this->GetSmiValueField(kStartPositionOffset_);
264   }
265 
GetEndPosition()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:
IsInstance(Handle<JSArray> array)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 
SharedInfoWrapper(Handle<JSArray> array)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