Index: src/runtime.cc |
=================================================================== |
--- src/runtime.cc (revision 2147) |
+++ src/runtime.cc (working copy) |
@@ -6106,6 +6106,406 @@ |
} |
+// Copy all the context locals into an object used to materialize a scope. |
+static void CopyContextLocalsToScopeObject(Handle<Code> code, |
+ ScopeInfo<>& scope_info, |
+ Handle<Context> context, |
+ Handle<JSObject> scope_object) { |
+ // Fill all context locals to the context extension. |
+ for (int i = Context::MIN_CONTEXT_SLOTS; |
+ i < scope_info.number_of_context_slots(); |
+ i++) { |
+ int context_index = |
+ ScopeInfo<>::ContextSlotIndex(*code, |
+ *scope_info.context_slot_name(i), |
+ NULL); |
+ |
+ // Don't include the arguments shadow (.arguments) context variable. |
+ if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) { |
+ SetProperty(scope_object, |
+ scope_info.context_slot_name(i), |
+ Handle<Object>(context->get(context_index)), NONE); |
+ } |
+ } |
+} |
+ |
+ |
+// Create a plain JSObject which materializes the local scope for the specified |
+// frame. |
+static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) { |
+ Handle<JSFunction> function(JSFunction::cast(frame->function())); |
+ Handle<Code> code(function->code()); |
+ ScopeInfo<> scope_info(*code); |
+ |
+ // Allocate and initialize a JSObject with all the arguments, stack locals |
+ // heap locals and extension properties of the debugged function. |
+ Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function()); |
+ |
+ // First fill all parameters. |
+ for (int i = 0; i < scope_info.number_of_parameters(); ++i) { |
+ SetProperty(local_scope, |
+ scope_info.parameter_name(i), |
+ Handle<Object>(frame->GetParameter(i)), NONE); |
+ } |
+ |
+ // Second fill all stack locals. |
+ for (int i = 0; i < scope_info.number_of_stack_slots(); i++) { |
+ SetProperty(local_scope, |
+ scope_info.stack_slot_name(i), |
+ Handle<Object>(frame->GetExpression(i)), NONE); |
+ } |
+ |
+ // Third fill all context locals. |
+ Handle<Context> frame_context(Context::cast(frame->context())); |
+ Handle<Context> function_context(frame_context->fcontext()); |
+ CopyContextLocalsToScopeObject(code, scope_info, |
+ function_context, local_scope); |
+ |
+ // Finally copy any properties from the function context extension. This will |
+ // be variables introduced by eval. |
+ if (function_context->closure() == *function) { |
+ if (function_context->has_extension() && |
+ !function_context->IsGlobalContext()) { |
+ Handle<JSObject> ext(JSObject::cast(function_context->extension())); |
+ Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext); |
+ for (int i = 0; i < keys->length(); i++) { |
+ // Names of variables introduced by eval are strings. |
+ ASSERT(keys->get(i)->IsString()); |
+ Handle<String> key(String::cast(keys->get(i))); |
+ SetProperty(local_scope, key, GetProperty(ext, key), NONE); |
+ } |
+ } |
+ } |
+ return local_scope; |
+} |
+ |
+ |
+// Create a plain JSObject which materializes the closure content for the |
+// context. |
+static Handle<JSObject> MaterializeClosure(Handle<Context> context) { |
+ ASSERT(context->is_function_context()); |
+ |
+ Handle<Code> code(context->closure()->code()); |
+ ScopeInfo<> scope_info(*code); |
+ |
+ // Allocate and initialize a JSObject with all the content of theis function |
+ // closure. |
+ Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function()); |
+ |
+ // Check whether the arguments shadow object exists. |
+ int arguments_shadow_index = |
+ ScopeInfo<>::ContextSlotIndex(*code, |
+ Heap::arguments_shadow_symbol(), |
+ NULL); |
+ if (arguments_shadow_index >= 0) { |
+ // In this case all the arguments are available in the arguments shadow |
+ // object. |
+ Handle<JSObject> arguments_shadow( |
+ JSObject::cast(context->get(arguments_shadow_index))); |
+ for (int i = 0; i < scope_info.number_of_parameters(); ++i) { |
+ SetProperty(closure_scope, |
+ scope_info.parameter_name(i), |
+ Handle<Object>(arguments_shadow->GetElement(i)), NONE); |
+ } |
+ } |
+ |
+ // Fill all context locals to the context extension. |
+ CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope); |
+ |
+ // Finally copy any properties from the function context extension. This will |
+ // be variables introduced by eval. |
+ if (context->has_extension()) { |
+ Handle<JSObject> ext(JSObject::cast(context->extension())); |
+ Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext); |
+ for (int i = 0; i < keys->length(); i++) { |
+ // Names of variables introduced by eval are strings. |
+ ASSERT(keys->get(i)->IsString()); |
+ Handle<String> key(String::cast(keys->get(i))); |
+ SetProperty(closure_scope, key, GetProperty(ext, key), NONE); |
+ } |
+ } |
+ |
+ return closure_scope; |
+} |
+ |
+ |
+// Iterate over the actual scopes visible from a stack frame. All scopes are |
+// backed by an actual context except the local scope, which is inserted |
+// "artifically" in the context chain. |
+class ScopeIterator { |
+ public: |
+ enum ScopeType { |
+ ScopeTypeGlobal = 0, |
+ ScopeTypeLocal, |
+ ScopeTypeWith, |
+ ScopeTypeClosure |
+ }; |
+ |
+ explicit ScopeIterator(JavaScriptFrame* frame) |
+ : frame_(frame), |
+ function_(JSFunction::cast(frame->function())), |
+ context_(Context::cast(frame->context())), |
+ local_done_(false), |
+ at_local_(false) { |
+ |
+ // Check whether the first scope is actually a local scope. |
+ if (context_->IsGlobalContext()) { |
+ // If there is a stack slot for .result then this local scope has been |
+ // created for evaluating top level code and it is not a real local scope. |
+ // Checking for the existence of .result seems fragile, but the scope info |
+ // saved with the code object does not otherwise have that information. |
+ Handle<Code> code(function_->code()); |
+ int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol()); |
+ at_local_ = index < 0; |
+ } else if (context_->is_function_context()) { |
+ at_local_ = true; |
+ } |
+ } |
+ |
+ // More scopes? |
+ bool Done() { return context_.is_null(); } |
+ |
+ // Move to the next scope. |
+ void Next() { |
+ // If at a local scope mark the local scope as passed. |
+ if (at_local_) { |
+ at_local_ = false; |
+ local_done_ = true; |
+ |
+ // If the current context is not associated with the local scope the |
+ // current context is the next real scope, so don't move to the next |
+ // context in this case. |
+ if (context_->closure() != *function_) { |
+ return; |
+ } |
+ } |
+ |
+ // The global scope is always the last in the chain. |
+ if (context_->IsGlobalContext()) { |
+ context_ = Handle<Context>(); |
+ return; |
+ } |
+ |
+ // Move to the next context. |
+ if (context_->is_function_context()) { |
+ context_ = Handle<Context>(Context::cast(context_->closure()->context())); |
+ } else { |
+ context_ = Handle<Context>(context_->previous()); |
+ } |
+ |
+ // If passing the local scope indicate that the current scope is now the |
+ // local scope. |
+ if (!local_done_ && |
+ (context_->IsGlobalContext() || (context_->is_function_context()))) { |
+ at_local_ = true; |
+ } |
+ } |
+ |
+ // Return the type of the current scope. |
+ int Type() { |
+ if (at_local_) { |
+ return ScopeTypeLocal; |
+ } |
+ if (context_->IsGlobalContext()) { |
+ ASSERT(context_->global()->IsGlobalObject()); |
+ return ScopeTypeGlobal; |
+ } |
+ if (context_->is_function_context()) { |
+ return ScopeTypeClosure; |
+ } |
+ ASSERT(context_->has_extension()); |
+ ASSERT(!context_->extension()->IsJSContextExtensionObject()); |
+ return ScopeTypeWith; |
+ } |
+ |
+ // Return the JavaScript object with the content of the current scope. |
+ Handle<JSObject> ScopeObject() { |
+ switch (Type()) { |
+ case ScopeIterator::ScopeTypeGlobal: |
+ return Handle<JSObject>(CurrentContext()->global()); |
+ break; |
+ case ScopeIterator::ScopeTypeLocal: |
+ // Materialize the content of the local scope into a JSObject. |
+ return MaterializeLocalScope(frame_); |
+ break; |
+ case ScopeIterator::ScopeTypeWith: |
+ // Return the with object. |
+ return Handle<JSObject>(CurrentContext()->extension()); |
+ break; |
+ case ScopeIterator::ScopeTypeClosure: |
+ // Materialize the content of the closure scope into a JSObject. |
+ return MaterializeClosure(CurrentContext()); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ return Handle<JSObject>(); |
+ } |
+ } |
+ |
+ // Return the context for this scope. For the local context there might not |
+ // be an actual context. |
+ Handle<Context> CurrentContext() { |
+ if (at_local_ && context_->closure() != *function_) { |
+ return Handle<Context>(); |
+ } |
+ return context_; |
+ } |
+ |
+#ifdef DEBUG |
+ // Debug print of the content of the current scope. |
+ void DebugPrint() { |
+ switch (Type()) { |
+ case ScopeIterator::ScopeTypeGlobal: |
+ PrintF("Global:\n"); |
+ CurrentContext()->Print(); |
+ break; |
+ |
+ case ScopeIterator::ScopeTypeLocal: { |
+ PrintF("Local:\n"); |
+ Handle<Code> code(function_->code()); |
+ ScopeInfo<> scope_info(*code); |
+ scope_info.Print(); |
+ if (!CurrentContext().is_null()) { |
+ CurrentContext()->Print(); |
+ if (CurrentContext()->has_extension()) { |
+ Handle<JSObject> extension = |
+ Handle<JSObject>(CurrentContext()->extension()); |
+ if (extension->IsJSContextExtensionObject()) { |
+ extension->Print(); |
+ } |
+ } |
+ } |
+ break; |
+ } |
+ |
+ case ScopeIterator::ScopeTypeWith: { |
+ PrintF("With:\n"); |
+ Handle<JSObject> extension = |
+ Handle<JSObject>(CurrentContext()->extension()); |
+ extension->Print(); |
+ break; |
+ } |
+ |
+ case ScopeIterator::ScopeTypeClosure: { |
+ PrintF("Closure:\n"); |
+ CurrentContext()->Print(); |
+ if (CurrentContext()->has_extension()) { |
+ Handle<JSObject> extension = |
+ Handle<JSObject>(CurrentContext()->extension()); |
+ if (extension->IsJSContextExtensionObject()) { |
+ extension->Print(); |
+ } |
+ } |
+ break; |
+ } |
+ |
+ default: |
+ UNREACHABLE(); |
+ } |
+ PrintF("\n"); |
+ } |
+#endif |
+ |
+ private: |
+ JavaScriptFrame* frame_; |
+ Handle<JSFunction> function_; |
+ Handle<Context> context_; |
+ bool local_done_; |
+ bool at_local_; |
+ |
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); |
+}; |
+ |
+ |
+static Object* Runtime_GetScopeCount(Arguments args) { |
+ HandleScope scope; |
+ ASSERT(args.length() == 2); |
+ |
+ // Check arguments. |
+ Object* check = Runtime_CheckExecutionState(args); |
+ if (check->IsFailure()) return check; |
+ CONVERT_CHECKED(Smi, wrapped_id, args[1]); |
+ |
+ // Get the frame where the debugging is performed. |
+ StackFrame::Id id = UnwrapFrameId(wrapped_id); |
+ JavaScriptFrameIterator it(id); |
+ JavaScriptFrame* frame = it.frame(); |
+ |
+ // Count the visible scopes. |
+ int n = 0; |
+ for (ScopeIterator it(frame); !it.Done(); it.Next()) { |
+ n++; |
+ } |
+ |
+ return Smi::FromInt(n); |
+} |
+ |
+ |
+static const int kScopeDetailsTypeIndex = 0; |
+static const int kScopeDetailsObjectIndex = 1; |
+static const int kScopeDetailsSize = 2; |
+ |
+// Return an array with scope details |
+// args[0]: number: break id |
+// args[1]: number: frame index |
+// args[2]: number: scope index |
+// |
+// The array returned contains the following information: |
+// 0: Scope type |
+// 1: Scope object |
+static Object* Runtime_GetScopeDetails(Arguments args) { |
+ HandleScope scope; |
+ ASSERT(args.length() == 3); |
+ |
+ // Check arguments. |
+ Object* check = Runtime_CheckExecutionState(args); |
+ if (check->IsFailure()) return check; |
+ CONVERT_CHECKED(Smi, wrapped_id, args[1]); |
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]); |
+ |
+ // Get the frame where the debugging is performed. |
+ StackFrame::Id id = UnwrapFrameId(wrapped_id); |
+ JavaScriptFrameIterator frame_it(id); |
+ JavaScriptFrame* frame = frame_it.frame(); |
+ |
+ // Find the requested scope. |
+ int n = 0; |
+ ScopeIterator it(frame); |
+ for (; !it.Done() && n < index; it.Next()) { |
+ n++; |
+ } |
+ if (it.Done()) { |
+ return Heap::undefined_value(); |
+ } |
+ |
+ // Calculate the size of the result. |
+ int details_size = kScopeDetailsSize; |
+ Handle<FixedArray> details = Factory::NewFixedArray(details_size); |
+ |
+ // Fill in scope details. |
+ details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type())); |
+ details->set(kScopeDetailsObjectIndex, *it.ScopeObject()); |
+ |
+ return *Factory::NewJSArrayWithElements(details); |
+} |
+ |
+ |
+static Object* Runtime_DebugPrintScopes(Arguments args) { |
+ HandleScope scope; |
+ ASSERT(args.length() == 0); |
+ |
+#ifdef DEBUG |
+ // Print the scopes for the top frame. |
+ StackFrameLocator locator; |
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); |
+ for (ScopeIterator it(frame); !it.Done(); it.Next()) { |
+ it.DebugPrint(); |
+ } |
+#endif |
+ return Heap::undefined_value(); |
+} |
+ |
+ |
static Object* Runtime_GetCFrames(Arguments args) { |
HandleScope scope; |
ASSERT(args.length() == 1); |
@@ -6568,54 +6968,17 @@ |
ASSERT(go_between_sinfo.number_of_context_slots() == 0); |
#endif |
- // Allocate and initialize a context extension object with all the |
- // arguments, stack locals heap locals and extension properties of the |
- // debugged function. |
- Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function()); |
- // First fill all parameters to the context extension. |
- for (int i = 0; i < sinfo.number_of_parameters(); ++i) { |
- SetProperty(context_ext, |
- sinfo.parameter_name(i), |
- Handle<Object>(frame->GetParameter(i)), NONE); |
- } |
- // Second fill all stack locals to the context extension. |
- for (int i = 0; i < sinfo.number_of_stack_slots(); i++) { |
- SetProperty(context_ext, |
- sinfo.stack_slot_name(i), |
- Handle<Object>(frame->GetExpression(i)), NONE); |
- } |
- // Third fill all context locals to the context extension. |
- Handle<Context> frame_context(Context::cast(frame->context())); |
- Handle<Context> function_context(frame_context->fcontext()); |
- for (int i = Context::MIN_CONTEXT_SLOTS; |
- i < sinfo.number_of_context_slots(); |
- ++i) { |
- int context_index = |
- ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL); |
- SetProperty(context_ext, |
- sinfo.context_slot_name(i), |
- Handle<Object>(function_context->get(context_index)), NONE); |
- } |
- // Finally copy any properties from the function context extension. This will |
- // be variables introduced by eval. |
- if (function_context->has_extension() && |
- !function_context->IsGlobalContext()) { |
- Handle<JSObject> ext(JSObject::cast(function_context->extension())); |
- Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext); |
- for (int i = 0; i < keys->length(); i++) { |
- // Names of variables introduced by eval are strings. |
- ASSERT(keys->get(i)->IsString()); |
- Handle<String> key(String::cast(keys->get(i))); |
- SetProperty(context_ext, key, GetProperty(ext, key), NONE); |
- } |
- } |
+ // Materialize the content of the local scope into a JSObject. |
+ Handle<JSObject> local_scope = MaterializeLocalScope(frame); |
// Allocate a new context for the debug evaluation and set the extension |
// object build. |
Handle<Context> context = |
Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between); |
- context->set_extension(*context_ext); |
+ context->set_extension(*local_scope); |
// Copy any with contexts present and chain them in front of this context. |
+ Handle<Context> frame_context(Context::cast(frame->context())); |
+ Handle<Context> function_context(frame_context->fcontext()); |
context = CopyWithContextChain(frame_context, context); |
// Wrap the evaluation statement in a new function compiled in the newly |
@@ -6657,6 +7020,13 @@ |
Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver, |
argc, argv, &has_pending_exception); |
if (has_pending_exception) return Failure::Exception(); |
+ |
+ // Skip the global proxy as it has no properties and always delegates to the |
+ // real global object. |
+ if (result->IsJSGlobalProxy()) { |
+ result = Handle<JSObject>(JSObject::cast(result->GetPrototype())); |
+ } |
+ |
return *result; |
} |