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 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 if (result->IsJSGlobalProxy()) { | 105 if (result->IsJSGlobalProxy()) { |
106 PrototypeIterator iter(isolate, result); | 106 PrototypeIterator iter(isolate, result); |
107 // TODO(verwaest): This will crash when the global proxy is detached. | 107 // TODO(verwaest): This will crash when the global proxy is detached. |
108 result = PrototypeIterator::GetCurrent<JSObject>(iter); | 108 result = PrototypeIterator::GetCurrent<JSObject>(iter); |
109 } | 109 } |
110 | 110 |
111 return result; | 111 return result; |
112 } | 112 } |
113 | 113 |
114 | 114 |
| 115 Handle<Context> FindScriptOrNativeContext(Handle<Context> context) { |
| 116 DisallowHeapAllocation no_gc; |
| 117 Context* raw_context = *context; |
| 118 while (!raw_context->IsScriptContext() && !raw_context->IsNativeContext()) { |
| 119 raw_context = raw_context->previous(); |
| 120 } |
| 121 return Handle<Context>(raw_context); |
| 122 } |
| 123 |
| 124 |
115 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, | 125 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, |
116 JavaScriptFrame* frame, | 126 JavaScriptFrame* frame, |
117 int inlined_jsframe_index) | 127 int inlined_jsframe_index) |
118 : isolate_(isolate), | 128 : isolate_(isolate), |
119 frame_(frame), | 129 frame_(frame), |
120 inlined_jsframe_index_(inlined_jsframe_index) { | 130 inlined_jsframe_index_(inlined_jsframe_index) { |
121 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); | 131 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); |
122 Handle<JSFunction> function = | 132 Handle<JSFunction> function = |
123 handle(JSFunction::cast(frame_inspector.GetFunction())); | 133 handle(JSFunction::cast(frame_inspector.GetFunction())); |
124 Handle<Context> outer_context = handle(function->context(), isolate); | 134 Handle<Context> outer_context = handle(function->context(), isolate); |
125 outer_info_ = handle(function->shared()); | 135 outer_info_ = handle(function->shared()); |
126 Handle<Context> inner_context; | 136 Handle<Context> inner_context; |
127 | 137 |
128 bool stop = false; | 138 bool stop = false; |
129 for (ScopeIterator it(isolate, &frame_inspector); | 139 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; |
| 140 for (ScopeIterator it(isolate, &frame_inspector, option); |
130 !it.Failed() && !it.Done() && !stop; it.Next()) { | 141 !it.Failed() && !it.Done() && !stop; it.Next()) { |
131 ScopeIterator::ScopeType scope_type = it.Type(); | 142 ScopeIterator::ScopeType scope_type = it.Type(); |
132 | 143 |
| 144 // Duplicate contexts up to the script context for debug evaluate. |
| 145 // The content of the duplicated contexts are written back to the original |
| 146 // at the end. This however means that any changes to the original will be |
| 147 // overwritten. |
133 if (scope_type == ScopeIterator::ScopeTypeLocal) { | 148 if (scope_type == ScopeIterator::ScopeTypeLocal) { |
134 Handle<Context> parent_context = | 149 // The context chain at this point looks like this: |
| 150 // <native context> <script context> <outer contexts> |
| 151 // <function context> <inner contexts> |
| 152 // We can only be sure to correctly resolve variables that our function |
| 153 // itself already references. Other variables may be stack-allocated, |
| 154 // and invisible to the context lookup. |
| 155 // To make sure that other variables are not accessible, we collect the |
| 156 // variables used by our function and replace the context chain between |
| 157 // the current function and the script context by a with-context that |
| 158 // materializes accessible context vars and stack vars. We also add a |
| 159 // catch-context to resolve 'this' if necessary. |
| 160 // After the replacement, it looks like this: |
| 161 // <native context> <script context> <receiver context> |
| 162 // <accessible context vars and materialized stack> <inner contexts> |
| 163 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); |
| 164 it.GetNonLocals(&non_locals_); |
| 165 Handle<Context> base_context = FindScriptOrNativeContext(outer_context); |
| 166 Handle<Context> local_context = |
135 it.HasContext() ? it.CurrentContext() : outer_context; | 167 it.HasContext() ? it.CurrentContext() : outer_context; |
136 | 168 |
137 // The "this" binding, if any, can't be bound via "with". If we need | 169 // 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". | 170 // to, add another node onto the outer context to bind "this". |
139 parent_context = MaterializeReceiver(parent_context, function); | 171 Handle<Context> receiver_context = MaterializeReceiver( |
| 172 base_context, local_context, function, it.ThisIsNonLocal()); |
140 | 173 |
141 Handle<JSObject> materialized_function = NewJSObjectWithNullProto(); | 174 Handle<JSObject> materialized_function = NewJSObjectWithNullProto(); |
142 | |
143 frame_inspector.MaterializeStackLocals(materialized_function, function); | 175 frame_inspector.MaterializeStackLocals(materialized_function, function); |
144 | |
145 MaterializeArgumentsObject(materialized_function, function); | 176 MaterializeArgumentsObject(materialized_function, function); |
| 177 MaterializeContextChain(materialized_function, local_context); |
146 | 178 |
147 Handle<Context> with_context = isolate->factory()->NewWithContext( | 179 Handle<Context> with_context = isolate->factory()->NewWithContext( |
148 function, parent_context, materialized_function); | 180 function, receiver_context, materialized_function); |
149 | 181 |
150 ContextChainElement context_chain_element; | 182 ContextChainElement context_chain_element; |
151 context_chain_element.original_context = it.CurrentContext(); | 183 context_chain_element.original_context = local_context; |
152 context_chain_element.materialized_object = materialized_function; | 184 context_chain_element.materialized_object = materialized_function; |
153 context_chain_element.scope_info = it.CurrentScopeInfo(); | 185 context_chain_element.scope_info = it.CurrentScopeInfo(); |
154 context_chain_.Add(context_chain_element); | 186 context_chain_.Add(context_chain_element); |
155 | 187 |
156 stop = true; | 188 stop = true; |
157 RecordContextsInChain(&inner_context, with_context, with_context); | 189 RecordContextsInChain(&inner_context, receiver_context, with_context); |
158 } else if (scope_type == ScopeIterator::ScopeTypeCatch || | 190 } else if (scope_type == ScopeIterator::ScopeTypeCatch || |
159 scope_type == ScopeIterator::ScopeTypeWith) { | 191 scope_type == ScopeIterator::ScopeTypeWith) { |
160 Handle<Context> cloned_context = Handle<Context>::cast( | 192 Handle<Context> cloned_context = Handle<Context>::cast( |
161 isolate->factory()->CopyFixedArray(it.CurrentContext())); | 193 isolate->factory()->CopyFixedArray(it.CurrentContext())); |
162 | 194 |
163 ContextChainElement context_chain_element; | 195 ContextChainElement context_chain_element; |
164 context_chain_element.original_context = it.CurrentContext(); | 196 context_chain_element.original_context = it.CurrentContext(); |
165 context_chain_element.cloned_context = cloned_context; | 197 context_chain_element.cloned_context = cloned_context; |
166 context_chain_.Add(context_chain_element); | 198 context_chain_.Add(context_chain_element); |
167 | 199 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 } | 232 } |
201 } | 233 } |
202 if (innermost_context_.is_null()) { | 234 if (innermost_context_.is_null()) { |
203 innermost_context_ = outer_context; | 235 innermost_context_ = outer_context; |
204 } | 236 } |
205 DCHECK(!innermost_context_.is_null()); | 237 DCHECK(!innermost_context_.is_null()); |
206 } | 238 } |
207 | 239 |
208 | 240 |
209 void DebugEvaluate::ContextBuilder::UpdateValues() { | 241 void DebugEvaluate::ContextBuilder::UpdateValues() { |
| 242 // TODO(yangguo): remove updating values. |
210 for (int i = 0; i < context_chain_.length(); i++) { | 243 for (int i = 0; i < context_chain_.length(); i++) { |
211 ContextChainElement element = context_chain_[i]; | 244 ContextChainElement element = context_chain_[i]; |
212 if (!element.original_context.is_null() && | 245 if (!element.original_context.is_null() && |
213 !element.cloned_context.is_null()) { | 246 !element.cloned_context.is_null()) { |
214 Handle<Context> cloned_context = element.cloned_context; | 247 Handle<Context> cloned_context = element.cloned_context; |
215 cloned_context->CopyTo( | 248 cloned_context->CopyTo( |
216 Context::MIN_CONTEXT_SLOTS, *element.original_context, | 249 Context::MIN_CONTEXT_SLOTS, *element.original_context, |
217 Context::MIN_CONTEXT_SLOTS, | 250 Context::MIN_CONTEXT_SLOTS, |
218 cloned_context->length() - Context::MIN_CONTEXT_SLOTS); | 251 cloned_context->length() - Context::MIN_CONTEXT_SLOTS); |
219 } | 252 } |
220 if (!element.materialized_object.is_null()) { | 253 if (!element.materialized_object.is_null()) { |
221 // Write back potential changes to materialized stack locals to the | 254 // Write back potential changes to materialized stack locals to the |
222 // stack. | 255 // stack. |
223 FrameInspector(frame_, inlined_jsframe_index_, isolate_) | 256 FrameInspector(frame_, inlined_jsframe_index_, isolate_) |
224 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, | 257 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, |
225 element.scope_info); | 258 element.scope_info); |
| 259 if (element.scope_info->scope_type() == FUNCTION_SCOPE) { |
| 260 DCHECK_EQ(context_chain_.length() - 1, i); |
| 261 UpdateContextChainFromMaterializedObject(element.materialized_object, |
| 262 element.original_context); |
| 263 } |
226 } | 264 } |
227 } | 265 } |
228 } | 266 } |
229 | 267 |
230 | 268 |
231 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { | 269 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { |
232 Handle<JSObject> result = | 270 Handle<JSObject> result = |
233 isolate_->factory()->NewJSObject(isolate_->object_function()); | 271 isolate_->factory()->NewJSObject(isolate_->object_function()); |
234 Handle<Map> new_map = | 272 Handle<Map> new_map = |
235 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); | 273 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); |
(...skipping 28 matching lines...) Expand all Loading... |
264 // FunctionGetArguments can't throw an exception. | 302 // FunctionGetArguments can't throw an exception. |
265 Handle<JSObject> arguments = | 303 Handle<JSObject> arguments = |
266 Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); | 304 Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); |
267 Handle<String> arguments_str = isolate_->factory()->arguments_string(); | 305 Handle<String> arguments_str = isolate_->factory()->arguments_string(); |
268 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, | 306 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, |
269 NONE) | 307 NONE) |
270 .Check(); | 308 .Check(); |
271 } | 309 } |
272 | 310 |
273 | 311 |
| 312 MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext( |
| 313 Handle<Context> context, Handle<String> name) { |
| 314 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; |
| 315 int index; |
| 316 PropertyAttributes attributes; |
| 317 BindingFlags binding; |
| 318 Handle<Object> holder = |
| 319 context->Lookup(name, flags, &index, &attributes, &binding); |
| 320 if (holder.is_null()) return MaybeHandle<Object>(); |
| 321 Handle<Object> value; |
| 322 if (index != Context::kNotFound) { // Found on context. |
| 323 Handle<Context> context = Handle<Context>::cast(holder); |
| 324 return Handle<Object>(context->get(index), isolate_); |
| 325 } else { // Found on object. |
| 326 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); |
| 327 return JSReceiver::GetDataProperty(object, name); |
| 328 } |
| 329 } |
| 330 |
| 331 |
| 332 void DebugEvaluate::ContextBuilder::MaterializeContextChain( |
| 333 Handle<JSObject> target, Handle<Context> context) { |
| 334 for (const Handle<String>& name : non_locals_) { |
| 335 HandleScope scope(isolate_); |
| 336 Handle<Object> value; |
| 337 if (!LoadFromContext(context, name).ToHandle(&value)) continue; |
| 338 JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); |
| 339 } |
| 340 } |
| 341 |
| 342 |
| 343 void DebugEvaluate::ContextBuilder::StoreToContext(Handle<Context> context, |
| 344 Handle<String> name, |
| 345 Handle<Object> value) { |
| 346 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; |
| 347 int index; |
| 348 PropertyAttributes attributes; |
| 349 BindingFlags binding; |
| 350 Handle<Object> holder = |
| 351 context->Lookup(name, flags, &index, &attributes, &binding); |
| 352 if (holder.is_null()) return; |
| 353 if (attributes & READ_ONLY) return; |
| 354 if (index != Context::kNotFound) { // Found on context. |
| 355 Handle<Context> context = Handle<Context>::cast(holder); |
| 356 context->set(index, *value); |
| 357 } else { // Found on object. |
| 358 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); |
| 359 LookupIterator lookup(object, name); |
| 360 if (lookup.state() != LookupIterator::DATA) return; |
| 361 CHECK(JSReceiver::SetDataProperty(&lookup, value).FromJust()); |
| 362 } |
| 363 } |
| 364 |
| 365 |
| 366 void DebugEvaluate::ContextBuilder::UpdateContextChainFromMaterializedObject( |
| 367 Handle<JSObject> source, Handle<Context> context) { |
| 368 // TODO(yangguo): check whether overwriting context fields is actually safe |
| 369 // wrt fields we consider constant. |
| 370 for (const Handle<String>& name : non_locals_) { |
| 371 HandleScope scope(isolate_); |
| 372 Handle<Object> value = JSReceiver::GetDataProperty(source, name); |
| 373 StoreToContext(context, name, value); |
| 374 } |
| 375 } |
| 376 |
| 377 |
274 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver( | 378 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver( |
275 Handle<Context> target, Handle<JSFunction> function) { | 379 Handle<Context> parent_context, Handle<Context> lookup_context, |
276 Handle<SharedFunctionInfo> shared(function->shared()); | 380 Handle<JSFunction> function, bool this_is_non_local) { |
277 Handle<ScopeInfo> scope_info(shared->scope_info()); | 381 Handle<Object> receiver = isolate_->factory()->undefined_value(); |
278 Handle<Object> receiver; | 382 Handle<String> this_string = isolate_->factory()->this_string(); |
279 switch (scope_info->scope_type()) { | 383 if (this_is_non_local) { |
280 case FUNCTION_SCOPE: { | 384 LoadFromContext(lookup_context, this_string).ToHandle(&receiver); |
281 VariableMode mode; | 385 } else if (function->shared()->scope_info()->HasReceiver()) { |
282 InitializationFlag init_flag; | 386 receiver = handle(frame_->receiver(), isolate_); |
283 MaybeAssignedFlag maybe_assigned_flag; | |
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(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 |