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
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
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
73class 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.
151class 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:
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:
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.
186template<typename S>
187class JSArrayBasedStruct {
188 public:
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
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
201  explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
202  }
203
204  Handle<JSArray> GetJSArray() {
205    return array_;
206  }
207
208  Isolate* isolate() const {
209    return array_->GetIsolate();
210  }
211
212 protected:
213  void SetField(int field_position, Handle<Object> value) {
214    Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
215        .Assert();
216  }
217
218  void SetSmiValueField(int field_position, int value) {
219    SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
220  }
221
222  Handle<Object> GetField(int field_position) {
223    return JSReceiver::GetElement(isolate(), array_, field_position)
224        .ToHandleChecked();
225  }
226
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.
240class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
241 public:
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
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
258  int GetParentIndex() {
259    return this->GetSmiValueField(kParentIndexOffset_);
260  }
261
262  int GetStartPosition() {
263    return this->GetSmiValueField(kStartPositionOffset_);
264  }
265
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.
286class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
287 public:
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
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