diff --git a/pkg/vm/tool/test_bytecode b/pkg/vm/tool/test_bytecode deleted file mode 100755 index 9f800ab906f..00000000000 --- a/pkg/vm/tool/test_bytecode +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file -# for details. All rights reserved. Use of this source code is governed by a -# BSD-style license that can be found in the LICENSE file. - -# Script for generating bytecode in a kernel file using Dart 2 pipeline and -# interpreting the resulting bytecode. - -# Usage -# pkg/vm/tool/test_bytecode ~/foo.dart - -set -e - -# Pick the architecture and mode to test. -BUILD_FLAGS="-m release -a x64" -BUILD_SUBDIR="ReleaseX64" - -function follow_links() { - file="$1" - while [ -h "$file" ]; do - # On Mac OS, readlink -f doesn't work. - file="$(readlink "$file")" - done - echo "$file" -} - -# Unlike $0, $BASH_SOURCE points to the absolute path of this file. -PROG_NAME="$(follow_links "$BASH_SOURCE")" - -# Handle the case where dart-sdk/bin has been symlinked to. -CUR_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" - -SDK_DIR="$CUR_DIR/../../.." -BUILD_DIR="$SDK_DIR/out/$BUILD_SUBDIR" - -# Regenerate vm_platform_strong.dill to contain bytecode if needed: -# $SDK_DIR/tools/gn.py $BUILD_FLAGS --bytecode -# $SDK_DIR/tools/build.py $BUILD_FLAGS runtime - -# Generate dill file containing bytecode for input dart source. -$CUR_DIR/gen_kernel --platform $BUILD_DIR/vm_platform_strong.dill \ - --gen-bytecode $@ -o $BUILD_DIR/test_bytecode.dill - -# Dump bytecode in generated vm_platform_strong.dill file to platform.txt. -# $BUILD_DIR/dart $SDK_DIR/pkg/vm/bin/dump_kernel.dart \ -# $BUILD_DIR/vm_platform_strong.dill $BUILD_DIR/platform.txt - -# Dump bytecode in generated test_bytecode.dill file to test_bytecode.txt. -# $BUILD_DIR/dart $SDK_DIR/pkg/vm/bin/dump_kernel.dart \ -# $BUILD_DIR/test_bytecode.dill $BUILD_DIR/test_bytecode.txt - -# Required flag. -DART_VM_FLAGS="--enable-interpreter $DART_VM_FLAGS" - -# Optional flags examples. Uncomment as needed. -# DART_VM_FLAGS="--compilation-counter-threshold=-1 $DART_VM_FLAGS" -# DART_VM_FLAGS="--force-log-flush --isolate-log-filter=\"\" $DART_VM_FLAGS" -# DART_VM_FLAGS="--dump-kernel-bytecode $DART_VM_FLAGS" -# DART_VM_FLAGS="--trace-interpreter-after=0 $DART_VM_FLAGS" - -# Execute dill file. -exec $BUILD_DIR/dart $DART_VM_FLAGS $BUILD_DIR/test_bytecode.dill - diff --git a/runtime/vm/compiler/assembler/disassembler_kbc.cc b/runtime/vm/compiler/assembler/disassembler_kbc.cc index ab094d4bbc1..edd8506417f 100644 --- a/runtime/vm/compiler/assembler/disassembler_kbc.cc +++ b/runtime/vm/compiler/assembler/disassembler_kbc.cc @@ -8,6 +8,7 @@ #include "vm/compiler/assembler/disassembler_kbc.h" #include "platform/assert.h" +#include "vm/compiler/frontend/bytecode_reader.h" #include "vm/constants_kbc.h" #include "vm/cpu.h" #include "vm/instructions.h" @@ -408,6 +409,22 @@ void KernelBytecodeDisassembler::Disassemble(const Function& function) { PcDescriptors::Handle(zone, bytecode.pc_descriptors()); THR_Print("%s}\n", descriptors.ToCString()); + if (bytecode.HasSourcePositions()) { + THR_Print("Source positions for function '%s' {\n", function_fullname); + // 4 bits per hex digit + 2 for "0x". + const int addr_width = (kBitsPerWord / 4) + 2; + // "*" in a printf format specifier tells it to read the field width from + // the printf argument list. + THR_Print("%-*s\ttok-ix\n", addr_width, "pc"); + kernel::BytecodeSourcePositionsIterator iter(zone, bytecode); + while (iter.MoveNext()) { + THR_Print("%#-*" Px "\t%s\n", addr_width, + bytecode.PayloadStart() + iter.PcOffset(), + iter.TokenPos().ToCString()); + } + THR_Print("}\n"); + } + THR_Print("Exception Handlers for function '%s' {\n", function_fullname); const ExceptionHandlers& handlers = ExceptionHandlers::Handle(zone, bytecode.exception_handlers()); diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc index 61f37dfe5de..71a60b5fc55 100644 --- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc @@ -1951,7 +1951,7 @@ FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() { } while (update_position && - pc_ >= source_pos_iter.BytecodeInstructionIndex()) { + static_cast(pc_) >= source_pos_iter.PcOffset()) { position_ = source_pos_iter.TokenPos(); update_position = source_pos_iter.MoveNext(); } diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc index 65aeab4a597..f4161ef5867 100644 --- a/runtime/vm/compiler/frontend/bytecode_reader.cc +++ b/runtime/vm/compiler/frontend/bytecode_reader.cc @@ -12,6 +12,7 @@ #include "vm/compiler/frontend/bytecode_scope_builder.h" #include "vm/constants_kbc.h" #include "vm/dart_entry.h" +#include "vm/debugger.h" #include "vm/longjump.h" #include "vm/object_store.h" #include "vm/reusable_handles.h" @@ -2231,6 +2232,10 @@ RawError* BytecodeReader::ReadFunctionBytecode(Thread* thread, bytecode_metadata_helper.ReadMetadata(function); +#if !defined(PRODUCT) + thread->isolate()->debugger()->NotifyBytecodeLoaded(function); +#endif + return Error::null(); } else { return thread->StealStickyError(); diff --git a/runtime/vm/compiler/frontend/bytecode_reader.h b/runtime/vm/compiler/frontend/bytecode_reader.h index 1e31e9a0586..b2e700f61ab 100644 --- a/runtime/vm/compiler/frontend/bytecode_reader.h +++ b/runtime/vm/compiler/frontend/bytecode_reader.h @@ -309,12 +309,7 @@ class BytecodeSourcePositionsIterator : ValueObject { return true; } - intptr_t BytecodeInstructionIndex() const { return cur_bci_; } - - uword PcOffset() const { - return KernelBytecode::BytecodePcToOffset(BytecodeInstructionIndex(), - /* is_return_address = */ true); - } + uword PcOffset() const { return cur_bci_; } TokenPosition TokenPos() const { return TokenPosition(cur_token_pos_); } diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc index 2645ba695de..d42a3cc1724 100644 --- a/runtime/vm/compiler/jit/compiler.cc +++ b/runtime/vm/compiler/jit/compiler.cc @@ -258,9 +258,7 @@ DEFINE_RUNTIME_ENTRY(CompileFunction, 1) { bool Compiler::CanOptimizeFunction(Thread* thread, const Function& function) { #if !defined(PRODUCT) - Isolate* isolate = thread->isolate(); - if (isolate->debugger()->IsStepping() || - isolate->debugger()->HasBreakpoint(function, thread->zone())) { + if (Debugger::IsDebugging(thread, function)) { // We cannot set breakpoints and single step in optimized code, // so do not optimize the function. Bump usage counter down to avoid // repeatedly entering the runtime for an optimization attempt. diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h index 4be5fce40d5..529d96c0a53 100644 --- a/runtime/vm/constants_kbc.h +++ b/runtime/vm/constants_kbc.h @@ -971,20 +971,12 @@ class KernelBytecode { } } - DART_FORCE_INLINE static bool IsCallOpcode(const KBCInstr* instr) { + DART_FORCE_INLINE static bool IsCallOpcode_Old(const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kDirectCall_Old: case KernelBytecode::kInterfaceCall_Old: case KernelBytecode::kUncheckedInterfaceCall_Old: case KernelBytecode::kDynamicCall_Old: - case KernelBytecode::kDirectCall: - case KernelBytecode::kDirectCall_Wide: - case KernelBytecode::kInterfaceCall: - case KernelBytecode::kInterfaceCall_Wide: - case KernelBytecode::kUncheckedInterfaceCall: - case KernelBytecode::kUncheckedInterfaceCall_Wide: - case KernelBytecode::kDynamicCall: - case KernelBytecode::kDynamicCall_Wide: return true; default: @@ -1014,7 +1006,7 @@ class KernelBytecode { // to new _GrowableList(0). return kNativeCallToGrowableListArgc; } - ASSERT(IsCallOpcode(call)); + ASSERT(IsCallOpcode_Old(call)); return DecodeA(call); } @@ -1030,6 +1022,7 @@ class KernelBytecode { // Converts bytecode PC into an offset. // For return addresses used in PcDescriptors, PC is also augmented by 1. + // TODO(regis): Eliminate this correction. static intptr_t BytecodePcToOffset(uint32_t pc, bool is_return_address) { return pc + (is_return_address ? 1 : 0); } diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index cbcfcec11ee..bf371bbe1f1 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -10,6 +10,7 @@ #include "vm/code_patcher.h" #include "vm/compiler/assembler/disassembler.h" +#include "vm/compiler/frontend/bytecode_reader.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_entry.h" #include "vm/deopt_instructions.h" @@ -66,14 +67,13 @@ BreakpointLocation::BreakpointLocation(const Script& script, url_(script.url()), token_pos_(token_pos), end_token_pos_(end_token_pos), - is_resolved_(false), next_(NULL), conditions_(NULL), requested_line_number_(requested_line_number), requested_column_number_(requested_column_number), function_(Function::null()), - line_number_(-1), - column_number_(-1) { + bytecode_token_pos_(TokenPosition::kNoSource), + code_token_pos_(TokenPosition::kNoSource) { ASSERT(!script.IsNull()); ASSERT(token_pos_.IsReal()); } @@ -86,14 +86,13 @@ BreakpointLocation::BreakpointLocation(const String& url, url_(url.raw()), token_pos_(TokenPosition::kNoSource), end_token_pos_(TokenPosition::kNoSource), - is_resolved_(false), next_(NULL), conditions_(NULL), requested_line_number_(requested_line_number), requested_column_number_(requested_column_number), function_(Function::null()), - line_number_(-1), - column_number_(-1) { + bytecode_token_pos_(TokenPosition::kNoSource), + code_token_pos_(TokenPosition::kNoSource) { ASSERT(requested_line_number_ >= 0); } @@ -110,7 +109,8 @@ bool BreakpointLocation::AnyEnabled() const { return breakpoints() != NULL; } -void BreakpointLocation::SetResolved(const Function& func, +void BreakpointLocation::SetResolved(bool in_bytecode, + const Function& func, TokenPosition token_pos) { ASSERT(!IsLatent()); ASSERT(func.script() == script_); @@ -120,53 +120,25 @@ void BreakpointLocation::SetResolved(const Function& func, function_ = func.raw(); token_pos_ = token_pos; end_token_pos_ = token_pos; - is_resolved_ = true; + if (in_bytecode) { + bytecode_token_pos_ = token_pos; + } else { + code_token_pos_ = token_pos; + } } -// TODO(hausner): Get rid of library parameter. A source breakpoint location -// does not imply a library, since the same source code can be included -// in more than one library, e.g. the text location of mixin functions. -void BreakpointLocation::GetCodeLocation(Library* lib, - Script* script, +// Returned resolved pos is either in code or in bytecode. +void BreakpointLocation::GetCodeLocation(Script* script, TokenPosition* pos) const { if (IsLatent()) { - *lib = Library::null(); *script = Script::null(); *pos = TokenPosition::kNoSource; } else { *script = this->script(); *pos = token_pos_; - if (IsResolved()) { - const Function& func = Function::Handle(function_); - ASSERT(!func.IsNull()); - const Class& cls = Class::Handle(func.origin()); - *lib = cls.library(); - } else { - *lib = Library::null(); - } } } -intptr_t BreakpointLocation::LineNumber() { - ASSERT(IsResolved()); - // Compute line number lazily since it causes scanning of the script. - if (line_number_ < 0) { - const Script& script = Script::Handle(this->script()); - script.GetTokenLocation(token_pos_, &line_number_, NULL); - } - return line_number_; -} - -intptr_t BreakpointLocation::ColumnNumber() { - ASSERT(IsResolved()); - // Compute column number lazily since it causes scanning of the script. - if (column_number_ < 0) { - const Script& script = Script::Handle(this->script()); - script.GetTokenLocation(token_pos_, &line_number_, &column_number_); - } - return column_number_; -} - void Breakpoint::set_bpt_location(BreakpointLocation* new_bpt_location) { // Only latent breakpoints can be moved. ASSERT((new_bpt_location == NULL) || bpt_location_->IsLatent()); @@ -208,6 +180,7 @@ void Breakpoint::PrintJSON(JSONStream* stream) { void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { visitor->VisitPointer(reinterpret_cast(&code_)); + visitor->VisitPointer(reinterpret_cast(&bytecode_)); #if !defined(TARGET_ARCH_DBC) visitor->VisitPointer(reinterpret_cast(&saved_value_)); #endif @@ -225,6 +198,7 @@ ActivationFrame::ActivationFrame(uword pc, sp_(sp), ctx_(Context::ZoneHandle()), code_(Code::ZoneHandle(code.raw())), + bytecode_(Bytecode::ZoneHandle()), function_(Function::ZoneHandle(code.function())), live_frame_((kind == kRegular) || (kind == kAsyncActivation)), token_pos_initialized_(false), @@ -237,11 +211,44 @@ ActivationFrame::ActivationFrame(uword pc, deopt_frame_(Array::ZoneHandle(deopt_frame.raw())), deopt_frame_offset_(deopt_frame_offset), kind_(kind), - is_interpreted_(false), // TODO(regis): No bytecode debugging support. vars_initialized_(false), var_descriptors_(LocalVarDescriptors::ZoneHandle()), desc_indices_(8), - pc_desc_(PcDescriptors::ZoneHandle()) {} + pc_desc_(PcDescriptors::ZoneHandle()) { + ASSERT(!function_.IsNull()); +} + +#if !defined(DART_PRECOMPILED_RUNTIME) +ActivationFrame::ActivationFrame(uword pc, + uword fp, + uword sp, + const Bytecode& bytecode, + ActivationFrame::Kind kind) + : pc_(pc), + fp_(fp), + sp_(sp), + ctx_(Context::ZoneHandle()), + code_(Code::ZoneHandle()), + bytecode_(Bytecode::ZoneHandle(bytecode.raw())), + function_(Function::ZoneHandle(bytecode.function())), + live_frame_((kind == kRegular) || (kind == kAsyncActivation)), + token_pos_initialized_(false), + token_pos_(TokenPosition::kNoSource), + try_index_(-1), + deopt_id_(DeoptId::kNone), + line_number_(-1), + column_number_(-1), + context_level_(-1), + deopt_frame_(Array::ZoneHandle()), + deopt_frame_offset_(0), + kind_(kind), + vars_initialized_(false), + var_descriptors_(LocalVarDescriptors::ZoneHandle()), + desc_indices_(8), + pc_desc_(PcDescriptors::ZoneHandle()) { + ASSERT(!function_.IsNull()); // Frames with bytecode stubs should be skipped. +} +#endif // !defined(DART_PRECOMPILED_RUNTIME) ActivationFrame::ActivationFrame(Kind kind) : pc_(0), @@ -249,6 +256,7 @@ ActivationFrame::ActivationFrame(Kind kind) sp_(0), ctx_(Context::ZoneHandle()), code_(Code::ZoneHandle()), + bytecode_(Bytecode::ZoneHandle()), function_(Function::ZoneHandle()), live_frame_(kind == kRegular), token_pos_initialized_(false), @@ -260,7 +268,6 @@ ActivationFrame::ActivationFrame(Kind kind) deopt_frame_(Array::ZoneHandle()), deopt_frame_offset_(0), kind_(kind), - is_interpreted_(false), vars_initialized_(false), var_descriptors_(LocalVarDescriptors::ZoneHandle()), desc_indices_(8), @@ -272,6 +279,7 @@ ActivationFrame::ActivationFrame(const Closure& async_activation) sp_(0), ctx_(Context::ZoneHandle()), code_(Code::ZoneHandle()), + bytecode_(Bytecode::ZoneHandle()), function_(Function::ZoneHandle()), live_frame_(false), token_pos_initialized_(false), @@ -283,15 +291,21 @@ ActivationFrame::ActivationFrame(const Closure& async_activation) deopt_frame_(Array::ZoneHandle()), deopt_frame_offset_(0), kind_(kAsyncActivation), - is_interpreted_(false), vars_initialized_(false), var_descriptors_(LocalVarDescriptors::ZoneHandle()), desc_indices_(8), pc_desc_(PcDescriptors::ZoneHandle()) { // Extract the function and the code from the asynchronous activation. function_ = async_activation.function(); - function_.EnsureHasCompiledUnoptimizedCode(); - code_ = function_.unoptimized_code(); +#if !defined(DART_PRECOMPILED_RUNTIME) + if (!function_.HasCode() && function_.HasBytecode()) { + bytecode_ = function_.bytecode(); + } +#endif + if (bytecode_.IsNull()) { + function_.EnsureHasCompiledUnoptimizedCode(); + code_ = function_.unoptimized_code(); + } ctx_ = async_activation.context(); ASSERT(fp_ == 0); ASSERT(!ctx_.IsNull()); @@ -462,7 +476,7 @@ static bool IsImplicitFunction(const Function& func) { } bool Debugger::HasBreakpoint(const Function& func, Zone* zone) { - if (!func.HasCode()) { + if (!func.HasCode() && !func.HasBytecode()) { // If the function is not compiled yet, just check whether there // is a user-defined breakpoint that falls into the token // range of the function. This may be a false positive: the breakpoint @@ -553,20 +567,26 @@ RawLibrary* ActivationFrame::Library() { } void ActivationFrame::GetPcDescriptors() { + ASSERT(!IsInterpreted()); // We need to set try_index_ simultaneously. if (pc_desc_.IsNull()) { pc_desc_ = code().pc_descriptors(); ASSERT(!pc_desc_.IsNull()); } } -// Compute token_pos_ and try_index_ and token_pos_initialized_. +// Compute token_pos_ and token_pos_initialized_. +// If not IsInterpreted(), then also compute try_index_ and deopt_id_. TokenPosition ActivationFrame::TokenPos() { if (!token_pos_initialized_) { token_pos_initialized_ = true; + if (IsInterpreted()) { + token_pos_ = bytecode().GetTokenIndexOfPC(pc_); + return token_pos_; + } token_pos_ = TokenPosition::kNoSource; GetPcDescriptors(); PcDescriptors::Iterator iter(pc_desc_, RawPcDescriptors::kAnyKind); - uword pc_offset = pc_ - code().PayloadStart(); + const uword pc_offset = pc_ - code().PayloadStart(); while (iter.MoveNext()) { if (iter.PcOffset() == pc_offset) { try_index_ = iter.TryIndex(); @@ -580,6 +600,16 @@ TokenPosition ActivationFrame::TokenPos() { } intptr_t ActivationFrame::TryIndex() { +#if !defined(DART_PRECOMPILED_RUNTIME) + if (IsInterpreted()) { + if (pc_desc_.IsNull()) { + ASSERT(try_index_ == -1); + pc_desc_ = bytecode().pc_descriptors(); + try_index_ = bytecode().GetTryIndexAtPc(pc_); + } + return try_index_; + } +#endif // !defined(DART_PRECOMPILED_RUNTIME) if (!token_pos_initialized_) { TokenPos(); // Side effect: computes token_pos_initialized_, try_index_. } @@ -587,6 +617,7 @@ intptr_t ActivationFrame::TryIndex() { } intptr_t ActivationFrame::DeoptId() { + ASSERT(!IsInterpreted()); if (!token_pos_initialized_) { TokenPos(); // Side effect: computes token_pos_initialized_, try_index_. } @@ -619,7 +650,7 @@ intptr_t ActivationFrame::ColumnNumber() { void ActivationFrame::GetVarDescriptors() { if (var_descriptors_.IsNull()) { - if (is_interpreted()) { + if (IsInterpreted()) { // TODO(regis): Kernel bytecode does not yet provide var descriptors. var_descriptors_ = Object::empty_var_descriptors().raw(); return; @@ -652,8 +683,13 @@ void ActivationFrame::PrintDescriptorsError(const char* message) { OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_); OS::PrintErr("context_level_ %" Px "\n", context_level_); DisassembleToStdout formatter; - code().Disassemble(&formatter); - PcDescriptors::Handle(code().pc_descriptors()).Print(); + if (IsInterpreted()) { + bytecode().Disassemble(&formatter); + PcDescriptors::Handle(bytecode().pc_descriptors()).Print(); + } else { + code().Disassemble(&formatter); + PcDescriptors::Handle(code().pc_descriptors()).Print(); + } StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, Thread::Current(), StackFrameIterator::kNoCrossThreadIteration); @@ -811,7 +847,11 @@ bool ActivationFrame::HandlesException(const Instance& exc_obj) { AbstractType& type = Type::Handle(); const bool is_async = function().IsAsyncClosure() || function().IsAsyncGenClosure(); - handlers = code().exception_handlers(); + if (IsInterpreted()) { + handlers = bytecode().exception_handlers(); + } else { + handlers = code().exception_handlers(); + } ASSERT(!handlers.IsNull()); intptr_t num_handlers_checked = 0; while (try_index != kInvalidTryIndex) { @@ -843,6 +883,10 @@ bool ActivationFrame::HandlesException(const Instance& exc_obj) { void ActivationFrame::ExtractTokenPositionFromAsyncClosure() { // Attempt to determine the token position from the async closure. + if (IsInterpreted()) { + // TODO(regis): Implement. + return; + } Thread* thread = Thread::Current(); Zone* zone = thread->zone(); const Script& script = Script::Handle(zone, function().script()); @@ -1436,7 +1480,11 @@ void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj) { const TokenPosition pos = TokenPos().SourcePosition(); jsobj->AddLocation(script, pos); jsobj->AddProperty("function", function()); - jsobj->AddProperty("code", code()); + if (IsInterpreted()) { + jsobj->AddProperty("bytecode", bytecode()); + } else { + jsobj->AddProperty("code", code()); + } { JSONArray jsvars(jsobj, "vars"); const int num_vars = NumLocalVariables(); @@ -1476,7 +1524,11 @@ void ActivationFrame::PrintToJSONObjectAsyncCausal(JSONObject* jsobj) { const TokenPosition pos = TokenPos().SourcePosition(); jsobj->AddLocation(script, pos); jsobj->AddProperty("function", function()); - jsobj->AddProperty("code", code()); + if (IsInterpreted()) { + jsobj->AddProperty("bytecode", bytecode()); + } else { + jsobj->AddProperty("code", code()); + } } void ActivationFrame::PrintToJSONObjectAsyncSuspensionMarker( @@ -1493,7 +1545,11 @@ void ActivationFrame::PrintToJSONObjectAsyncActivation(JSONObject* jsobj) { const TokenPosition pos = TokenPos().SourcePosition(); jsobj->AddLocation(script, pos); jsobj->AddProperty("function", function()); - jsobj->AddProperty("code", code()); + if (IsInterpreted()) { + jsobj->AddProperty("bytecode", bytecode()); + } else { + jsobj->AddProperty("code", code()); + } } static bool IsFunctionVisible(const Function& function) { @@ -1516,6 +1572,14 @@ void DebuggerStackTrace::AddAsyncCausalFrame(uword pc, const Code& code) { ActivationFrame::kAsyncCausal)); } +#if !defined(DART_PRECOMPILED_RUNTIME) +void DebuggerStackTrace::AddAsyncCausalFrame(uword pc, + const Bytecode& bytecode) { + trace_.Add( + new ActivationFrame(pc, 0, 0, bytecode, ActivationFrame::kAsyncCausal)); +} +#endif // !defined(DART_PRECOMPILED_RUNTIME) + const uint8_t kSafepointKind = RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall | RawPcDescriptors::kRuntimeCall; @@ -1525,6 +1589,7 @@ CodeBreakpoint::CodeBreakpoint(const Code& code, uword pc, RawPcDescriptors::Kind kind) : code_(code.raw()), + bytecode_(Bytecode::null()), token_pos_(token_pos), pc_(pc), line_number_(-1), @@ -1545,12 +1610,37 @@ CodeBreakpoint::CodeBreakpoint(const Code& code, ASSERT((breakpoint_kind_ & kSafepointKind) != 0); } +CodeBreakpoint::CodeBreakpoint(const Bytecode& bytecode, + TokenPosition token_pos, + uword pc) + : code_(Code::null()), + bytecode_(bytecode.raw()), + token_pos_(token_pos), + pc_(pc), + line_number_(-1), + is_enabled_(false), + bpt_location_(NULL), + next_(NULL), + breakpoint_kind_(RawPcDescriptors::kAnyKind), +#if !defined(TARGET_ARCH_DBC) + saved_value_(Code::null()) +#else + saved_value_(SimulatorBytecode::kTrap), + saved_value_fastsmi_(SimulatorBytecode::kTrap) +#endif +{ + ASSERT(!bytecode.IsNull()); + ASSERT(token_pos_.IsReal()); + ASSERT(pc_ != 0); +} + CodeBreakpoint::~CodeBreakpoint() { // Make sure we don't leave patched code behind. ASSERT(!IsEnabled()); // Poison the data so we catch use after free errors. #ifdef DEBUG code_ = Code::null(); + bytecode_ = Bytecode::null(); pc_ = 0ul; bpt_location_ = NULL; next_ = NULL; @@ -1559,7 +1649,12 @@ CodeBreakpoint::~CodeBreakpoint() { } RawFunction* CodeBreakpoint::function() const { - return Code::Handle(code_).function(); + if (IsInterpreted()) { + ASSERT(Bytecode::Handle(bytecode_).function() != Function::null()); + return Bytecode::Handle(bytecode_).function(); + } else { + return Code::Handle(code_).function(); + } } RawScript* CodeBreakpoint::SourceCode() { @@ -1583,14 +1678,30 @@ intptr_t CodeBreakpoint::LineNumber() { void CodeBreakpoint::Enable() { if (!is_enabled_) { - PatchCode(); + if (IsInterpreted()) { +#if !defined(DART_PRECOMPILED_RUNTIME) + SetBytecodeBreak(); +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + PatchCode(); + } } ASSERT(is_enabled_); } void CodeBreakpoint::Disable() { if (is_enabled_) { - RestoreCode(); + if (IsInterpreted()) { +#if !defined(DART_PRECOMPILED_RUNTIME) + UnsetBytecodeBreak(); +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + RestoreCode(); + } } ASSERT(!is_enabled_); } @@ -1818,6 +1929,24 @@ ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate, } #if !defined(DART_PRECOMPILED_RUNTIME) +ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate, + uword pc, + StackFrame* frame, + const Bytecode& bytecode, + ActivationFrame::Kind kind) { + ASSERT(bytecode.ContainsInstructionAt(pc)); + ActivationFrame* activation = + new ActivationFrame(pc, frame->fp(), frame->sp(), bytecode, kind); + if (FLAG_trace_debugger_stacktrace) { + const Context& ctx = activation->GetSavedCurrentContext(); + OS::PrintErr("\tUsing saved context: %s\n", ctx.ToCString()); + } + if (FLAG_trace_debugger_stacktrace) { + OS::PrintErr("\tLine number: %" Pd "\n", activation->LineNumber()); + } + return activation; +} + RawArray* Debugger::DeoptimizeToArray(Thread* thread, StackFrame* frame, const Code& code) { @@ -1850,6 +1979,9 @@ DebuggerStackTrace* Debugger::CollectStackTrace() { Thread::Current(), StackFrameIterator::kNoCrossThreadIteration); Code& code = Code::Handle(zone); +#if !defined(DART_PRECOMPILED_RUNTIME) + Bytecode& bytecode = Bytecode::Handle(zone); +#endif // !defined(DART_PRECOMPILED_RUNTIME) Code& inlined_code = Code::Handle(zone); Array& deopt_frame = Array::Handle(zone); @@ -1862,14 +1994,21 @@ DebuggerStackTrace* Debugger::CollectStackTrace() { } if (frame->IsDartFrame()) { if (frame->is_interpreted()) { - // TODO(regis): Support debugging of interpreted frames. - // For now, do not abort, but skip the frame, as this code is run - // while handling a stack overflow. See HandleStackOverflowTestCases. - continue; +#if !defined(DART_PRECOMPILED_RUNTIME) + bytecode = frame->LookupDartBytecode(); + if (bytecode.function() == Function::null()) { + continue; // Skip bytecode stub frame. + } + stack_trace->AddActivation( + CollectDartFrame(isolate, frame->pc(), frame, bytecode)); +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + code = frame->LookupDartCode(); + AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code, + &inlined_code, &deopt_frame); } - code = frame->LookupDartCode(); - AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code, - &inlined_code, &deopt_frame); } } return stack_trace; @@ -1917,7 +2056,11 @@ DebuggerStackTrace* Debugger::CollectAsyncCausalStackTrace() { Isolate* isolate = thread->isolate(); DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8); + Object& code_obj = Object::Handle(zone); Code& code = Code::Handle(zone); +#if !defined(DART_PRECOMPILED_RUNTIME) + Bytecode& bytecode = Bytecode::Handle(zone); +#endif // !defined(DART_PRECOMPILED_RUNTIME) Smi& offset = Smi::Handle(); Code& inlined_code = Code::Handle(zone); Array& deopt_frame = Array::Handle(zone); @@ -1949,12 +2092,21 @@ DebuggerStackTrace* Debugger::CollectAsyncCausalStackTrace() { ASSERT(frame != NULL); if (frame->IsDartFrame()) { if (frame->is_interpreted()) { - // TODO(regis): Support debugging of interpreted frames. - UNIMPLEMENTED(); +#if !defined(DART_PRECOMPILED_RUNTIME) + bytecode = frame->LookupDartBytecode(); + if (bytecode.function() == Function::null()) { + continue; // Skip bytecode stub frame. + } + stack_trace->AddActivation( + CollectDartFrame(isolate, frame->pc(), frame, bytecode)); +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + code = frame->LookupDartCode(); + AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code, + &inlined_code, &deopt_frame); } - code = frame->LookupDartCode(); - AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code, - &inlined_code, &deopt_frame); synchronous_stack_trace_length--; } frame = iterator.NextFrame(); @@ -1965,19 +2117,27 @@ DebuggerStackTrace* Debugger::CollectAsyncCausalStackTrace() { // activated. while (!async_stack_trace.IsNull()) { for (intptr_t i = 0; i < async_stack_trace.Length(); i++) { - if (async_stack_trace.CodeAtFrame(i) == Code::null()) { + code_obj = async_stack_trace.CodeAtFrame(i); + if (code_obj.IsNull()) { break; } - if (async_stack_trace.CodeAtFrame(i) == - StubCode::AsynchronousGapMarker().raw()) { + if (code_obj.raw() == StubCode::AsynchronousGapMarker().raw()) { stack_trace->AddMarker(ActivationFrame::kAsyncSuspensionMarker); // The frame immediately below the asynchronous gap marker is the // identical to the frame above the marker. Skip the frame to enhance // the readability of the trace. i++; } else { - code = Code::RawCast(async_stack_trace.CodeAtFrame(i)); offset = Smi::RawCast(async_stack_trace.PcOffsetAtFrame(i)); +#if !defined(DART_PRECOMPILED_RUNTIME) + if (code_obj.IsBytecode()) { + bytecode ^= code_obj.raw(); + uword pc = bytecode.PayloadStart() + offset.Value(); + stack_trace->AddAsyncCausalFrame(pc, bytecode); + continue; + } +#endif // !defined(DART_PRECOMPILED_RUNTIME) + code ^= code_obj.raw(); uword pc = code.PayloadStart() + offset.Value(); if (code.is_optimized()) { for (InlinedFunctionsIterator it(code, pc); !it.Done(); @@ -2027,6 +2187,7 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { StackFrameIterator::kNoCrossThreadIteration); Code& code = Code::Handle(zone); + Bytecode& bytecode = Bytecode::Handle(zone); Smi& offset = Smi::Handle(zone); Function& function = Function::Handle(zone); Code& inlined_code = Code::Handle(zone); @@ -2048,73 +2209,11 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { } if (frame->IsDartFrame()) { if (frame->is_interpreted()) { - // TODO(regis): Support debugging of interpreted frames. - UNIMPLEMENTED(); - } - code = frame->LookupDartCode(); - if (code.is_optimized()) { - deopt_frame = DeoptimizeToArray(thread, frame, code); - bool found_async_awaiter = false; - bool abort_attempt_to_navigate_through_sync_async = false; - for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done(); - it.Advance()) { - inlined_code = it.code(); - function = it.function(); - - if (skip_sync_async_frames_count > 0) { - function_name = function.QualifiedScrubbedName(); - if (CheckAndSkipAsync(skip_sync_async_frames_count, - function_name)) { - skip_sync_async_frames_count--; - } else { - // Unexpected function in sync async call - skip_sync_async_frames_count = -1; - abort_attempt_to_navigate_through_sync_async = true; - break; - } - } - - if (FLAG_trace_debugger_stacktrace) { - ASSERT(!function.IsNull()); - OS::PrintErr( - "CollectAwaiterReturnStackTrace: visiting inlined function: " - "%s\n", - function.ToFullyQualifiedCString()); - } - intptr_t deopt_frame_offset = it.GetDeoptFpOffset(); - if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { - ActivationFrame* activation = CollectDartFrame( - isolate, it.pc(), frame, inlined_code, deopt_frame, - deopt_frame_offset, ActivationFrame::kAsyncActivation); - ASSERT(activation != NULL); - stack_trace->AddActivation(activation); - stack_has_async_function = true; - // Grab the awaiter. - async_activation ^= activation->GetAsyncAwaiter(); - found_async_awaiter = true; - // async function might have been called synchronously, in which - // case we need to keep going down the stack. - // To determine how we are called we peek few more frames further - // expecting to see Closure_call followed by - // AsyncAwaitCompleter_start. - // If we are able to see those functions we continue going down - // thestack, if we are not, we break out of the loop as we are - // not interested in exploring rest of the stack - there is only - // dart-internal code left. - skip_sync_async_frames_count = 2; - } else { - stack_trace->AddActivation( - CollectDartFrame(isolate, it.pc(), frame, inlined_code, - deopt_frame, deopt_frame_offset)); - } + bytecode = frame->LookupDartBytecode(); + function = bytecode.function(); + if (function.IsNull()) { + continue; // Skip bytecode stub frame. } - // Break out of outer loop. - if (found_async_awaiter || - abort_attempt_to_navigate_through_sync_async) { - break; - } - } else { - function = code.function(); if (skip_sync_async_frames_count > 0) { function_name = function.QualifiedScrubbedName(); @@ -2127,9 +2226,9 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { } if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { - ActivationFrame* activation = CollectDartFrame( - isolate, frame->pc(), frame, code, Object::null_array(), 0, - ActivationFrame::kAsyncActivation); + ActivationFrame* activation = + CollectDartFrame(isolate, frame->pc(), frame, bytecode, + ActivationFrame::kAsyncActivation); ASSERT(activation != NULL); stack_trace->AddActivation(activation); stack_has_async_function = true; @@ -2140,8 +2239,103 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() { // synchronously above. skip_sync_async_frames_count = 2; } else { - stack_trace->AddActivation(CollectDartFrame( - isolate, frame->pc(), frame, code, Object::null_array(), 0)); + stack_trace->AddActivation( + CollectDartFrame(isolate, frame->pc(), frame, bytecode)); + } + } else { + code = frame->LookupDartCode(); + if (code.is_optimized()) { + deopt_frame = DeoptimizeToArray(thread, frame, code); + bool found_async_awaiter = false; + bool abort_attempt_to_navigate_through_sync_async = false; + for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done(); + it.Advance()) { + inlined_code = it.code(); + function = it.function(); + + if (skip_sync_async_frames_count > 0) { + function_name ^= function.QualifiedScrubbedName(); + if (CheckAndSkipAsync(skip_sync_async_frames_count, + function_name)) { + skip_sync_async_frames_count--; + } else { + // Unexpected function in sync async call + skip_sync_async_frames_count = -1; + abort_attempt_to_navigate_through_sync_async = true; + break; + } + } + + if (FLAG_trace_debugger_stacktrace) { + ASSERT(!function.IsNull()); + OS::PrintErr( + "CollectAwaiterReturnStackTrace: visiting inlined function: " + "%s\n", + function.ToFullyQualifiedCString()); + } + intptr_t deopt_frame_offset = it.GetDeoptFpOffset(); + if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { + ActivationFrame* activation = CollectDartFrame( + isolate, it.pc(), frame, inlined_code, deopt_frame, + deopt_frame_offset, ActivationFrame::kAsyncActivation); + ASSERT(activation != NULL); + stack_trace->AddActivation(activation); + stack_has_async_function = true; + // Grab the awaiter. + async_activation ^= activation->GetAsyncAwaiter(); + found_async_awaiter = true; + // async function might have been called synchronously, in which + // case we need to keep going down the stack. + // To determine how we are called we peek few more frames further + // expecting to see Closure_call followed by + // AsyncAwaitCompleter_start. + // If we are able to see those functions we continue going down + // thestack, if we are not, we break out of the loop as we are + // not interested in exploring rest of the stack - there is only + // dart-internal code left. + skip_sync_async_frames_count = 2; + } else { + stack_trace->AddActivation( + CollectDartFrame(isolate, it.pc(), frame, inlined_code, + deopt_frame, deopt_frame_offset)); + } + } + // Break out of outer loop. + if (found_async_awaiter || + abort_attempt_to_navigate_through_sync_async) { + break; + } + } else { + function = code.function(); + + if (skip_sync_async_frames_count > 0) { + function_name ^= function.QualifiedScrubbedName(); + if (CheckAndSkipAsync(skip_sync_async_frames_count, + function_name)) { + skip_sync_async_frames_count--; + } else { + // Unexpected function in synchronous call of async function. + break; + } + } + + if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { + ActivationFrame* activation = CollectDartFrame( + isolate, frame->pc(), frame, code, Object::null_array(), 0, + ActivationFrame::kAsyncActivation); + ASSERT(activation != NULL); + stack_trace->AddActivation(activation); + stack_has_async_function = true; + // Grab the awaiter. + async_activation ^= activation->GetAsyncAwaiter(); + async_stack_trace ^= activation->GetCausalStack(); + // see comment regarding skipping frames of async functions called + // synchronously above. + skip_sync_async_frames_count = 2; + } else { + stack_trace->AddActivation(CollectDartFrame( + isolate, frame->pc(), frame, code, Object::null_array(), 0)); + } } } } @@ -2214,11 +2408,14 @@ ActivationFrame* Debugger::TopDartFrame() const { frame = iterator.NextFrame(); } ASSERT(frame != NULL); +#if !defined(DART_PRECOMPILED_RUNTIME) if (frame->is_interpreted()) { - // TODO(regis): Support debugging of interpreted frames. - UNIMPLEMENTED(); - return NULL; + Bytecode& bytecode = Bytecode::Handle(frame->LookupDartBytecode()); + ActivationFrame* activation = + new ActivationFrame(frame->pc(), frame->fp(), frame->sp(), bytecode); + return activation; } +#endif // !defined(DART_PRECOMPILED_RUNTIME) Code& code = Code::Handle(frame->LookupDartCode()); ActivationFrame* activation = new ActivationFrame( frame->pc(), frame->fp(), frame->sp(), code, Object::null_array(), 0); @@ -2256,6 +2453,9 @@ DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) { Function& function = Function::Handle(); Object& code_object = Object::Handle(); Code& code = Code::Handle(); +#if !defined(DART_PRECOMPILED_RUNTIME) + Bytecode& bytecode = Bytecode::Handle(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) const uword fp = 0; const uword sp = 0; @@ -2271,36 +2471,50 @@ DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) { // a shallow stack). if (!code_object.IsNull()) { if (code_object.IsBytecode()) { - // TODO(regis): Support debugging of interpreted frames. - UNIMPLEMENTED(); - } - code ^= code_object.raw(); - ASSERT(code.IsFunctionCode()); - function = code.function(); - if (function.is_visible()) { - ASSERT(function.raw() == code.function()); - uword pc = - code.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i)); - if (code.is_optimized() && ex_trace.expand_inlined()) { - // Traverse inlined frames. - for (InlinedFunctionsIterator it(code, pc); !it.Done(); - it.Advance()) { - function = it.function(); - code = it.code(); - ASSERT(function.raw() == code.function()); - uword pc = it.pc(); - ASSERT(pc != 0); - ASSERT(code.PayloadStart() <= pc); - ASSERT(pc < (code.PayloadStart() + code.Size())); +#if !defined(DART_PRECOMPILED_RUNTIME) + bytecode ^= code_object.raw(); + function = bytecode.function(); + // Skip bytecode stub frames and frames with invisible function. + if (!function.IsNull() && function.is_visible()) { + ASSERT(function.raw() == bytecode.function()); + uword pc = + bytecode.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i)); + ActivationFrame* activation = + new ActivationFrame(pc, fp, sp, bytecode); + stack_trace->AddActivation(activation); + } +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + code ^= code_object.raw(); + ASSERT(code.IsFunctionCode()); + function = code.function(); + if (function.is_visible()) { + ASSERT(function.raw() == code.function()); + uword pc = + code.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i)); + if (code.is_optimized() && ex_trace.expand_inlined()) { + // Traverse inlined frames. + for (InlinedFunctionsIterator it(code, pc); !it.Done(); + it.Advance()) { + function = it.function(); + code = it.code(); + ASSERT(function.raw() == code.function()); + uword pc = it.pc(); + ASSERT(pc != 0); + ASSERT(code.PayloadStart() <= pc); + ASSERT(pc < (code.PayloadStart() + code.Size())); + ActivationFrame* activation = new ActivationFrame( + pc, fp, sp, code, deopt_frame, deopt_frame_offset); + stack_trace->AddActivation(activation); + } + } else { ActivationFrame* activation = new ActivationFrame( pc, fp, sp, code, deopt_frame, deopt_frame_offset); stack_trace->AddActivation(activation); } - } else { - ActivationFrame* activation = new ActivationFrame( - pc, fp, sp, code, deopt_frame, deopt_frame_offset); - stack_trace->AddActivation(activation); } } } @@ -2431,12 +2645,12 @@ void Debugger::PauseException(const Instance& exc) { // algorithm, which would be simpler. I believe that it only needs // two passes to support the recursive try-the-whole-function case. // Rewrite this later, once there are more tests in place. -TokenPosition Debugger::ResolveBreakpointPos(const Function& func, +TokenPosition Debugger::ResolveBreakpointPos(bool in_bytecode, + const Function& func, TokenPosition requested_token_pos, TokenPosition last_token_pos, intptr_t requested_column, TokenPosition exact_token_pos) { - ASSERT(func.HasCode()); ASSERT(!func.HasOptimizedCode()); if (requested_token_pos < func.token_pos()) { @@ -2448,9 +2662,25 @@ TokenPosition Debugger::ResolveBreakpointPos(const Function& func, Zone* zone = Thread::Current()->zone(); Script& script = Script::Handle(zone, func.script()); - Code& code = Code::Handle(zone, func.unoptimized_code()); - ASSERT(!code.IsNull()); - PcDescriptors& desc = PcDescriptors::Handle(zone, code.pc_descriptors()); + Code& code = Code::Handle(zone); +#if !defined(DART_PRECOMPILED_RUNTIME) + Bytecode& bytecode = Bytecode::Handle(zone); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + PcDescriptors& desc = PcDescriptors::Handle(zone); + if (in_bytecode) { +#if !defined(DART_PRECOMPILED_RUNTIME) + ASSERT(func.HasBytecode()); + bytecode = func.bytecode(); + ASSERT(!bytecode.IsNull()); +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + ASSERT(func.HasCode()); + code = func.unoptimized_code(); + ASSERT(!code.IsNull()); + desc = code.pc_descriptors(); + } // First pass: find the safe point which is closest to the beginning // of the given token range. @@ -2460,53 +2690,109 @@ TokenPosition Debugger::ResolveBreakpointPos(const Function& func, // best_token_pos and exact_token_pos are only used // if column number is provided. TokenPosition best_token_pos = TokenPosition::kNoSource; - PcDescriptors::Iterator iter(desc, kSafepointKind); - while (iter.MoveNext()) { - const TokenPosition pos = iter.TokenPos(); - if ((!pos.IsReal()) || (pos < requested_token_pos) || - (pos > last_token_pos)) { - // Token is not in the target range. - continue; - } - intptr_t token_start_column = -1; - intptr_t token_line = -1; - if (requested_column >= 0) { - // Find next closest safepoint - PcDescriptors::Iterator iter2(desc, kSafepointKind); - TokenPosition next_closest_token_position = TokenPosition::kMaxSource; - while (iter2.MoveNext()) { - const TokenPosition next = iter2.TokenPos(); - if (next < next_closest_token_position && next > pos) { - next_closest_token_position = next; + if (in_bytecode) { +#if !defined(DART_PRECOMPILED_RUNTIME) + kernel::BytecodeSourcePositionsIterator iter(zone, bytecode); + while (iter.MoveNext()) { + const TokenPosition pos = iter.TokenPos(); + if ((!pos.IsReal()) || (pos < requested_token_pos) || + (pos > last_token_pos)) { + // Token is not in the target range. + continue; + } + + intptr_t token_start_column = -1; + intptr_t token_line = -1; + if (requested_column >= 0) { + kernel::BytecodeSourcePositionsIterator iter2(zone, bytecode); + TokenPosition next_closest_token_position = TokenPosition::kMaxSource; + while (iter2.MoveNext()) { + const TokenPosition next = iter2.TokenPos(); + if (next < next_closest_token_position && next > pos) { + next_closest_token_position = next; + } + } + + TokenPosition ignored; + TokenPosition end_of_line_pos; + script.GetTokenLocation(pos, &token_line, &token_start_column); + script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos); + TokenPosition token_end_pos = + (end_of_line_pos < next_closest_token_position) + ? end_of_line_pos + : next_closest_token_position; + + if ((token_end_pos < exact_token_pos) || + (token_start_column > best_column)) { + // Prefer the token with the lowest column number compatible + // with the requested column. + continue; } } - TokenPosition ignored; - TokenPosition end_of_line_pos; - script.GetTokenLocation(pos, &token_line, &token_start_column); - script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos); - TokenPosition token_end_pos = - (end_of_line_pos < next_closest_token_position) - ? end_of_line_pos - : next_closest_token_position; - - if ((token_end_pos < exact_token_pos) || - (token_start_column > best_column)) { - // Prefer the token with the lowest column number compatible - // with the requested column. - continue; + // Prefer the lowest (first) token pos. + if (pos < best_fit_pos) { + best_fit_pos = pos; + best_line = token_line; + best_column = token_start_column; + // best_token_pos is only used when column number is specified. + best_token_pos = TokenPosition(exact_token_pos.value() - + (requested_column - best_column)); } } +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + PcDescriptors::Iterator iter(desc, kSafepointKind); + while (iter.MoveNext()) { + const TokenPosition pos = iter.TokenPos(); + if ((!pos.IsReal()) || (pos < requested_token_pos) || + (pos > last_token_pos)) { + // Token is not in the target range. + continue; + } - // Prefer the lowest (first) token pos. - if (pos < best_fit_pos) { - best_fit_pos = pos; - best_line = token_line; - best_column = token_start_column; - // best_token_pos is only used when column number is specified. - best_token_pos = TokenPosition(exact_token_pos.value() - - (requested_column - best_column)); + intptr_t token_start_column = -1; + intptr_t token_line = -1; + if (requested_column >= 0) { + // Find next closest safepoint + PcDescriptors::Iterator iter2(desc, kSafepointKind); + TokenPosition next_closest_token_position = TokenPosition::kMaxSource; + while (iter2.MoveNext()) { + const TokenPosition next = iter2.TokenPos(); + if (next < next_closest_token_position && next > pos) { + next_closest_token_position = next; + } + } + + TokenPosition ignored; + TokenPosition end_of_line_pos; + script.GetTokenLocation(pos, &token_line, &token_start_column); + script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos); + TokenPosition token_end_pos = + (end_of_line_pos < next_closest_token_position) + ? end_of_line_pos + : next_closest_token_position; + + if ((token_end_pos < exact_token_pos) || + (token_start_column > best_column)) { + // Prefer the token with the lowest column number compatible + // with the requested column. + continue; + } + } + + // Prefer the lowest (first) token pos. + if (pos < best_fit_pos) { + best_fit_pos = pos; + best_line = token_line; + best_column = token_start_column; + // best_token_pos is only used when column number is specified. + best_token_pos = TokenPosition(exact_token_pos.value() - + (requested_column - best_column)); + } } } @@ -2534,24 +2820,51 @@ TokenPosition Debugger::ResolveBreakpointPos(const Function& func, } uword lowest_pc_offset = kUwordMax; - PcDescriptors::Iterator iter(desc, kSafepointKind); - while (iter.MoveNext()) { - const TokenPosition pos = iter.TokenPos(); - if (!pos.IsReal() || (pos < begin_pos) || (pos > end_of_line_pos)) { - // Token is not on same line as best fit. - continue; - } - - if (requested_column >= 0) { - if (pos != best_token_pos) { + if (in_bytecode) { +#if !defined(DART_PRECOMPILED_RUNTIME) + kernel::BytecodeSourcePositionsIterator iter(zone, bytecode); + while (iter.MoveNext()) { + const TokenPosition pos = iter.TokenPos(); + if (!pos.IsReal() || (pos < begin_pos) || (pos > end_of_line_pos)) { + // Token is not on same line as best fit. continue; } - } - // Prefer the lowest pc offset. - if (iter.PcOffset() < lowest_pc_offset) { - lowest_pc_offset = iter.PcOffset(); - best_fit_pos = pos; + if (requested_column >= 0) { + if (pos != best_token_pos) { + continue; + } + } + + // Prefer the lowest pc offset. + if (iter.PcOffset() < lowest_pc_offset) { + lowest_pc_offset = iter.PcOffset(); + best_fit_pos = pos; + } + } +#else + UNREACHABLE(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + } else { + PcDescriptors::Iterator iter(desc, kSafepointKind); + while (iter.MoveNext()) { + const TokenPosition pos = iter.TokenPos(); + if (!pos.IsReal() || (pos < begin_pos) || (pos > end_of_line_pos)) { + // Token is not on same line as best fit. + continue; + } + + if (requested_column >= 0) { + if (pos != best_token_pos) { + continue; + } + } + + // Prefer the lowest pc offset. + if (iter.PcOffset() < lowest_pc_offset) { + lowest_pc_offset = iter.PcOffset(); + best_fit_pos = pos; + } } } return best_fit_pos; @@ -2562,8 +2875,9 @@ TokenPosition Debugger::ResolveBreakpointPos(const Function& func, // Since we have moved to the next line of the function, we no // longer are requesting a specific column number. if (last_token_pos < func.end_token_pos()) { - return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos(), - -1 /* no column */, TokenPosition::kNoSource); + return ResolveBreakpointPos(in_bytecode, func, last_token_pos, + func.end_token_pos(), -1 /* no column */, + TokenPosition::kNoSource); } return TokenPosition::kNoSource; } @@ -2573,43 +2887,78 @@ void Debugger::MakeCodeBreakpointAt(const Function& func, ASSERT(loc->token_pos_.IsReal()); ASSERT((loc != NULL) && loc->IsResolved()); ASSERT(!func.HasOptimizedCode()); - Code& code = Code::Handle(func.unoptimized_code()); - ASSERT(!code.IsNull()); - PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); - uword lowest_pc_offset = kUwordMax; - RawPcDescriptors::Kind lowest_kind = RawPcDescriptors::kAnyKind; - // Find the safe point with the lowest compiled code address - // that maps to the token position of the source breakpoint. - PcDescriptors::Iterator iter(desc, kSafepointKind); - while (iter.MoveNext()) { - if (iter.TokenPos() == loc->token_pos_) { - if (iter.PcOffset() < lowest_pc_offset) { - lowest_pc_offset = iter.PcOffset(); - lowest_kind = iter.Kind(); + ASSERT(func.HasCode() || func.HasBytecode()); +#if !defined(DART_PRECOMPILED_RUNTIME) + if (func.HasBytecode()) { + Bytecode& bytecode = Bytecode::Handle(func.bytecode()); + ASSERT(!bytecode.IsNull()); + uword lowest_pc_offset = kUwordMax; + if (bytecode.HasSourcePositions()) { + kernel::BytecodeSourcePositionsIterator iter(Thread::Current()->zone(), + bytecode); + while (iter.MoveNext()) { + if (iter.TokenPos() == loc->token_pos_) { + if (iter.PcOffset() < lowest_pc_offset) { + lowest_pc_offset = iter.PcOffset(); + } + } + } + } + if (lowest_pc_offset != kUwordMax) { + uword lowest_pc = bytecode.PayloadStart() + lowest_pc_offset; + CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc); + if (code_bpt == NULL) { + // No code breakpoint for this code exists; create one. + code_bpt = new CodeBreakpoint(bytecode, loc->token_pos_, lowest_pc); + RegisterCodeBreakpoint(code_bpt); + } + code_bpt->set_bpt_location(loc); + if (loc->AnyEnabled()) { + code_bpt->Enable(); } } } - if (lowest_pc_offset == kUwordMax) { - return; - } - uword lowest_pc = code.PayloadStart() + lowest_pc_offset; - CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc); - if (code_bpt == NULL) { - // No code breakpoint for this code exists; create one. - code_bpt = - new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind); - RegisterCodeBreakpoint(code_bpt); - } - code_bpt->set_bpt_location(loc); - if (loc->AnyEnabled()) { - code_bpt->Enable(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + if (func.HasCode()) { + Code& code = Code::Handle(func.unoptimized_code()); + ASSERT(!code.IsNull()); + PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); + uword lowest_pc_offset = kUwordMax; + RawPcDescriptors::Kind lowest_kind = RawPcDescriptors::kAnyKind; + // Find the safe point with the lowest compiled code address + // that maps to the token position of the source breakpoint. + PcDescriptors::Iterator iter(desc, kSafepointKind); + while (iter.MoveNext()) { + if (iter.TokenPos() == loc->token_pos_) { + if (iter.PcOffset() < lowest_pc_offset) { + lowest_pc_offset = iter.PcOffset(); + lowest_kind = iter.Kind(); + } + } + } + if (lowest_pc_offset != kUwordMax) { + uword lowest_pc = code.PayloadStart() + lowest_pc_offset; + CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc); + if (code_bpt == NULL) { + // No code breakpoint for this code exists; create one. + code_bpt = + new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind); + RegisterCodeBreakpoint(code_bpt); + } + code_bpt->set_bpt_location(loc); + if (loc->AnyEnabled()) { + code_bpt->Enable(); + } + } } } -void Debugger::FindCompiledFunctions(const Script& script, - TokenPosition start_pos, - TokenPosition end_pos, - GrowableObjectArray* function_list) { +void Debugger::FindCompiledFunctions( + const Script& script, + TokenPosition start_pos, + TokenPosition end_pos, + GrowableObjectArray* bytecode_function_list, + GrowableObjectArray* code_function_list) { Zone* zone = Thread::Current()->zone(); Class& cls = Class::Handle(zone); Array& functions = Array::Handle(zone); @@ -2624,13 +2973,23 @@ void Debugger::FindCompiledFunctions(const Script& script, if ((function.token_pos() == start_pos) && (function.end_token_pos() == end_pos) && (function.script() == script.raw())) { - if (function.HasCode() && function.is_debuggable()) { - function_list->Add(function); + if (function.is_debuggable()) { + if (function.HasBytecode()) { + bytecode_function_list->Add(function); + } + if (function.HasCode()) { + code_function_list->Add(function); + } } if (function.HasImplicitClosureFunction()) { function = function.ImplicitClosureFunction(); - if (function.HasCode() && function.is_debuggable()) { - function_list->Add(function); + if (function.is_debuggable()) { + if (function.HasBytecode()) { + bytecode_function_list->Add(function); + } + if (function.HasCode()) { + code_function_list->Add(function); + } } } } @@ -2662,13 +3021,23 @@ void Debugger::FindCompiledFunctions(const Script& script, if ((function.token_pos() == start_pos) && (function.end_token_pos() == end_pos) && (function.script() == script.raw())) { - if (function.HasCode() && function.is_debuggable()) { - function_list->Add(function); + if (function.is_debuggable()) { + if (function.HasBytecode()) { + bytecode_function_list->Add(function); + } + if (function.HasCode()) { + code_function_list->Add(function); + } } if (function.HasImplicitClosureFunction()) { function = function.ImplicitClosureFunction(); - if (function.HasCode() && function.is_debuggable()) { - function_list->Add(function); + if (function.is_debuggable()) { + if (function.HasBytecode()) { + bytecode_function_list->Add(function); + } + if (function.HasCode()) { + code_function_list->Add(function); + } } } } @@ -2800,6 +3169,71 @@ bool Debugger::FindBestFit(const Script& script, return false; } +BreakpointLocation* Debugger::SetCodeBreakpoints( + bool in_bytecode, + BreakpointLocation* loc, + const Script& script, + TokenPosition token_pos, + TokenPosition last_token_pos, + intptr_t requested_line, + intptr_t requested_column, + TokenPosition exact_token_pos, + const GrowableObjectArray& functions) { + Function& function = Function::Handle(); + function ^= functions.At(0); + TokenPosition breakpoint_pos = + ResolveBreakpointPos(in_bytecode, function, token_pos, last_token_pos, + requested_column, exact_token_pos); + if (!breakpoint_pos.IsReal()) { + return NULL; + } + if (loc == NULL) { + // Find an existing resolved breakpoint location. + loc = GetBreakpointLocation( + script, TokenPosition::kNoSource, + /* requested_line = */ -1, + /* requested_column = */ -1, + in_bytecode ? breakpoint_pos : TokenPosition::kNoSource, + !in_bytecode ? breakpoint_pos : TokenPosition::kNoSource); + } + if (loc == NULL) { + // Find an existing unresolved breakpoint location. + loc = GetBreakpointLocation(script, token_pos, requested_line, + requested_column); + } + if (loc == NULL) { + loc = new BreakpointLocation(script, breakpoint_pos, breakpoint_pos, + requested_line, requested_column); + RegisterBreakpointLocation(loc); + } + // A source breakpoint for this location may already exists, but it may + // not yet be resolved in both bytecode and code. + if (loc->IsResolved(in_bytecode)) { + return loc; + } + loc->SetResolved(in_bytecode, function, breakpoint_pos); + + // Create code breakpoints for all compiled functions we found. + Function& func = Function::Handle(); + const intptr_t num_functions = functions.Length(); + for (intptr_t i = 0; i < num_functions; i++) { + func ^= functions.At(i); + ASSERT((in_bytecode && func.HasBytecode()) || + (!in_bytecode && func.HasCode())); + MakeCodeBreakpointAt(func, loc); + } + if (FLAG_verbose_debug) { + intptr_t line_number; + intptr_t column_number; + script.GetTokenLocation(breakpoint_pos, &line_number, &column_number); + OS::PrintErr( + "Resolved BP for " + "function '%s' at line %" Pd " col %" Pd "\n", + func.ToFullyQualifiedCString(), line_number, column_number); + } + return loc; +} + BreakpointLocation* Debugger::SetBreakpoint(const Script& script, TokenPosition token_pos, TokenPosition last_token_pos, @@ -2811,65 +3245,53 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, if (!FindBestFit(script, token_pos, last_token_pos, &func)) { return NULL; } + // If func was not set (still Null), the best fit is a field. } else { func = function.raw(); + if (!func.token_pos().IsReal()) { + return NULL; // Missing source positions in bytecode? + } } - if (!func.IsNull()) { // There may be more than one function object for a given function // in source code. There may be implicit closure functions, and // there may be copies of mixin functions. Collect all compiled // functions whose source code range matches exactly the best fit // function we found. - GrowableObjectArray& functions = + GrowableObjectArray& bytecode_functions = + GrowableObjectArray::Handle(GrowableObjectArray::New()); + GrowableObjectArray& code_functions = GrowableObjectArray::Handle(GrowableObjectArray::New()); FindCompiledFunctions(script, func.token_pos(), func.end_token_pos(), - &functions); + &bytecode_functions, &code_functions); - if (functions.Length() > 0) { + if (bytecode_functions.Length() > 0 || code_functions.Length() > 0) { // One or more function object containing this breakpoint location // have already been compiled. We can resolve the breakpoint now. - DeoptimizeWorld(); - func ^= functions.At(0); - TokenPosition exact_token_pos = TokenPosition(-1); - // if requested_column is larger than zero, [token_pos, last_token_pos] + // If requested_column is larger than zero, [token_pos, last_token_pos] // governs one single line of code. + TokenPosition exact_token_pos = TokenPosition(-1); if (token_pos != last_token_pos && requested_column >= 0) { #if !defined(DART_PRECOMPILED_RUNTIME) exact_token_pos = FindExactTokenPosition(script, token_pos, requested_column); #endif // !defined(DART_PRECOMPILED_RUNTIME) } - TokenPosition breakpoint_pos = ResolveBreakpointPos( - func, token_pos, last_token_pos, requested_column, exact_token_pos); - if (breakpoint_pos.IsReal()) { - BreakpointLocation* loc = GetBreakpointLocation(script, breakpoint_pos, - requested_column, true); - if (loc != NULL) { - // A source breakpoint for this location already exists. - return loc; - } - loc = new BreakpointLocation(script, breakpoint_pos, breakpoint_pos, - requested_line, requested_column); - loc->SetResolved(func, breakpoint_pos); - RegisterBreakpointLocation(loc); - - // Create code breakpoints for all compiled functions we found. - const intptr_t num_functions = functions.Length(); - for (intptr_t i = 0; i < num_functions; i++) { - func ^= functions.At(i); - ASSERT(func.HasCode()); - MakeCodeBreakpointAt(func, loc); - } - if (FLAG_verbose_debug) { - intptr_t line_number; - intptr_t column_number; - script.GetTokenLocation(breakpoint_pos, &line_number, &column_number); - OS::PrintErr( - "Resolved BP for " - "function '%s' at line %" Pd " col %" Pd "\n", - func.ToFullyQualifiedCString(), line_number, column_number); - } + // Since source positions may differ in code and bytecode, process + // breakpoints in bytecode and code separately. + BreakpointLocation* loc = NULL; + if (bytecode_functions.Length() > 0) { + loc = SetCodeBreakpoints(true, loc, script, token_pos, last_token_pos, + requested_line, requested_column, + exact_token_pos, bytecode_functions); + } + if (code_functions.Length() > 0) { + DeoptimizeWorld(); + loc = SetCodeBreakpoints(false, loc, script, token_pos, last_token_pos, + requested_line, requested_column, + exact_token_pos, code_functions); + } + if (loc != NULL) { return loc; } } @@ -2894,7 +3316,7 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, } } BreakpointLocation* loc = - GetBreakpointLocation(script, token_pos, requested_column); + GetBreakpointLocation(script, token_pos, -1, requested_column); if (loc == NULL) { loc = new BreakpointLocation(script, token_pos, last_token_pos, requested_line, requested_column); @@ -3525,6 +3947,12 @@ bool Debugger::IsDebuggable(const Function& func) { return lib.IsDebuggable(); } +bool Debugger::IsDebugging(Thread* thread, const Function& func) { + Debugger* debugger = thread->isolate()->debugger(); + return debugger->IsStepping() || + debugger->HasBreakpoint(func, thread->zone()); +} + void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) { resume_action_ = kContinue; ResetSteppingFramePointers(); @@ -3836,7 +4264,7 @@ TokenPosition Debugger::FindExactTokenPosition(const Script& script, } #endif // !defined(DART_PRECOMPILED_RUNTIME) -void Debugger::NotifyCompilation(const Function& func) { +void Debugger::HandleCodeChange(bool bytecode_loaded, const Function& func) { if (breakpoint_locations_ == NULL) { // Return with minimal overhead if there are no breakpoints. return; @@ -3868,6 +4296,8 @@ void Debugger::NotifyCompilation(const Function& func) { loc->requested_column_number()); #endif // !defined(DART_PRECOMPILED_RUNTIME) } + // TODO(regis): Bytecode closures are not currently added to + // object_store()->closure_functions(). Should they? Revisit. inner_function = FindInnermostClosure(func, token_pos); if (!inner_function.IsNull()) { // The local function of a function we just compiled cannot @@ -3887,11 +4317,11 @@ void Debugger::NotifyCompilation(const Function& func) { // There is no local function within func that contains the // breakpoint token position. Resolve the breakpoint if necessary // and set the code breakpoints. - if (!loc->IsResolved()) { + if (!loc->IsResolved(bytecode_loaded)) { // Resolve source breakpoint in the newly compiled function. - TokenPosition bp_pos = - ResolveBreakpointPos(func, loc->token_pos(), loc->end_token_pos(), - loc->requested_column_number(), token_pos); + TokenPosition bp_pos = ResolveBreakpointPos( + bytecode_loaded, func, loc->token_pos(), loc->end_token_pos(), + loc->requested_column_number(), token_pos); if (!bp_pos.IsDebugPause()) { if (FLAG_verbose_debug) { OS::PrintErr("Failed resolving breakpoint for function '%s'\n", @@ -3901,34 +4331,28 @@ void Debugger::NotifyCompilation(const Function& func) { } TokenPosition requested_pos = loc->token_pos(); TokenPosition requested_end_pos = loc->end_token_pos(); - loc->SetResolved(func, bp_pos); + loc->SetResolved(bytecode_loaded, func, bp_pos); Breakpoint* bpt = loc->breakpoints(); while (bpt != NULL) { if (FLAG_verbose_debug) { OS::PrintErr( "Resolved BP %" Pd " to pos %s, " - "line %" Pd " col %" Pd - ", " "function '%s' (requested range %s-%s, " "requested col %" Pd ")\n", - bpt->id(), loc->token_pos().ToCString(), loc->LineNumber(), - loc->ColumnNumber(), func.ToFullyQualifiedCString(), - requested_pos.ToCString(), requested_end_pos.ToCString(), - loc->requested_column_number()); + bpt->id(), loc->token_pos().ToCString(), + func.ToFullyQualifiedCString(), requested_pos.ToCString(), + requested_end_pos.ToCString(), loc->requested_column_number()); } SendBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt); bpt = bpt->next(); } } - ASSERT(loc->IsResolved()); + ASSERT(loc->IsResolved(bytecode_loaded)); if (FLAG_verbose_debug) { Breakpoint* bpt = loc->breakpoints(); while (bpt != NULL) { - OS::PrintErr("Setting breakpoint %" Pd " at line %" Pd " col %" Pd - "" - " for %s '%s'\n", - bpt->id(), loc->LineNumber(), loc->ColumnNumber(), + OS::PrintErr("Setting breakpoint %" Pd " for %s '%s'\n", bpt->id(), func.IsClosureFunction() ? "closure" : "function", String::Handle(func.name()).ToCString()); bpt = bpt->next(); @@ -3998,7 +4422,7 @@ void Debugger::NotifyDoneLoading() { // If there is one, assert in debug build but silently drop // the latent breakpoint in release build. BreakpointLocation* existing_loc = - GetBreakpointLocation(script, first_token_pos, column_number); + GetBreakpointLocation(script, first_token_pos, -1, column_number); ASSERT(existing_loc == NULL); if (existing_loc == NULL) { // Create and register a new source breakpoint for the @@ -4201,21 +4625,25 @@ void Debugger::RemoveUnlinkedCodeBreakpoints() { needs_breakpoint_cleanup_ = false; } -BreakpointLocation* Debugger::GetBreakpointLocation(const Script& script, - TokenPosition token_pos, - intptr_t requested_column, - bool is_resolved) { +BreakpointLocation* Debugger::GetBreakpointLocation( + const Script& script, + TokenPosition token_pos, + intptr_t requested_line, + intptr_t requested_column, + TokenPosition bytecode_token_pos, + TokenPosition code_token_pos) { BreakpointLocation* loc = breakpoint_locations_; while (loc != NULL) { - // When BreakpointLocation has been resolved BreakpointLocation - // should check only token position. Whether column or line - // breakpoint should be the same once resolved into the - // same token position. - bool column_match = - (is_resolved) ? true - : (loc->requested_column_number_ == requested_column); - if ((loc->script_ == script.raw()) && (loc->token_pos_ == token_pos) && - column_match) { + if (loc->script_ == script.raw() && + (!token_pos.IsReal() || (loc->token_pos_ == token_pos)) && + ((requested_line == -1) || + (loc->requested_line_number_ == requested_line)) && + ((requested_column == -1) || + (loc->requested_column_number_ == requested_column)) && + (!bytecode_token_pos.IsReal() || + (loc->bytecode_token_pos_ == bytecode_token_pos)) && + (!code_token_pos.IsReal() || + (loc->code_token_pos_ == code_token_pos))) { return loc; } loc = loc->next(); diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h index 2541c2d94d2..f8fee7b5c01 100644 --- a/runtime/vm/debugger.h +++ b/runtime/vm/debugger.h @@ -7,6 +7,7 @@ #include "include/dart_tools_api.h" +#include "vm/constants_kbc.h" #include "vm/kernel_isolate.h" #include "vm/object.h" #include "vm/port.h" @@ -140,12 +141,7 @@ class BreakpointLocation { intptr_t requested_line_number() const { return requested_line_number_; } intptr_t requested_column_number() const { return requested_column_number_; } - intptr_t LineNumber(); - intptr_t ColumnNumber(); - - void GetCodeLocation(Library* lib, - Script* script, - TokenPosition* token_pos) const; + void GetCodeLocation(Script* script, TokenPosition* token_pos) const; Breakpoint* AddRepeated(Debugger* dbg); Breakpoint* AddSingleShot(Debugger* dbg); @@ -154,13 +150,21 @@ class BreakpointLocation { bool for_over_await); bool AnyEnabled() const; - bool IsResolved() const { return is_resolved_; } + bool IsResolved() const { + return bytecode_token_pos_.IsReal() || code_token_pos_.IsReal(); + } + bool IsResolved(bool in_bytecode) const { + return in_bytecode ? bytecode_token_pos_.IsReal() + : code_token_pos_.IsReal(); + } bool IsLatent() const { return !token_pos_.IsReal(); } private: void VisitObjectPointers(ObjectPointerVisitor* visitor); - void SetResolved(const Function& func, TokenPosition token_pos); + void SetResolved(bool in_bytecode, + const Function& func, + TokenPosition token_pos); BreakpointLocation* next() const { return this->next_; } void set_next(BreakpointLocation* value) { next_ = value; } @@ -174,7 +178,6 @@ class BreakpointLocation { RawString* url_; TokenPosition token_pos_; TokenPosition end_token_pos_; - bool is_resolved_; BreakpointLocation* next_; Breakpoint* conditions_; intptr_t requested_line_number_; @@ -182,22 +185,23 @@ class BreakpointLocation { // Valid for resolved breakpoints: RawFunction* function_; - intptr_t line_number_; - intptr_t column_number_; + TokenPosition bytecode_token_pos_; + TokenPosition code_token_pos_; friend class Debugger; DISALLOW_COPY_AND_ASSIGN(BreakpointLocation); }; -// CodeBreakpoint represents a location in compiled code. There may be -// more than one CodeBreakpoint for one BreakpointLocation, e.g. when a -// function gets compiled as a regular function and as a closure. +// CodeBreakpoint represents a location in compiled or interpreted code. +// There may be more than one CodeBreakpoint for one BreakpointLocation, +// e.g. when a function gets compiled as a regular function and as a closure. class CodeBreakpoint { public: CodeBreakpoint(const Code& code, TokenPosition token_pos, uword pc, RawPcDescriptors::Kind kind); + CodeBreakpoint(const Bytecode& bytecode, TokenPosition token_pos, uword pc); ~CodeBreakpoint(); RawFunction* function() const; @@ -211,6 +215,7 @@ class CodeBreakpoint { void Enable(); void Disable(); bool IsEnabled() const { return is_enabled_; } + bool IsInterpreted() const { return bytecode_ != Bytecode::null(); } RawCode* OrigStubAddress() const; @@ -225,8 +230,11 @@ class CodeBreakpoint { void PatchCode(); void RestoreCode(); + void SetBytecodeBreak(); + void UnsetBytecodeBreak(); RawCode* code_; + RawBytecode* bytecode_; TokenPosition token_pos_; uword pc_; intptr_t line_number_; @@ -271,6 +279,16 @@ class ActivationFrame : public ZoneAllocated { ActivationFrame(uword pc, const Code& code); +#if !defined(DART_PRECOMPILED_RUNTIME) + ActivationFrame(uword pc, + uword fp, + uword sp, + const Bytecode& bytecode, + Kind kind = kRegular); + + ActivationFrame(uword pc, const Bytecode& bytecode); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + explicit ActivationFrame(Kind kind); explicit ActivationFrame(const Closure& async_activation); @@ -286,7 +304,11 @@ class ActivationFrame : public ZoneAllocated { ASSERT(!code_.IsNull()); return code_; } - bool is_interpreted() const { return is_interpreted_; } + const Bytecode& bytecode() const { + ASSERT(!bytecode_.IsNull()); + return bytecode_; + } + bool IsInterpreted() const { return !bytecode_.IsNull(); } RawString* QualifiedFunctionName(); RawString* SourceUrl(); @@ -398,6 +420,7 @@ class ActivationFrame : public ZoneAllocated { // The anchor of the context chain for this function. Context& ctx_; Code& code_; + Bytecode& bytecode_; Function& function_; bool live_frame_; // Is this frame a live frame? bool token_pos_initialized_; @@ -415,7 +438,6 @@ class ActivationFrame : public ZoneAllocated { Kind kind_; - bool is_interpreted_; // Running under kernel bytecode interpreter. bool vars_initialized_; LocalVarDescriptors& var_descriptors_; ZoneGrowableArray desc_indices_; @@ -441,6 +463,7 @@ class DebuggerStackTrace : public ZoneAllocated { void AddActivation(ActivationFrame* frame); void AddMarker(ActivationFrame::Kind marker); void AddAsyncCausalFrame(uword pc, const Code& code); + void AddAsyncCausalFrame(uword pc, const Bytecode& bytecode); ZoneGrowableArray trace_; @@ -475,7 +498,12 @@ class Debugger { void OnIsolateRunnable(); - void NotifyCompilation(const Function& func); + void NotifyCompilation(const Function& func) { + HandleCodeChange(/* bytecode_loaded = */ false, func); + } + void NotifyBytecodeLoaded(const Function& func) { + HandleCodeChange(/* bytecode_loaded = */ true, func); + } void NotifyDoneLoading(); RawFunction* ResolveFunction(const Library& library, @@ -546,6 +574,7 @@ class Debugger { // debugger's zone. bool HasBreakpoint(const Function& func, Zone* zone); bool HasBreakpoint(const Code& code); + // A Bytecode version of HasBreakpoint is not needed. // Returns true if the call at address pc is patched to point to // a debugger stub. @@ -597,6 +626,7 @@ class Debugger { void PrintSettingsToJSONObject(JSONObject* jsobj) const; static bool IsDebuggable(const Function& func); + static bool IsDebugging(Thread* thread, const Function& func); intptr_t limitBreakpointId() { return next_id_; } @@ -625,19 +655,30 @@ class Debugger { void FindCompiledFunctions(const Script& script, TokenPosition start_pos, TokenPosition end_pos, - GrowableObjectArray* function_list); + GrowableObjectArray* bytecode_function_list, + GrowableObjectArray* code_function_list); bool FindBestFit(const Script& script, TokenPosition token_pos, TokenPosition last_token_pos, Function* best_fit); RawFunction* FindInnermostClosure(const Function& function, TokenPosition token_pos); - TokenPosition ResolveBreakpointPos(const Function& func, + TokenPosition ResolveBreakpointPos(bool in_bytecode, + const Function& func, TokenPosition requested_token_pos, TokenPosition last_token_pos, intptr_t requested_column, TokenPosition exact_token_pos); void DeoptimizeWorld(); + BreakpointLocation* SetCodeBreakpoints(bool in_bytecode, + BreakpointLocation* loc, + const Script& script, + TokenPosition token_pos, + TokenPosition last_token_pos, + intptr_t requested_line, + intptr_t requested_column, + TokenPosition exact_token_pos, + const GrowableObjectArray& functions); BreakpointLocation* SetBreakpoint(const Script& script, TokenPosition token_pos, TokenPosition last_token_pos, @@ -653,10 +694,13 @@ class Debugger { intptr_t column); void RegisterBreakpointLocation(BreakpointLocation* bpt); void RegisterCodeBreakpoint(CodeBreakpoint* bpt); - BreakpointLocation* GetBreakpointLocation(const Script& script, - TokenPosition token_pos, - intptr_t requested_column, - bool is_resolved = false); + BreakpointLocation* GetBreakpointLocation( + const Script& script, + TokenPosition token_pos, + intptr_t requested_line, + intptr_t requested_column, + TokenPosition bytecode_token_pos = TokenPosition::kNoSource, + TokenPosition code_token_pos = TokenPosition::kNoSource); void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt); // Returns NULL if no breakpoint exists for the given address. CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address); @@ -665,6 +709,8 @@ class Debugger { void PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt, JSONArray* jsarr) const; + void HandleCodeChange(bool bytecode_loaded, const Function& func); + ActivationFrame* TopDartFrame() const; static ActivationFrame* CollectDartFrame( Isolate* isolate, @@ -675,6 +721,12 @@ class Debugger { intptr_t deopt_frame_offset, ActivationFrame::Kind kind = ActivationFrame::kRegular); #if !defined(DART_PRECOMPILED_RUNTIME) + static ActivationFrame* CollectDartFrame( + Isolate* isolate, + uword pc, + StackFrame* frame, + const Bytecode& bytecode, + ActivationFrame::Kind kind = ActivationFrame::kRegular); static RawArray* DeoptimizeToArray(Thread* thread, StackFrame* frame, const Code& code); diff --git a/runtime/vm/debugger_kbc.cc b/runtime/vm/debugger_kbc.cc new file mode 100644 index 00000000000..2bfbb65d484 --- /dev/null +++ b/runtime/vm/debugger_kbc.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if !defined(DART_PRECOMPILED_RUNTIME) + +#include "vm/debugger.h" +#include "vm/instructions_kbc.h" + +namespace dart { + +#ifndef PRODUCT + +void CodeBreakpoint::SetBytecodeBreak() { + ASSERT(!is_enabled_); + ASSERT(!Isolate::Current()->is_using_old_bytecode_instructions()); + // TODO(regis): Register pc_ (or the token pos range including pc_) with the + // interpreter as a debug break address. + is_enabled_ = true; +} + +void CodeBreakpoint::UnsetBytecodeBreak() { + ASSERT(is_enabled_); + // TODO(regis): Unregister pc_ (or the token pos range including pc_) with the + // interpreter as a debug break address. + is_enabled_ = false; +} +#endif // !PRODUCT + +} // namespace dart + +#endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc index a0ba01eb85c..e815a7fb449 100644 --- a/runtime/vm/interpreter.cc +++ b/runtime/vm/interpreter.cc @@ -896,7 +896,7 @@ DART_FORCE_INLINE bool Interpreter::InstanceCall2(Thread* thread, } while (0) #endif -// Fetch next operation from PC, increment program counter and dispatch. +// Fetch next operation from PC and dispatch. #define DISPATCH() DISPATCH_OP(*pc) // Load target of a jump instruction into PC. diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc index 1a78ed80453..aeaa9d03e99 100644 --- a/runtime/vm/json_stream.cc +++ b/runtime/vm/json_stream.cc @@ -524,10 +524,9 @@ void JSONObject::AddLocation(const BreakpointLocation* bpt_loc) const { ASSERT(bpt_loc->IsResolved()); Zone* zone = Thread::Current()->zone(); - Library& library = Library::Handle(zone); Script& script = Script::Handle(zone); TokenPosition token_pos = TokenPosition::kNoSource; - bpt_loc->GetCodeLocation(&library, &script, &token_pos); + bpt_loc->GetCodeLocation(&script, &token_pos); AddLocation(script, token_pos); } @@ -536,10 +535,9 @@ void JSONObject::AddUnresolvedLocation( ASSERT(!bpt_loc->IsResolved()); Zone* zone = Thread::Current()->zone(); - Library& library = Library::Handle(zone); Script& script = Script::Handle(zone); TokenPosition token_pos = TokenPosition::kNoSource; - bpt_loc->GetCodeLocation(&library, &script, &token_pos); + bpt_loc->GetCodeLocation(&script, &token_pos); JSONObject location(this, "location"); location.AddProperty("type", "UnresolvedSourceLocation"); diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc index 5c5b1ab4418..545c477cc2b 100644 --- a/runtime/vm/kernel.cc +++ b/runtime/vm/kernel.cc @@ -256,7 +256,7 @@ static RawArray* AsSortedDuplicateFreeArray(GrowableArray* source) { return array_object.raw(); } -static void ProcessTokenPositionsEntry( +static void CollectKernelDataTokenPositions( const TypedDataBase& kernel_data, const Script& script, const Script& entry_script, @@ -278,6 +278,64 @@ static void ProcessTokenPositionsEntry( token_position_collector.CollectTokenPositions(kernel_offset); } +static void CollectBytecodeTokenPositions( + const Bytecode& bytecode, + Zone* zone, + GrowableArray* token_positions, + GrowableArray* yield_positions) { + ASSERT(bytecode.HasSourcePositions()); + BytecodeSourcePositionsIterator iter(zone, bytecode); + while (iter.MoveNext()) { + const TokenPosition pos = iter.TokenPos(); + if (pos.IsReal()) { + // TODO(alexmarkov): collect yield positions from bytecode. + token_positions->Add(pos.value()); + } + } +} + +static void CollectBytecodeFunctionTokenPositions( + const Function& function, + GrowableArray* token_positions, + GrowableArray* yield_positions) { + Thread* thread = Thread::Current(); + Zone* zone = thread->zone(); + ASSERT(function.is_declared_in_bytecode()); + if (!function.HasBytecode()) { + const Object& result = Object::Handle( + zone, BytecodeReader::ReadFunctionBytecode(thread, function)); + if (!result.IsNull()) { + Exceptions::PropagateError(Error::Cast(result)); + } + } + Bytecode& bytecode = Bytecode::Handle(zone, function.bytecode()); + ASSERT(!bytecode.IsNull()); + if (bytecode.HasSourcePositions()) { + CollectBytecodeTokenPositions(bytecode, zone, token_positions, + yield_positions); + // Find closure functions in the object pool. + const ObjectPool& pool = ObjectPool::Handle(zone, bytecode.object_pool()); + Object& object = Object::Handle(zone); + Function& closure = Function::Handle(zone); + for (intptr_t i = 0; i < pool.Length(); i++) { + ObjectPool::EntryType entry_type = pool.TypeAt(i); + if (entry_type != ObjectPool::EntryType::kTaggedObject) { + continue; + } + object = pool.ObjectAt(i); + if (object.IsFunction()) { + closure ^= object.raw(); + if ((closure.kind() == RawFunction::kClosureFunction) && + (closure.raw() != function.raw())) { + bytecode = closure.bytecode(); + CollectBytecodeTokenPositions(bytecode, zone, token_positions, + yield_positions); + } + } + } + } +} + void CollectTokenPositionsFor(const Script& interesting_script) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -325,29 +383,30 @@ void CollectTokenPositionsFor(const Script& interesting_script) { continue; } data = temp_field.KernelData(); - ProcessTokenPositionsEntry(data, interesting_script, entry_script, - temp_field.kernel_offset(), - temp_field.KernelDataProgramOffset(), - zone, &helper, &token_positions, - &yield_positions); + CollectKernelDataTokenPositions( + data, interesting_script, entry_script, + temp_field.kernel_offset(), + temp_field.KernelDataProgramOffset(), zone, &helper, + &token_positions, &yield_positions); } temp_array = klass.functions(); for (intptr_t i = 0; i < temp_array.Length(); ++i) { temp_function ^= temp_array.At(i); entry_script = temp_function.script(); - // TODO(alexmarkov): collect token positions from bytecode - if (temp_function.is_declared_in_bytecode()) { - continue; - } if (entry_script.raw() != interesting_script.raw()) { continue; } - data = temp_function.KernelData(); - ProcessTokenPositionsEntry(data, interesting_script, entry_script, - temp_function.kernel_offset(), - temp_function.KernelDataProgramOffset(), - zone, &helper, &token_positions, - &yield_positions); + if (temp_function.is_declared_in_bytecode()) { + CollectBytecodeFunctionTokenPositions( + temp_function, &token_positions, &yield_positions); + } else { + data = temp_function.KernelData(); + CollectKernelDataTokenPositions( + data, interesting_script, entry_script, + temp_function.kernel_offset(), + temp_function.KernelDataProgramOffset(), zone, &helper, + &token_positions, &yield_positions); + } } } else { // Class isn't finalized yet: read the data attached to it. @@ -362,27 +421,28 @@ void CollectTokenPositionsFor(const Script& interesting_script) { if (entry_script.raw() != interesting_script.raw()) { continue; } - ProcessTokenPositionsEntry(data, interesting_script, entry_script, - class_offset, library_kernel_offset, zone, - &helper, &token_positions, - &yield_positions); + CollectKernelDataTokenPositions(data, interesting_script, + entry_script, class_offset, + library_kernel_offset, zone, &helper, + &token_positions, &yield_positions); } } else if (entry.IsFunction()) { temp_function ^= entry.raw(); - // TODO(alexmarkov): collect token positions from bytecode - if (temp_function.is_declared_in_bytecode()) { - continue; - } entry_script = temp_function.script(); if (entry_script.raw() != interesting_script.raw()) { continue; } - data = temp_function.KernelData(); - ProcessTokenPositionsEntry(data, interesting_script, entry_script, - temp_function.kernel_offset(), - temp_function.KernelDataProgramOffset(), - zone, &helper, &token_positions, - &yield_positions); + if (temp_function.is_declared_in_bytecode()) { + CollectBytecodeFunctionTokenPositions(temp_function, &token_positions, + &yield_positions); + } else { + data = temp_function.KernelData(); + CollectKernelDataTokenPositions( + data, interesting_script, entry_script, + temp_function.kernel_offset(), + temp_function.KernelDataProgramOffset(), zone, &helper, + &token_positions, &yield_positions); + } } else if (entry.IsField()) { const Field& field = Field::Cast(entry); // TODO(alexmarkov): collect token positions from bytecode @@ -395,10 +455,10 @@ void CollectTokenPositionsFor(const Script& interesting_script) { continue; } data = field.KernelData(); - ProcessTokenPositionsEntry(data, interesting_script, entry_script, - field.kernel_offset(), - field.KernelDataProgramOffset(), zone, - &helper, &token_positions, &yield_positions); + CollectKernelDataTokenPositions( + data, interesting_script, entry_script, field.kernel_offset(), + field.KernelDataProgramOffset(), zone, &helper, &token_positions, + &yield_positions); } } } diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 9c15b9abc29..f40a3bcf9bc 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -5727,9 +5727,10 @@ void Function::AttachBytecode(const Bytecode& value) const { StorePointer(&raw_ptr()->bytecode_, value.raw()); // We should not have loaded the bytecode if the function had code. - ASSERT(!HasCode()); - - if (FLAG_enable_interpreter) { + // However, we may load the bytecode to access source positions (see + // ProcessBytecodeTokenPositionsEntry in kernel.cc). + // In that case, do not install InterpretCall stub below. + if (FLAG_enable_interpreter && !HasCode()) { // Set the code entry_point to InterpretCall stub. SetInstructions(StubCode::InterpretCall()); } @@ -15235,6 +15236,35 @@ TokenPosition Bytecode::GetTokenIndexOfPC(uword pc) const { #endif } +intptr_t Bytecode::GetTryIndexAtPc(uword return_address) const { +#if defined(DART_PRECOMPILED_RUNTIME) + UNREACHABLE(); +#else + intptr_t try_index = -1; + const uword pc_offset = return_address - PayloadStart(); + const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors()); + PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind); + while (iter.MoveNext()) { + // PC descriptors for try blocks in bytecode are generated in pairs, + // marking start and end of a try block. + // See BytecodeMetadataHelper::ReadExceptionsTable for details. + const intptr_t current_try_index = iter.TryIndex(); + const uword start_pc = iter.PcOffset(); + if (pc_offset < start_pc) { + break; + } + const bool has_next = iter.MoveNext(); + ASSERT(has_next); + const uword end_pc = iter.PcOffset(); + if (start_pc <= pc_offset && pc_offset < end_pc) { + ASSERT(try_index < current_try_index); + try_index = current_try_index; + } + } + return try_index; +#endif +} + const char* Bytecode::ToCString() const { return Thread::Current()->zone()->PrintToString("Bytecode(%s)", QualifiedName()); diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 7d5190571d7..56878fbf437 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -2182,6 +2182,8 @@ class Function : public Object { void AttachBytecode(const Bytecode& bytecode) const; RawBytecode* bytecode() const { return raw_ptr()->bytecode_; } inline bool HasBytecode() const; +#else + inline bool HasBytecode() const { return false; } #endif virtual intptr_t Hash() const; @@ -5501,6 +5503,7 @@ class Bytecode : public Object { RawTypedDataBase* GetBinary(Zone* zone) const; TokenPosition GetTokenIndexOfPC(uword pc) const; + intptr_t GetTryIndexAtPc(uword return_address) const; intptr_t instructions_binary_offset() const { return raw_ptr()->instructions_binary_offset_; diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc index 27d808f46bb..eb5d39a34d3 100644 --- a/runtime/vm/raw_object.cc +++ b/runtime/vm/raw_object.cc @@ -537,7 +537,7 @@ bool RawBytecode::ContainsPC(RawObject* raw_obj, uword pc) { RawBytecode* raw_bytecode = static_cast(raw_obj); uword start = raw_bytecode->ptr()->instructions_; uword size = raw_bytecode->ptr()->instructions_size_; - return (pc - start) < size; + return (pc - start) <= size; // pc may point past last instruction. } return false; } diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 3328633b1f7..c7edb9ee650 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -1349,6 +1349,7 @@ class RawBytecode : public RawObject { VISIT_FROM(RawObject*, object_pool_); RawObjectPool* object_pool_; RawFunction* function_; + RawArray* closures_; RawExceptionHandlers* exception_handlers_; RawPcDescriptors* pc_descriptors_; NOT_IN_PRODUCT(RawLocalVarDescriptors* var_descriptors_); diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc index b4b4563baef..cac4d229f7b 100644 --- a/runtime/vm/runtime_entry.cc +++ b/runtime/vm/runtime_entry.cc @@ -976,6 +976,7 @@ DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) { StackFrameIterator::kNoCrossThreadIteration); StackFrame* caller_frame = iterator.NextFrame(); ASSERT(caller_frame != NULL); + ASSERT(!caller_frame->is_interpreted()); const Code& orig_stub = Code::Handle( zone, isolate->debugger()->GetPatchedStubAddress(caller_frame->pc())); const Error& error = @@ -1964,13 +1965,13 @@ static void HandleStackOverflowTestCases(Thread* thread) { for (intptr_t i = 0; i < num_frames; i++) { ActivationFrame* frame = stack->FrameAt(i); #ifndef DART_PRECOMPILED_RUNTIME - if (!frame->is_interpreted()) { + if (!frame->IsInterpreted()) { // Ensure that we have unoptimized code. frame->function().EnsureHasCompiledUnoptimizedCode(); } // TODO(regis): Provide var descriptors in kernel bytecode. const int num_vars = - frame->is_interpreted() ? 0 : frame->NumLocalVariables(); + frame->IsInterpreted() ? 0 : frame->NumLocalVariables(); #else // Variable locations and number are unknown when precompiling. const int num_vars = 0; @@ -2130,8 +2131,14 @@ DEFINE_RUNTIME_ENTRY(OptimizeInvokedFunction, 1) { ASSERT(FLAG_enable_interpreter || optimizing_compilation); ASSERT((!optimizing_compilation) || function.HasCode()); - if ((!optimizing_compilation) || +#if defined(PRODUCT) + if (!optimizing_compilation || Compiler::CanOptimizeFunction(thread, function)) { +#else + if ((!optimizing_compilation && !Debugger::IsDebugging(thread, function)) || + (optimizing_compilation && + Compiler::CanOptimizeFunction(thread, function))) { +#endif // defined(PRODUCT) if (FLAG_background_compilation) { if (FLAG_enable_inlining_annotations) { FATAL("Cannot enable inlining annotations and background compilation"); diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc index 8f0e9cf39d5..019cb7c9f3c 100644 --- a/runtime/vm/source_report.cc +++ b/runtime/vm/source_report.cc @@ -191,6 +191,10 @@ bool SourceReport::ScriptIsLoadedByLibrary(const Script& script, void SourceReport::PrintCallSitesData(JSONObject* jsobj, const Function& function, const Code& code) { + if (code.IsNull()) { + // TODO(regis): implement for bytecode. + return; + } const TokenPosition begin_pos = function.token_pos(); const TokenPosition end_pos = function.end_token_pos(); @@ -230,6 +234,10 @@ void SourceReport::PrintCallSitesData(JSONObject* jsobj, void SourceReport::PrintCoverageData(JSONObject* jsobj, const Function& function, const Code& code) { + if (code.IsNull()) { + // TODO(regis): implement for bytecode. + return; + } const TokenPosition begin_pos = function.token_pos(); const TokenPosition end_pos = function.end_token_pos(); @@ -312,15 +320,8 @@ void SourceReport::PrintCoverageData(JSONObject* jsobj, void SourceReport::PrintPossibleBreakpointsData(JSONObject* jsobj, const Function& func, const Code& code) { - const uint8_t kSafepointKind = - (RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall | - RawPcDescriptors::kRuntimeCall); const TokenPosition begin_pos = func.token_pos(); const TokenPosition end_pos = func.end_token_pos(); - - const PcDescriptors& descriptors = - PcDescriptors::Handle(zone(), code.pc_descriptors()); - intptr_t func_length = (end_pos.Pos() - begin_pos.Pos()) + 1; GrowableArray possible(func_length); possible.SetLength(func_length); @@ -328,15 +329,37 @@ void SourceReport::PrintPossibleBreakpointsData(JSONObject* jsobj, possible[i] = false; } - PcDescriptors::Iterator iter(descriptors, kSafepointKind); - while (iter.MoveNext()) { - const TokenPosition token_pos = iter.TokenPos(); - if ((token_pos < begin_pos) || (token_pos > end_pos)) { - // Does not correspond to a valid source position. - continue; + if (code.IsNull()) { + const Bytecode& bytecode = Bytecode::Handle(func.bytecode()); + ASSERT(!bytecode.IsNull()); + kernel::BytecodeSourcePositionsIterator iter(zone(), bytecode); + while (iter.MoveNext()) { + const TokenPosition token_pos = iter.TokenPos(); + if ((token_pos < begin_pos) || (token_pos > end_pos)) { + // Does not correspond to a valid source position. + continue; + } + intptr_t token_offset = token_pos.Pos() - begin_pos.Pos(); + possible[token_offset] = true; + } + } else { + const uint8_t kSafepointKind = + (RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall | + RawPcDescriptors::kRuntimeCall); + + const PcDescriptors& descriptors = + PcDescriptors::Handle(zone(), code.pc_descriptors()); + + PcDescriptors::Iterator iter(descriptors, kSafepointKind); + while (iter.MoveNext()) { + const TokenPosition token_pos = iter.TokenPos(); + if ((token_pos < begin_pos) || (token_pos > end_pos)) { + // Does not correspond to a valid source position. + continue; + } + intptr_t token_offset = token_pos.Pos() - begin_pos.Pos(); + possible[token_offset] = true; } - intptr_t token_offset = token_pos.Pos() - begin_pos.Pos(); - possible[token_offset] = true; } JSONArray bpts(jsobj, "possibleBreakpoints"); @@ -465,14 +488,8 @@ void SourceReport::VisitFunction(JSONArray* jsarr, const Function& func) { range.AddProperty("scriptIndex", GetScriptIndex(script)); range.AddProperty("startPos", begin_pos); range.AddProperty("endPos", end_pos); - // TODO(regis): What is the meaning of 'compiled' in the presence of bytecode? - // If it means 'called', it should say 'true' if bytecode is present. - range.AddProperty("compiled", !code.IsNull()); + range.AddProperty("compiled", true); // bytecode or code. - // TODO(regis): Do we want a report covering interpreted functions too? - if (code.IsNull()) { - return; - } if (IsReportRequested(kCallSites)) { PrintCallSitesData(&range, func, code); } diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc index b1d4b790374..4d97d94f5b7 100644 --- a/runtime/vm/stack_frame.cc +++ b/runtime/vm/stack_frame.cc @@ -180,11 +180,9 @@ const char* StackFrame::ToCString() const { if (is_interpreted()) { const Bytecode& bytecode = Bytecode::Handle(zone, LookupDartBytecode()); ASSERT(!bytecode.IsNull()); - const Function& function = Function::Handle(zone, bytecode.function()); - ASSERT(!function.IsNull()); - return zone->PrintToString( - "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ") bytecode %s ]", - GetName(), sp(), fp(), pc(), function.ToFullyQualifiedCString()); + return zone->PrintToString("[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px + ") %s ]", + GetName(), sp(), fp(), pc(), bytecode.Name()); } const Code& code = Code::Handle(zone, LookupDartCode()); ASSERT(!code.IsNull()); @@ -501,7 +499,6 @@ bool StackFrame::FindExceptionHandler(Thread* thread, ASSERT(!bytecode.IsNull()); start = bytecode.PayloadStart(); handlers = bytecode.exception_handlers(); - descriptors = bytecode.pc_descriptors(); } else { code = LookupDartCode(); if (code.IsNull()) { @@ -520,33 +517,17 @@ bool StackFrame::FindExceptionHandler(Thread* thread, *has_catch_all = info->has_catch_all; return true; } - uword pc_offset = pc() - start; if (handlers.num_entries() == 0) { return false; } - PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind); intptr_t try_index = -1; if (is_interpreted()) { - while (iter.MoveNext()) { - // PC descriptors for try blocks in bytecode are generated in pairs, - // marking start and end of a try block. - // See BytecodeMetadataHelper::ReadExceptionsTable for details. - const intptr_t current_try_index = iter.TryIndex(); - const uword start_pc = iter.PcOffset(); - if (pc_offset < start_pc) { - break; - } - const bool has_next = iter.MoveNext(); - ASSERT(has_next); - const uword end_pc = iter.PcOffset(); - if (start_pc <= pc_offset && pc_offset < end_pc) { - ASSERT(try_index < current_try_index); - try_index = current_try_index; - } - } + try_index = bytecode.GetTryIndexAtPc(pc()); } else { + uword pc_offset = pc() - code.PayloadStart(); + PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind); while (iter.MoveNext()) { const intptr_t current_try_index = iter.TryIndex(); if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) { diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index ea250c4d7db..a1f20bfcc26 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni @@ -85,6 +85,7 @@ vm_sources = [ "debugger_arm64.cc", "debugger_dbc.cc", "debugger_ia32.cc", + "debugger_kbc.cc", "debugger_x64.cc", "deferred_objects.cc", "deferred_objects.h",