| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/debug/debug-evaluate.h" | 5 #include "src/debug/debug-evaluate.h" |
| 6 | 6 |
| 7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
| 8 #include "src/contexts.h" | 8 #include "src/contexts.h" |
| 9 #include "src/debug/debug.h" | 9 #include "src/debug/debug.h" |
| 10 #include "src/debug/debug-frames.h" | 10 #include "src/debug/debug-frames.h" |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 JavaScriptFrameIterator it(isolate, frame_id); | 57 JavaScriptFrameIterator it(isolate, frame_id); |
| 58 JavaScriptFrame* frame = it.frame(); | 58 JavaScriptFrame* frame = it.frame(); |
| 59 | 59 |
| 60 // Traverse the saved contexts chain to find the active context for the | 60 // Traverse the saved contexts chain to find the active context for the |
| 61 // selected frame. | 61 // selected frame. |
| 62 SaveContext* save = | 62 SaveContext* save = |
| 63 DebugFrameHelper::FindSavedContextForFrame(isolate, frame); | 63 DebugFrameHelper::FindSavedContextForFrame(isolate, frame); |
| 64 SaveContext savex(isolate); | 64 SaveContext savex(isolate); |
| 65 isolate->set_context(*(save->context())); | 65 isolate->set_context(*(save->context())); |
| 66 | 66 |
| 67 // Materialize stack locals and the arguments object. | 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. |
| 68 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index); | 71 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index); |
| 69 if (isolate->has_pending_exception()) return MaybeHandle<Object>(); | 72 if (isolate->has_pending_exception()) return MaybeHandle<Object>(); |
| 70 | 73 |
| 71 Handle<Object> receiver(frame->receiver(), isolate); | 74 Handle<Context> context = isolate->native_context(); |
| 75 Handle<JSObject> receiver(context->global_proxy()); |
| 76 Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate); |
| 72 MaybeHandle<Object> maybe_result = Evaluate( | 77 MaybeHandle<Object> maybe_result = Evaluate( |
| 73 isolate, context_builder.outer_info(), | 78 isolate, context_builder.outer_info(), |
| 74 context_builder.innermost_context(), context_extension, receiver, source); | 79 context_builder.innermost_context(), context_extension, receiver, source); |
| 75 if (!maybe_result.is_null()) context_builder.UpdateValues(); | 80 if (!maybe_result.is_null()) context_builder.UpdateValues(); |
| 76 return maybe_result; | 81 return maybe_result; |
| 77 } | 82 } |
| 78 | 83 |
| 79 | 84 |
| 80 // Compile and evaluate source for the given context. | 85 // Compile and evaluate source for the given context. |
| 81 MaybeHandle<Object> DebugEvaluate::Evaluate( | 86 MaybeHandle<Object> DebugEvaluate::Evaluate( |
| (...skipping 30 matching lines...) Expand all Loading... |
| 112 } | 117 } |
| 113 | 118 |
| 114 | 119 |
| 115 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, | 120 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, |
| 116 JavaScriptFrame* frame, | 121 JavaScriptFrame* frame, |
| 117 int inlined_jsframe_index) | 122 int inlined_jsframe_index) |
| 118 : isolate_(isolate), | 123 : isolate_(isolate), |
| 119 frame_(frame), | 124 frame_(frame), |
| 120 inlined_jsframe_index_(inlined_jsframe_index) { | 125 inlined_jsframe_index_(inlined_jsframe_index) { |
| 121 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); | 126 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); |
| 122 Handle<JSFunction> function = | 127 Handle<JSFunction> local_function = |
| 123 handle(JSFunction::cast(frame_inspector.GetFunction())); | 128 handle(JSFunction::cast(frame_inspector.GetFunction())); |
| 124 Handle<Context> outer_context = handle(function->context(), isolate); | 129 Handle<Context> outer_context(local_function->context()); |
| 125 outer_info_ = handle(function->shared()); | 130 Handle<Context> native_context = isolate->native_context(); |
| 131 Handle<JSFunction> global_function(native_context->closure()); |
| 132 outer_info_ = handle(global_function->shared()); |
| 126 Handle<Context> inner_context; | 133 Handle<Context> inner_context; |
| 127 | 134 |
| 128 bool stop = false; | 135 bool stop = false; |
| 129 for (ScopeIterator it(isolate, &frame_inspector); | 136 |
| 137 // Iterate the original context chain to create a context chain that reflects |
| 138 // our needs. The original context chain may look like this: |
| 139 // <native context> <outer contexts> <function context> <inner contexts> |
| 140 // In the resulting context chain, we want to materialize the receiver, |
| 141 // the parameters of the current function, the stack locals. We only |
| 142 // materialize context variables that the function already references, |
| 143 // because only for those variables we can be sure that they will be resolved |
| 144 // correctly. Variables that are not referenced by the function may be |
| 145 // context-allocated and thus accessible, but may be shadowed by stack- |
| 146 // allocated variables and the resolution would be incorrect. |
| 147 // The result will look like this: |
| 148 // <native context> <receiver context> |
| 149 // <materialized stack and accessible context vars> <inner contexts> |
| 150 // All contexts use the closure of the native context, since there is no |
| 151 // function context in the chain. Variables that cannot be resolved are |
| 152 // bound to toplevel (script contexts or global object). |
| 153 // Once debug-evaluate has been executed, the changes to the materialized |
| 154 // objects are written back to the original context chain. Any changes to |
| 155 // the original context chain will therefore be overwritten. |
| 156 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; |
| 157 for (ScopeIterator it(isolate, &frame_inspector, option); |
| 130 !it.Failed() && !it.Done() && !stop; it.Next()) { | 158 !it.Failed() && !it.Done() && !stop; it.Next()) { |
| 131 ScopeIterator::ScopeType scope_type = it.Type(); | 159 ScopeIterator::ScopeType scope_type = it.Type(); |
| 132 | |
| 133 if (scope_type == ScopeIterator::ScopeTypeLocal) { | 160 if (scope_type == ScopeIterator::ScopeTypeLocal) { |
| 134 Handle<Context> parent_context = | 161 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); |
| 162 it.GetNonLocals(&non_locals_); |
| 163 Handle<Context> local_context = |
| 135 it.HasContext() ? it.CurrentContext() : outer_context; | 164 it.HasContext() ? it.CurrentContext() : outer_context; |
| 136 | 165 |
| 137 // The "this" binding, if any, can't be bound via "with". If we need | 166 // The "this" binding, if any, can't be bound via "with". If we need |
| 138 // to, add another node onto the outer context to bind "this". | 167 // to, add another node onto the outer context to bind "this". |
| 139 parent_context = MaterializeReceiver(parent_context, function); | 168 Handle<Context> receiver_context = |
| 169 MaterializeReceiver(native_context, local_context, local_function, |
| 170 global_function, it.ThisIsNonLocal()); |
| 140 | 171 |
| 141 Handle<JSObject> materialized_function = NewJSObjectWithNullProto(); | 172 Handle<JSObject> materialized_function = NewJSObjectWithNullProto(); |
| 142 | 173 frame_inspector.MaterializeStackLocals(materialized_function, |
| 143 frame_inspector.MaterializeStackLocals(materialized_function, function); | 174 local_function); |
| 144 | 175 MaterializeArgumentsObject(materialized_function, local_function); |
| 145 MaterializeArgumentsObject(materialized_function, function); | 176 MaterializeContextChain(materialized_function, local_context); |
| 146 | 177 |
| 147 Handle<Context> with_context = isolate->factory()->NewWithContext( | 178 Handle<Context> with_context = isolate->factory()->NewWithContext( |
| 148 function, parent_context, materialized_function); | 179 global_function, receiver_context, materialized_function); |
| 149 | 180 |
| 150 ContextChainElement context_chain_element; | 181 ContextChainElement context_chain_element; |
| 151 context_chain_element.original_context = it.CurrentContext(); | 182 context_chain_element.original_context = local_context; |
| 152 context_chain_element.materialized_object = materialized_function; | 183 context_chain_element.materialized_object = materialized_function; |
| 153 context_chain_element.scope_info = it.CurrentScopeInfo(); | 184 context_chain_element.scope_info = it.CurrentScopeInfo(); |
| 154 context_chain_.Add(context_chain_element); | 185 context_chain_.Add(context_chain_element); |
| 155 | 186 |
| 156 stop = true; | 187 stop = true; |
| 157 RecordContextsInChain(&inner_context, with_context, with_context); | 188 RecordContextsInChain(&inner_context, receiver_context, with_context); |
| 158 } else if (scope_type == ScopeIterator::ScopeTypeCatch || | 189 } else if (scope_type == ScopeIterator::ScopeTypeCatch || |
| 159 scope_type == ScopeIterator::ScopeTypeWith) { | 190 scope_type == ScopeIterator::ScopeTypeWith) { |
| 160 Handle<Context> cloned_context = Handle<Context>::cast( | 191 Handle<Context> cloned_context = Handle<Context>::cast( |
| 161 isolate->factory()->CopyFixedArray(it.CurrentContext())); | 192 isolate->factory()->CopyFixedArray(it.CurrentContext())); |
| 162 | 193 |
| 163 ContextChainElement context_chain_element; | 194 ContextChainElement context_chain_element; |
| 164 context_chain_element.original_context = it.CurrentContext(); | 195 context_chain_element.original_context = it.CurrentContext(); |
| 165 context_chain_element.cloned_context = cloned_context; | 196 context_chain_element.cloned_context = cloned_context; |
| 166 context_chain_.Add(context_chain_element); | 197 context_chain_.Add(context_chain_element); |
| 167 | 198 |
| 168 RecordContextsInChain(&inner_context, cloned_context, cloned_context); | 199 RecordContextsInChain(&inner_context, cloned_context, cloned_context); |
| 169 } else if (scope_type == ScopeIterator::ScopeTypeBlock) { | 200 } else if (scope_type == ScopeIterator::ScopeTypeBlock) { |
| 170 Handle<JSObject> materialized_object = NewJSObjectWithNullProto(); | 201 Handle<JSObject> materialized_object = NewJSObjectWithNullProto(); |
| 171 frame_inspector.MaterializeStackLocals(materialized_object, | 202 frame_inspector.MaterializeStackLocals(materialized_object, |
| 172 it.CurrentScopeInfo()); | 203 it.CurrentScopeInfo()); |
| 173 if (it.HasContext()) { | 204 if (it.HasContext()) { |
| 174 Handle<Context> cloned_context = Handle<Context>::cast( | 205 Handle<Context> cloned_context = Handle<Context>::cast( |
| 175 isolate->factory()->CopyFixedArray(it.CurrentContext())); | 206 isolate->factory()->CopyFixedArray(it.CurrentContext())); |
| 176 Handle<Context> with_context = isolate->factory()->NewWithContext( | 207 Handle<Context> with_context = isolate->factory()->NewWithContext( |
| 177 function, cloned_context, materialized_object); | 208 global_function, cloned_context, materialized_object); |
| 178 | 209 |
| 179 ContextChainElement context_chain_element; | 210 ContextChainElement context_chain_element; |
| 180 context_chain_element.original_context = it.CurrentContext(); | 211 context_chain_element.original_context = it.CurrentContext(); |
| 181 context_chain_element.cloned_context = cloned_context; | 212 context_chain_element.cloned_context = cloned_context; |
| 182 context_chain_element.materialized_object = materialized_object; | 213 context_chain_element.materialized_object = materialized_object; |
| 183 context_chain_element.scope_info = it.CurrentScopeInfo(); | 214 context_chain_element.scope_info = it.CurrentScopeInfo(); |
| 184 context_chain_.Add(context_chain_element); | 215 context_chain_.Add(context_chain_element); |
| 185 | 216 |
| 186 RecordContextsInChain(&inner_context, cloned_context, with_context); | 217 RecordContextsInChain(&inner_context, cloned_context, with_context); |
| 187 } else { | 218 } else { |
| 188 Handle<Context> with_context = isolate->factory()->NewWithContext( | 219 Handle<Context> with_context = isolate->factory()->NewWithContext( |
| 189 function, outer_context, materialized_object); | 220 global_function, outer_context, materialized_object); |
| 190 | 221 |
| 191 ContextChainElement context_chain_element; | 222 ContextChainElement context_chain_element; |
| 192 context_chain_element.materialized_object = materialized_object; | 223 context_chain_element.materialized_object = materialized_object; |
| 193 context_chain_element.scope_info = it.CurrentScopeInfo(); | 224 context_chain_element.scope_info = it.CurrentScopeInfo(); |
| 194 context_chain_.Add(context_chain_element); | 225 context_chain_.Add(context_chain_element); |
| 195 | 226 |
| 196 RecordContextsInChain(&inner_context, with_context, with_context); | 227 RecordContextsInChain(&inner_context, with_context, with_context); |
| 197 } | 228 } |
| 198 } else { | 229 } else { |
| 199 stop = true; | 230 stop = true; |
| 200 } | 231 } |
| 201 } | 232 } |
| 202 if (innermost_context_.is_null()) { | 233 if (innermost_context_.is_null()) { |
| 203 innermost_context_ = outer_context; | 234 innermost_context_ = outer_context; |
| 204 } | 235 } |
| 205 DCHECK(!innermost_context_.is_null()); | 236 DCHECK(!innermost_context_.is_null()); |
| 206 } | 237 } |
| 207 | 238 |
| 208 | 239 |
| 209 void DebugEvaluate::ContextBuilder::UpdateValues() { | 240 void DebugEvaluate::ContextBuilder::UpdateValues() { |
| 241 // TODO(yangguo): remove updating values. |
| 210 for (int i = 0; i < context_chain_.length(); i++) { | 242 for (int i = 0; i < context_chain_.length(); i++) { |
| 211 ContextChainElement element = context_chain_[i]; | 243 ContextChainElement element = context_chain_[i]; |
| 212 if (!element.original_context.is_null() && | 244 if (!element.original_context.is_null() && |
| 213 !element.cloned_context.is_null()) { | 245 !element.cloned_context.is_null()) { |
| 214 Handle<Context> cloned_context = element.cloned_context; | 246 Handle<Context> cloned_context = element.cloned_context; |
| 215 cloned_context->CopyTo( | 247 cloned_context->CopyTo( |
| 216 Context::MIN_CONTEXT_SLOTS, *element.original_context, | 248 Context::MIN_CONTEXT_SLOTS, *element.original_context, |
| 217 Context::MIN_CONTEXT_SLOTS, | 249 Context::MIN_CONTEXT_SLOTS, |
| 218 cloned_context->length() - Context::MIN_CONTEXT_SLOTS); | 250 cloned_context->length() - Context::MIN_CONTEXT_SLOTS); |
| 219 } | 251 } |
| 220 if (!element.materialized_object.is_null()) { | 252 if (!element.materialized_object.is_null()) { |
| 221 // Write back potential changes to materialized stack locals to the | 253 // Write back potential changes to materialized stack locals to the |
| 222 // stack. | 254 // stack. |
| 223 FrameInspector(frame_, inlined_jsframe_index_, isolate_) | 255 FrameInspector(frame_, inlined_jsframe_index_, isolate_) |
| 224 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, | 256 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, |
| 225 element.scope_info); | 257 element.scope_info); |
| 258 if (element.scope_info->scope_type() == FUNCTION_SCOPE) { |
| 259 DCHECK_EQ(context_chain_.length() - 1, i); |
| 260 UpdateContextChainFromMaterializedObject(element.materialized_object, |
| 261 element.original_context); |
| 262 } |
| 226 } | 263 } |
| 227 } | 264 } |
| 228 } | 265 } |
| 229 | 266 |
| 230 | 267 |
| 231 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { | 268 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { |
| 232 Handle<JSObject> result = | 269 Handle<JSObject> result = |
| 233 isolate_->factory()->NewJSObject(isolate_->object_function()); | 270 isolate_->factory()->NewJSObject(isolate_->object_function()); |
| 234 Handle<Map> new_map = | 271 Handle<Map> new_map = |
| 235 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); | 272 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 264 // FunctionGetArguments can't throw an exception. | 301 // FunctionGetArguments can't throw an exception. |
| 265 Handle<JSObject> arguments = | 302 Handle<JSObject> arguments = |
| 266 Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); | 303 Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); |
| 267 Handle<String> arguments_str = isolate_->factory()->arguments_string(); | 304 Handle<String> arguments_str = isolate_->factory()->arguments_string(); |
| 268 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, | 305 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, |
| 269 NONE) | 306 NONE) |
| 270 .Check(); | 307 .Check(); |
| 271 } | 308 } |
| 272 | 309 |
| 273 | 310 |
| 311 MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext( |
| 312 Handle<Context> context, Handle<String> name) { |
| 313 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; |
| 314 int index; |
| 315 PropertyAttributes attributes; |
| 316 BindingFlags binding; |
| 317 Handle<Object> holder = |
| 318 context->Lookup(name, flags, &index, &attributes, &binding); |
| 319 if (holder.is_null()) return MaybeHandle<Object>(); |
| 320 Handle<Object> value; |
| 321 if (index != Context::kNotFound) { // Found on context. |
| 322 Handle<Context> context = Handle<Context>::cast(holder); |
| 323 return Handle<Object>(context->get(index), isolate_); |
| 324 } else { // Found on object. |
| 325 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); |
| 326 return JSReceiver::GetDataProperty(object, name); |
| 327 } |
| 328 } |
| 329 |
| 330 |
| 331 void DebugEvaluate::ContextBuilder::MaterializeContextChain( |
| 332 Handle<JSObject> target, Handle<Context> context) { |
| 333 for (const Handle<String>& name : non_locals_) { |
| 334 HandleScope scope(isolate_); |
| 335 Handle<Object> value; |
| 336 if (!LoadFromContext(context, name).ToHandle(&value)) continue; |
| 337 JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); |
| 338 } |
| 339 } |
| 340 |
| 341 |
| 342 void DebugEvaluate::ContextBuilder::StoreToContext(Handle<Context> context, |
| 343 Handle<String> name, |
| 344 Handle<Object> value) { |
| 345 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; |
| 346 int index; |
| 347 PropertyAttributes attributes; |
| 348 BindingFlags binding; |
| 349 Handle<Object> holder = |
| 350 context->Lookup(name, flags, &index, &attributes, &binding); |
| 351 if (holder.is_null()) return; |
| 352 if (attributes & READ_ONLY) return; |
| 353 if (index != Context::kNotFound) { // Found on context. |
| 354 Handle<Context> context = Handle<Context>::cast(holder); |
| 355 context->set(index, *value); |
| 356 } else { // Found on object. |
| 357 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); |
| 358 LookupIterator lookup(object, name); |
| 359 if (lookup.state() != LookupIterator::DATA) return; |
| 360 CHECK(JSReceiver::SetDataProperty(&lookup, value).FromJust()); |
| 361 } |
| 362 } |
| 363 |
| 364 |
| 365 void DebugEvaluate::ContextBuilder::UpdateContextChainFromMaterializedObject( |
| 366 Handle<JSObject> source, Handle<Context> context) { |
| 367 // TODO(yangguo): check whether overwriting context fields is actually safe |
| 368 // wrt fields we consider constant. |
| 369 for (const Handle<String>& name : non_locals_) { |
| 370 HandleScope scope(isolate_); |
| 371 Handle<Object> value = JSReceiver::GetDataProperty(source, name); |
| 372 StoreToContext(context, name, value); |
| 373 } |
| 374 } |
| 375 |
| 376 |
| 274 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver( | 377 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver( |
| 275 Handle<Context> target, Handle<JSFunction> function) { | 378 Handle<Context> parent_context, Handle<Context> lookup_context, |
| 276 Handle<SharedFunctionInfo> shared(function->shared()); | 379 Handle<JSFunction> local_function, Handle<JSFunction> global_function, |
| 277 Handle<ScopeInfo> scope_info(shared->scope_info()); | 380 bool this_is_non_local) { |
| 278 Handle<Object> receiver; | 381 Handle<Object> receiver = isolate_->factory()->undefined_value(); |
| 279 switch (scope_info->scope_type()) { | 382 Handle<String> this_string = isolate_->factory()->this_string(); |
| 280 case FUNCTION_SCOPE: { | 383 if (this_is_non_local) { |
| 281 VariableMode mode; | 384 LoadFromContext(lookup_context, this_string).ToHandle(&receiver); |
| 282 InitializationFlag init_flag; | 385 } else if (local_function->shared()->scope_info()->HasReceiver()) { |
| 283 MaybeAssignedFlag maybe_assigned_flag; | 386 receiver = handle(frame_->receiver(), isolate_); |
| 284 | |
| 285 // Don't bother creating a fake context node if "this" is in the context | |
| 286 // already. | |
| 287 if (ScopeInfo::ContextSlotIndex(scope_info, | |
| 288 isolate_->factory()->this_string(), &mode, | |
| 289 &init_flag, &maybe_assigned_flag) >= 0) { | |
| 290 return target; | |
| 291 } | |
| 292 receiver = handle(frame_->receiver(), isolate_); | |
| 293 break; | |
| 294 } | |
| 295 case MODULE_SCOPE: | |
| 296 receiver = isolate_->factory()->undefined_value(); | |
| 297 break; | |
| 298 case SCRIPT_SCOPE: | |
| 299 receiver = handle(function->global_proxy(), isolate_); | |
| 300 break; | |
| 301 default: | |
| 302 // For eval code, arrow functions, and the like, there's no "this" binding | |
| 303 // to materialize. | |
| 304 return target; | |
| 305 } | 387 } |
| 306 | 388 return isolate_->factory()->NewCatchContext(global_function, parent_context, |
| 307 return isolate_->factory()->NewCatchContext( | 389 this_string, receiver); |
| 308 function, target, isolate_->factory()->this_string(), receiver); | |
| 309 } | 390 } |
| 310 | 391 |
| 311 } // namespace internal | 392 } // namespace internal |
| 312 } // namespace v8 | 393 } // namespace v8 |
| OLD | NEW |