blob: 32328d9da7d9cd3dae2a22b80db725a9518e41dd [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
Ben Murdochc5610432016-08-08 18:44:38 +010035// in a particular script.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000036//
37// The primary interest of the Tracker is to record function scope structures
Ben Murdochc5610432016-08-08 18:44:38 +010038// in order to analyze whether function code may be safely patched (with new
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000039// code successfully reading existing data from function scopes). The Tracker
40// also collects compiled function codes.
Ben Murdochc5610432016-08-08 18:44:38 +010041class LiveEditFunctionTracker : public AstTraversalVisitor {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000042 public:
Ben Murdochc5610432016-08-08 18:44:38 +010043 // 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);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000048
Ben Murdochc5610432016-08-08 18:44:38 +010049 virtual ~LiveEditFunctionTracker() {}
50 void VisitFunctionLiteral(FunctionLiteral* node) override;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000051
52 private:
Ben Murdochc5610432016-08-08 18:44:38 +010053 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_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000061 Isolate* isolate_;
Ben Murdochc5610432016-08-08 18:44:38 +010062
63 Handle<JSArray> result_;
64 int len_;
65 int current_parent_index_;
66
67 DISALLOW_COPY_AND_ASSIGN(LiveEditFunctionTracker);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000068};
69
70
71class 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 */
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000186 // 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.
196class 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:
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:
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.
231template<typename S>
232class JSArrayBasedStruct {
233 public:
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
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
246 explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
247 }
248
249 Handle<JSArray> GetJSArray() {
250 return array_;
251 }
252
253 Isolate* isolate() const {
254 return array_->GetIsolate();
255 }
256
257 protected:
258 void SetField(int field_position, Handle<Object> value) {
259 Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
260 .Assert();
261 }
262
263 void SetSmiValueField(int field_position, int value) {
264 SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
265 }
266
267 Handle<Object> GetField(int field_position) {
Ben Murdochda12d292016-06-02 14:46:10 +0100268 return JSReceiver::GetElement(isolate(), array_, field_position)
269 .ToHandleChecked();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000270 }
271
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.
285class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
286 public:
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
Ben Murdoch61f157c2016-09-16 13:49:30 +0100295 void SetFunctionCode(Handle<AbstractCode> function_code,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000296 Handle<HeapObject> code_scope_info);
297
298 void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
299 this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
300 }
301
302 void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
303
304 int GetLiteralCount() {
305 return this->GetSmiValueField(kLiteralNumOffset_);
306 }
307
308 int GetParentIndex() {
309 return this->GetSmiValueField(kParentIndexOffset_);
310 }
311
Ben Murdoch61f157c2016-09-16 13:49:30 +0100312 Handle<AbstractCode> GetFunctionCode();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000313
Ben Murdoch61f157c2016-09-16 13:49:30 +0100314 MaybeHandle<TypeFeedbackMetadata> GetFeedbackMetadata();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000315
316 Handle<Object> GetCodeScopeInfo();
317
318 int GetStartPosition() {
319 return this->GetSmiValueField(kStartPositionOffset_);
320 }
321
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.
344class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
345 public:
346 static bool IsInstance(Handle<JSArray> array) {
347 if (array->length() != Smi::FromInt(kSize_)) return false;
348 Handle<Object> element(
Ben Murdochda12d292016-06-02 14:46:10 +0100349 JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
350 .ToHandleChecked());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000351 if (!element->IsJSValue()) return false;
352 return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
353 }
354
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_ */