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 |