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