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 31 namespace v8 { 32 namespace internal { 33 34 // This class collects some specific information on structure of functions 35 // in a particular script. It gets called from compiler all the time, but 36 // actually records any data only when liveedit operation is in process; 37 // in any other time this class is very cheap. 38 // 39 // The primary interest of the Tracker is to record function scope structures 40 // in order to analyze whether function code maybe 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: 45 explicit LiveEditFunctionTracker(Isolate* isolate, FunctionLiteral* fun); 46 ~LiveEditFunctionTracker(); 47 void RecordFunctionInfo(Handle<SharedFunctionInfo> info, 48 FunctionLiteral* lit, Zone* zone); 49 void RecordRootFunctionInfo(Handle<Code> code); 50 51 static bool IsActive(Isolate* isolate); 52 53 private: 54 Isolate* isolate_; 55 }; 56 57 58 class LiveEdit : AllStatic { 59 public: 60 // Describes how exactly a frame has been dropped from stack. 61 enum FrameDropMode { 62 // No frame has been dropped. 63 FRAMES_UNTOUCHED, 64 // The top JS frame had been calling debug break slot stub. Patch the 65 // address this stub jumps to in the end. 66 FRAME_DROPPED_IN_DEBUG_SLOT_CALL, 67 // The top JS frame had been calling some C++ function. The return address 68 // gets patched automatically. 69 FRAME_DROPPED_IN_DIRECT_CALL, 70 FRAME_DROPPED_IN_RETURN_CALL, 71 CURRENTLY_SET_MODE 72 }; 73 74 static void InitializeThreadLocal(Debug* debug); 75 76 static bool SetAfterBreakTarget(Debug* debug); 77 78 MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo( 79 Handle<Script> script, 80 Handle<String> source); 81 82 static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array, 83 Handle<JSArray> shared_info_array); 84 85 static void FunctionSourceUpdated(Handle<JSArray> shared_info_array); 86 87 // Updates script field in FunctionSharedInfo. 88 static void SetFunctionScript(Handle<JSValue> function_wrapper, 89 Handle<Object> script_handle); 90 91 static void PatchFunctionPositions(Handle<JSArray> shared_info_array, 92 Handle<JSArray> position_change_array); 93 94 // For a script updates its source field. If old_script_name is provided 95 // (i.e. is a String), also creates a copy of the script with its original 96 // source and sends notification to debugger. 97 static Handle<Object> ChangeScriptSource(Handle<Script> original_script, 98 Handle<String> new_source, 99 Handle<Object> old_script_name); 100 101 // In a code of a parent function replaces original function as embedded 102 // object with a substitution one. 103 static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared, 104 Handle<JSValue> orig_function_shared, 105 Handle<JSValue> subst_function_shared); 106 107 // Find open generator activations, and set corresponding "result" elements to 108 // FUNCTION_BLOCKED_ACTIVE_GENERATOR. 109 static bool FindActiveGenerators(Handle<FixedArray> shared_info_array, 110 Handle<FixedArray> result, int len); 111 112 // Checks listed functions on stack and return array with corresponding 113 // FunctionPatchabilityStatus statuses; extra array element may 114 // contain general error message. Modifies the current stack and 115 // has restart the lowest found frames and drops all other frames above 116 // if possible and if do_drop is true. 117 static Handle<JSArray> CheckAndDropActivations( 118 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array, 119 bool do_drop); 120 121 // Restarts the call frame and completely drops all frames above it. 122 // Return error message or NULL. 123 static const char* RestartFrame(JavaScriptFrame* frame); 124 125 // A copy of this is in liveedit.js. 126 enum FunctionPatchabilityStatus { 127 FUNCTION_AVAILABLE_FOR_PATCH = 1, 128 FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2, 129 FUNCTION_BLOCKED_ON_OTHER_STACK = 3, 130 FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4, 131 FUNCTION_REPLACED_ON_ACTIVE_STACK = 5, 132 FUNCTION_BLOCKED_UNDER_GENERATOR = 6, 133 FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7, 134 FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8 135 }; 136 137 // Compares 2 strings line-by-line, then token-wise and returns diff in form 138 // of array of triplets (pos1, pos1_end, pos2_end) describing list 139 // of diff chunks. 140 static Handle<JSArray> CompareStrings(Handle<String> s1, 141 Handle<String> s2); 142 143 // Architecture-specific constant. 144 static const bool kFrameDropperSupported; 145 146 /** 147 * Defines layout of a stack frame that supports padding. This is a regular 148 * internal frame that has a flexible stack structure. LiveEdit can shift 149 * its lower part up the stack, taking up the 'padding' space when additional 150 * stack memory is required. 151 * Such frame is expected immediately above the topmost JavaScript frame. 152 * 153 * Stack Layout: 154 * --- Top 155 * LiveEdit routine frames 156 * --- 157 * C frames of debug handler 158 * --- 159 * ... 160 * --- 161 * An internal frame that has n padding words: 162 * - any number of words as needed by code -- upper part of frame 163 * - padding size: a Smi storing n -- current size of padding 164 * - padding: n words filled with kPaddingValue in form of Smi 165 * - 3 context/type words of a regular InternalFrame 166 * - fp 167 * --- 168 * Topmost JavaScript frame 169 * --- 170 * ... 171 * --- Bottom 172 */ 173 // A size of frame base including fp. Padding words starts right above 174 // the base. 175 static const int kFrameDropperFrameSize = 176 4 + StandardFrameConstants::kCPSlotCount; 177 // A number of words that should be reserved on stack for the LiveEdit use. 178 // Stored on stack in form of Smi. 179 static const int kFramePaddingInitialSize = 1; 180 // A value that padding words are filled with (in form of Smi). Going 181 // bottom-top, the first word not having this value is a counter word. 182 static const int kFramePaddingValue = kFramePaddingInitialSize + 1; 183 }; 184 185 186 // A general-purpose comparator between 2 arrays. 187 class Comparator { 188 public: 189 // Holds 2 arrays of some elements allowing to compare any pair of 190 // element from the first array and element from the second array. 191 class Input { 192 public: 193 virtual int GetLength1() = 0; 194 virtual int GetLength2() = 0; 195 virtual bool Equals(int index1, int index2) = 0; 196 197 protected: ~Input()198 virtual ~Input() {} 199 }; 200 201 // Receives compare result as a series of chunks. 202 class Output { 203 public: 204 // Puts another chunk in result list. Note that technically speaking 205 // only 3 arguments actually needed with 4th being derivable. 206 virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0; 207 208 protected: ~Output()209 virtual ~Output() {} 210 }; 211 212 // Finds the difference between 2 arrays of elements. 213 static void CalculateDifference(Input* input, 214 Output* result_writer); 215 }; 216 217 218 219 // Simple helper class that creates more or less typed structures over 220 // JSArray object. This is an adhoc method of passing structures from C++ 221 // to JavaScript. 222 template<typename S> 223 class JSArrayBasedStruct { 224 public: Create(Isolate * isolate)225 static S Create(Isolate* isolate) { 226 Factory* factory = isolate->factory(); 227 Handle<JSArray> array = factory->NewJSArray(S::kSize_); 228 return S(array); 229 } 230 cast(Object * object)231 static S cast(Object* object) { 232 JSArray* array = JSArray::cast(object); 233 Handle<JSArray> array_handle(array); 234 return S(array_handle); 235 } 236 JSArrayBasedStruct(Handle<JSArray> array)237 explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) { 238 } 239 GetJSArray()240 Handle<JSArray> GetJSArray() { 241 return array_; 242 } 243 isolate()244 Isolate* isolate() const { 245 return array_->GetIsolate(); 246 } 247 248 protected: SetField(int field_position,Handle<Object> value)249 void SetField(int field_position, Handle<Object> value) { 250 Object::SetElement(isolate(), array_, field_position, value, SLOPPY) 251 .Assert(); 252 } 253 SetSmiValueField(int field_position,int value)254 void SetSmiValueField(int field_position, int value) { 255 SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate())); 256 } 257 GetField(int field_position)258 Handle<Object> GetField(int field_position) { 259 return Object::GetElement( 260 isolate(), array_, field_position).ToHandleChecked(); 261 } 262 GetSmiValueField(int field_position)263 int GetSmiValueField(int field_position) { 264 Handle<Object> res = GetField(field_position); 265 return Handle<Smi>::cast(res)->value(); 266 } 267 268 private: 269 Handle<JSArray> array_; 270 }; 271 272 273 // Represents some function compilation details. This structure will be used 274 // from JavaScript. It contains Code object, which is kept wrapped 275 // into a BlindReference for sanitizing reasons. 276 class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> { 277 public: FunctionInfoWrapper(Handle<JSArray> array)278 explicit FunctionInfoWrapper(Handle<JSArray> array) 279 : JSArrayBasedStruct<FunctionInfoWrapper>(array) { 280 } 281 282 void SetInitialProperties(Handle<String> name, int start_position, 283 int end_position, int param_num, int literal_count, 284 int parent_index); 285 286 void SetFunctionCode(Handle<Code> function_code, 287 Handle<HeapObject> code_scope_info); 288 SetFunctionScopeInfo(Handle<Object> scope_info_array)289 void SetFunctionScopeInfo(Handle<Object> scope_info_array) { 290 this->SetField(kFunctionScopeInfoOffset_, scope_info_array); 291 } 292 293 void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info); 294 GetLiteralCount()295 int GetLiteralCount() { 296 return this->GetSmiValueField(kLiteralNumOffset_); 297 } 298 GetParentIndex()299 int GetParentIndex() { 300 return this->GetSmiValueField(kParentIndexOffset_); 301 } 302 303 Handle<Code> GetFunctionCode(); 304 305 MaybeHandle<TypeFeedbackVector> GetFeedbackVector(); 306 307 Handle<Object> GetCodeScopeInfo(); 308 GetStartPosition()309 int GetStartPosition() { 310 return this->GetSmiValueField(kStartPositionOffset_); 311 } 312 GetEndPosition()313 int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); } 314 315 private: 316 static const int kFunctionNameOffset_ = 0; 317 static const int kStartPositionOffset_ = 1; 318 static const int kEndPositionOffset_ = 2; 319 static const int kParamNumOffset_ = 3; 320 static const int kCodeOffset_ = 4; 321 static const int kCodeScopeInfoOffset_ = 5; 322 static const int kFunctionScopeInfoOffset_ = 6; 323 static const int kParentIndexOffset_ = 7; 324 static const int kSharedFunctionInfoOffset_ = 8; 325 static const int kLiteralNumOffset_ = 9; 326 static const int kSize_ = 10; 327 328 friend class JSArrayBasedStruct<FunctionInfoWrapper>; 329 }; 330 331 332 // Wraps SharedFunctionInfo along with some of its fields for passing it 333 // back to JavaScript. SharedFunctionInfo object itself is additionally 334 // wrapped into BlindReference for sanitizing reasons. 335 class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> { 336 public: IsInstance(Handle<JSArray> array)337 static bool IsInstance(Handle<JSArray> array) { 338 if (array->length() != Smi::FromInt(kSize_)) return false; 339 Handle<Object> element( 340 Object::GetElement(array->GetIsolate(), 341 array, 342 kSharedInfoOffset_).ToHandleChecked()); 343 if (!element->IsJSValue()) return false; 344 return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo(); 345 } 346 SharedInfoWrapper(Handle<JSArray> array)347 explicit SharedInfoWrapper(Handle<JSArray> array) 348 : JSArrayBasedStruct<SharedInfoWrapper>(array) { 349 } 350 351 void SetProperties(Handle<String> name, 352 int start_position, 353 int end_position, 354 Handle<SharedFunctionInfo> info); 355 356 Handle<SharedFunctionInfo> GetInfo(); 357 358 private: 359 static const int kFunctionNameOffset_ = 0; 360 static const int kStartPositionOffset_ = 1; 361 static const int kEndPositionOffset_ = 2; 362 static const int kSharedInfoOffset_ = 3; 363 static const int kSize_ = 4; 364 365 friend class JSArrayBasedStruct<SharedInfoWrapper>; 366 }; 367 368 } // namespace internal 369 } // namespace v8 370 371 #endif /* V8_DEBUG_LIVEEDIT_H_ */ 372