Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Unified Diff: src/runtime.cc

Issue 123021: Add scope chain information to the debugger (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/runtime.h ('k') | test/mjsunit/debug-scopes.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
« no previous file with comments | « src/runtime.h ('k') | test/mjsunit/debug-scopes.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698