diff options
Diffstat (limited to 'deps/v8/src/runtime/runtime-debug.cc')
-rw-r--r-- | deps/v8/src/runtime/runtime-debug.cc | 1722 |
1 files changed, 111 insertions, 1611 deletions
diff --git a/deps/v8/src/runtime/runtime-debug.cc b/deps/v8/src/runtime/runtime-debug.cc index e7aaed1f6f..1cd524f17c 100644 --- a/deps/v8/src/runtime/runtime-debug.cc +++ b/deps/v8/src/runtime/runtime-debug.cc @@ -2,16 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "src/v8.h" +#include "src/runtime/runtime-utils.h" -#include "src/accessors.h" #include "src/arguments.h" -#include "src/compiler.h" -#include "src/debug.h" -#include "src/deoptimizer.h" -#include "src/parser.h" +#include "src/debug/debug.h" +#include "src/debug/debug-evaluate.h" +#include "src/debug/debug-frames.h" +#include "src/debug/debug-scopes.h" +#include "src/frames-inl.h" #include "src/runtime/runtime.h" -#include "src/runtime/runtime-utils.h" namespace v8 { namespace internal { @@ -19,20 +18,19 @@ namespace internal { RUNTIME_FUNCTION(Runtime_DebugBreak) { SealHandleScope shs(isolate); DCHECK(args.length() == 0); - isolate->debug()->HandleDebugBreak(); + // Get the top-most JavaScript frame. + JavaScriptFrameIterator it(isolate); + isolate->debug()->Break(args, it.frame()); + isolate->debug()->SetAfterBreakTarget(it.frame()); return isolate->heap()->undefined_value(); } -// Helper functions for wrapping and unwrapping stack frame ids. -static Smi* WrapFrameId(StackFrame::Id id) { - DCHECK(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4))); - return Smi::FromInt(id >> 2); -} - - -static StackFrame::Id UnwrapFrameId(int wrapped) { - return static_cast<StackFrame::Id>(wrapped << 2); +RUNTIME_FUNCTION(Runtime_HandleDebuggerStatement) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 0); + isolate->debug()->HandleDebugBreak(); + return isolate->heap()->undefined_value(); } @@ -311,9 +309,8 @@ RUNTIME_FUNCTION(Runtime_DebugGetPropertyDetails) { if (name->AsArrayIndex(&index)) { Handle<FixedArray> details = isolate->factory()->NewFixedArray(2); Handle<Object> element_or_char; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, element_or_char, - Runtime::GetElementOrCharAt(isolate, obj, index)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_or_char, + Object::GetElement(isolate, obj, index)); details->set(0, *element_or_char); details->set(1, PropertyDetails::Empty().AsSmi()); return *isolate->factory()->NewJSArrayWithElements(details); @@ -465,93 +462,6 @@ RUNTIME_FUNCTION(Runtime_GetFrameCount) { } -class FrameInspector { - public: - FrameInspector(JavaScriptFrame* frame, int inlined_jsframe_index, - Isolate* isolate) - : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) { - has_adapted_arguments_ = frame_->has_adapted_arguments(); - is_bottommost_ = inlined_jsframe_index == 0; - is_optimized_ = frame_->is_optimized(); - // Calculate the deoptimized frame. - if (frame->is_optimized()) { - // TODO(turbofan): Revisit once we support deoptimization. - if (frame->LookupCode()->is_turbofanned() && - frame->function()->shared()->asm_function() && - !FLAG_turbo_asm_deoptimization) { - is_optimized_ = false; - return; - } - - deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame( - frame, inlined_jsframe_index, isolate); - } - } - - ~FrameInspector() { - // Get rid of the calculated deoptimized frame if any. - if (deoptimized_frame_ != NULL) { - Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame_, isolate_); - } - } - - int GetParametersCount() { - return is_optimized_ ? deoptimized_frame_->parameters_count() - : frame_->ComputeParametersCount(); - } - int expression_count() { return deoptimized_frame_->expression_count(); } - Object* GetFunction() { - return is_optimized_ ? deoptimized_frame_->GetFunction() - : frame_->function(); - } - Object* GetParameter(int index) { - return is_optimized_ ? deoptimized_frame_->GetParameter(index) - : frame_->GetParameter(index); - } - Object* GetExpression(int index) { - // TODO(turbofan): Revisit once we support deoptimization. - if (frame_->LookupCode()->is_turbofanned() && - frame_->function()->shared()->asm_function() && - !FLAG_turbo_asm_deoptimization) { - return isolate_->heap()->undefined_value(); - } - return is_optimized_ ? deoptimized_frame_->GetExpression(index) - : frame_->GetExpression(index); - } - int GetSourcePosition() { - return is_optimized_ ? deoptimized_frame_->GetSourcePosition() - : frame_->LookupCode()->SourcePosition(frame_->pc()); - } - bool IsConstructor() { - return is_optimized_ && !is_bottommost_ - ? deoptimized_frame_->HasConstructStub() - : frame_->IsConstructor(); - } - Object* GetContext() { - return is_optimized_ ? deoptimized_frame_->GetContext() : frame_->context(); - } - - // To inspect all the provided arguments the frame might need to be - // replaced with the arguments frame. - void SetArgumentsFrame(JavaScriptFrame* frame) { - DCHECK(has_adapted_arguments_); - frame_ = frame; - is_optimized_ = frame_->is_optimized(); - DCHECK(!is_optimized_); - } - - private: - JavaScriptFrame* frame_; - DeoptimizedFrameInfo* deoptimized_frame_; - Isolate* isolate_; - bool is_optimized_; - bool is_bottommost_; - bool has_adapted_arguments_; - - DISALLOW_COPY_AND_ASSIGN(FrameInspector); -}; - - static const int kFrameDetailsFrameIdIndex = 0; static const int kFrameDetailsReceiverIndex = 1; static const int kFrameDetailsFunctionIndex = 2; @@ -564,34 +474,6 @@ static const int kFrameDetailsFlagsIndex = 8; static const int kFrameDetailsFirstDynamicIndex = 9; -static SaveContext* FindSavedContextForFrame(Isolate* isolate, - JavaScriptFrame* frame) { - SaveContext* save = isolate->save_context(); - while (save != NULL && !save->IsBelowFrame(frame)) { - save = save->prev(); - } - DCHECK(save != NULL); - return save; -} - - -// Advances the iterator to the frame that matches the index and returns the -// inlined frame index, or -1 if not found. Skips native JS functions. -int Runtime::FindIndexedNonNativeFrame(JavaScriptFrameIterator* it, int index) { - int count = -1; - for (; !it->done(); it->Advance()) { - List<FrameSummary> frames(FLAG_max_inlining_levels + 1); - it->frame()->Summarize(&frames); - for (int i = frames.length() - 1; i >= 0; i--) { - // Omit functions from native and extension scripts. - if (!frames[i].function()->IsSubjectToDebugging()) continue; - if (++count == index) return i; - } - } - return -1; -} - - // Return an array with frame details // args[0]: number: break id // args[1]: number: frame index @@ -627,7 +509,8 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) { JavaScriptFrameIterator it(isolate, id); // Inlined frame index in optimized frame, starting from outer function. - int inlined_jsframe_index = Runtime::FindIndexedNonNativeFrame(&it, index); + int inlined_jsframe_index = + DebugFrameHelper::FindIndexedNonNativeFrame(&it, index); if (inlined_jsframe_index == -1) return heap->undefined_value(); FrameInspector frame_inspector(it.frame(), inlined_jsframe_index, isolate); @@ -635,10 +518,12 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) { // Traverse the saved contexts chain to find the active context for the // selected frame. - SaveContext* save = FindSavedContextForFrame(isolate, it.frame()); + SaveContext* save = + DebugFrameHelper::FindSavedContextForFrame(isolate, it.frame()); // Get the frame id. - Handle<Object> frame_id(WrapFrameId(it.frame()->id()), isolate); + Handle<Object> frame_id(DebugFrameHelper::WrapFrameId(it.frame()->id()), + isolate); // Find source position in unoptimized code. int position = frame_inspector.GetSourcePosition(); @@ -855,932 +740,6 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) { } -static bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info, - Handle<String> parameter_name) { - VariableMode mode; - VariableLocation location; - InitializationFlag init_flag; - MaybeAssignedFlag maybe_assigned_flag; - return ScopeInfo::ContextSlotIndex(info, parameter_name, &mode, &location, - &init_flag, &maybe_assigned_flag) != -1; -} - - -static Handle<Context> MaterializeReceiver(Isolate* isolate, - Handle<Context> target, - Handle<JSFunction> function, - JavaScriptFrame* frame) { - Handle<SharedFunctionInfo> shared(function->shared()); - Handle<ScopeInfo> scope_info(shared->scope_info()); - Handle<Object> receiver; - switch (scope_info->scope_type()) { - case FUNCTION_SCOPE: { - VariableMode mode; - VariableLocation location; - InitializationFlag init_flag; - MaybeAssignedFlag maybe_assigned_flag; - - // Don't bother creating a fake context node if "this" is in the context - // already. - if (ScopeInfo::ContextSlotIndex( - scope_info, isolate->factory()->this_string(), &mode, &location, - &init_flag, &maybe_assigned_flag) >= 0) { - return target; - } - receiver = handle(frame->receiver(), isolate); - break; - } - case MODULE_SCOPE: - receiver = isolate->factory()->undefined_value(); - break; - case SCRIPT_SCOPE: - receiver = handle(function->global_proxy(), isolate); - break; - default: - // For eval code, arrow functions, and the like, there's no "this" binding - // to materialize. - return target; - } - - return isolate->factory()->NewCatchContext( - function, target, isolate->factory()->this_string(), receiver); -} - - -// Create a plain JSObject which materializes the local scope for the specified -// frame. -static void MaterializeStackLocalsWithFrameInspector( - Isolate* isolate, Handle<JSObject> target, Handle<ScopeInfo> scope_info, - FrameInspector* frame_inspector) { - // First fill all parameters. - for (int i = 0; i < scope_info->ParameterCount(); ++i) { - // Do not materialize the parameter if it is shadowed by a context local. - Handle<String> name(scope_info->ParameterName(i)); - if (ParameterIsShadowedByContextLocal(scope_info, name)) continue; - - DCHECK_NOT_NULL(frame_inspector); - - HandleScope scope(isolate); - Handle<Object> value(i < frame_inspector->GetParametersCount() - ? frame_inspector->GetParameter(i) - : isolate->heap()->undefined_value(), - isolate); - DCHECK(!value->IsTheHole()); - - JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); - } - - // Second fill all stack locals. - for (int i = 0; i < scope_info->StackLocalCount(); ++i) { - if (scope_info->LocalIsSynthetic(i)) continue; - Handle<String> name(scope_info->StackLocalName(i)); - Handle<Object> value( - frame_inspector->GetExpression(scope_info->StackLocalIndex(i)), - isolate); - if (value->IsTheHole()) { - value = isolate->factory()->undefined_value(); - } - - JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); - } -} - -static void MaterializeStackLocalsWithFrameInspector( - Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function, - FrameInspector* frame_inspector) { - Handle<SharedFunctionInfo> shared(function->shared()); - Handle<ScopeInfo> scope_info(shared->scope_info()); - - MaterializeStackLocalsWithFrameInspector(isolate, target, scope_info, - frame_inspector); -} - - -static void UpdateStackLocalsFromMaterializedObject( - Isolate* isolate, Handle<JSObject> target, Handle<ScopeInfo> scope_info, - JavaScriptFrame* frame, int inlined_jsframe_index) { - if (inlined_jsframe_index != 0 || frame->is_optimized()) { - // Optimized frames are not supported. - // TODO(yangguo): make sure all code deoptimized when debugger is active - // and assert that this cannot happen. - return; - } - - // Parameters. - for (int i = 0; i < scope_info->ParameterCount(); ++i) { - // Shadowed parameters were not materialized. - Handle<String> name(scope_info->ParameterName(i)); - if (ParameterIsShadowedByContextLocal(scope_info, name)) continue; - - DCHECK(!frame->GetParameter(i)->IsTheHole()); - HandleScope scope(isolate); - Handle<Object> value = - Object::GetPropertyOrElement(target, name).ToHandleChecked(); - frame->SetParameterValue(i, *value); - } - - // Stack locals. - for (int i = 0; i < scope_info->StackLocalCount(); ++i) { - if (scope_info->LocalIsSynthetic(i)) continue; - int index = scope_info->StackLocalIndex(i); - if (frame->GetExpression(index)->IsTheHole()) continue; - HandleScope scope(isolate); - Handle<Object> value = Object::GetPropertyOrElement( - target, handle(scope_info->StackLocalName(i), - isolate)).ToHandleChecked(); - frame->SetExpression(index, *value); - } -} - - -MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeLocalContext( - Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function, - JavaScriptFrame* frame) { - HandleScope scope(isolate); - Handle<SharedFunctionInfo> shared(function->shared()); - Handle<ScopeInfo> scope_info(shared->scope_info()); - - if (!scope_info->HasContext()) return target; - - // Third fill all context locals. - Handle<Context> frame_context(Context::cast(frame->context())); - Handle<Context> function_context(frame_context->declaration_context()); - ScopeInfo::CopyContextLocalsToScopeObject(scope_info, function_context, - target); - - // 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, target, key, value, SLOPPY), - JSObject); - } - } - } - - return target; -} - - -MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeScriptScope( - Handle<GlobalObject> global) { - Isolate* isolate = global->GetIsolate(); - 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())); - ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context, - script_scope); - } - return script_scope; -} - - -MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeLocalScope( - Isolate* isolate, JavaScriptFrame* frame, int inlined_jsframe_index) { - FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); - Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction())); - - Handle<JSObject> local_scope = - isolate->factory()->NewJSObject(isolate->object_function()); - MaterializeStackLocalsWithFrameInspector(isolate, local_scope, function, - &frame_inspector); - - return MaterializeLocalContext(isolate, local_scope, function, frame); -} - - -// Set the context local variable value. -static bool SetContextLocalValue(Isolate* isolate, 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; -} - - -static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame, - int inlined_jsframe_index, - Handle<String> variable_name, - Handle<Object> new_value) { - if (inlined_jsframe_index != 0 || frame->is_optimized()) { - // Optimized frames are not supported. - 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(isolate, 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; -} - - -static bool SetBlockVariableValue(Isolate* isolate, - Handle<Context> block_context, - Handle<ScopeInfo> scope_info, - JavaScriptFrame* frame, - Handle<String> variable_name, - Handle<Object> new_value) { - if (frame != nullptr) { - 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 (!block_context.is_null()) { - return SetContextLocalValue(block_context->GetIsolate(), scope_info, - block_context, variable_name, new_value); - } - return false; -} - - -// Create a plain JSObject which materializes the closure content for the -// context. -static Handle<JSObject> MaterializeClosure(Isolate* isolate, - Handle<Context> context) { - 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. - ScopeInfo::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; -} - - -// This method copies structure of MaterializeClosure method above. -static bool SetClosureVariableValue(Isolate* isolate, Handle<Context> context, - Handle<String> variable_name, - Handle<Object> new_value) { - DCHECK(context->IsFunctionContext()); - - Handle<SharedFunctionInfo> shared(context->closure()->shared()); - Handle<ScopeInfo> scope_info(shared->scope_info()); - - // Context locals to the context extension. - if (SetContextLocalValue(isolate, 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; -} - - -static bool SetScriptVariableValue(Handle<Context> context, - Handle<String> variable_name, - Handle<Object> new_value) { - 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; -} - - -// Create a plain JSObject which materializes the scope for the specified -// catch context. -static Handle<JSObject> MaterializeCatchScope(Isolate* isolate, - Handle<Context> context) { - 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; -} - - -static bool SetCatchVariableValue(Isolate* isolate, Handle<Context> context, - Handle<String> variable_name, - Handle<Object> new_value) { - 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; -} - - -// Create a plain JSObject which materializes the block scope for the specified -// block context. -static Handle<JSObject> MaterializeBlockScope(Isolate* isolate, - Handle<ScopeInfo> scope_info, - Handle<Context> context, - JavaScriptFrame* frame, - int inlined_jsframe_index) { - Handle<JSObject> block_scope = - isolate->factory()->NewJSObject(isolate->object_function()); - - if (frame != nullptr) { - FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); - MaterializeStackLocalsWithFrameInspector(isolate, block_scope, scope_info, - &frame_inspector); - } - - if (!context.is_null()) { - Handle<ScopeInfo> scope_info_from_context( - ScopeInfo::cast(context->extension())); - // Fill all context locals. - ScopeInfo::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. -MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeModuleScope( - Isolate* isolate, Handle<Context> context) { - 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. - ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context, module_scope); - - return module_scope; -} - - -// Iterate over the actual scopes visible from a stack frame or from a closure. -// The iteration proceeds from the innermost visible nested scope outwards. -// All scopes are backed by an actual context except the local scope, -// which is inserted "artificially" in the context chain. -class ScopeIterator { - public: - enum ScopeType { - ScopeTypeGlobal = 0, - ScopeTypeLocal, - ScopeTypeWith, - ScopeTypeClosure, - ScopeTypeCatch, - ScopeTypeBlock, - ScopeTypeScript, - ScopeTypeModule - }; - - ScopeIterator(Isolate* isolate, JavaScriptFrame* frame, - int inlined_jsframe_index, bool ignore_nested_scopes = false) - : isolate_(isolate), - frame_(frame), - inlined_jsframe_index_(inlined_jsframe_index), - function_(frame->function()), - context_(Context::cast(frame->context())), - nested_scope_chain_(4), - seen_script_scope_(false), - failed_(false) { - // Catch the case when the debugger stops in an internal function. - 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; - } - - // Get the debug info (create it if it does not exist). - if (!isolate->debug()->EnsureDebugInfo(shared_info, function_)) { - // Return if ensuring debug info failed. - 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) { - Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info); - - // 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 = frame->pc() - 1; - - // Find the break point where execution has stopped. - BreakLocation location = - BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc); - - // Within the return sequence at the moment it is not possible to - // get a source position which is consistent with the current scope chain. - // Thus all nested with, catch and block contexts are skipped and we only - // provide the function scope. - ignore_nested_scopes = location.IsExit(); - } - - 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()); - 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(Isolate* isolate, Handle<JSFunction> function) - : isolate_(isolate), - frame_(NULL), - inlined_jsframe_index_(0), - function_(function), - context_(function->context()), - seen_script_scope_(false), - failed_(false) { - if (function->IsBuiltin()) { - context_ = Handle<Context>(); - } - } - - // More scopes? - bool Done() { - DCHECK(!failed_); - return context_.is_null(); - } - - bool Failed() { return failed_; } - - // Move to the next scope. - void 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. - ScopeType 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; - } - - // Return the JavaScript object with the content of the current scope. - MaybeHandle<JSObject> ScopeObject() { - DCHECK(!failed_); - switch (Type()) { - case ScopeIterator::ScopeTypeGlobal: - return Handle<JSObject>(CurrentContext()->global_object()); - case ScopeIterator::ScopeTypeScript: - return MaterializeScriptScope( - Handle<GlobalObject>(CurrentContext()->global_object())); - case ScopeIterator::ScopeTypeLocal: - // Materialize the content of the local scope into a JSObject. - DCHECK(nested_scope_chain_.length() == 1); - return MaterializeLocalScope(isolate_, frame_, inlined_jsframe_index_); - case ScopeIterator::ScopeTypeWith: - // Return the with object. - return Handle<JSObject>(JSObject::cast(CurrentContext()->extension())); - case ScopeIterator::ScopeTypeCatch: - return MaterializeCatchScope(isolate_, CurrentContext()); - case ScopeIterator::ScopeTypeClosure: - // Materialize the content of the closure scope into a JSObject. - return MaterializeClosure(isolate_, CurrentContext()); - case ScopeIterator::ScopeTypeBlock: { - if (!nested_scope_chain_.is_empty()) { - // this is a block scope on the stack. - Handle<ScopeInfo> scope_info = nested_scope_chain_.last(); - Handle<Context> context = scope_info->HasContext() - ? CurrentContext() - : Handle<Context>::null(); - return MaterializeBlockScope(isolate_, scope_info, context, frame_, - inlined_jsframe_index_); - } else { - return MaterializeBlockScope(isolate_, Handle<ScopeInfo>::null(), - CurrentContext(), nullptr, 0); - } - } - case ScopeIterator::ScopeTypeModule: - return MaterializeModuleScope(isolate_, CurrentContext()); - } - UNREACHABLE(); - return Handle<JSObject>(); - } - - bool HasContext() { - ScopeType type = Type(); - if (type == ScopeTypeBlock || type == ScopeTypeLocal) { - if (!nested_scope_chain_.is_empty()) { - return nested_scope_chain_.last()->HasContext(); - } - } - return true; - } - - bool SetVariableValue(Handle<String> variable_name, - Handle<Object> new_value) { - DCHECK(!failed_); - switch (Type()) { - case ScopeIterator::ScopeTypeGlobal: - break; - case ScopeIterator::ScopeTypeLocal: - return SetLocalVariableValue(isolate_, frame_, inlined_jsframe_index_, - variable_name, new_value); - case ScopeIterator::ScopeTypeWith: - break; - case ScopeIterator::ScopeTypeCatch: - return SetCatchVariableValue(isolate_, CurrentContext(), variable_name, - new_value); - case ScopeIterator::ScopeTypeClosure: - return SetClosureVariableValue(isolate_, CurrentContext(), - variable_name, new_value); - case ScopeIterator::ScopeTypeScript: - return SetScriptVariableValue(CurrentContext(), variable_name, - new_value); - case ScopeIterator::ScopeTypeBlock: - return SetBlockVariableValue( - isolate_, HasContext() ? CurrentContext() : Handle<Context>::null(), - CurrentScopeInfo(), frame_, variable_name, new_value); - case ScopeIterator::ScopeTypeModule: - // TODO(2399): should we implement it? - break; - } - return false; - } - - Handle<ScopeInfo> 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(); - } - - // Return the context for this scope. For the local context there might not - // be an actual context. - Handle<Context> 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 DebugPrint() { - OFStream os(stdout); - DCHECK(!failed_); - switch (Type()) { - case ScopeIterator::ScopeTypeGlobal: - os << "Global:\n"; - CurrentContext()->Print(os); - break; - - case ScopeIterator::ScopeTypeLocal: { - os << "Local:\n"; - function_->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 - - private: - Isolate* isolate_; - JavaScriptFrame* frame_; - int inlined_jsframe_index_; - Handle<JSFunction> function_; - Handle<Context> context_; - List<Handle<ScopeInfo> > nested_scope_chain_; - bool seen_script_scope_; - bool failed_; - - void RetrieveScopeChain(Scope* scope, - Handle<SharedFunctionInfo> shared_info) { - if (scope != NULL) { - int source_position = shared_info->code()->SourcePosition(frame_->pc()); - 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; - } - } - - DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); -}; - - RUNTIME_FUNCTION(Runtime_GetScopeCount) { HandleScope scope(isolate); DCHECK(args.length() == 2); @@ -1790,13 +749,14 @@ RUNTIME_FUNCTION(Runtime_GetScopeCount) { CONVERT_SMI_ARG_CHECKED(wrapped_id, 1); // Get the frame where the debugging is performed. - StackFrame::Id id = UnwrapFrameId(wrapped_id); + StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); JavaScriptFrameIterator it(isolate, id); JavaScriptFrame* frame = it.frame(); + FrameInspector frame_inspector(frame, 0, isolate); // Count the visible scopes. int n = 0; - for (ScopeIterator it(isolate, frame, 0); !it.Done(); it.Next()) { + for (ScopeIterator it(isolate, &frame_inspector); !it.Done(); it.Next()) { n++; } @@ -1816,85 +776,18 @@ RUNTIME_FUNCTION(Runtime_GetStepInPositions) { CONVERT_SMI_ARG_CHECKED(wrapped_id, 1); // Get the frame where the debugging is performed. - StackFrame::Id id = UnwrapFrameId(wrapped_id); + StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); JavaScriptFrameIterator frame_it(isolate, id); RUNTIME_ASSERT(!frame_it.done()); - List<FrameSummary> frames(FLAG_max_inlining_levels + 1); - frame_it.frame()->Summarize(&frames); - FrameSummary summary = frames.first(); - - Handle<JSFunction> fun = Handle<JSFunction>(summary.function()); - Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared()); - - if (!isolate->debug()->EnsureDebugInfo(shared, fun)) { - return isolate->heap()->undefined_value(); - } - - Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared); - - // Find range of break points starting from the break point where execution - // has stopped. - Address call_pc = summary.pc() - 1; - List<BreakLocation> locations; - BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS, - call_pc, &locations); - - Handle<JSArray> array = isolate->factory()->NewJSArray(locations.length()); - - int index = 0; - for (BreakLocation location : locations) { - bool accept; - if (location.pc() > summary.pc()) { - accept = true; - } else { - StackFrame::Id break_frame_id = isolate->debug()->break_frame_id(); - // The break point is near our pc. Could be a step-in possibility, - // that is currently taken by active debugger call. - if (break_frame_id == StackFrame::NO_ID) { - // We are not stepping. - accept = false; - } else { - JavaScriptFrameIterator additional_frame_it(isolate, break_frame_id); - // If our frame is a top frame and we are stepping, we can do step-in - // at this place. - accept = additional_frame_it.frame()->id() == id; - } - } - if (accept) { - if (location.IsStepInLocation()) { - Smi* position_value = Smi::FromInt(location.position()); - RETURN_FAILURE_ON_EXCEPTION( - isolate, - JSObject::SetElement(array, index, handle(position_value, isolate), - SLOPPY)); - index++; - } - } + List<int> positions; + isolate->debug()->GetStepinPositions(frame_it.frame(), id, &positions); + Factory* factory = isolate->factory(); + Handle<FixedArray> array = factory->NewFixedArray(positions.length()); + for (int i = 0; i < positions.length(); ++i) { + array->set(i, Smi::FromInt(positions[i])); } - return *array; -} - - -static const int kScopeDetailsTypeIndex = 0; -static const int kScopeDetailsObjectIndex = 1; -static const int kScopeDetailsSize = 2; - - -MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeScopeDetails( - Isolate* isolate, ScopeIterator* it) { - // Calculate the size of the result. - int details_size = kScopeDetailsSize; - Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size); - - // Fill in scope details. - details->set(kScopeDetailsTypeIndex, Smi::FromInt(it->Type())); - Handle<JSObject> scope_object; - ASSIGN_RETURN_ON_EXCEPTION(isolate, scope_object, it->ScopeObject(), - JSObject); - details->set(kScopeDetailsObjectIndex, *scope_object); - - return isolate->factory()->NewJSArrayWithElements(details); + return *factory->NewJSArrayWithElements(array, FAST_SMI_ELEMENTS); } @@ -1918,13 +811,14 @@ RUNTIME_FUNCTION(Runtime_GetScopeDetails) { CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]); // Get the frame where the debugging is performed. - StackFrame::Id id = UnwrapFrameId(wrapped_id); + StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); JavaScriptFrameIterator frame_it(isolate, id); JavaScriptFrame* frame = frame_it.frame(); + FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); // Find the requested scope. int n = 0; - ScopeIterator it(isolate, frame, inlined_jsframe_index); + ScopeIterator it(isolate, &frame_inspector); for (; !it.Done() && n < index; it.Next()) { n++; } @@ -1933,7 +827,7 @@ RUNTIME_FUNCTION(Runtime_GetScopeDetails) { } Handle<JSObject> details; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details, - MaterializeScopeDetails(isolate, &it)); + it.MaterializeScopeDetails()); return *details; } @@ -1963,16 +857,17 @@ RUNTIME_FUNCTION(Runtime_GetAllScopesDetails) { } // Get the frame where the debugging is performed. - StackFrame::Id id = UnwrapFrameId(wrapped_id); + StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); JavaScriptFrameIterator frame_it(isolate, id); JavaScriptFrame* frame = frame_it.frame(); + FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); List<Handle<JSObject> > result(4); - ScopeIterator it(isolate, frame, inlined_jsframe_index, ignore_nested_scopes); + ScopeIterator it(isolate, &frame_inspector, ignore_nested_scopes); for (; !it.Done(); it.Next()) { Handle<JSObject> details; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details, - MaterializeScopeDetails(isolate, &it)); + it.MaterializeScopeDetails()); result.Add(details); } @@ -2021,7 +916,7 @@ RUNTIME_FUNCTION(Runtime_GetFunctionScopeDetails) { Handle<JSObject> details; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details, - MaterializeScopeDetails(isolate, &it)); + it.MaterializeScopeDetails()); return *details; } @@ -2066,11 +961,12 @@ RUNTIME_FUNCTION(Runtime_SetScopeVariableValue) { CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]); // Get the frame where the debugging is performed. - StackFrame::Id id = UnwrapFrameId(wrapped_id); + StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); JavaScriptFrameIterator frame_it(isolate, id); JavaScriptFrame* frame = frame_it.frame(); + FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); - ScopeIterator it(isolate, frame, inlined_jsframe_index); + ScopeIterator it(isolate, &frame_inspector); res = SetScopeVariableValue(&it, index, variable_name, new_value); } else { CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0); @@ -2090,7 +986,9 @@ RUNTIME_FUNCTION(Runtime_DebugPrintScopes) { // Print the scopes for the top frame. StackFrameLocator locator(isolate); JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); - for (ScopeIterator it(isolate, frame, 0); !it.Done(); it.Next()) { + FrameInspector frame_inspector(frame, 0, isolate); + + for (ScopeIterator it(isolate, &frame_inspector); !it.Done(); it.Next()) { it.DebugPrint(); } #endif @@ -2336,7 +1234,7 @@ RUNTIME_FUNCTION(Runtime_PrepareStep) { if (wrapped_frame_id == 0) { frame_id = StackFrame::NO_ID; } else { - frame_id = UnwrapFrameId(wrapped_frame_id); + frame_id = DebugFrameHelper::UnwrapFrameId(wrapped_frame_id); } // Get the step action and check validity. @@ -2378,254 +1276,6 @@ RUNTIME_FUNCTION(Runtime_ClearStepping) { } -// Helper function to find or create the arguments object for -// Runtime_DebugEvaluate. -static void MaterializeArgumentsObject(Isolate* isolate, - Handle<JSObject> target, - Handle<JSFunction> function) { - // Do not materialize the arguments object for eval or top-level code. - // Skip if "arguments" is already taken. - if (!function->shared()->is_function()) return; - Maybe<bool> maybe = JSReceiver::HasOwnProperty( - target, isolate->factory()->arguments_string()); - DCHECK(maybe.IsJust()); - if (maybe.FromJust()) return; - - // FunctionGetArguments can't throw an exception. - Handle<JSObject> arguments = - Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); - Handle<String> arguments_str = isolate->factory()->arguments_string(); - JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, - NONE).Check(); -} - - -// Compile and evaluate source for the given context. -static MaybeHandle<Object> DebugEvaluate(Isolate* isolate, - Handle<SharedFunctionInfo> outer_info, - Handle<Context> context, - Handle<Object> context_extension, - Handle<Object> receiver, - Handle<String> source) { - if (context_extension->IsJSObject()) { - Handle<JSObject> extension = Handle<JSObject>::cast(context_extension); - Handle<JSFunction> closure(context->closure(), isolate); - context = isolate->factory()->NewWithContext(closure, context, extension); - } - - Handle<JSFunction> eval_fun; - ASSIGN_RETURN_ON_EXCEPTION(isolate, eval_fun, - Compiler::GetFunctionFromEval( - source, outer_info, context, SLOPPY, - NO_PARSE_RESTRICTION, RelocInfo::kNoPosition), - Object); - - Handle<Object> result; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), - Object); - - // Skip the global proxy as it has no properties and always delegates to the - // real global object. - if (result->IsJSGlobalProxy()) { - PrototypeIterator iter(isolate, result); - // TODO(verwaest): This will crash when the global proxy is detached. - result = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); - } - - return result; -} - - -static Handle<JSObject> NewJSObjectWithNullProto(Isolate* isolate) { - Handle<JSObject> result = - isolate->factory()->NewJSObject(isolate->object_function()); - Handle<Map> new_map = - Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); - Map::SetPrototype(new_map, isolate->factory()->null_value()); - JSObject::MigrateToMap(result, new_map); - return result; -} - - -namespace { - -// This class builds a context chain for evaluation of expressions -// in debugger. -// The scope chain leading up to a breakpoint where evaluation occurs -// looks like: -// - [a mix of with, catch and block scopes] -// - [function stack + context] -// - [outer context] -// The builder materializes all stack variables into properties of objects; -// the expression is then evaluated as if it is inside a series of 'with' -// statements using those objects. To this end, the builder builds a new -// context chain, based on a scope chain: -// - every With and Catch scope begets a cloned context -// - Block scope begets one or two contexts: -// - if a block has context-allocated varaibles, its context is cloned -// - stack locals are materizalized as a With context -// - Local scope begets a With context for materizalized locals, chained to -// original function context. Original function context is the end of -// the chain. -class EvaluationContextBuilder { - public: - EvaluationContextBuilder(Isolate* isolate, JavaScriptFrame* frame, - int inlined_jsframe_index) - : isolate_(isolate), - frame_(frame), - inlined_jsframe_index_(inlined_jsframe_index) { - FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); - Handle<JSFunction> function = - handle(JSFunction::cast(frame_inspector.GetFunction())); - Handle<Context> outer_context = handle(function->context(), isolate); - outer_info_ = handle(function->shared()); - Handle<Context> inner_context; - - bool stop = false; - for (ScopeIterator it(isolate, frame, inlined_jsframe_index); - !it.Failed() && !it.Done() && !stop; it.Next()) { - ScopeIterator::ScopeType scope_type = it.Type(); - - if (scope_type == ScopeIterator::ScopeTypeLocal) { - Handle<Context> parent_context = - it.HasContext() ? it.CurrentContext() : outer_context; - - // The "this" binding, if any, can't be bound via "with". If we need - // to, add another node onto the outer context to bind "this". - parent_context = - MaterializeReceiver(isolate, parent_context, function, frame); - - Handle<JSObject> materialized_function = - NewJSObjectWithNullProto(isolate); - - MaterializeStackLocalsWithFrameInspector(isolate, materialized_function, - function, &frame_inspector); - - MaterializeArgumentsObject(isolate, materialized_function, function); - - Handle<Context> with_context = isolate->factory()->NewWithContext( - function, parent_context, materialized_function); - - ContextChainElement context_chain_element; - context_chain_element.original_context = it.CurrentContext(); - context_chain_element.materialized_object = materialized_function; - context_chain_element.scope_info = it.CurrentScopeInfo(); - context_chain_.Add(context_chain_element); - - stop = true; - RecordContextsInChain(&inner_context, with_context, with_context); - } else if (scope_type == ScopeIterator::ScopeTypeCatch || - scope_type == ScopeIterator::ScopeTypeWith) { - Handle<Context> cloned_context = - Handle<Context>::cast(FixedArray::CopySize( - it.CurrentContext(), it.CurrentContext()->length())); - - ContextChainElement context_chain_element; - context_chain_element.original_context = it.CurrentContext(); - context_chain_element.cloned_context = cloned_context; - context_chain_.Add(context_chain_element); - - RecordContextsInChain(&inner_context, cloned_context, cloned_context); - } else if (scope_type == ScopeIterator::ScopeTypeBlock) { - Handle<JSObject> materialized_object = - NewJSObjectWithNullProto(isolate); - MaterializeStackLocalsWithFrameInspector(isolate, materialized_object, - it.CurrentScopeInfo(), - &frame_inspector); - if (it.HasContext()) { - Handle<Context> cloned_context = - Handle<Context>::cast(FixedArray::CopySize( - it.CurrentContext(), it.CurrentContext()->length())); - Handle<Context> with_context = isolate->factory()->NewWithContext( - function, cloned_context, materialized_object); - - ContextChainElement context_chain_element; - context_chain_element.original_context = it.CurrentContext(); - context_chain_element.cloned_context = cloned_context; - context_chain_element.materialized_object = materialized_object; - context_chain_element.scope_info = it.CurrentScopeInfo(); - context_chain_.Add(context_chain_element); - - RecordContextsInChain(&inner_context, cloned_context, with_context); - } else { - Handle<Context> with_context = isolate->factory()->NewWithContext( - function, outer_context, materialized_object); - - ContextChainElement context_chain_element; - context_chain_element.materialized_object = materialized_object; - context_chain_element.scope_info = it.CurrentScopeInfo(); - context_chain_.Add(context_chain_element); - - RecordContextsInChain(&inner_context, with_context, with_context); - } - } else { - stop = true; - } - } - if (innermost_context_.is_null()) { - innermost_context_ = outer_context; - } - DCHECK(!innermost_context_.is_null()); - } - - void UpdateVariables() { - for (int i = 0; i < context_chain_.length(); i++) { - ContextChainElement element = context_chain_[i]; - if (!element.original_context.is_null() && - !element.cloned_context.is_null()) { - Handle<Context> cloned_context = element.cloned_context; - cloned_context->CopyTo( - Context::MIN_CONTEXT_SLOTS, *element.original_context, - Context::MIN_CONTEXT_SLOTS, - cloned_context->length() - Context::MIN_CONTEXT_SLOTS); - } - if (!element.materialized_object.is_null()) { - // Write back potential changes to materialized stack locals to the - // stack. - UpdateStackLocalsFromMaterializedObject( - isolate_, element.materialized_object, element.scope_info, frame_, - inlined_jsframe_index_); - } - } - } - - Handle<Context> innermost_context() const { return innermost_context_; } - Handle<SharedFunctionInfo> outer_info() const { return outer_info_; } - - private: - struct ContextChainElement { - Handle<Context> original_context; - Handle<Context> cloned_context; - Handle<JSObject> materialized_object; - Handle<ScopeInfo> scope_info; - }; - - void RecordContextsInChain(Handle<Context>* inner_context, - Handle<Context> first, Handle<Context> last) { - if (!inner_context->is_null()) { - (*inner_context)->set_previous(*last); - } else { - innermost_context_ = last; - } - *inner_context = first; - } - - Handle<SharedFunctionInfo> outer_info_; - Handle<Context> innermost_context_; - List<ContextChainElement> context_chain_; - Isolate* isolate_; - JavaScriptFrame* frame_; - int inlined_jsframe_index_; -}; -} - - -// Evaluate a piece of JavaScript in the context of a stack frame for -// debugging. Things that need special attention are: -// - Parameters and stack-allocated locals need to be materialized. Altered -// values need to be written back to the stack afterwards. -// - The arguments object needs to materialized. RUNTIME_FUNCTION(Runtime_DebugEvaluate) { HandleScope scope(isolate); @@ -2641,50 +1291,17 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) { CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 4); CONVERT_ARG_HANDLE_CHECKED(Object, context_extension, 5); - // Handle the processing of break. - DisableBreak disable_break_scope(isolate->debug(), disable_break); - - // Get the frame where the debugging is performed. - StackFrame::Id id = UnwrapFrameId(wrapped_id); - JavaScriptFrameIterator it(isolate, id); - JavaScriptFrame* frame = it.frame(); - - // Traverse the saved contexts chain to find the active context for the - // selected frame. - SaveContext* save = FindSavedContextForFrame(isolate, frame); - - SaveContext savex(isolate); - isolate->set_context(*(save->context())); - - // Materialize stack locals and the arguments object. - - EvaluationContextBuilder context_builder(isolate, frame, - inlined_jsframe_index); - if (isolate->has_pending_exception()) { - return isolate->heap()->exception(); - } - - - Handle<Object> receiver(frame->receiver(), isolate); - MaybeHandle<Object> maybe_result = DebugEvaluate( - isolate, context_builder.outer_info(), - context_builder.innermost_context(), context_extension, receiver, source); + StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); Handle<Object> result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result); - context_builder.UpdateVariables(); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + DebugEvaluate::Local(isolate, id, inlined_jsframe_index, source, + disable_break, context_extension)); return *result; } -static inline bool IsDebugContext(Isolate* isolate, Context* context) { - // Try to unwrap script context if it exist. - if (context->IsScriptContext()) context = context->previous(); - DCHECK_NOT_NULL(context); - return context == *isolate->debug()->debug_context(); -} - - RUNTIME_FUNCTION(Runtime_DebugEvaluateGlobal) { HandleScope scope(isolate); @@ -2698,28 +1315,10 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluateGlobal) { CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 2); CONVERT_ARG_HANDLE_CHECKED(Object, context_extension, 3); - // Handle the processing of break. - DisableBreak disable_break_scope(isolate->debug(), disable_break); - - // Enter the top context from before the debugger was invoked. - SaveContext save(isolate); - SaveContext* top = &save; - while (top != NULL && IsDebugContext(isolate, *top->context())) { - top = top->prev(); - } - if (top != NULL) { - isolate->set_context(*top->context()); - } - - // Get the native context now set to the top context from before the - // debugger was invoked. - Handle<Context> context = isolate->native_context(); - Handle<JSObject> receiver(context->global_proxy()); - Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate); Handle<Object> result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, DebugEvaluate(isolate, outer_info, context, - context_extension, receiver, source)); + isolate, result, + DebugEvaluate::Global(isolate, source, disable_break, context_extension)); return *result; } @@ -2760,71 +1359,6 @@ RUNTIME_FUNCTION(Runtime_DebugGetLoadedScripts) { } -// Helper function used by Runtime_DebugReferencedBy below. -static int DebugReferencedBy(HeapIterator* iterator, JSObject* target, - Object* instance_filter, int max_references, - FixedArray* instances, int instances_size, - JSFunction* arguments_function) { - Isolate* isolate = target->GetIsolate(); - SealHandleScope shs(isolate); - DisallowHeapAllocation no_allocation; - - // Iterate the heap. - int count = 0; - JSObject* last = NULL; - HeapObject* heap_obj = NULL; - while (((heap_obj = iterator->next()) != NULL) && - (max_references == 0 || count < max_references)) { - // Only look at all JSObjects. - if (heap_obj->IsJSObject()) { - // Skip context extension objects and argument arrays as these are - // checked in the context of functions using them. - JSObject* obj = JSObject::cast(heap_obj); - if (obj->IsJSContextExtensionObject() || - obj->map()->GetConstructor() == arguments_function) { - continue; - } - - // Check if the JS object has a reference to the object looked for. - if (obj->ReferencesObject(target)) { - // Check instance filter if supplied. This is normally used to avoid - // references from mirror objects (see Runtime_IsInPrototypeChain). - if (!instance_filter->IsUndefined()) { - for (PrototypeIterator iter(isolate, obj); !iter.IsAtEnd(); - iter.Advance()) { - if (iter.GetCurrent() == instance_filter) { - obj = NULL; // Don't add this object. - break; - } - } - } - - if (obj != NULL) { - // Valid reference found add to instance array if supplied an update - // count. - if (instances != NULL && count < instances_size) { - instances->set(count, obj); - } - last = obj; - count++; - } - } - } - } - - // Check for circular reference only. This can happen when the object is only - // referenced from mirrors and has a circular reference in which case the - // object is not really alive and would have been garbage collected if not - // referenced from the mirror. - if (count == 1 && last == target) { - count = 0; - } - - // Return the number of referencing objects found. - return count; -} - - // Scan the heap for objects with direct references to an object // args[0]: the object to find references to // args[1]: constructor function for instances to exclude (Mirror) @@ -2832,79 +1366,54 @@ static int DebugReferencedBy(HeapIterator* iterator, JSObject* target, RUNTIME_FUNCTION(Runtime_DebugReferencedBy) { HandleScope scope(isolate); DCHECK(args.length() == 3); - - // Check parameters. CONVERT_ARG_HANDLE_CHECKED(JSObject, target, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, instance_filter, 1); - RUNTIME_ASSERT(instance_filter->IsUndefined() || - instance_filter->IsJSObject()); + CONVERT_ARG_HANDLE_CHECKED(Object, filter, 1); + RUNTIME_ASSERT(filter->IsUndefined() || filter->IsJSObject()); CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]); RUNTIME_ASSERT(max_references >= 0); - - // Get the constructor function for context extension and arguments array. - Handle<JSFunction> arguments_function( - JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor())); - - // Get the number of referencing objects. - int count; - // First perform a full GC in order to avoid dead objects and to make the heap - // iterable. + List<Handle<JSObject> > instances; Heap* heap = isolate->heap(); - heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "%DebugConstructedBy"); - { - HeapIterator heap_iterator(heap); - count = DebugReferencedBy(&heap_iterator, *target, *instance_filter, - max_references, NULL, 0, *arguments_function); - } - - // Allocate an array to hold the result. - Handle<FixedArray> instances = isolate->factory()->NewFixedArray(count); - - // Fill the referencing objects. { - HeapIterator heap_iterator(heap); - count = DebugReferencedBy(&heap_iterator, *target, *instance_filter, - max_references, *instances, count, - *arguments_function); - } - - // Return result as JS array. - Handle<JSFunction> constructor = isolate->array_function(); - - Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); - JSArray::SetContent(Handle<JSArray>::cast(result), instances); - return *result; -} - - -// Helper function used by Runtime_DebugConstructedBy below. -static int DebugConstructedBy(HeapIterator* iterator, JSFunction* constructor, - int max_references, FixedArray* instances, - int instances_size) { - DisallowHeapAllocation no_allocation; - - // Iterate the heap. - int count = 0; - HeapObject* heap_obj = NULL; - while (((heap_obj = iterator->next()) != NULL) && - (max_references == 0 || count < max_references)) { - // Only look at all JSObjects. - if (heap_obj->IsJSObject()) { + HeapIterator iterator(heap, HeapIterator::kFilterUnreachable); + // Get the constructor function for context extension and arguments array. + Object* arguments_fun = isolate->sloppy_arguments_map()->GetConstructor(); + HeapObject* heap_obj; + while ((heap_obj = iterator.next())) { + if (!heap_obj->IsJSObject()) continue; JSObject* obj = JSObject::cast(heap_obj); - if (obj->map()->GetConstructor() == constructor) { - // Valid reference found add to instance array if supplied an update - // count. - if (instances != NULL && count < instances_size) { - instances->set(count, obj); - } - count++; + if (obj->IsJSContextExtensionObject()) continue; + if (obj->map()->GetConstructor() == arguments_fun) continue; + if (!obj->ReferencesObject(*target)) continue; + // Check filter if supplied. This is normally used to avoid + // references from mirror objects. + if (!filter->IsUndefined() && + obj->HasInPrototypeChain(isolate, *filter)) { + continue; + } + if (obj->IsJSGlobalObject()) { + obj = JSGlobalObject::cast(obj)->global_proxy(); } + instances.Add(Handle<JSObject>(obj)); + if (instances.length() == max_references) break; + } + // Iterate the rest of the heap to satisfy HeapIterator constraints. + while (iterator.next()) { } } - // Return the number of referencing objects found. - return count; + Handle<FixedArray> result; + if (instances.length() == 1 && instances.last().is_identical_to(target)) { + // Check for circular reference only. This can happen when the object is + // only referenced from mirrors and has a circular reference in which case + // the object is not really alive and would have been garbage collected if + // not referenced from the mirror. + result = isolate->factory()->empty_fixed_array(); + } else { + result = isolate->factory()->NewFixedArray(instances.length()); + for (int i = 0; i < instances.length(); ++i) result->set(i, *instances[i]); + } + return *isolate->factory()->NewJSArrayWithElements(result); } @@ -2914,40 +1423,31 @@ static int DebugConstructedBy(HeapIterator* iterator, JSFunction* constructor, RUNTIME_FUNCTION(Runtime_DebugConstructedBy) { HandleScope scope(isolate); DCHECK(args.length() == 2); - - - // Check parameters. CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0); CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]); RUNTIME_ASSERT(max_references >= 0); - // Get the number of referencing objects. - int count; - // First perform a full GC in order to avoid dead objects and to make the heap - // iterable. + List<Handle<JSObject> > instances; Heap* heap = isolate->heap(); - heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "%DebugConstructedBy"); - { - HeapIterator heap_iterator(heap); - count = DebugConstructedBy(&heap_iterator, *constructor, max_references, - NULL, 0); - } - - // Allocate an array to hold the result. - Handle<FixedArray> instances = isolate->factory()->NewFixedArray(count); - - // Fill the referencing objects. { - HeapIterator heap_iterator2(heap); - count = DebugConstructedBy(&heap_iterator2, *constructor, max_references, - *instances, count); + HeapIterator iterator(heap, HeapIterator::kFilterUnreachable); + HeapObject* heap_obj; + while ((heap_obj = iterator.next())) { + if (!heap_obj->IsJSObject()) continue; + JSObject* obj = JSObject::cast(heap_obj); + if (obj->map()->GetConstructor() != *constructor) continue; + instances.Add(Handle<JSObject>(obj)); + if (instances.length() == max_references) break; + } + // Iterate the rest of the heap to satisfy HeapIterator constraints. + while (iterator.next()) { + } } - // Return result as JS array. - Handle<JSFunction> array_function = isolate->array_function(); - Handle<JSObject> result = isolate->factory()->NewJSObject(array_function); - JSArray::SetContent(Handle<JSArray>::cast(result), instances); - return *result; + Handle<FixedArray> result = + isolate->factory()->NewFixedArray(instances.length()); + for (int i = 0; i < instances.length(); ++i) result->set(i, *instances[i]); + return *isolate->factory()->NewJSArrayWithElements(result); } |