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

Unified Diff: src/debug/debug-scopes.cc

Issue 1264993002: Debugger: refactor ScopeIterator, FrameInspector and DebugEvaluate. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: readd include Created 5 years, 4 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/debug/debug-scopes.h ('k') | src/globals.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/debug/debug-scopes.cc
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e3b4e2c9985d55f1288b613eaf46877f4c7640e6
--- /dev/null
+++ b/src/debug/debug-scopes.cc
@@ -0,0 +1,769 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/debug-scopes.h"
+
+#include "src/debug/debug.h"
+#include "src/globals.h"
+#include "src/parser.h"
+#include "src/scopes.h"
+
+namespace v8 {
+namespace internal {
+
+ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
+ bool ignore_nested_scopes)
+ : isolate_(isolate),
+ frame_inspector_(frame_inspector),
+ nested_scope_chain_(4),
+ seen_script_scope_(false),
+ failed_(false) {
+ if (!frame_inspector->GetContext()->IsContext() ||
+ !frame_inspector->GetFunction()->IsJSFunction()) {
+ // Optimized frame, context or function cannot be materialized. Give up.
+ return;
+ }
+
+ context_ = Handle<Context>(Context::cast(frame_inspector->GetContext()));
+
+ // Catch the case when the debugger stops in an internal function.
+ Handle<JSFunction> function = GetFunction();
+ Handle<SharedFunctionInfo> shared_info(function->shared());
+ Handle<ScopeInfo> scope_info(shared_info->scope_info());
+ if (shared_info->script() == isolate->heap()->undefined_value()) {
+ while (context_->closure() == *function) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ return;
+ }
+
+ // Currently it takes too much time to find nested scopes due to script
+ // parsing. Sometimes we want to run the ScopeIterator as fast as possible
+ // (for example, while collecting async call stacks on every
+ // addEventListener call), even if we drop some nested scopes.
+ // Later we may optimize getting the nested scopes (cache the result?)
+ // and include nested scopes into the "fast" iteration case as well.
+
+ if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
+ // The source position at return is always the end of the function,
+ // which is not consistent with the current scope chain. Therefore all
+ // nested with, catch and block contexts are skipped, and we can only
+ // inspect the function scope.
+ // This can only happen if we set a break point inside right before the
+ // return, which requires a debug info to be available.
+ Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
+
+ // PC points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ Address call_pc = GetFrame()->pc() - 1;
+
+ // Find the break point where execution has stopped.
+ BreakLocation location =
+ BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
+
+ ignore_nested_scopes = location.IsReturn();
+ }
+
+ if (ignore_nested_scopes) {
+ if (scope_info->HasContext()) {
+ context_ = Handle<Context>(context_->declaration_context(), isolate_);
+ } else {
+ while (context_->closure() == *function) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ }
+ if (scope_info->scope_type() == FUNCTION_SCOPE ||
+ scope_info->scope_type() == ARROW_SCOPE) {
+ nested_scope_chain_.Add(scope_info);
+ }
+ } else {
+ // Reparse the code and analyze the scopes.
+ Handle<Script> script(Script::cast(shared_info->script()));
+ Scope* scope = NULL;
+
+ // Check whether we are in global, eval or function code.
+ Handle<ScopeInfo> scope_info(shared_info->scope_info());
brucedawson 2015/08/10 18:08:08 This variable shadows the same-named variable in t
Yang 2015/08/11 08:13:36 You are right. This is indeed redundant. I simply
+ Zone zone;
+ if (scope_info->scope_type() != FUNCTION_SCOPE &&
+ scope_info->scope_type() != ARROW_SCOPE) {
+ // Global or eval code.
+ ParseInfo info(&zone, script);
+ if (scope_info->scope_type() == SCRIPT_SCOPE) {
+ info.set_global();
+ } else {
+ DCHECK(scope_info->scope_type() == EVAL_SCOPE);
+ info.set_eval();
+ info.set_context(Handle<Context>(function->context()));
+ }
+ if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ } else {
+ // Function code
+ ParseInfo info(&zone, function);
+ if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ }
+ }
+}
+
+
+ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
+ : isolate_(isolate),
+ frame_inspector_(NULL),
+ context_(function->context()),
+ seen_script_scope_(false),
+ failed_(false) {
+ if (function->IsBuiltin()) context_ = Handle<Context>();
+}
+
+
+MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
+ // Calculate the size of the result.
+ Handle<FixedArray> details =
+ isolate_->factory()->NewFixedArray(kScopeDetailsSize);
+ // Fill in scope details.
+ details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
+ Handle<JSObject> scope_object;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
+ details->set(kScopeDetailsObjectIndex, *scope_object);
+ return isolate_->factory()->NewJSArrayWithElements(details);
+}
+
+
+void ScopeIterator::Next() {
+ DCHECK(!failed_);
+ ScopeType scope_type = Type();
+ if (scope_type == ScopeTypeGlobal) {
+ // The global scope is always the last in the chain.
+ DCHECK(context_->IsNativeContext());
+ context_ = Handle<Context>();
+ return;
+ }
+ if (scope_type == ScopeTypeScript) {
+ seen_script_scope_ = true;
+ if (context_->IsScriptContext()) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ if (!nested_scope_chain_.is_empty()) {
+ DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE);
+ nested_scope_chain_.RemoveLast();
+ DCHECK(nested_scope_chain_.is_empty());
+ }
+ CHECK(context_->IsNativeContext());
+ return;
+ }
+ if (nested_scope_chain_.is_empty()) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ } else {
+ if (nested_scope_chain_.last()->HasContext()) {
+ DCHECK(context_->previous() != NULL);
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ nested_scope_chain_.RemoveLast();
+ }
+}
+
+
+// Return the type of the current scope.
+ScopeIterator::ScopeType ScopeIterator::Type() {
+ DCHECK(!failed_);
+ if (!nested_scope_chain_.is_empty()) {
+ Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
+ switch (scope_info->scope_type()) {
+ case FUNCTION_SCOPE:
+ case ARROW_SCOPE:
+ DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
+ return ScopeTypeLocal;
+ case MODULE_SCOPE:
+ DCHECK(context_->IsModuleContext());
+ return ScopeTypeModule;
+ case SCRIPT_SCOPE:
+ DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
+ return ScopeTypeScript;
+ case WITH_SCOPE:
+ DCHECK(context_->IsWithContext());
+ return ScopeTypeWith;
+ case CATCH_SCOPE:
+ DCHECK(context_->IsCatchContext());
+ return ScopeTypeCatch;
+ case BLOCK_SCOPE:
+ DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
+ return ScopeTypeBlock;
+ case EVAL_SCOPE:
+ UNREACHABLE();
+ }
+ }
+ if (context_->IsNativeContext()) {
+ DCHECK(context_->global_object()->IsGlobalObject());
+ // If we are at the native context and have not yet seen script scope,
+ // fake it.
+ return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
+ }
+ if (context_->IsFunctionContext()) {
+ return ScopeTypeClosure;
+ }
+ if (context_->IsCatchContext()) {
+ return ScopeTypeCatch;
+ }
+ if (context_->IsBlockContext()) {
+ return ScopeTypeBlock;
+ }
+ if (context_->IsModuleContext()) {
+ return ScopeTypeModule;
+ }
+ if (context_->IsScriptContext()) {
+ return ScopeTypeScript;
+ }
+ DCHECK(context_->IsWithContext());
+ return ScopeTypeWith;
+}
+
+
+MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
+ DCHECK(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ return Handle<JSObject>(CurrentContext()->global_object());
+ case ScopeIterator::ScopeTypeScript:
+ return MaterializeScriptScope();
+ case ScopeIterator::ScopeTypeLocal:
+ // Materialize the content of the local scope into a JSObject.
+ DCHECK(nested_scope_chain_.length() == 1);
+ return MaterializeLocalScope();
+ case ScopeIterator::ScopeTypeWith:
+ // Return the with object.
+ return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
+ case ScopeIterator::ScopeTypeCatch:
+ return MaterializeCatchScope();
+ case ScopeIterator::ScopeTypeClosure:
+ // Materialize the content of the closure scope into a JSObject.
+ return MaterializeClosure();
+ case ScopeIterator::ScopeTypeBlock:
+ return MaterializeBlockScope();
+ case ScopeIterator::ScopeTypeModule:
+ return MaterializeModuleScope();
+ }
+ UNREACHABLE();
+ return Handle<JSObject>();
+}
+
+
+bool ScopeIterator::HasContext() {
+ ScopeType type = Type();
+ if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
+ if (!nested_scope_chain_.is_empty()) {
+ return nested_scope_chain_.last()->HasContext();
+ }
+ }
+ return true;
+}
+
+
+bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ DCHECK(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ break;
+ case ScopeIterator::ScopeTypeLocal:
+ return SetLocalVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeWith:
+ break;
+ case ScopeIterator::ScopeTypeCatch:
+ return SetCatchVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeClosure:
+ return SetClosureVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeScript:
+ return SetScriptVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeBlock:
+ return SetBlockVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeModule:
+ // TODO(2399): should we implement it?
+ break;
+ }
+ return false;
+}
+
+
+Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
+ DCHECK(!failed_);
+ if (!nested_scope_chain_.is_empty()) {
+ return nested_scope_chain_.last();
+ } else if (context_->IsBlockContext()) {
+ return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
+ } else if (context_->IsFunctionContext()) {
+ return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
+ }
+ return Handle<ScopeInfo>::null();
+}
+
+
+Handle<Context> ScopeIterator::CurrentContext() {
+ DCHECK(!failed_);
+ if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
+ nested_scope_chain_.is_empty()) {
+ return context_;
+ } else if (nested_scope_chain_.last()->HasContext()) {
+ return context_;
+ } else {
+ return Handle<Context>();
+ }
+}
+
+#ifdef DEBUG
+// Debug print of the content of the current scope.
+void ScopeIterator::DebugPrint() {
+ OFStream os(stdout);
+ DCHECK(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ os << "Global:\n";
+ CurrentContext()->Print(os);
+ break;
+
+ case ScopeIterator::ScopeTypeLocal: {
+ os << "Local:\n";
+ GetFunction()->shared()->scope_info()->Print();
+ if (!CurrentContext().is_null()) {
+ CurrentContext()->Print(os);
+ if (CurrentContext()->has_extension()) {
+ Handle<Object> extension(CurrentContext()->extension(), isolate_);
+ if (extension->IsJSContextExtensionObject()) {
+ extension->Print(os);
+ }
+ }
+ }
+ break;
+ }
+
+ case ScopeIterator::ScopeTypeWith:
+ os << "With:\n";
+ CurrentContext()->extension()->Print(os);
+ break;
+
+ case ScopeIterator::ScopeTypeCatch:
+ os << "Catch:\n";
+ CurrentContext()->extension()->Print(os);
+ CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
+ break;
+
+ case ScopeIterator::ScopeTypeClosure:
+ os << "Closure:\n";
+ CurrentContext()->Print(os);
+ if (CurrentContext()->has_extension()) {
+ Handle<Object> extension(CurrentContext()->extension(), isolate_);
+ if (extension->IsJSContextExtensionObject()) {
+ extension->Print(os);
+ }
+ }
+ break;
+
+ case ScopeIterator::ScopeTypeScript:
+ os << "Script:\n";
+ CurrentContext()
+ ->global_object()
+ ->native_context()
+ ->script_context_table()
+ ->Print(os);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ PrintF("\n");
+}
+#endif
+
+
+void ScopeIterator::RetrieveScopeChain(Scope* scope,
+ Handle<SharedFunctionInfo> shared_info) {
+ if (scope != NULL) {
+ int source_position = frame_inspector_->GetSourcePosition();
+ scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position);
+ } else {
+ // A failed reparse indicates that the preparser has diverged from the
+ // parser or that the preparse data given to the initial parse has been
+ // faulty. We fail in debug mode but in release mode we only provide the
+ // information we get from the context chain but nothing about
+ // completely stack allocated scopes or stack allocated locals.
+ // Or it could be due to stack overflow.
+ DCHECK(isolate_->has_pending_exception());
+ failed_ = true;
+ }
+}
+
+
+MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
+ Handle<GlobalObject> global(CurrentContext()->global_object());
+ Handle<ScriptContextTable> script_contexts(
+ global->native_context()->script_context_table());
+
+ Handle<JSObject> script_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ for (int context_index = 0; context_index < script_contexts->used();
+ context_index++) {
+ Handle<Context> context =
+ ScriptContextTable::GetContext(script_contexts, context_index);
+ Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+ CopyContextLocalsToScopeObject(scope_info, context, script_scope);
+ }
+ return script_scope;
+}
+
+
+MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
+ Handle<JSFunction> function = GetFunction();
+
+ Handle<JSObject> local_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+ frame_inspector_->MaterializeStackLocals(local_scope, function);
+
+ Handle<Context> frame_context(Context::cast(frame_inspector_->GetContext()));
+
+ HandleScope scope(isolate_);
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ if (!scope_info->HasContext()) return local_scope;
+
+ // Third fill all context locals.
+ Handle<Context> function_context(frame_context->declaration_context());
+ CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
+
+ // Finally copy any properties from the function context extension.
+ // These will be variables introduced by eval.
+ if (function_context->closure() == *function) {
+ if (function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate_, keys, JSReceiver::GetKeys(ext, JSReceiver::INCLUDE_PROTOS),
+ JSObject);
+
+ for (int i = 0; i < keys->length(); i++) {
+ // Names of variables introduced by eval are strings.
+ DCHECK(keys->get(i)->IsString());
+ Handle<String> key(String::cast(keys->get(i)));
+ Handle<Object> value;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate_, value, Object::GetPropertyOrElement(ext, key), JSObject);
+ RETURN_ON_EXCEPTION(isolate_,
+ Runtime::SetObjectProperty(isolate_, local_scope,
+ key, value, SLOPPY),
+ JSObject);
+ }
+ }
+ }
+
+ return local_scope;
+}
+
+
+// Create a plain JSObject which materializes the closure content for the
+// context.
+Handle<JSObject> ScopeIterator::MaterializeClosure() {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsFunctionContext());
+
+ Handle<SharedFunctionInfo> shared(context->closure()->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // Allocate and initialize a JSObject with all the content of this function
+ // closure.
+ Handle<JSObject> closure_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ // Fill all context locals to the context extension.
+ CopyContextLocalsToScopeObject(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()));
+ DCHECK(ext->IsJSContextExtensionObject());
+ Handle<FixedArray> keys =
+ JSReceiver::GetKeys(ext, JSReceiver::OWN_ONLY).ToHandleChecked();
+
+ for (int i = 0; i < keys->length(); i++) {
+ HandleScope scope(isolate_);
+ // Names of variables introduced by eval are strings.
+ DCHECK(keys->get(i)->IsString());
+ Handle<String> key(String::cast(keys->get(i)));
+ Handle<Object> value = Object::GetProperty(ext, key).ToHandleChecked();
+ JSObject::SetOwnPropertyIgnoreAttributes(closure_scope, key, value, NONE)
+ .Check();
+ }
+ }
+
+ return closure_scope;
+}
+
+
+// Create a plain JSObject which materializes the scope for the specified
+// catch context.
+Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsCatchContext());
+ Handle<String> name(String::cast(context->extension()));
+ Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
+ isolate_);
+ Handle<JSObject> catch_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+ JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
+ NONE)
+ .Check();
+ return catch_scope;
+}
+
+
+// Create a plain JSObject which materializes the block scope for the specified
+// block context.
+Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
+ Handle<JSObject> block_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ Handle<Context> context = Handle<Context>::null();
+ if (!nested_scope_chain_.is_empty()) {
+ Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
+ frame_inspector_->MaterializeStackLocals(block_scope, scope_info);
+ if (scope_info->HasContext()) context = CurrentContext();
+ } else {
+ context = CurrentContext();
+ }
+
+ if (!context.is_null()) {
+ Handle<ScopeInfo> scope_info_from_context(
+ ScopeInfo::cast(context->extension()));
+ // Fill all context locals.
+ CopyContextLocalsToScopeObject(scope_info_from_context, context,
+ block_scope);
+ }
+ return block_scope;
+}
+
+
+// Create a plain JSObject which materializes the module scope for the specified
+// module context.
+MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsModuleContext());
+ Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+
+ // Allocate and initialize a JSObject with all the members of the debugged
+ // module.
+ Handle<JSObject> module_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ // Fill all context locals.
+ CopyContextLocalsToScopeObject(scope_info, context, module_scope);
+
+ return module_scope;
+}
+
+
+// Set the context local variable value.
+bool ScopeIterator::SetContextLocalValue(Handle<ScopeInfo> scope_info,
+ Handle<Context> context,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
+ Handle<String> next_name(scope_info->ContextLocalName(i));
+ if (String::Equals(variable_name, next_name)) {
+ VariableMode mode;
+ VariableLocation location;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+ int context_index =
+ ScopeInfo::ContextSlotIndex(scope_info, next_name, &mode, &location,
+ &init_flag, &maybe_assigned_flag);
+ context->set(context_index, *new_value);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ JavaScriptFrame* frame = GetFrame();
+ // Optimized frames are not supported.
+ if (frame->is_optimized()) return false;
+
+ Handle<JSFunction> function(frame->function());
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ bool default_result = false;
+
+ // Parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ HandleScope scope(isolate_);
+ if (String::Equals(handle(scope_info->ParameterName(i)), variable_name)) {
+ frame->SetParameterValue(i, *new_value);
+ // Argument might be shadowed in heap context, don't stop here.
+ default_result = true;
+ }
+ }
+
+ // Stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ HandleScope scope(isolate_);
+ if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
+ frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
+ return true;
+ }
+ }
+
+ if (scope_info->HasContext()) {
+ // Context locals.
+ Handle<Context> frame_context(Context::cast(frame->context()));
+ Handle<Context> function_context(frame_context->declaration_context());
+ if (SetContextLocalValue(scope_info, function_context, variable_name,
+ new_value)) {
+ return true;
+ }
+
+ // Function context extension. These are variables introduced by eval.
+ if (function_context->closure() == *function) {
+ if (function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+
+ Maybe<bool> maybe = JSReceiver::HasProperty(ext, variable_name);
+ DCHECK(maybe.IsJust());
+ if (maybe.FromJust()) {
+ // We don't expect this to do anything except replacing
+ // property value.
+ Runtime::SetObjectProperty(isolate_, ext, variable_name, new_value,
+ SLOPPY)
+ .Assert();
+ return true;
+ }
+ }
+ }
+ }
+
+ return default_result;
+}
+
+
+bool ScopeIterator::SetBlockVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<ScopeInfo> scope_info = CurrentScopeInfo();
+ JavaScriptFrame* frame = GetFrame();
+
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ HandleScope scope(isolate_);
+ if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
+ frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
+ return true;
+ }
+ }
+
+ if (HasContext()) {
+ return SetContextLocalValue(scope_info, CurrentContext(), variable_name,
+ new_value);
+ }
+ return false;
+}
+
+
+// This method copies structure of MaterializeClosure method above.
+bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsFunctionContext());
+
+ // Context locals to the context extension.
+ Handle<SharedFunctionInfo> shared(context->closure()->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+ if (SetContextLocalValue(scope_info, context, variable_name, new_value)) {
+ return true;
+ }
+
+ // Properties from the function context extension. This will
+ // be variables introduced by eval.
+ if (context->has_extension()) {
+ Handle<JSObject> ext(JSObject::cast(context->extension()));
+ DCHECK(ext->IsJSContextExtensionObject());
+ Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
+ DCHECK(maybe.IsJust());
+ if (maybe.FromJust()) {
+ // We don't expect this to do anything except replacing property value.
+ JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
+ NONE)
+ .Check();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<Context> context = CurrentContext();
+ Handle<ScriptContextTable> script_contexts(
+ context->global_object()->native_context()->script_context_table());
+ ScriptContextTable::LookupResult lookup_result;
+ if (ScriptContextTable::Lookup(script_contexts, variable_name,
+ &lookup_result)) {
+ Handle<Context> script_context = ScriptContextTable::GetContext(
+ script_contexts, lookup_result.context_index);
+ script_context->set(lookup_result.slot_index, *new_value);
+ return true;
+ }
+
+ return false;
+}
+
+
+bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsCatchContext());
+ Handle<String> name(String::cast(context->extension()));
+ if (!String::Equals(name, variable_name)) {
+ return false;
+ }
+ context->set(Context::THROWN_OBJECT_INDEX, *new_value);
+ return true;
+}
+
+
+void ScopeIterator::CopyContextLocalsToScopeObject(
+ Handle<ScopeInfo> scope_info, Handle<Context> context,
+ Handle<JSObject> scope_object) {
+ Isolate* isolate = scope_info->GetIsolate();
+ int local_count = scope_info->ContextLocalCount();
+ if (local_count == 0) return;
+ // Fill all context locals to the context extension.
+ int first_context_var = scope_info->StackLocalCount();
+ int start = scope_info->ContextLocalNameEntriesIndex();
+ for (int i = 0; i < local_count; ++i) {
+ if (scope_info->LocalIsSynthetic(first_context_var + i)) continue;
+ int context_index = Context::MIN_CONTEXT_SLOTS + i;
+ Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
+ // Reflect variables under TDZ as undefined in scope object.
+ if (value->IsTheHole()) continue;
+ // This should always succeed.
+ // TODO(verwaest): Use AddDataProperty instead.
+ JSObject::SetOwnPropertyIgnoreAttributes(
+ scope_object, handle(String::cast(scope_info->get(i + start))), value,
+ ::NONE)
+ .Check();
+ }
+}
+
+} // namespace internal
+} // namespace v8
« no previous file with comments | « src/debug/debug-scopes.h ('k') | src/globals.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698