Line data Source code
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 :
31 : namespace v8 {
32 : namespace 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.
41 : class 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 :
73 : class 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.
151 : class 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 1792 : 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 872 : virtual ~Input() {}
163 : };
164 :
165 : // Receives compare result as a series of chunks.
166 1792 : 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 872 : 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.
186 : template<typename S>
187 : class JSArrayBasedStruct {
188 : public:
189 2420 : static S Create(Isolate* isolate) {
190 : Factory* factory = isolate->factory();
191 7300 : Handle<JSArray> array = factory->NewJSArray(S::kSize_);
192 2420 : return S(array);
193 : }
194 :
195 4880 : static S cast(Object* object) {
196 : JSArray* array = JSArray::cast(object);
197 : Handle<JSArray> array_handle(array);
198 4880 : return S(array_handle);
199 : }
200 :
201 3382 : explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
202 : }
203 :
204 2420 : Handle<JSArray> GetJSArray() {
205 7300 : return array_;
206 : }
207 :
208 : Isolate* isolate() const {
209 : return array_->GetIsolate();
210 : }
211 :
212 : protected:
213 48720 : void SetField(int field_position, Handle<Object> value) {
214 97440 : Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
215 : .Assert();
216 48720 : }
217 :
218 29240 : void SetSmiValueField(int field_position, int value) {
219 29240 : SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
220 29240 : }
221 :
222 10272 : Handle<Object> GetField(int field_position) {
223 : return JSReceiver::GetElement(isolate(), array_, field_position)
224 30816 : .ToHandleChecked();
225 : }
226 :
227 : int GetSmiValueField(int field_position) {
228 6220 : 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.
240 : class 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 4880 : 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.
286 : class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
287 : public:
288 3382 : static bool IsInstance(Handle<JSArray> array) {
289 3382 : if (array->length() != Smi::FromInt(kSize_)) return false;
290 : Handle<Object> element(
291 : JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
292 6764 : .ToHandleChecked());
293 3382 : 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_ */
|