1 // Copyright 2015 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 #include "src/debug/debug-evaluate.h" 6 7 #include "src/accessors.h" 8 #include "src/contexts.h" 9 #include "src/debug/debug.h" 10 #include "src/debug/debug-frames.h" 11 #include "src/debug/debug-scopes.h" 12 #include "src/frames-inl.h" 13 #include "src/isolate-inl.h" 14 15 namespace v8 { 16 namespace internal { 17 18 static inline bool IsDebugContext(Isolate* isolate, Context* context) { 19 return context->native_context() == *isolate->debug()->debug_context(); 20 } 21 22 23 MaybeHandle<Object> DebugEvaluate::Global( 24 Isolate* isolate, Handle<String> source, bool disable_break, 25 Handle<HeapObject> context_extension) { 26 // Handle the processing of break. 27 DisableBreak disable_break_scope(isolate->debug(), disable_break); 28 29 // Enter the top context from before the debugger was invoked. 30 SaveContext save(isolate); 31 SaveContext* top = &save; 32 while (top != NULL && IsDebugContext(isolate, *top->context())) { 33 top = top->prev(); 34 } 35 if (top != NULL) isolate->set_context(*top->context()); 36 37 // Get the native context now set to the top context from before the 38 // debugger was invoked. 39 Handle<Context> context = isolate->native_context(); 40 Handle<JSObject> receiver(context->global_proxy()); 41 Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate); 42 return Evaluate(isolate, outer_info, context, context_extension, receiver, 43 source); 44 } 45 46 47 MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, 48 StackFrame::Id frame_id, 49 int inlined_jsframe_index, 50 Handle<String> source, 51 bool disable_break, 52 Handle<HeapObject> context_extension) { 53 // Handle the processing of break. 54 DisableBreak disable_break_scope(isolate->debug(), disable_break); 55 56 // Get the frame where the debugging is performed. 57 JavaScriptFrameIterator it(isolate, frame_id); 58 JavaScriptFrame* frame = it.frame(); 59 60 // Traverse the saved contexts chain to find the active context for the 61 // selected frame. 62 SaveContext* save = 63 DebugFrameHelper::FindSavedContextForFrame(isolate, frame); 64 SaveContext savex(isolate); 65 isolate->set_context(*(save->context())); 66 67 // This is not a lot different than DebugEvaluate::Global, except that 68 // variables accessible by the function we are evaluating from are 69 // materialized and included on top of the native context. Changes to 70 // the materialized object are written back afterwards. 71 // Note that the native context is taken from the original context chain, 72 // which may not be the current native context of the isolate. 73 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index); 74 if (isolate->has_pending_exception()) return MaybeHandle<Object>(); 75 76 Handle<Context> context = context_builder.native_context(); 77 Handle<JSObject> receiver(context->global_proxy()); 78 MaybeHandle<Object> maybe_result = Evaluate( 79 isolate, context_builder.outer_info(), 80 context_builder.innermost_context(), context_extension, receiver, source); 81 if (!maybe_result.is_null() && !FLAG_debug_eval_readonly_locals) { 82 context_builder.UpdateValues(); 83 } 84 return maybe_result; 85 } 86 87 88 // Compile and evaluate source for the given context. 89 MaybeHandle<Object> DebugEvaluate::Evaluate( 90 Isolate* isolate, Handle<SharedFunctionInfo> outer_info, 91 Handle<Context> context, Handle<HeapObject> context_extension, 92 Handle<Object> receiver, Handle<String> source) { 93 if (context_extension->IsJSObject()) { 94 Handle<JSObject> extension = Handle<JSObject>::cast(context_extension); 95 Handle<JSFunction> closure(context->closure(), isolate); 96 context = isolate->factory()->NewWithContext(closure, context, extension); 97 } 98 99 Handle<JSFunction> eval_fun; 100 ASSIGN_RETURN_ON_EXCEPTION(isolate, eval_fun, 101 Compiler::GetFunctionFromEval( 102 source, outer_info, context, SLOPPY, 103 NO_PARSE_RESTRICTION, RelocInfo::kNoPosition), 104 Object); 105 106 Handle<Object> result; 107 ASSIGN_RETURN_ON_EXCEPTION( 108 isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), 109 Object); 110 111 // Skip the global proxy as it has no properties and always delegates to the 112 // real global object. 113 if (result->IsJSGlobalProxy()) { 114 PrototypeIterator iter(isolate, result); 115 // TODO(verwaest): This will crash when the global proxy is detached. 116 result = PrototypeIterator::GetCurrent<JSObject>(iter); 117 } 118 119 return result; 120 } 121 122 123 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, 124 JavaScriptFrame* frame, 125 int inlined_jsframe_index) 126 : isolate_(isolate), 127 frame_(frame), 128 inlined_jsframe_index_(inlined_jsframe_index) { 129 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); 130 Handle<JSFunction> local_function = 131 handle(JSFunction::cast(frame_inspector.GetFunction())); 132 Handle<Context> outer_context(local_function->context()); 133 native_context_ = Handle<Context>(outer_context->native_context()); 134 Handle<JSFunction> global_function(native_context_->closure()); 135 outer_info_ = handle(global_function->shared()); 136 Handle<Context> inner_context; 137 138 bool stop = false; 139 140 // Iterate the original context chain to create a context chain that reflects 141 // our needs. The original context chain may look like this: 142 // <native context> <outer contexts> <function context> <inner contexts> 143 // In the resulting context chain, we want to materialize the receiver, 144 // the parameters of the current function, the stack locals. We only 145 // materialize context variables that the function already references, 146 // because only for those variables we can be sure that they will be resolved 147 // correctly. Variables that are not referenced by the function may be 148 // context-allocated and thus accessible, but may be shadowed by stack- 149 // allocated variables and the resolution would be incorrect. 150 // The result will look like this: 151 // <native context> <receiver context> 152 // <materialized stack and accessible context vars> <inner contexts> 153 // All contexts use the closure of the native context, since there is no 154 // function context in the chain. Variables that cannot be resolved are 155 // bound to toplevel (script contexts or global object). 156 // Once debug-evaluate has been executed, the changes to the materialized 157 // objects are written back to the original context chain. Any changes to 158 // the original context chain will therefore be overwritten. 159 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; 160 for (ScopeIterator it(isolate, &frame_inspector, option); 161 !it.Failed() && !it.Done() && !stop; it.Next()) { 162 ScopeIterator::ScopeType scope_type = it.Type(); 163 if (scope_type == ScopeIterator::ScopeTypeLocal) { 164 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); 165 it.GetNonLocals(&non_locals_); 166 Handle<Context> local_context = 167 it.HasContext() ? it.CurrentContext() : outer_context; 168 169 // The "this" binding, if any, can't be bound via "with". If we need 170 // to, add another node onto the outer context to bind "this". 171 Handle<Context> receiver_context = 172 MaterializeReceiver(native_context_, local_context, local_function, 173 global_function, it.ThisIsNonLocal()); 174 175 Handle<JSObject> materialized_function = NewJSObjectWithNullProto(); 176 frame_inspector.MaterializeStackLocals(materialized_function, 177 local_function); 178 MaterializeArgumentsObject(materialized_function, local_function); 179 MaterializeContextChain(materialized_function, local_context); 180 181 Handle<Context> with_context = isolate->factory()->NewWithContext( 182 global_function, receiver_context, materialized_function); 183 184 ContextChainElement context_chain_element; 185 context_chain_element.original_context = local_context; 186 context_chain_element.materialized_object = materialized_function; 187 context_chain_element.scope_info = it.CurrentScopeInfo(); 188 context_chain_.Add(context_chain_element); 189 190 stop = true; 191 RecordContextsInChain(&inner_context, receiver_context, with_context); 192 } else if (scope_type == ScopeIterator::ScopeTypeCatch || 193 scope_type == ScopeIterator::ScopeTypeWith) { 194 Handle<Context> cloned_context = Handle<Context>::cast( 195 isolate->factory()->CopyFixedArray(it.CurrentContext())); 196 197 ContextChainElement context_chain_element; 198 context_chain_element.original_context = it.CurrentContext(); 199 context_chain_element.cloned_context = cloned_context; 200 context_chain_.Add(context_chain_element); 201 202 RecordContextsInChain(&inner_context, cloned_context, cloned_context); 203 } else if (scope_type == ScopeIterator::ScopeTypeBlock) { 204 Handle<JSObject> materialized_object = NewJSObjectWithNullProto(); 205 frame_inspector.MaterializeStackLocals(materialized_object, 206 it.CurrentScopeInfo()); 207 if (it.HasContext()) { 208 Handle<Context> cloned_context = Handle<Context>::cast( 209 isolate->factory()->CopyFixedArray(it.CurrentContext())); 210 Handle<Context> with_context = isolate->factory()->NewWithContext( 211 global_function, cloned_context, materialized_object); 212 213 ContextChainElement context_chain_element; 214 context_chain_element.original_context = it.CurrentContext(); 215 context_chain_element.cloned_context = cloned_context; 216 context_chain_element.materialized_object = materialized_object; 217 context_chain_element.scope_info = it.CurrentScopeInfo(); 218 context_chain_.Add(context_chain_element); 219 220 RecordContextsInChain(&inner_context, cloned_context, with_context); 221 } else { 222 Handle<Context> with_context = isolate->factory()->NewWithContext( 223 global_function, outer_context, materialized_object); 224 225 ContextChainElement context_chain_element; 226 context_chain_element.materialized_object = materialized_object; 227 context_chain_element.scope_info = it.CurrentScopeInfo(); 228 context_chain_.Add(context_chain_element); 229 230 RecordContextsInChain(&inner_context, with_context, with_context); 231 } 232 } else { 233 stop = true; 234 } 235 } 236 if (innermost_context_.is_null()) { 237 innermost_context_ = outer_context; 238 } 239 DCHECK(!innermost_context_.is_null()); 240 } 241 242 243 void DebugEvaluate::ContextBuilder::UpdateValues() { 244 // TODO(yangguo): remove updating values. 245 for (int i = 0; i < context_chain_.length(); i++) { 246 ContextChainElement element = context_chain_[i]; 247 if (!element.original_context.is_null() && 248 !element.cloned_context.is_null()) { 249 Handle<Context> cloned_context = element.cloned_context; 250 cloned_context->CopyTo( 251 Context::MIN_CONTEXT_SLOTS, *element.original_context, 252 Context::MIN_CONTEXT_SLOTS, 253 cloned_context->length() - Context::MIN_CONTEXT_SLOTS); 254 } 255 if (!element.materialized_object.is_null()) { 256 // Write back potential changes to materialized stack locals to the 257 // stack. 258 FrameInspector(frame_, inlined_jsframe_index_, isolate_) 259 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, 260 element.scope_info); 261 if (element.scope_info->scope_type() == FUNCTION_SCOPE) { 262 DCHECK_EQ(context_chain_.length() - 1, i); 263 UpdateContextChainFromMaterializedObject(element.materialized_object, 264 element.original_context); 265 } 266 } 267 } 268 } 269 270 271 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { 272 Handle<JSObject> result = 273 isolate_->factory()->NewJSObject(isolate_->object_function()); 274 Handle<Map> new_map = 275 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); 276 Map::SetPrototype(new_map, isolate_->factory()->null_value()); 277 JSObject::MigrateToMap(result, new_map); 278 return result; 279 } 280 281 282 void DebugEvaluate::ContextBuilder::RecordContextsInChain( 283 Handle<Context>* inner_context, Handle<Context> first, 284 Handle<Context> last) { 285 if (!inner_context->is_null()) { 286 (*inner_context)->set_previous(*last); 287 } else { 288 innermost_context_ = last; 289 } 290 *inner_context = first; 291 } 292 293 294 void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject( 295 Handle<JSObject> target, Handle<JSFunction> function) { 296 // Do not materialize the arguments object for eval or top-level code. 297 // Skip if "arguments" is already taken. 298 if (!function->shared()->is_function()) return; 299 Maybe<bool> maybe = JSReceiver::HasOwnProperty( 300 target, isolate_->factory()->arguments_string()); 301 DCHECK(maybe.IsJust()); 302 if (maybe.FromJust()) return; 303 304 // FunctionGetArguments can't throw an exception. 305 Handle<JSObject> arguments = 306 Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); 307 Handle<String> arguments_str = isolate_->factory()->arguments_string(); 308 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, 309 NONE) 310 .Check(); 311 } 312 313 314 MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext( 315 Handle<Context> context, Handle<String> name, bool* global) { 316 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; 317 int index; 318 PropertyAttributes attributes; 319 BindingFlags binding; 320 Handle<Object> holder = 321 context->Lookup(name, flags, &index, &attributes, &binding); 322 if (holder.is_null()) return MaybeHandle<Object>(); 323 Handle<Object> value; 324 if (index != Context::kNotFound) { // Found on context. 325 Handle<Context> context = Handle<Context>::cast(holder); 326 // Do not shadow variables on the script context. 327 *global = context->IsScriptContext(); 328 return Handle<Object>(context->get(index), isolate_); 329 } else { // Found on object. 330 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); 331 // Do not shadow properties on the global object. 332 *global = object->IsJSGlobalObject(); 333 return JSReceiver::GetDataProperty(object, name); 334 } 335 } 336 337 338 void DebugEvaluate::ContextBuilder::MaterializeContextChain( 339 Handle<JSObject> target, Handle<Context> context) { 340 for (const Handle<String>& name : non_locals_) { 341 HandleScope scope(isolate_); 342 Handle<Object> value; 343 bool global; 344 if (!LoadFromContext(context, name, &global).ToHandle(&value) || global) { 345 // If resolving the variable fails, skip it. If it resolves to a global 346 // variable, skip it as well since it's not read-only and can be resolved 347 // within debug-evaluate. 348 continue; 349 } 350 JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); 351 } 352 } 353 354 355 void DebugEvaluate::ContextBuilder::StoreToContext(Handle<Context> context, 356 Handle<String> name, 357 Handle<Object> value) { 358 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; 359 int index; 360 PropertyAttributes attributes; 361 BindingFlags binding; 362 Handle<Object> holder = 363 context->Lookup(name, flags, &index, &attributes, &binding); 364 if (holder.is_null()) return; 365 if (attributes & READ_ONLY) return; 366 if (index != Context::kNotFound) { // Found on context. 367 Handle<Context> context = Handle<Context>::cast(holder); 368 context->set(index, *value); 369 } else { // Found on object. 370 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); 371 LookupIterator lookup(object, name); 372 if (lookup.state() != LookupIterator::DATA) return; 373 CHECK(JSReceiver::SetDataProperty(&lookup, value).FromJust()); 374 } 375 } 376 377 378 void DebugEvaluate::ContextBuilder::UpdateContextChainFromMaterializedObject( 379 Handle<JSObject> source, Handle<Context> context) { 380 // TODO(yangguo): check whether overwriting context fields is actually safe 381 // wrt fields we consider constant. 382 for (const Handle<String>& name : non_locals_) { 383 HandleScope scope(isolate_); 384 Handle<Object> value = JSReceiver::GetDataProperty(source, name); 385 StoreToContext(context, name, value); 386 } 387 } 388 389 390 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver( 391 Handle<Context> parent_context, Handle<Context> lookup_context, 392 Handle<JSFunction> local_function, Handle<JSFunction> global_function, 393 bool this_is_non_local) { 394 Handle<Object> receiver = isolate_->factory()->undefined_value(); 395 Handle<String> this_string = isolate_->factory()->this_string(); 396 if (this_is_non_local) { 397 bool global; 398 LoadFromContext(lookup_context, this_string, &global).ToHandle(&receiver); 399 } else if (local_function->shared()->scope_info()->HasReceiver()) { 400 receiver = handle(frame_->receiver(), isolate_); 401 } 402 return isolate_->factory()->NewCatchContext(global_function, parent_context, 403 this_string, receiver); 404 } 405 406 } // namespace internal 407 } // namespace v8 408