OLD | NEW |
| (Empty) |
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: | |
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: | |
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: | |
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 | |
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 | |
239 explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) { | |
240 } | |
241 | |
242 Handle<JSArray> GetJSArray() { | |
243 return array_; | |
244 } | |
245 | |
246 Isolate* isolate() const { | |
247 return array_->GetIsolate(); | |
248 } | |
249 | |
250 protected: | |
251 void SetField(int field_position, Handle<Object> value) { | |
252 Object::SetElement(isolate(), array_, field_position, value, SLOPPY) | |
253 .Assert(); | |
254 } | |
255 | |
256 void SetSmiValueField(int field_position, int value) { | |
257 SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate())); | |
258 } | |
259 | |
260 Handle<Object> GetField(int field_position) { | |
261 return Object::GetElement( | |
262 isolate(), array_, field_position).ToHandleChecked(); | |
263 } | |
264 | |
265 int GetSmiValueField(int field_position) { | |
266 Handle<Object> res = GetField(field_position); | |
267 return Handle<Smi>::cast(res)->value(); | |
268 } | |
269 | |
270 private: | |
271 Handle<JSArray> array_; | |
272 }; | |
273 | |
274 | |
275 // Represents some function compilation details. This structure will be used | |
276 // from JavaScript. It contains Code object, which is kept wrapped | |
277 // into a BlindReference for sanitizing reasons. | |
278 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> { | |
279 public: | |
280 explicit FunctionInfoWrapper(Handle<JSArray> array) | |
281 : JSArrayBasedStruct<FunctionInfoWrapper>(array) { | |
282 } | |
283 | |
284 void SetInitialProperties(Handle<String> name, int start_position, | |
285 int end_position, int param_num, int literal_count, | |
286 int parent_index); | |
287 | |
288 void SetFunctionCode(Handle<Code> function_code, | |
289 Handle<HeapObject> code_scope_info); | |
290 | |
291 void SetFunctionScopeInfo(Handle<Object> scope_info_array) { | |
292 this->SetField(kFunctionScopeInfoOffset_, scope_info_array); | |
293 } | |
294 | |
295 void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info); | |
296 | |
297 int GetLiteralCount() { | |
298 return this->GetSmiValueField(kLiteralNumOffset_); | |
299 } | |
300 | |
301 int GetParentIndex() { | |
302 return this->GetSmiValueField(kParentIndexOffset_); | |
303 } | |
304 | |
305 Handle<Code> GetFunctionCode(); | |
306 | |
307 MaybeHandle<TypeFeedbackVector> GetFeedbackVector(); | |
308 | |
309 Handle<Object> GetCodeScopeInfo(); | |
310 | |
311 int GetStartPosition() { | |
312 return this->GetSmiValueField(kStartPositionOffset_); | |
313 } | |
314 | |
315 int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); } | |
316 | |
317 private: | |
318 static const int kFunctionNameOffset_ = 0; | |
319 static const int kStartPositionOffset_ = 1; | |
320 static const int kEndPositionOffset_ = 2; | |
321 static const int kParamNumOffset_ = 3; | |
322 static const int kCodeOffset_ = 4; | |
323 static const int kCodeScopeInfoOffset_ = 5; | |
324 static const int kFunctionScopeInfoOffset_ = 6; | |
325 static const int kParentIndexOffset_ = 7; | |
326 static const int kSharedFunctionInfoOffset_ = 8; | |
327 static const int kLiteralNumOffset_ = 9; | |
328 static const int kSize_ = 10; | |
329 | |
330 friend class JSArrayBasedStruct<FunctionInfoWrapper>; | |
331 }; | |
332 | |
333 | |
334 // Wraps SharedFunctionInfo along with some of its fields for passing it | |
335 // back to JavaScript. SharedFunctionInfo object itself is additionally | |
336 // wrapped into BlindReference for sanitizing reasons. | |
337 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> { | |
338 public: | |
339 static bool IsInstance(Handle<JSArray> array) { | |
340 if (array->length() != Smi::FromInt(kSize_)) return false; | |
341 Handle<Object> element( | |
342 Object::GetElement(array->GetIsolate(), | |
343 array, | |
344 kSharedInfoOffset_).ToHandleChecked()); | |
345 if (!element->IsJSValue()) return false; | |
346 return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo(); | |
347 } | |
348 | |
349 explicit SharedInfoWrapper(Handle<JSArray> array) | |
350 : JSArrayBasedStruct<SharedInfoWrapper>(array) { | |
351 } | |
352 | |
353 void SetProperties(Handle<String> name, | |
354 int start_position, | |
355 int end_position, | |
356 Handle<SharedFunctionInfo> info); | |
357 | |
358 Handle<SharedFunctionInfo> GetInfo(); | |
359 | |
360 private: | |
361 static const int kFunctionNameOffset_ = 0; | |
362 static const int kStartPositionOffset_ = 1; | |
363 static const int kEndPositionOffset_ = 2; | |
364 static const int kSharedInfoOffset_ = 3; | |
365 static const int kSize_ = 4; | |
366 | |
367 friend class JSArrayBasedStruct<SharedInfoWrapper>; | |
368 }; | |
369 | |
370 } } // namespace v8::internal | |
371 | |
372 #endif /* V*_LIVEEDIT_H_ */ | |
OLD | NEW |