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