Index: src/debug/debug-scopes.cc |
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc |
index 3b0016fdfe35a633b9ad8fbcea6cc4c1115256bb..d9c615b01b4c58db22aadf5ab8d3eb004ae792c0 100644 |
--- a/src/debug/debug-scopes.cc |
+++ b/src/debug/debug-scopes.cc |
@@ -108,6 +108,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, |
if (!ignore_nested_scopes) RetrieveScopeChain(scope); |
if (collect_non_locals) CollectNonLocals(scope); |
} |
+ UnwrapEvaluationContext(); |
} |
@@ -118,6 +119,23 @@ ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) |
seen_script_scope_(false), |
failed_(false) { |
if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>(); |
+ UnwrapEvaluationContext(); |
+} |
+ |
+void ScopeIterator::UnwrapEvaluationContext() { |
+ while (true) { |
+ if (context_.is_null()) return; |
+ if (!context_->IsDebugEvaluateContext()) return; |
Camillo Bruni
2016/04/05 11:37:59
so you can have only one debug context (or directl
Yang
2016/04/05 11:43:00
No. The point of this CL is that the scope iterato
|
+ // An existing debug-evaluate context can only be outside the local scope. |
+ DCHECK(nested_scope_chain_.is_empty()); |
+ Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX), |
+ isolate_); |
+ if (wrapped->IsContext()) { |
+ context_ = Handle<Context>::cast(wrapped); |
+ } else { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ } |
} |
@@ -168,9 +186,7 @@ void ScopeIterator::Next() { |
// The global scope is always the last in the chain. |
DCHECK(context_->IsNativeContext()); |
context_ = Handle<Context>(); |
- return; |
- } |
- if (scope_type == ScopeTypeScript) { |
+ } else if (scope_type == ScopeTypeScript) { |
seen_script_scope_ = true; |
if (context_->IsScriptContext()) { |
context_ = Handle<Context>(context_->previous(), isolate_); |
@@ -182,9 +198,7 @@ void ScopeIterator::Next() { |
DCHECK(nested_scope_chain_.is_empty()); |
} |
CHECK(context_->IsNativeContext()); |
- return; |
- } |
- if (nested_scope_chain_.is_empty()) { |
+ } else if (nested_scope_chain_.is_empty()) { |
context_ = Handle<Context>(context_->previous(), isolate_); |
} else { |
if (nested_scope_chain_.last().scope_info->HasContext()) { |
@@ -193,6 +207,7 @@ void ScopeIterator::Next() { |
} |
nested_scope_chain_.RemoveLast(); |
} |
+ UnwrapEvaluationContext(); |
} |
@@ -262,9 +277,7 @@ MaybeHandle<JSObject> ScopeIterator::ScopeObject() { |
DCHECK(nested_scope_chain_.length() == 1); |
return MaterializeLocalScope(); |
case ScopeIterator::ScopeTypeWith: |
- // Return the with object. |
- // TODO(neis): This breaks for proxies. |
- return handle(JSObject::cast(CurrentContext()->extension_receiver())); |
+ return WithContextExtension(); |
case ScopeIterator::ScopeTypeCatch: |
return MaterializeCatchScope(); |
case ScopeIterator::ScopeTypeClosure: |
@@ -534,6 +547,16 @@ Handle<JSObject> ScopeIterator::MaterializeCatchScope() { |
return catch_scope; |
} |
+// Retrieve the with-context extension object. If the extension object is |
+// a proxy, return an empty object. |
+Handle<JSObject> ScopeIterator::WithContextExtension() { |
+ Handle<Context> context = CurrentContext(); |
+ DCHECK(context->IsWithContext()); |
+ if (context->extension_receiver()->IsJSProxy()) { |
+ return isolate_->factory()->NewJSObjectWithNullProto(); |
+ } |
+ return handle(JSObject::cast(context->extension_receiver())); |
+} |
// Create a plain JSObject which materializes the block scope for the specified |
// block context. |