[vm/debugger] Support debugging of interpreted frames.
Change-Id: Iaf59e6ed887ed973fcfc7f1c414ad52ef98f01d7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100270 Commit-Queue: Régis Crelier <regis@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
0e07987ccd
commit
cddf2bbdfe
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -1951,7 +1951,7 @@ FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() {
|
||||
}
|
||||
|
||||
while (update_position &&
|
||||
pc_ >= source_pos_iter.BytecodeInstructionIndex()) {
|
||||
static_cast<uword>(pc_) >= source_pos_iter.PcOffset()) {
|
||||
position_ = source_pos_iter.TokenPos();
|
||||
update_position = source_pos_iter.MoveNext();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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_); }
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<E>(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);
|
||||
}
|
||||
|
||||
+775
-347
File diff suppressed because it is too large
Load Diff
+75
-23
@@ -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<intptr_t> 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<ActivationFrame*> 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);
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
+94
-34
@@ -256,7 +256,7 @@ static RawArray* AsSortedDuplicateFreeArray(GrowableArray<intptr_t>* 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<intptr_t>* token_positions,
|
||||
GrowableArray<intptr_t>* 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<intptr_t>* token_positions,
|
||||
GrowableArray<intptr_t>* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-3
@@ -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());
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -537,7 +537,7 @@ bool RawBytecode::ContainsPC(RawObject* raw_obj, uword pc) {
|
||||
RawBytecode* raw_bytecode = static_cast<RawBytecode*>(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;
|
||||
}
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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");
|
||||
|
||||
+39
-22
@@ -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<char> 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);
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user