| // 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-frames.h" | |
| #include "src/frames-inl.h" | |
| namespace v8 { | |
| namespace internal { | |
| FrameInspector::FrameInspector(StandardFrame* frame, int inlined_jsframe_index, | |
| Isolate* isolate) | |
| : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) { | |
| JavaScriptFrame* js_frame = | |
| frame->is_java_script() ? javascript_frame() : nullptr; | |
| DCHECK(js_frame || frame->is_wasm()); | |
| has_adapted_arguments_ = js_frame && js_frame->has_adapted_arguments(); | |
| is_bottommost_ = inlined_jsframe_index == 0; | |
| is_optimized_ = frame_->is_optimized(); | |
| is_interpreted_ = frame_->is_interpreted(); | |
| // Calculate the deoptimized frame. | |
| if (frame->is_optimized()) { | |
| DCHECK(js_frame != nullptr); | |
| // TODO(turbofan): Revisit once we support deoptimization. | |
| if (js_frame->LookupCode()->is_turbofanned() && | |
| js_frame->function()->shared()->asm_function() && | |
| !FLAG_turbo_asm_deoptimization) { | |
| is_optimized_ = false; | |
| return; | |
| } | |
| deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame( | |
| js_frame, inlined_jsframe_index, isolate); | |
| } | |
| } | |
| FrameInspector::~FrameInspector() { | |
| // Get rid of the calculated deoptimized frame if any. | |
| if (deoptimized_frame_ != nullptr) { | |
| delete deoptimized_frame_; | |
| } | |
| } | |
| int FrameInspector::GetParametersCount() { | |
| return is_optimized_ ? deoptimized_frame_->parameters_count() | |
| : frame_->ComputeParametersCount(); | |
| } | |
| Handle<Script> FrameInspector::GetScript() { | |
| Object* script = is_optimized_ | |
| ? deoptimized_frame_->GetFunction()->shared()->script() | |
| : frame_->script(); | |
| return handle(Script::cast(script), isolate_); | |
| } | |
| Handle<JSFunction> FrameInspector::GetFunction() { | |
| DCHECK(!frame_->is_wasm()); | |
| return is_optimized_ ? deoptimized_frame_->GetFunction() | |
| : handle(javascript_frame()->function(), isolate_); | |
| } | |
| Handle<Object> FrameInspector::GetParameter(int index) { | |
| return is_optimized_ ? deoptimized_frame_->GetParameter(index) | |
| : handle(frame_->GetParameter(index), isolate_); | |
| } | |
| Handle<Object> FrameInspector::GetExpression(int index) { | |
| // TODO(turbofan): Revisit once we support deoptimization. | |
| if (frame_->is_java_script() && | |
| javascript_frame()->LookupCode()->is_turbofanned() && | |
| javascript_frame()->function()->shared()->asm_function() && | |
| !FLAG_turbo_asm_deoptimization) { | |
| return isolate_->factory()->undefined_value(); | |
| } | |
| return is_optimized_ ? deoptimized_frame_->GetExpression(index) | |
| : handle(frame_->GetExpression(index), isolate_); | |
| } | |
| int FrameInspector::GetSourcePosition() { | |
| return is_optimized_ ? deoptimized_frame_->GetSourcePosition() | |
| : frame_->position(); | |
| } | |
| bool FrameInspector::IsConstructor() { | |
| return is_optimized_ && !is_bottommost_ | |
| ? deoptimized_frame_->HasConstructStub() | |
| : frame_->IsConstructor(); | |
| } | |
| Handle<Object> FrameInspector::GetContext() { | |
| return is_optimized_ ? deoptimized_frame_->GetContext() | |
| : handle(frame_->context(), isolate_); | |
| } | |
| // To inspect all the provided arguments the frame might need to be | |
| // replaced with the arguments frame. | |
| void FrameInspector::SetArgumentsFrame(StandardFrame* frame) { | |
| DCHECK(has_adapted_arguments_); | |
| DCHECK(frame->is_arguments_adaptor()); | |
| frame_ = frame; | |
| is_optimized_ = frame_->is_optimized(); | |
| is_interpreted_ = frame_->is_interpreted(); | |
| DCHECK(!is_optimized_); | |
| } | |
| // Create a plain JSObject which materializes the local scope for the specified | |
| // frame. | |
| void FrameInspector::MaterializeStackLocals(Handle<JSObject> target, | |
| Handle<ScopeInfo> scope_info) { | |
| HandleScope scope(isolate_); | |
| // 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. | |
| // TODO(yangguo): check whether this is necessary, now that we materialize | |
| // context locals as well. | |
| Handle<String> name(scope_info->ParameterName(i)); | |
| if (ScopeInfo::VariableIsSynthetic(*name)) continue; | |
| if (ParameterIsShadowedByContextLocal(scope_info, name)) continue; | |
| Handle<Object> value = | |
| i < GetParametersCount() | |
| ? GetParameter(i) | |
| : Handle<Object>::cast(isolate_->factory()->undefined_value()); | |
| DCHECK(!value->IsTheHole(isolate_)); | |
| JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); | |
| } | |
| // Second fill all stack locals. | |
| for (int i = 0; i < scope_info->StackLocalCount(); ++i) { | |
| Handle<String> name(scope_info->StackLocalName(i)); | |
| if (ScopeInfo::VariableIsSynthetic(*name)) continue; | |
| Handle<Object> value = GetExpression(scope_info->StackLocalIndex(i)); | |
| // TODO(yangguo): We convert optimized out values to {undefined} when they | |
| // are passed to the debugger. Eventually we should handle them somehow. | |
| if (value->IsTheHole(isolate_)) { | |
| value = isolate_->factory()->undefined_value(); | |
| } | |
| if (value->IsOptimizedOut(isolate_)) { | |
| value = isolate_->factory()->undefined_value(); | |
| } | |
| JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); | |
| } | |
| } | |
| void FrameInspector::MaterializeStackLocals(Handle<JSObject> target, | |
| Handle<JSFunction> function) { | |
| Handle<SharedFunctionInfo> shared(function->shared()); | |
| Handle<ScopeInfo> scope_info(shared->scope_info()); | |
| MaterializeStackLocals(target, scope_info); | |
| } | |
| void FrameInspector::UpdateStackLocalsFromMaterializedObject( | |
| Handle<JSObject> target, Handle<ScopeInfo> scope_info) { | |
| // Optimized frames and wasm frames are not supported. Simply give up. | |
| if (is_optimized_ || frame_->is_wasm()) return; | |
| HandleScope scope(isolate_); | |
| // Parameters. | |
| for (int i = 0; i < scope_info->ParameterCount(); ++i) { | |
| // Shadowed parameters were not materialized. | |
| Handle<String> name(scope_info->ParameterName(i)); | |
| if (ScopeInfo::VariableIsSynthetic(*name)) continue; | |
| if (ParameterIsShadowedByContextLocal(scope_info, name)) continue; | |
| DCHECK(!javascript_frame()->GetParameter(i)->IsTheHole(isolate_)); | |
| Handle<Object> value = | |
| Object::GetPropertyOrElement(target, name).ToHandleChecked(); | |
| javascript_frame()->SetParameterValue(i, *value); | |
| } | |
| // Stack locals. | |
| for (int i = 0; i < scope_info->StackLocalCount(); ++i) { | |
| Handle<String> name(scope_info->StackLocalName(i)); | |
| if (ScopeInfo::VariableIsSynthetic(*name)) continue; | |
| int index = scope_info->StackLocalIndex(i); | |
| if (frame_->GetExpression(index)->IsTheHole(isolate_)) continue; | |
| Handle<Object> value = | |
| Object::GetPropertyOrElement(target, name).ToHandleChecked(); | |
| frame_->SetExpression(index, *value); | |
| } | |
| } | |
| bool FrameInspector::ParameterIsShadowedByContextLocal( | |
| Handle<ScopeInfo> info, Handle<String> parameter_name) { | |
| VariableMode mode; | |
| InitializationFlag init_flag; | |
| MaybeAssignedFlag maybe_assigned_flag; | |
| return ScopeInfo::ContextSlotIndex(info, parameter_name, &mode, &init_flag, | |
| &maybe_assigned_flag) != -1; | |
| } | |
| SaveContext* DebugFrameHelper::FindSavedContextForFrame(Isolate* isolate, | |
| StandardFrame* frame) { | |
| SaveContext* save = isolate->save_context(); | |
| while (save != NULL && !save->IsBelowFrame(frame)) { | |
| save = save->prev(); | |
| } | |
| DCHECK(save != NULL); | |
| return save; | |
| } | |
| int DebugFrameHelper::FindIndexedNonNativeFrame(StackTraceFrameIterator* it, | |
| int index) { | |
| int count = -1; | |
| for (; !it->done(); it->Advance()) { | |
| if (it->is_wasm()) { | |
| if (++count == index) return 0; | |
| continue; | |
| } | |
| List<FrameSummary> frames(FLAG_max_inlining_levels + 1); | |
| it->javascript_frame()->Summarize(&frames); | |
| for (int i = frames.length() - 1; i >= 0; i--) { | |
| // Omit functions from native and extension scripts. | |
| if (!frames[i].function()->shared()->IsSubjectToDebugging()) continue; | |
| if (++count == index) return i; | |
| } | |
| } | |
| return -1; | |
| } | |
| } // namespace internal | |
| } // namespace v8 |