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/compiler.h"
30
31namespace v8 {
32namespace 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.
41class LiveEditFunctionTracker : public AstTraversalVisitor {
42 public:
43  // 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);
48
49  virtual ~LiveEditFunctionTracker() {}
50  void VisitFunctionLiteral(FunctionLiteral* node) override;
51
52 private:
53  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_;
61  Isolate* isolate_;
62
63  Handle<JSArray> result_;
64  int len_;
65  int current_parent_index_;
66
67  DISALLOW_COPY_AND_ASSIGN(LiveEditFunctionTracker);
68};
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   */
186  // 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) {
268    return JSReceiver::GetElement(isolate(), array_, field_position)
269        .ToHandleChecked();
270  }
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
295  void SetFunctionCode(Handle<AbstractCode> function_code,
296                       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
312  Handle<AbstractCode> GetFunctionCode();
313
314  MaybeHandle<TypeFeedbackMetadata> GetFeedbackMetadata();
315
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(
349        JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
350            .ToHandleChecked());
351    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_ */
380