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 : class JavaScriptFrame;
35 :
36 : // This class collects some specific information on structure of functions
37 : // in a particular script.
38 : //
39 : // The primary interest of the Tracker is to record function scope structures
40 : // in order to analyze whether function code may be safely patched (with new
41 : // code successfully reading existing data from function scopes). The Tracker
42 : // also collects compiled function codes.
43 : class LiveEditFunctionTracker
44 : : public AstTraversalVisitor<LiveEditFunctionTracker> {
45 : public:
46 : // Traverses the entire AST, and records information about all
47 : // FunctionLiterals for further use by LiveEdit code patching. The collected
48 : // information is returned as a serialized array.
49 : static Handle<JSArray> Collect(FunctionLiteral* node, Handle<Script> script,
50 : Zone* zone, Isolate* isolate);
51 :
52 : protected:
53 : friend AstTraversalVisitor<LiveEditFunctionTracker>;
54 : void VisitFunctionLiteral(FunctionLiteral* node);
55 :
56 : private:
57 : LiveEditFunctionTracker(Handle<Script> script, Zone* zone, Isolate* isolate);
58 :
59 : void FunctionStarted(FunctionLiteral* fun);
60 : void FunctionDone(Handle<SharedFunctionInfo> shared, Scope* scope);
61 : Handle<Object> SerializeFunctionScope(Scope* scope);
62 :
63 : Handle<Script> script_;
64 : Zone* zone_;
65 : Isolate* isolate_;
66 :
67 : Handle<JSArray> result_;
68 : int len_;
69 : int current_parent_index_;
70 :
71 : DISALLOW_COPY_AND_ASSIGN(LiveEditFunctionTracker);
72 : };
73 :
74 :
75 : class LiveEdit : AllStatic {
76 : public:
77 : static void InitializeThreadLocal(Debug* debug);
78 :
79 : MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
80 : Handle<Script> script,
81 : Handle<String> source);
82 :
83 : static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
84 : Handle<JSArray> shared_info_array);
85 :
86 : static void FixupScript(Handle<Script> script, int max_function_literal_id);
87 :
88 : static void FunctionSourceUpdated(Handle<JSArray> shared_info_array,
89 : int new_function_literal_id);
90 :
91 : // Updates script field in FunctionSharedInfo.
92 : static void SetFunctionScript(Handle<JSValue> function_wrapper,
93 : Handle<Object> script_handle);
94 :
95 : static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
96 : Handle<JSArray> position_change_array);
97 :
98 : // For a script updates its source field. If old_script_name is provided
99 : // (i.e. is a String), also creates a copy of the script with its original
100 : // source and sends notification to debugger.
101 : static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
102 : Handle<String> new_source,
103 : Handle<Object> old_script_name);
104 :
105 : // In a code of a parent function replaces original function as embedded
106 : // object with a substitution one.
107 : static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
108 : Handle<JSValue> orig_function_shared,
109 : Handle<JSValue> subst_function_shared);
110 :
111 : // Find open generator activations, and set corresponding "result" elements to
112 : // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
113 : static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
114 : Handle<FixedArray> result, int len);
115 :
116 : // Checks listed functions on stack and return array with corresponding
117 : // FunctionPatchabilityStatus statuses; extra array element may
118 : // contain general error message. Modifies the current stack and
119 : // has restart the lowest found frames and drops all other frames above
120 : // if possible and if do_drop is true.
121 : static Handle<JSArray> CheckAndDropActivations(
122 : Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
123 : bool do_drop);
124 :
125 : // Restarts the call frame and completely drops all frames above it.
126 : // Return error message or nullptr.
127 : static const char* RestartFrame(JavaScriptFrame* frame);
128 :
129 : // A copy of this is in liveedit.js.
130 : enum FunctionPatchabilityStatus {
131 : FUNCTION_AVAILABLE_FOR_PATCH = 1,
132 : FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
133 : FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
134 : FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
135 : FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
136 : FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
137 : FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7,
138 : FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8
139 : };
140 :
141 : // Compares 2 strings line-by-line, then token-wise and returns diff in form
142 : // of array of triplets (pos1, pos1_end, pos2_end) describing list
143 : // of diff chunks.
144 : static Handle<JSArray> CompareStrings(Handle<String> s1,
145 : Handle<String> s2);
146 :
147 : // Architecture-specific constant.
148 : static const bool kFrameDropperSupported;
149 : };
150 :
151 :
152 : // A general-purpose comparator between 2 arrays.
153 : class Comparator {
154 : public:
155 : // Holds 2 arrays of some elements allowing to compare any pair of
156 : // element from the first array and element from the second array.
157 1400 : class Input {
158 : public:
159 : virtual int GetLength1() = 0;
160 : virtual int GetLength2() = 0;
161 : virtual bool Equals(int index1, int index2) = 0;
162 :
163 : protected:
164 600 : virtual ~Input() {}
165 : };
166 :
167 : // Receives compare result as a series of chunks.
168 1400 : class Output {
169 : public:
170 : // Puts another chunk in result list. Note that technically speaking
171 : // only 3 arguments actually needed with 4th being derivable.
172 : virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
173 :
174 : protected:
175 768 : virtual ~Output() {}
176 : };
177 :
178 : // Finds the difference between 2 arrays of elements.
179 : static void CalculateDifference(Input* input,
180 : Output* result_writer);
181 : };
182 :
183 :
184 :
185 : // Simple helper class that creates more or less typed structures over
186 : // JSArray object. This is an adhoc method of passing structures from C++
187 : // to JavaScript.
188 : template<typename S>
189 : class JSArrayBasedStruct {
190 : public:
191 1758 : static S Create(Isolate* isolate) {
192 : Factory* factory = isolate->factory();
193 5292 : Handle<JSArray> array = factory->NewJSArray(S::kSize_);
194 1758 : return S(array);
195 : }
196 :
197 3534 : static S cast(Object* object) {
198 : JSArray* array = JSArray::cast(object);
199 : Handle<JSArray> array_handle(array);
200 3534 : return S(array_handle);
201 : }
202 :
203 2510 : explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
204 : }
205 :
206 1758 : Handle<JSArray> GetJSArray() {
207 5292 : return array_;
208 : }
209 :
210 : Isolate* isolate() const {
211 : return array_->GetIsolate();
212 : }
213 :
214 : protected:
215 35304 : void SetField(int field_position, Handle<Object> value) {
216 35304 : Object::SetElement(isolate(), array_, field_position, value,
217 70608 : LanguageMode::kSloppy)
218 : .Assert();
219 35304 : }
220 :
221 21186 : void SetSmiValueField(int field_position, int value) {
222 21186 : SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
223 21186 : }
224 :
225 7478 : Handle<Object> GetField(int field_position) {
226 14956 : return JSReceiver::GetElement(isolate(), array_, field_position)
227 22434 : .ToHandleChecked();
228 : }
229 :
230 : int GetSmiValueField(int field_position) {
231 4490 : Handle<Object> res = GetField(field_position);
232 : return Handle<Smi>::cast(res)->value();
233 : }
234 :
235 : private:
236 : Handle<JSArray> array_;
237 : };
238 :
239 :
240 : // Represents some function compilation details. This structure will be used
241 : // from JavaScript. It contains Code object, which is kept wrapped
242 : // into a BlindReference for sanitizing reasons.
243 : class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
244 : public:
245 : explicit FunctionInfoWrapper(Handle<JSArray> array)
246 : : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
247 : }
248 :
249 : void SetInitialProperties(Handle<String> name, int start_position,
250 : int end_position, int param_num, int parent_index,
251 : int function_literal_id);
252 :
253 : void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
254 3534 : this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
255 : }
256 :
257 : void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
258 :
259 : Handle<SharedFunctionInfo> GetSharedFunctionInfo();
260 :
261 : int GetParentIndex() {
262 : return this->GetSmiValueField(kParentIndexOffset_);
263 : }
264 :
265 : int GetStartPosition() {
266 : return this->GetSmiValueField(kStartPositionOffset_);
267 : }
268 :
269 : int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
270 :
271 : private:
272 : static const int kFunctionNameOffset_ = 0;
273 : static const int kStartPositionOffset_ = 1;
274 : static const int kEndPositionOffset_ = 2;
275 : static const int kParamNumOffset_ = 3;
276 : static const int kFunctionScopeInfoOffset_ = 4;
277 : static const int kParentIndexOffset_ = 5;
278 : static const int kSharedFunctionInfoOffset_ = 6;
279 : static const int kFunctionLiteralIdOffset_ = 7;
280 : static const int kSize_ = 8;
281 :
282 : friend class JSArrayBasedStruct<FunctionInfoWrapper>;
283 : };
284 :
285 :
286 : // Wraps SharedFunctionInfo along with some of its fields for passing it
287 : // back to JavaScript. SharedFunctionInfo object itself is additionally
288 : // wrapped into BlindReference for sanitizing reasons.
289 : class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
290 : public:
291 2510 : static bool IsInstance(Handle<JSArray> array) {
292 2510 : if (array->length() != Smi::FromInt(kSize_)) return false;
293 : Handle<Object> element(
294 2510 : JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
295 5020 : .ToHandleChecked());
296 2510 : if (!element->IsJSValue()) return false;
297 : return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
298 : }
299 :
300 : explicit SharedInfoWrapper(Handle<JSArray> array)
301 : : JSArrayBasedStruct<SharedInfoWrapper>(array) {
302 : }
303 :
304 : void SetProperties(Handle<String> name,
305 : int start_position,
306 : int end_position,
307 : Handle<SharedFunctionInfo> info);
308 :
309 : Handle<SharedFunctionInfo> GetInfo();
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 kSharedInfoOffset_ = 3;
316 : static const int kSize_ = 4;
317 :
318 : friend class JSArrayBasedStruct<SharedInfoWrapper>;
319 : };
320 :
321 : } // namespace internal
322 : } // namespace v8
323 :
324 : #endif /* V8_DEBUG_LIVEEDIT_H_ */
|