[vm/aot] Avoid using most Code objects in stack traces with --dwarf-stack-traces
The following changes are done in preparation for the removal of Code objects in AOT with --dwarf-stack-traces: * Stack trace objects are extended to hold uword PCs (which may not fit into Smi range). * Scanning stack frames in GC (StackFrame::VisitObjectPointers) now avoids using Code objects. In order to find CompressedStackMaps it now calls ReversePc::FindCompressedStackMaps. * Singleton Code object (StubCode::UnknownDartCode()) is prepared as a replacement for Code objects in stack traces. It has PayloadStart() == 0 and Size() == kUwordMax so it includes arbitrary PCs. * In --dwarf-stack-traces mode, most Code objects obtained from stack frames are replaced with StubCode::UnknownDartCode(). This simulates future behavior of ReversePc::Lookup when Code objects will be removed. Issue: https://github.com/dart-lang/sdk/issues/44852 Change-Id: I7cec7b8b9396c9cfeca3c256a412ba4e82a7e0c4 TEST=ci Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182720 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
dfd52f60a1
commit
b6dc4dad4d
+31
-24
@@ -18,25 +18,35 @@ DECLARE_FLAG(bool, show_invisible_frames);
|
||||
|
||||
static const intptr_t kDefaultStackAllocation = 8;
|
||||
|
||||
static StackTracePtr CreateStackTraceObject(
|
||||
Zone* zone,
|
||||
const GrowableObjectArray& code_list,
|
||||
const GrowableArray<uword>& pc_offset_list) {
|
||||
const auto& code_array =
|
||||
Array::Handle(zone, Array::MakeFixedLength(code_list));
|
||||
const auto& pc_offset_array = TypedData::Handle(
|
||||
zone, TypedData::New(kUintPtrCid, pc_offset_list.length()));
|
||||
{
|
||||
NoSafepointScope no_safepoint;
|
||||
memmove(pc_offset_array.DataAddr(0), pc_offset_list.data(),
|
||||
pc_offset_list.length() * kWordSize);
|
||||
}
|
||||
return StackTrace::New(code_array, pc_offset_array);
|
||||
}
|
||||
|
||||
static StackTracePtr CurrentSyncStackTraceLazy(Thread* thread,
|
||||
intptr_t skip_frames = 1) {
|
||||
Zone* zone = thread->zone();
|
||||
|
||||
const auto& code_array = GrowableObjectArray::ZoneHandle(
|
||||
zone, GrowableObjectArray::New(kDefaultStackAllocation));
|
||||
const auto& pc_offset_array = GrowableObjectArray::ZoneHandle(
|
||||
zone, GrowableObjectArray::New(kDefaultStackAllocation));
|
||||
GrowableArray<uword> pc_offset_array;
|
||||
|
||||
// Collect the frames.
|
||||
StackTraceUtils::CollectFramesLazy(thread, code_array, pc_offset_array,
|
||||
StackTraceUtils::CollectFramesLazy(thread, code_array, &pc_offset_array,
|
||||
skip_frames);
|
||||
|
||||
const auto& code_array_fixed =
|
||||
Array::Handle(zone, Array::MakeFixedLength(code_array));
|
||||
const auto& pc_offset_array_fixed =
|
||||
Array::Handle(zone, Array::MakeFixedLength(pc_offset_array));
|
||||
|
||||
return StackTrace::New(code_array_fixed, pc_offset_array_fixed);
|
||||
return CreateStackTraceObject(zone, code_array, pc_offset_array);
|
||||
}
|
||||
|
||||
static StackTracePtr CurrentSyncStackTrace(Thread* thread,
|
||||
@@ -51,8 +61,8 @@ static StackTracePtr CurrentSyncStackTrace(Thread* thread,
|
||||
// Allocate once.
|
||||
const Array& code_array =
|
||||
Array::ZoneHandle(zone, Array::New(stack_trace_length));
|
||||
const Array& pc_offset_array =
|
||||
Array::ZoneHandle(zone, Array::New(stack_trace_length));
|
||||
const TypedData& pc_offset_array = TypedData::ZoneHandle(
|
||||
zone, TypedData::New(kUintPtrCid, stack_trace_length));
|
||||
|
||||
// Collect the frames.
|
||||
const intptr_t collected_frames_count = StackTraceUtils::CollectFrames(
|
||||
@@ -89,7 +99,7 @@ DEFINE_NATIVE_ENTRY(StackTrace_current, 0, 0) {
|
||||
}
|
||||
|
||||
static void AppendFrames(const GrowableObjectArray& code_list,
|
||||
const GrowableObjectArray& pc_offset_list,
|
||||
GrowableArray<uword>* pc_offset_list,
|
||||
int skip_frames) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
@@ -98,7 +108,6 @@ static void AppendFrames(const GrowableObjectArray& code_list,
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
||||
Code& code = Code::Handle(zone);
|
||||
Smi& offset = Smi::Handle(zone);
|
||||
for (; frame != NULL; frame = frames.NextFrame()) {
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
@@ -109,9 +118,9 @@ static void AppendFrames(const GrowableObjectArray& code_list,
|
||||
}
|
||||
|
||||
code = frame->LookupDartCode();
|
||||
offset = Smi::New(frame->pc() - code.PayloadStart());
|
||||
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
|
||||
code_list.Add(code);
|
||||
pc_offset_list.Add(offset);
|
||||
pc_offset_list->Add(pc_offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,16 +128,14 @@ static void AppendFrames(const GrowableObjectArray& code_list,
|
||||
//
|
||||
// Skips the first skip_frames Dart frames.
|
||||
const StackTrace& GetCurrentStackTrace(int skip_frames) {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const GrowableObjectArray& code_list =
|
||||
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
||||
const GrowableObjectArray& pc_offset_list =
|
||||
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
||||
AppendFrames(code_list, pc_offset_list, skip_frames);
|
||||
const Array& code_array = Array::Handle(Array::MakeFixedLength(code_list));
|
||||
const Array& pc_offset_array =
|
||||
Array::Handle(Array::MakeFixedLength(pc_offset_list));
|
||||
const StackTrace& stacktrace =
|
||||
StackTrace::Handle(StackTrace::New(code_array, pc_offset_array));
|
||||
GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
||||
GrowableArray<uword> pc_offset_list;
|
||||
AppendFrames(code_list, &pc_offset_list, skip_frames);
|
||||
|
||||
const StackTrace& stacktrace = StackTrace::Handle(
|
||||
zone, CreateStackTraceObject(zone, code_list, pc_offset_list));
|
||||
return stacktrace;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,9 +96,8 @@ void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
|
||||
const Code& caller_code,
|
||||
const Object& data,
|
||||
const Code& target) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCallPattern call(return_address, caller_code);
|
||||
BareSwitchableCallPattern call(return_address);
|
||||
call.SetData(data);
|
||||
call.SetTarget(target);
|
||||
} else {
|
||||
@@ -110,9 +109,8 @@ void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
|
||||
|
||||
uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
|
||||
const Code& caller_code) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCallPattern call(return_address, caller_code);
|
||||
BareSwitchableCallPattern call(return_address);
|
||||
return call.target_entry();
|
||||
} else {
|
||||
SwitchableCallPattern call(return_address, caller_code);
|
||||
@@ -122,9 +120,8 @@ uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
|
||||
|
||||
ObjectPtr CodePatcher::GetSwitchableCallDataAt(uword return_address,
|
||||
const Code& caller_code) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCallPattern call(return_address, caller_code);
|
||||
BareSwitchableCallPattern call(return_address);
|
||||
return call.data();
|
||||
} else {
|
||||
SwitchableCallPattern call(return_address, caller_code);
|
||||
|
||||
@@ -132,9 +132,8 @@ void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
|
||||
const Code& caller_code,
|
||||
const Object& data,
|
||||
const Code& target) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCallPattern call(return_address, caller_code);
|
||||
BareSwitchableCallPattern call(return_address);
|
||||
call.SetData(data);
|
||||
call.SetTarget(target);
|
||||
} else {
|
||||
@@ -146,9 +145,8 @@ void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
|
||||
|
||||
uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
|
||||
const Code& caller_code) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCallPattern call(return_address, caller_code);
|
||||
BareSwitchableCallPattern call(return_address);
|
||||
return call.target_entry();
|
||||
} else {
|
||||
SwitchableCallPattern call(return_address, caller_code);
|
||||
@@ -158,9 +156,8 @@ uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
|
||||
|
||||
ObjectPtr CodePatcher::GetSwitchableCallDataAt(uword return_address,
|
||||
const Code& caller_code) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCallPattern call(return_address, caller_code);
|
||||
BareSwitchableCallPattern call(return_address);
|
||||
return call.data();
|
||||
} else {
|
||||
SwitchableCallPattern call(return_address, caller_code);
|
||||
|
||||
@@ -220,10 +220,8 @@ class PoolPointerCall : public ValueObject {
|
||||
// call target.entry call stub.entry call stub.entry
|
||||
class SwitchableCallBase : public ValueObject {
|
||||
public:
|
||||
explicit SwitchableCallBase(const Code& code)
|
||||
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
|
||||
target_index_(-1),
|
||||
data_index_(-1) {}
|
||||
explicit SwitchableCallBase(const ObjectPool& object_pool)
|
||||
: object_pool_(object_pool), target_index_(-1), data_index_(-1) {}
|
||||
|
||||
intptr_t data_index() const { return data_index_; }
|
||||
intptr_t target_index() const { return target_index_; }
|
||||
@@ -237,7 +235,7 @@ class SwitchableCallBase : public ValueObject {
|
||||
}
|
||||
|
||||
protected:
|
||||
ObjectPool& object_pool_;
|
||||
const ObjectPool& object_pool_;
|
||||
intptr_t target_index_;
|
||||
intptr_t data_index_;
|
||||
|
||||
@@ -251,8 +249,9 @@ class SwitchableCallBase : public ValueObject {
|
||||
// monomorphic function or a stub code.
|
||||
class SwitchableCall : public SwitchableCallBase {
|
||||
public:
|
||||
SwitchableCall(uword return_address, const Code& code)
|
||||
: SwitchableCallBase(code) {
|
||||
SwitchableCall(uword return_address, const Code& caller_code)
|
||||
: SwitchableCallBase(ObjectPool::Handle(caller_code.GetObjectPool())) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
uword pc = return_address;
|
||||
|
||||
// callq RCX
|
||||
@@ -333,11 +332,9 @@ class SwitchableCall : public SwitchableCallBase {
|
||||
// of the monomorphic function or a stub entry point.
|
||||
class BareSwitchableCall : public SwitchableCallBase {
|
||||
public:
|
||||
BareSwitchableCall(uword return_address, const Code& code)
|
||||
: SwitchableCallBase(code) {
|
||||
object_pool_ = ObjectPool::RawCast(
|
||||
IsolateGroup::Current()->object_store()->global_object_pool());
|
||||
|
||||
explicit BareSwitchableCall(uword return_address)
|
||||
: SwitchableCallBase(ObjectPool::Handle(
|
||||
IsolateGroup::Current()->object_store()->global_object_pool())) {
|
||||
uword pc = return_address;
|
||||
|
||||
// callq RCX
|
||||
@@ -489,9 +486,8 @@ void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
|
||||
const Code& caller_code,
|
||||
const Object& data,
|
||||
const Code& target) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCall call(return_address, caller_code);
|
||||
BareSwitchableCall call(return_address);
|
||||
call.SetData(data);
|
||||
call.SetTarget(target);
|
||||
} else {
|
||||
@@ -503,9 +499,8 @@ void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
|
||||
|
||||
uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
|
||||
const Code& caller_code) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCall call(return_address, caller_code);
|
||||
BareSwitchableCall call(return_address);
|
||||
return call.target_entry();
|
||||
} else {
|
||||
SwitchableCall call(return_address, caller_code);
|
||||
@@ -515,9 +510,8 @@ uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
|
||||
|
||||
ObjectPtr CodePatcher::GetSwitchableCallDataAt(uword return_address,
|
||||
const Code& caller_code) {
|
||||
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
BareSwitchableCall call(return_address, caller_code);
|
||||
BareSwitchableCall call(return_address);
|
||||
return call.data();
|
||||
} else {
|
||||
SwitchableCall call(return_address, caller_code);
|
||||
|
||||
@@ -457,6 +457,9 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
||||
void Disassembler::DisassembleCode(const Function& function,
|
||||
const Code& code,
|
||||
bool optimized) {
|
||||
if (code.IsUnknownDartCode()) {
|
||||
return;
|
||||
}
|
||||
TextBuffer buffer(128);
|
||||
const char* function_fullname = function.ToFullyQualifiedCString();
|
||||
buffer.Printf("%s", Function::KindToCString(function.kind()));
|
||||
|
||||
@@ -796,6 +796,19 @@ void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
|
||||
Assembler* assembler) {
|
||||
__ Breakpoint(); // Marker stub.
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
|
||||
__ Breakpoint(); // Marker stub.
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateUnknownDartCodeStub(Assembler* assembler) {
|
||||
__ Breakpoint(); // Marker stub.
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -3291,15 +3291,6 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) {
|
||||
CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
|
||||
Assembler* assembler) {
|
||||
__ bkpt(0);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
|
||||
__ bkpt(0);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ CallRuntime(kNotLoadedRuntimeEntry, 0);
|
||||
|
||||
@@ -3435,15 +3435,6 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) {
|
||||
__ br(R1);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
|
||||
Assembler* assembler) {
|
||||
__ brk(0);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
|
||||
__ brk(0);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ CallRuntime(kNotLoadedRuntimeEntry, 0);
|
||||
|
||||
@@ -2788,15 +2788,6 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) {
|
||||
__ int3(); // AOT only.
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
|
||||
Assembler* assembler) {
|
||||
__ int3(); // Marker stub.
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
|
||||
__ int3(); // Marker stub.
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ CallRuntime(kNotLoadedRuntimeEntry, 0);
|
||||
|
||||
@@ -3365,15 +3365,6 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) {
|
||||
__ jmp(RCX);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
|
||||
Assembler* assembler) {
|
||||
__ int3();
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
|
||||
__ int3();
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ CallRuntime(kNotLoadedRuntimeEntry, 0);
|
||||
|
||||
@@ -1846,7 +1846,6 @@ DebuggerStackTrace* Debugger::CollectAsyncLazyStackTrace() {
|
||||
Code& code = Code::Handle(zone);
|
||||
Code& inlined_code = Code::Handle(zone);
|
||||
Array& deopt_frame = Array::Handle(zone);
|
||||
Smi& offset = Smi::Handle(zone);
|
||||
Function& function = Function::Handle(zone);
|
||||
|
||||
constexpr intptr_t kDefaultStackAllocation = 8;
|
||||
@@ -1854,8 +1853,7 @@ DebuggerStackTrace* Debugger::CollectAsyncLazyStackTrace() {
|
||||
|
||||
const auto& code_array = GrowableObjectArray::ZoneHandle(
|
||||
zone, GrowableObjectArray::New(kDefaultStackAllocation));
|
||||
const auto& pc_offset_array = GrowableObjectArray::ZoneHandle(
|
||||
zone, GrowableObjectArray::New(kDefaultStackAllocation));
|
||||
GrowableArray<uword> pc_offset_array(kDefaultStackAllocation);
|
||||
bool has_async = false;
|
||||
|
||||
std::function<void(StackFrame*)> on_sync_frame = [&](StackFrame* frame) {
|
||||
@@ -1864,7 +1862,7 @@ DebuggerStackTrace* Debugger::CollectAsyncLazyStackTrace() {
|
||||
&inlined_code, &deopt_frame);
|
||||
};
|
||||
|
||||
StackTraceUtils::CollectFramesLazy(thread, code_array, pc_offset_array,
|
||||
StackTraceUtils::CollectFramesLazy(thread, code_array, &pc_offset_array,
|
||||
/*skip_frames=*/0, &on_sync_frame,
|
||||
&has_async);
|
||||
|
||||
@@ -1900,8 +1898,8 @@ DebuggerStackTrace* Debugger::CollectAsyncLazyStackTrace() {
|
||||
continue;
|
||||
}
|
||||
|
||||
offset ^= pc_offset_array.At(i);
|
||||
const uword absolute_pc = code.PayloadStart() + offset.Value();
|
||||
const uword pc_offset = pc_offset_array[i];
|
||||
const uword absolute_pc = code.PayloadStart() + pc_offset;
|
||||
stack_trace->AddAsyncCausalFrame(absolute_pc, code);
|
||||
}
|
||||
|
||||
@@ -2136,8 +2134,7 @@ DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) {
|
||||
function = code.function();
|
||||
if (function.is_visible()) {
|
||||
ASSERT(function.ptr() == code.function());
|
||||
uword pc =
|
||||
code.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i));
|
||||
uword pc = code.PayloadStart() + ex_trace.PcOffsetAtFrame(i);
|
||||
if (code.is_optimized() && ex_trace.expand_inlined()) {
|
||||
// Traverse inlined frames.
|
||||
for (InlinedFunctionsIterator it(code, pc); !it.Done();
|
||||
|
||||
+2
-2
@@ -146,7 +146,7 @@ void Dwarf::AddCode(const Code& orig_code, const char* name) {
|
||||
// Dwarf object (which is currently true). Otherwise, need to copy.
|
||||
code_to_name_.Insert({&code, name});
|
||||
|
||||
if (code.IsFunctionCode()) {
|
||||
if (code.IsFunctionCode() && !code.IsUnknownDartCode()) {
|
||||
const Function& function = Function::Handle(zone_, code.function());
|
||||
AddFunction(function);
|
||||
}
|
||||
@@ -364,7 +364,7 @@ void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
|
||||
for (intptr_t i = 0; i < codes_.length(); i++) {
|
||||
const Code& code = *(codes_[i]);
|
||||
RELEASE_ASSERT(!code.IsNull());
|
||||
if (!code.IsFunctionCode()) {
|
||||
if (!code.IsFunctionCode() || code.IsUnknownDartCode()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class StackTraceBuilder : public ValueObject {
|
||||
StackTraceBuilder() {}
|
||||
virtual ~StackTraceBuilder() {}
|
||||
|
||||
virtual void AddFrame(const Object& code, const Smi& offset) = 0;
|
||||
virtual void AddFrame(const Object& code, uword pc_offset) = 0;
|
||||
};
|
||||
|
||||
class PreallocatedStackTraceBuilder : public StackTraceBuilder {
|
||||
@@ -50,7 +50,7 @@ class PreallocatedStackTraceBuilder : public StackTraceBuilder {
|
||||
}
|
||||
~PreallocatedStackTraceBuilder() {}
|
||||
|
||||
virtual void AddFrame(const Object& code, const Smi& offset);
|
||||
void AddFrame(const Object& code, uword pc_offset) override;
|
||||
|
||||
private:
|
||||
static const int kNumTopframes = StackTrace::kPreallocatedStackdepth / 2;
|
||||
@@ -63,11 +63,10 @@ class PreallocatedStackTraceBuilder : public StackTraceBuilder {
|
||||
};
|
||||
|
||||
void PreallocatedStackTraceBuilder::AddFrame(const Object& code,
|
||||
const Smi& offset) {
|
||||
uword pc_offset) {
|
||||
if (cur_index_ >= StackTrace::kPreallocatedStackdepth) {
|
||||
// The number of frames is overflowing the preallocated stack trace object.
|
||||
Object& frame_code = Object::Handle();
|
||||
Smi& frame_offset = Smi::Handle();
|
||||
intptr_t start = StackTrace::kPreallocatedStackdepth - (kNumTopframes - 1);
|
||||
intptr_t null_slot = start - 2;
|
||||
// We are going to drop one frame.
|
||||
@@ -80,20 +79,19 @@ void PreallocatedStackTraceBuilder::AddFrame(const Object& code,
|
||||
dropped_frames_++;
|
||||
}
|
||||
// Encode the number of dropped frames into the pc offset.
|
||||
frame_offset = Smi::New(dropped_frames_);
|
||||
stacktrace_.SetPcOffsetAtFrame(null_slot, frame_offset);
|
||||
stacktrace_.SetPcOffsetAtFrame(null_slot, dropped_frames_);
|
||||
// Move frames one slot down so that we can accommodate the new frame.
|
||||
for (intptr_t i = start; i < StackTrace::kPreallocatedStackdepth; i++) {
|
||||
intptr_t prev = (i - 1);
|
||||
frame_code = stacktrace_.CodeAtFrame(i);
|
||||
frame_offset = stacktrace_.PcOffsetAtFrame(i);
|
||||
const uword frame_offset = stacktrace_.PcOffsetAtFrame(i);
|
||||
stacktrace_.SetCodeAtFrame(prev, frame_code);
|
||||
stacktrace_.SetPcOffsetAtFrame(prev, frame_offset);
|
||||
}
|
||||
cur_index_ = (StackTrace::kPreallocatedStackdepth - 1);
|
||||
}
|
||||
stacktrace_.SetCodeAtFrame(cur_index_, code);
|
||||
stacktrace_.SetPcOffsetAtFrame(cur_index_, offset);
|
||||
stacktrace_.SetPcOffsetAtFrame(cur_index_, pc_offset);
|
||||
cur_index_ += 1;
|
||||
}
|
||||
|
||||
@@ -104,15 +102,14 @@ static void BuildStackTrace(StackTraceBuilder* builder) {
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
||||
Code& code = Code::Handle();
|
||||
Smi& offset = Smi::Handle();
|
||||
for (; frame != NULL; frame = frames.NextFrame()) {
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
}
|
||||
code = frame->LookupDartCode();
|
||||
ASSERT(code.ContainsInstructionAt(frame->pc()));
|
||||
offset = Smi::New(frame->pc() - code.PayloadStart());
|
||||
builder->AddFrame(code, offset);
|
||||
const uword pc_offset = frame->pc() - code.PayloadStart();
|
||||
builder->AddFrame(code, pc_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,10 +263,9 @@ void ICCallPattern::SetTargetCode(const Code& target_code) const {
|
||||
object_pool_.SetObjectAt(target_pool_index_, target_code);
|
||||
}
|
||||
|
||||
SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
|
||||
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
|
||||
data_pool_index_(-1),
|
||||
target_pool_index_(-1) {}
|
||||
SwitchableCallPatternBase::SwitchableCallPatternBase(
|
||||
const ObjectPool& object_pool)
|
||||
: object_pool_(object_pool), data_pool_index_(-1), target_pool_index_(-1) {}
|
||||
|
||||
ObjectPtr SwitchableCallPatternBase::data() const {
|
||||
return object_pool_.ObjectAt(data_pool_index_);
|
||||
@@ -278,7 +277,7 @@ void SwitchableCallPatternBase::SetData(const Object& data) const {
|
||||
}
|
||||
|
||||
SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code)
|
||||
: SwitchableCallPatternBase(code) {
|
||||
: SwitchableCallPatternBase(ObjectPool::Handle(code.GetObjectPool())) {
|
||||
ASSERT(code.ContainsInstructionAt(pc));
|
||||
// Last instruction: blx lr.
|
||||
ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
|
||||
@@ -302,9 +301,9 @@ void SwitchableCallPattern::SetTarget(const Code& target) const {
|
||||
object_pool_.SetObjectAt(target_pool_index_, target);
|
||||
}
|
||||
|
||||
BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc, const Code& code)
|
||||
: SwitchableCallPatternBase(code) {
|
||||
ASSERT(code.ContainsInstructionAt(pc));
|
||||
BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc)
|
||||
: SwitchableCallPatternBase(ObjectPool::Handle(
|
||||
IsolateGroup::Current()->object_store()->global_object_pool())) {
|
||||
// Last instruction: blx lr.
|
||||
ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ class NativeCallPattern : public ValueObject {
|
||||
// call target.entry call stub.entry call stub.entry
|
||||
class SwitchableCallPatternBase : public ValueObject {
|
||||
public:
|
||||
explicit SwitchableCallPatternBase(const Code& code);
|
||||
explicit SwitchableCallPatternBase(const ObjectPool& object_pool);
|
||||
|
||||
ObjectPtr data() const;
|
||||
void SetData(const Object& data) const;
|
||||
@@ -166,7 +166,7 @@ class SwitchableCallPattern : public SwitchableCallPatternBase {
|
||||
// of the monomorphic function or a stub entry point.
|
||||
class BareSwitchableCallPattern : public SwitchableCallPatternBase {
|
||||
public:
|
||||
BareSwitchableCallPattern(uword pc, const Code& code);
|
||||
explicit BareSwitchableCallPattern(uword pc);
|
||||
|
||||
uword target_entry() const;
|
||||
void SetTarget(const Code& target) const;
|
||||
|
||||
@@ -397,10 +397,9 @@ void ICCallPattern::SetTargetCode(const Code& target) const {
|
||||
// No need to flush the instruction cache, since the code is not modified.
|
||||
}
|
||||
|
||||
SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
|
||||
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
|
||||
data_pool_index_(-1),
|
||||
target_pool_index_(-1) {}
|
||||
SwitchableCallPatternBase::SwitchableCallPatternBase(
|
||||
const ObjectPool& object_pool)
|
||||
: object_pool_(object_pool), data_pool_index_(-1), target_pool_index_(-1) {}
|
||||
|
||||
ObjectPtr SwitchableCallPatternBase::data() const {
|
||||
return object_pool_.ObjectAt(data_pool_index_);
|
||||
@@ -412,7 +411,7 @@ void SwitchableCallPatternBase::SetData(const Object& data) const {
|
||||
}
|
||||
|
||||
SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code)
|
||||
: SwitchableCallPatternBase(code) {
|
||||
: SwitchableCallPatternBase(ObjectPool::Handle(code.GetObjectPool())) {
|
||||
ASSERT(code.ContainsInstructionAt(pc));
|
||||
// Last instruction: blr lr.
|
||||
ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xd63f03c0);
|
||||
@@ -438,9 +437,9 @@ void SwitchableCallPattern::SetTarget(const Code& target) const {
|
||||
object_pool_.SetObjectAt(target_pool_index_, target);
|
||||
}
|
||||
|
||||
BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc, const Code& code)
|
||||
: SwitchableCallPatternBase(code) {
|
||||
ASSERT(code.ContainsInstructionAt(pc));
|
||||
BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc)
|
||||
: SwitchableCallPatternBase(ObjectPool::Handle(
|
||||
IsolateGroup::Current()->object_store()->global_object_pool())) {
|
||||
// Last instruction: blr lr.
|
||||
ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xd63f03c0);
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class NativeCallPattern : public ValueObject {
|
||||
// call target.entry call stub.entry call stub.entry
|
||||
class SwitchableCallPatternBase : public ValueObject {
|
||||
public:
|
||||
explicit SwitchableCallPatternBase(const Code& code);
|
||||
explicit SwitchableCallPatternBase(const ObjectPool& object_pool);
|
||||
|
||||
ObjectPtr data() const;
|
||||
void SetData(const Object& data) const;
|
||||
@@ -176,7 +176,7 @@ class SwitchableCallPattern : public SwitchableCallPatternBase {
|
||||
// of the monomorphic function or a stub entry point.
|
||||
class BareSwitchableCallPattern : public SwitchableCallPatternBase {
|
||||
public:
|
||||
BareSwitchableCallPattern(uword pc, const Code& code);
|
||||
explicit BareSwitchableCallPattern(uword pc);
|
||||
|
||||
uword target_entry() const;
|
||||
void SetTarget(const Code& target) const;
|
||||
|
||||
+66
-14
@@ -75,7 +75,7 @@
|
||||
|
||||
namespace dart {
|
||||
|
||||
DEFINE_FLAG(int,
|
||||
DEFINE_FLAG(uint64_t,
|
||||
huge_method_cutoff_in_code_size,
|
||||
200000,
|
||||
"Huge method cutoff in unoptimized code size (in bytes).");
|
||||
@@ -1059,6 +1059,11 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
cls.set_is_declaration_loaded();
|
||||
cls.set_is_type_finalized();
|
||||
|
||||
cls = Class::New<FunctionType, RTN::FunctionType>(isolate_group);
|
||||
cls.set_is_allocate_finalized();
|
||||
cls.set_is_declaration_loaded();
|
||||
cls.set_is_type_finalized();
|
||||
|
||||
cls = dynamic_class_;
|
||||
*dynamic_type_ =
|
||||
Type::New(cls, Object::null_type_arguments(), Nullability::kNullable);
|
||||
@@ -1365,6 +1370,12 @@ void Object::FinalizeVMIsolate(IsolateGroup* isolate_group) {
|
||||
cls = isolate_group->class_table()->At(kForwardingCorpse);
|
||||
cls.set_name(Symbols::ForwardingCorpse());
|
||||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
const auto& function =
|
||||
Function::Handle(StubCode::UnknownDartCode().function());
|
||||
function.set_name(Symbols::OptimizedOut());
|
||||
#endif // defined(DART_PRECOMPILER)
|
||||
|
||||
{
|
||||
ASSERT(isolate_group == Dart::vm_isolate_group());
|
||||
Thread* thread = Thread::Current();
|
||||
@@ -16773,7 +16784,8 @@ void Code::NotifyCodeObservers(const char* name,
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
bool Code::SlowFindRawCodeVisitor::FindObject(ObjectPtr raw_obj) const {
|
||||
return UntaggedCode::ContainsPC(raw_obj, pc_);
|
||||
return UntaggedCode::ContainsPC(raw_obj, pc_) &&
|
||||
!Code::IsUnknownDartCode(Code::RawCast(raw_obj));
|
||||
}
|
||||
|
||||
CodePtr Code::LookupCodeInIsolateGroup(IsolateGroup* isolate_group, uword pc) {
|
||||
@@ -16928,6 +16940,10 @@ bool Code::IsFunctionCode() const {
|
||||
return OwnerClassId() == kFunctionCid;
|
||||
}
|
||||
|
||||
bool Code::IsUnknownDartCode(CodePtr code) {
|
||||
return code == StubCode::UnknownDartCode().ptr();
|
||||
}
|
||||
|
||||
void Code::DisableDartCode() const {
|
||||
SafepointOperationScope safepoint(Thread::Current());
|
||||
ASSERT(IsFunctionCode());
|
||||
@@ -17044,6 +17060,41 @@ void Code::DumpSourcePositions(bool relative_addresses) const {
|
||||
reader.DumpSourcePositions(relative_addresses ? 0 : PayloadStart());
|
||||
}
|
||||
|
||||
bool Code::CanBeOmittedFromAOTSnapshot() const {
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
// Code objects are stored in stack frames if not use_bare_instructions.
|
||||
// Code objects are used by stack traces if not dwarf_stack_traces.
|
||||
if (!FLAG_precompiled_mode || !FLAG_use_bare_instructions ||
|
||||
!FLAG_dwarf_stack_traces_mode) {
|
||||
return false;
|
||||
}
|
||||
// Only omit Code objects corresponding to Dart functions.
|
||||
if (!IsFunctionCode()) {
|
||||
return false;
|
||||
}
|
||||
// Retain Code object if it has exception handlers or PC descriptors.
|
||||
if ((exception_handlers() != Object::empty_exception_handlers().ptr()) ||
|
||||
(pc_descriptors() != Object::empty_descriptors().ptr())) {
|
||||
return false;
|
||||
}
|
||||
if (!owner()->IsHeapObject()) {
|
||||
// Can drop Code if precompiler dropped the Function and only left Smi
|
||||
// classId.
|
||||
return true;
|
||||
}
|
||||
// Retain Code objects corresponding to:
|
||||
// * invisible functions (to filter them from stack traces);
|
||||
// * async/async* closures (to construct async stacks).
|
||||
// * native functions (to find native implementation).
|
||||
const auto& func = Function::Handle(function());
|
||||
if (!func.is_visible() || func.is_native() || func.IsAsyncClosure() ||
|
||||
func.IsAsyncGenClosure()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
intptr_t Context::GetLevel() const {
|
||||
intptr_t level = 0;
|
||||
Context& parent_ctx = Context::Handle(parent());
|
||||
@@ -24666,15 +24717,17 @@ void StackTrace::SetCodeAtFrame(intptr_t frame_index,
|
||||
code_array.SetAt(frame_index, code);
|
||||
}
|
||||
|
||||
SmiPtr StackTrace::PcOffsetAtFrame(intptr_t frame_index) const {
|
||||
const Array& pc_offset_array = Array::Handle(untag()->pc_offset_array());
|
||||
return static_cast<SmiPtr>(pc_offset_array.At(frame_index));
|
||||
uword StackTrace::PcOffsetAtFrame(intptr_t frame_index) const {
|
||||
const TypedData& pc_offset_array =
|
||||
TypedData::Handle(untag()->pc_offset_array());
|
||||
return pc_offset_array.GetUintPtr(frame_index * kWordSize);
|
||||
}
|
||||
|
||||
void StackTrace::SetPcOffsetAtFrame(intptr_t frame_index,
|
||||
const Smi& pc_offset) const {
|
||||
const Array& pc_offset_array = Array::Handle(untag()->pc_offset_array());
|
||||
pc_offset_array.SetAt(frame_index, pc_offset);
|
||||
uword pc_offset) const {
|
||||
const TypedData& pc_offset_array =
|
||||
TypedData::Handle(untag()->pc_offset_array());
|
||||
pc_offset_array.SetUintPtr(frame_index * kWordSize, pc_offset);
|
||||
}
|
||||
|
||||
void StackTrace::set_async_link(const StackTrace& async_link) const {
|
||||
@@ -24685,7 +24738,7 @@ void StackTrace::set_code_array(const Array& code_array) const {
|
||||
untag()->set_code_array(code_array.ptr());
|
||||
}
|
||||
|
||||
void StackTrace::set_pc_offset_array(const Array& pc_offset_array) const {
|
||||
void StackTrace::set_pc_offset_array(const TypedData& pc_offset_array) const {
|
||||
untag()->set_pc_offset_array(pc_offset_array.ptr());
|
||||
}
|
||||
|
||||
@@ -24698,7 +24751,7 @@ bool StackTrace::expand_inlined() const {
|
||||
}
|
||||
|
||||
StackTracePtr StackTrace::New(const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
const TypedData& pc_offset_array,
|
||||
Heap::Space space) {
|
||||
StackTrace& result = StackTrace::Handle();
|
||||
{
|
||||
@@ -24715,7 +24768,7 @@ StackTracePtr StackTrace::New(const Array& code_array,
|
||||
}
|
||||
|
||||
StackTracePtr StackTrace::New(const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
const TypedData& pc_offset_array,
|
||||
const StackTrace& async_link,
|
||||
bool skip_sync_start_in_parent_stack,
|
||||
Heap::Space space) {
|
||||
@@ -24902,9 +24955,8 @@ const char* StackTrace::ToCString() const {
|
||||
if ((i < (stack_trace.Length() - 1)) &&
|
||||
(stack_trace.CodeAtFrame(i + 1) != Code::null())) {
|
||||
buffer.AddString("...\n...\n");
|
||||
ASSERT(stack_trace.PcOffsetAtFrame(i) != Smi::null());
|
||||
// To account for gap frames.
|
||||
frame_index += Smi::Value(stack_trace.PcOffsetAtFrame(i));
|
||||
frame_index += stack_trace.PcOffsetAtFrame(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -24917,7 +24969,7 @@ const char* StackTrace::ToCString() const {
|
||||
continue;
|
||||
}
|
||||
|
||||
intptr_t pc_offset = Smi::Value(stack_trace.PcOffsetAtFrame(i));
|
||||
const uword pc_offset = stack_trace.PcOffsetAtFrame(i);
|
||||
ASSERT(code_object.IsCode());
|
||||
code ^= code_object.ptr();
|
||||
ASSERT(code.IsFunctionCode());
|
||||
|
||||
+20
-8
@@ -5970,6 +5970,7 @@ class Code : public Object {
|
||||
uword PayloadStart() const { return PayloadStartOf(ptr()); }
|
||||
static uword PayloadStartOf(const CodePtr code) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (IsUnknownDartCode(code)) return 0;
|
||||
const uword entry_offset = HasMonomorphicEntry(code)
|
||||
? Instructions::kPolymorphicEntryOffsetAOT
|
||||
: 0;
|
||||
@@ -6015,9 +6016,10 @@ class Code : public Object {
|
||||
}
|
||||
|
||||
// Returns the size of [instructions()].
|
||||
intptr_t Size() const { return PayloadSizeOf(ptr()); }
|
||||
static intptr_t PayloadSizeOf(const CodePtr code) {
|
||||
uword Size() const { return PayloadSizeOf(ptr()); }
|
||||
static uword PayloadSizeOf(const CodePtr code) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (IsUnknownDartCode(code)) return kUwordMax;
|
||||
return code->untag()->instructions_length_;
|
||||
#else
|
||||
return Instructions::Size(InstructionsOf(code));
|
||||
@@ -6347,6 +6349,11 @@ class Code : public Object {
|
||||
bool IsTypeTestStubCode() const;
|
||||
bool IsFunctionCode() const;
|
||||
|
||||
// Returns true if this Code object represents
|
||||
// Dart function code without any additional information.
|
||||
bool IsUnknownDartCode() const { return IsUnknownDartCode(ptr()); }
|
||||
static bool IsUnknownDartCode(CodePtr code);
|
||||
|
||||
void DisableDartCode() const;
|
||||
|
||||
void DisableStubCode() const;
|
||||
@@ -6371,6 +6378,11 @@ class Code : public Object {
|
||||
untag()->set_object_pool(object_pool);
|
||||
}
|
||||
|
||||
// Returns true if given Code object can be omitted from
|
||||
// the AOT snapshot (when corresponding instructions are
|
||||
// included).
|
||||
bool CanBeOmittedFromAOTSnapshot() const;
|
||||
|
||||
private:
|
||||
void set_state_bits(intptr_t bits) const;
|
||||
|
||||
@@ -10803,9 +10815,9 @@ class StackTrace : public Instance {
|
||||
ObjectPtr CodeAtFrame(intptr_t frame_index) const;
|
||||
void SetCodeAtFrame(intptr_t frame_index, const Object& code) const;
|
||||
|
||||
ArrayPtr pc_offset_array() const { return untag()->pc_offset_array(); }
|
||||
SmiPtr PcOffsetAtFrame(intptr_t frame_index) const;
|
||||
void SetPcOffsetAtFrame(intptr_t frame_index, const Smi& pc_offset) const;
|
||||
TypedDataPtr pc_offset_array() const { return untag()->pc_offset_array(); }
|
||||
uword PcOffsetAtFrame(intptr_t frame_index) const;
|
||||
void SetPcOffsetAtFrame(intptr_t frame_index, uword pc_offset) const;
|
||||
|
||||
bool skip_sync_start_in_parent_stack() const;
|
||||
void set_skip_sync_start_in_parent_stack(bool value) const;
|
||||
@@ -10828,18 +10840,18 @@ class StackTrace : public Instance {
|
||||
return RoundedAllocationSize(sizeof(UntaggedStackTrace));
|
||||
}
|
||||
static StackTracePtr New(const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
const TypedData& pc_offset_array,
|
||||
Heap::Space space = Heap::kNew);
|
||||
|
||||
static StackTracePtr New(const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
const TypedData& pc_offset_array,
|
||||
const StackTrace& async_link,
|
||||
bool skip_sync_start_in_parent_stack,
|
||||
Heap::Space space = Heap::kNew);
|
||||
|
||||
private:
|
||||
void set_code_array(const Array& code_array) const;
|
||||
void set_pc_offset_array(const Array& pc_offset_array) const;
|
||||
void set_pc_offset_array(const TypedData& pc_offset_array) const;
|
||||
bool expand_inlined() const;
|
||||
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(StackTrace, Instance);
|
||||
|
||||
@@ -963,7 +963,11 @@ class Pass2Visitor : public ObjectVisitor,
|
||||
ScrubAndWriteUtf8(static_cast<FunctionPtr>(obj)->untag()->name_);
|
||||
} else if (cid == kCodeCid) {
|
||||
ObjectPtr owner = static_cast<CodePtr>(obj)->untag()->owner_;
|
||||
if (owner->IsFunction()) {
|
||||
if (!owner->IsHeapObject()) {
|
||||
// Precompiler removed owner object from the snapshot,
|
||||
// only leaving Smi classId.
|
||||
writer_->WriteUnsigned(kNoData);
|
||||
} else if (owner->IsFunction()) {
|
||||
writer_->WriteUnsigned(kNameData);
|
||||
ScrubAndWriteUtf8(static_cast<FunctionPtr>(owner)->untag()->name_);
|
||||
} else if (owner->IsClass()) {
|
||||
|
||||
@@ -53,8 +53,9 @@ void IsolateObjectStore::PrintToJSONObject(JSONObject* jsobj) {
|
||||
static StackTracePtr CreatePreallocatedStackTrace(Zone* zone) {
|
||||
const Array& code_array = Array::Handle(
|
||||
zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
|
||||
const Array& pc_offset_array = Array::Handle(
|
||||
zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
|
||||
const TypedData& pc_offset_array = TypedData::Handle(
|
||||
zone, TypedData::New(kUintPtrCid, StackTrace::kPreallocatedStackdepth,
|
||||
Heap::kOld));
|
||||
const StackTrace& stack_trace =
|
||||
StackTrace::Handle(zone, StackTrace::New(code_array, pc_offset_array));
|
||||
// Expansion of inlined functions requires additional memory at run time,
|
||||
|
||||
@@ -1410,7 +1410,7 @@ class CodeLookupTableBuilder : public ObjectVisitor {
|
||||
~CodeLookupTableBuilder() {}
|
||||
|
||||
void VisitObject(ObjectPtr raw_obj) {
|
||||
if (raw_obj->IsCode()) {
|
||||
if (raw_obj->IsCode() && !Code::IsUnknownDartCode(Code::RawCast(raw_obj))) {
|
||||
table_->Add(Code::Handle(Code::RawCast(raw_obj)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ const char* ProfileFunction::ResolvedScriptUrl() const {
|
||||
return NULL;
|
||||
}
|
||||
const Script& script = Script::Handle(function_.script());
|
||||
if (script.IsNull()) {
|
||||
return NULL;
|
||||
}
|
||||
const String& uri = String::Handle(script.resolved_url());
|
||||
if (uri.IsNull()) {
|
||||
return NULL;
|
||||
|
||||
@@ -2288,7 +2288,7 @@ static void InsertFakeSample(SampleBuffer* sample_buffer, uword* pc_offsets) {
|
||||
static uword FindPCForTokenPosition(const Code& code, TokenPosition tp) {
|
||||
GrowableArray<const Function*> functions;
|
||||
GrowableArray<TokenPosition> token_positions;
|
||||
for (intptr_t pc_offset = 0; pc_offset < code.Size(); pc_offset++) {
|
||||
for (uword pc_offset = 0; pc_offset < code.Size(); pc_offset++) {
|
||||
code.GetInlinedFunctionsAtInstruction(pc_offset, &functions,
|
||||
&token_positions);
|
||||
if (token_positions[0] == tp) {
|
||||
|
||||
+10
-1
@@ -2835,10 +2835,18 @@ COMPILE_ASSERT(sizeof(UntaggedFloat64x2) == 24);
|
||||
// Define an aliases for intptr_t.
|
||||
#if defined(ARCH_IS_32_BIT)
|
||||
#define kIntPtrCid kTypedDataInt32ArrayCid
|
||||
#define GetIntPtr GetInt32
|
||||
#define SetIntPtr SetInt32
|
||||
#define kUintPtrCid kTypedDataUint32ArrayCid
|
||||
#define GetUintPtr GetUint32
|
||||
#define SetUintPtr SetUint32
|
||||
#elif defined(ARCH_IS_64_BIT)
|
||||
#define kIntPtrCid kTypedDataInt64ArrayCid
|
||||
#define GetIntPtr GetInt64
|
||||
#define SetIntPtr SetInt64
|
||||
#define kUintPtrCid kTypedDataUint64ArrayCid
|
||||
#define GetUintPtr GetUint64
|
||||
#define SetUintPtr SetUint64
|
||||
#else
|
||||
#error Architecture is not 32-bit or 64-bit.
|
||||
#endif // ARCH_IS_32_BIT
|
||||
@@ -2917,7 +2925,8 @@ class UntaggedStackTrace : public UntaggedInstance {
|
||||
async_link); // Link to parent async stack trace.
|
||||
POINTER_FIELD(ArrayPtr,
|
||||
code_array); // Code object for each frame in the stack trace.
|
||||
POINTER_FIELD(ArrayPtr, pc_offset_array); // Offset of PC for each frame.
|
||||
POINTER_FIELD(TypedDataPtr, pc_offset_array); // Offset of PC for each frame.
|
||||
|
||||
VISIT_TO(ObjectPtr, pc_offset_array)
|
||||
ObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
namespace dart {
|
||||
|
||||
CodePtr ReversePc::Lookup(IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address) {
|
||||
CodePtr ReversePc::LookupInGroup(IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
// This can run in the middle of GC and must not allocate handles.
|
||||
NoSafepointScope no_safepoint;
|
||||
@@ -67,4 +67,35 @@ CodePtr ReversePc::Lookup(IsolateGroup* group,
|
||||
return Code::null();
|
||||
}
|
||||
|
||||
CodePtr ReversePc::Lookup(IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address) {
|
||||
ASSERT(FLAG_precompiled_mode && FLAG_use_bare_instructions);
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
CodePtr code = LookupInGroup(group, pc, is_return_address);
|
||||
if (code == Code::null()) {
|
||||
code = LookupInGroup(Dart::vm_isolate_group(), pc, is_return_address);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
CompressedStackMapsPtr ReversePc::FindCompressedStackMaps(
|
||||
IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address,
|
||||
uword* code_start) {
|
||||
ASSERT(FLAG_precompiled_mode && FLAG_use_bare_instructions);
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
CodePtr code = Lookup(group, pc, is_return_address);
|
||||
if (code != Code::null()) {
|
||||
*code_start = Code::PayloadStartOf(code);
|
||||
return code->untag()->compressed_stackmaps();
|
||||
}
|
||||
|
||||
*code_start = 0;
|
||||
return CompressedStackMaps::null();
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -13,11 +13,28 @@ namespace dart {
|
||||
|
||||
class IsolateGroup;
|
||||
|
||||
// This class provides mechanism to find Code and CompressedStackMaps
|
||||
// objects corresponding to the given PC.
|
||||
// Can only be used in AOT runtime with bare instructions.
|
||||
class ReversePc : public AllStatic {
|
||||
public:
|
||||
static CodePtr Lookup(IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address = false);
|
||||
// Looks for Code object corresponding to |pc| in the
|
||||
// given isolate |group| and vm isolate group.
|
||||
static CodePtr Lookup(IsolateGroup* group, uword pc, bool is_return_address);
|
||||
|
||||
// Looks for CompressedStackMaps corresponding to |pc| in the
|
||||
// given isolate |group| and vm isolate group.
|
||||
// Sets |code_start| to the beginning of the instructions corresponding
|
||||
// to |pc| (like Code::PayloadStart()).
|
||||
static CompressedStackMapsPtr FindCompressedStackMaps(IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address,
|
||||
uword* code_start);
|
||||
|
||||
private:
|
||||
static CodePtr LookupInGroup(IsolateGroup* group,
|
||||
uword pc,
|
||||
bool is_return_address);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -259,7 +259,7 @@ ISOLATE_UNIT_TEST_CASE(Service_Code) {
|
||||
// Use the entry of the code object as it's reference.
|
||||
uword entry = code_c.PayloadStart();
|
||||
int64_t compile_timestamp = code_c.compile_timestamp();
|
||||
EXPECT_GT(code_c.Size(), 16);
|
||||
EXPECT_GT(code_c.Size(), 16u);
|
||||
uword last = entry + code_c.Size();
|
||||
|
||||
// Build a mock message handler and wrap it in a dart port.
|
||||
|
||||
+40
-30
@@ -105,6 +105,9 @@ void UntaggedFrame::Init() {
|
||||
}
|
||||
|
||||
bool StackFrame::IsBareInstructionsDartFrame() const {
|
||||
if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) {
|
||||
return false;
|
||||
}
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
Code code;
|
||||
@@ -115,18 +118,14 @@ bool StackFrame::IsBareInstructionsDartFrame() const {
|
||||
ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
|
||||
return cid == kFunctionCid;
|
||||
}
|
||||
code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
|
||||
/*is_return_address=*/true);
|
||||
if (!code.IsNull()) {
|
||||
auto const cid = code.OwnerClassId();
|
||||
ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
|
||||
return cid == kFunctionCid;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StackFrame::IsBareInstructionsStubFrame() const {
|
||||
if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) {
|
||||
return false;
|
||||
}
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
Code code;
|
||||
@@ -137,13 +136,6 @@ bool StackFrame::IsBareInstructionsStubFrame() const {
|
||||
ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
|
||||
return cid == kNullCid || cid == kClassCid;
|
||||
}
|
||||
code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
|
||||
/*is_return_address=*/true);
|
||||
if (!code.IsNull()) {
|
||||
auto const cid = code.OwnerClassId();
|
||||
ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
|
||||
return cid == kNullCid || cid == kClassCid;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -225,9 +217,13 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
// helper functions to the raw object interface.
|
||||
NoSafepointScope no_safepoint;
|
||||
Code code;
|
||||
CompressedStackMaps maps;
|
||||
uword code_start;
|
||||
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
code = GetCodeObject();
|
||||
maps = ReversePc::FindCompressedStackMaps(isolate_group(), pc(),
|
||||
/*is_return_address=*/true,
|
||||
&code_start);
|
||||
} else {
|
||||
ObjectPtr pc_marker = *(reinterpret_cast<ObjectPtr*>(
|
||||
fp() + (runtime_frame_layout.code_from_fp * kWordSize)));
|
||||
@@ -236,23 +232,23 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
visitor->VisitPointer(&pc_marker);
|
||||
if (pc_marker->IsHeapObject() && (pc_marker->GetClassId() == kCodeCid)) {
|
||||
code ^= pc_marker;
|
||||
code_start = code.PayloadStart();
|
||||
maps = code.compressed_stackmaps();
|
||||
ASSERT(!maps.IsNull());
|
||||
} else {
|
||||
ASSERT(pc_marker == Object::null());
|
||||
}
|
||||
}
|
||||
|
||||
if (!code.IsNull()) {
|
||||
if (!maps.IsNull()) {
|
||||
// Optimized frames have a stack map. We need to visit the frame based
|
||||
// on the stack map.
|
||||
CompressedStackMaps maps;
|
||||
maps = code.compressed_stackmaps();
|
||||
CompressedStackMaps global_table;
|
||||
|
||||
global_table =
|
||||
isolate_group()->object_store()->canonicalized_stack_map_entries();
|
||||
CompressedStackMaps::Iterator it(maps, global_table);
|
||||
const uword start = code.PayloadStart();
|
||||
const uint32_t pc_offset = pc() - start;
|
||||
const uint32_t pc_offset = pc() - code_start;
|
||||
if (it.Find(pc_offset)) {
|
||||
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
|
||||
ObjectPtr* last = reinterpret_cast<ObjectPtr*>(
|
||||
@@ -305,8 +301,14 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
// unoptimized code, code with no stack map information at all, or the entry
|
||||
// to an osr function. In each of these cases, all stack slots contain
|
||||
// tagged pointers, so fall through.
|
||||
ASSERT(!code.is_optimized() || maps.IsNull() ||
|
||||
(pc_offset == code.EntryPoint() - code.PayloadStart()));
|
||||
#if defined(DEBUG)
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
ASSERT(IsStubFrame());
|
||||
} else {
|
||||
ASSERT(!code.is_optimized() ||
|
||||
(pc_offset == code.EntryPoint() - code.PayloadStart()));
|
||||
}
|
||||
#endif // defined(DEBUG)
|
||||
}
|
||||
|
||||
// For normal unoptimized Dart frames and Stub frames each slot
|
||||
@@ -348,15 +350,23 @@ CodePtr StackFrame::LookupDartCode() const {
|
||||
CodePtr StackFrame::GetCodeObject() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
CodePtr code = ReversePc::Lookup(isolate_group(), pc(),
|
||||
/*is_return_address=*/true);
|
||||
if (code != Code::null()) {
|
||||
return code;
|
||||
}
|
||||
code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
|
||||
NoSafepointScope no_safepoint;
|
||||
Code code;
|
||||
code = ReversePc::Lookup(isolate_group(), pc(),
|
||||
/*is_return_address=*/true);
|
||||
if (code != Code::null()) {
|
||||
return code;
|
||||
if (!code.IsNull()) {
|
||||
// This is needed in order to test stack traces with the future
|
||||
// behavior of ReversePc::Lookup which will return
|
||||
// StubCode::UnknownDartCode() if code object is omitted from
|
||||
// the snapshot.
|
||||
if (FLAG_dwarf_stack_traces_mode && code.CanBeOmittedFromAOTSnapshot()) {
|
||||
ASSERT(StubCode::UnknownDartCode().PayloadStart() == 0);
|
||||
ASSERT(StubCode::UnknownDartCode().Size() == kUwordMax);
|
||||
ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
|
||||
ASSERT(StubCode::UnknownDartCode().IsUnknownDartCode());
|
||||
return StubCode::UnknownDartCode().ptr();
|
||||
}
|
||||
return code.ptr();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
+12
-18
@@ -429,19 +429,17 @@ ClosurePtr StackTraceUtils::ClosureFromFrameFunction(
|
||||
void StackTraceUtils::UnwindAwaiterChain(
|
||||
Zone* zone,
|
||||
const GrowableObjectArray& code_array,
|
||||
const GrowableObjectArray& pc_offset_array,
|
||||
GrowableArray<uword>* pc_offset_array,
|
||||
CallerClosureFinder* caller_closure_finder,
|
||||
const Closure& leaf_closure) {
|
||||
auto& code = Code::Handle(zone);
|
||||
auto& function = Function::Handle(zone);
|
||||
auto& closure = Closure::Handle(zone, leaf_closure.ptr());
|
||||
auto& pc_descs = PcDescriptors::Handle(zone);
|
||||
auto& offset = Smi::Handle(zone);
|
||||
|
||||
// Inject async suspension marker.
|
||||
code_array.Add(StubCode::AsynchronousGapMarker());
|
||||
offset = Smi::New(0);
|
||||
pc_offset_array.Add(offset);
|
||||
pc_offset_array->Add(0);
|
||||
|
||||
// Traverse the trail of async futures all the way up.
|
||||
for (; !closure.IsNull();
|
||||
@@ -455,23 +453,22 @@ void StackTraceUtils::UnwindAwaiterChain(
|
||||
RELEASE_ASSERT(!code.IsNull());
|
||||
code_array.Add(code);
|
||||
pc_descs = code.pc_descriptors();
|
||||
offset = Smi::New(FindPcOffset(pc_descs, GetYieldIndex(closure)));
|
||||
const intptr_t pc_offset = FindPcOffset(pc_descs, GetYieldIndex(closure));
|
||||
// Unlike other sources of PC offsets, the offset may be 0 here if we
|
||||
// reach a non-async closure receiving the yielded value.
|
||||
ASSERT(offset.Value() >= 0);
|
||||
pc_offset_array.Add(offset);
|
||||
ASSERT(pc_offset >= 0);
|
||||
pc_offset_array->Add(pc_offset);
|
||||
|
||||
// Inject async suspension marker.
|
||||
code_array.Add(StubCode::AsynchronousGapMarker());
|
||||
offset = Smi::New(0);
|
||||
pc_offset_array.Add(offset);
|
||||
pc_offset_array->Add(0);
|
||||
}
|
||||
}
|
||||
|
||||
void StackTraceUtils::CollectFramesLazy(
|
||||
Thread* thread,
|
||||
const GrowableObjectArray& code_array,
|
||||
const GrowableObjectArray& pc_offset_array,
|
||||
GrowableArray<uword>* pc_offset_array,
|
||||
int skip_frames,
|
||||
std::function<void(StackFrame*)>* on_sync_frames,
|
||||
bool* has_async) {
|
||||
@@ -489,7 +486,6 @@ void StackTraceUtils::CollectFramesLazy(
|
||||
}
|
||||
|
||||
auto& code = Code::Handle(zone);
|
||||
auto& offset = Smi::Handle(zone);
|
||||
auto& closure = Closure::Handle(zone);
|
||||
|
||||
CallerClosureFinder caller_closure_finder(zone);
|
||||
@@ -513,10 +509,9 @@ void StackTraceUtils::CollectFramesLazy(
|
||||
// Add the current synchronous frame.
|
||||
code = frame->LookupDartCode();
|
||||
code_array.Add(code);
|
||||
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
|
||||
const uword pc_offset = frame->pc() - code.PayloadStart();
|
||||
ASSERT(pc_offset > 0 && pc_offset <= code.Size());
|
||||
offset = Smi::New(pc_offset);
|
||||
pc_offset_array.Add(offset);
|
||||
pc_offset_array->Add(pc_offset);
|
||||
// Callback for sync frame.
|
||||
if (on_sync_frames != nullptr) {
|
||||
(*on_sync_frames)(frame);
|
||||
@@ -593,7 +588,7 @@ intptr_t StackTraceUtils::CountFrames(Thread* thread,
|
||||
|
||||
intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
||||
const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
const TypedData& pc_offset_array,
|
||||
intptr_t array_offset,
|
||||
intptr_t count,
|
||||
int skip_frames) {
|
||||
@@ -602,7 +597,6 @@ intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
||||
Code& code = Code::Handle(zone);
|
||||
Smi& offset = Smi::Handle(zone);
|
||||
intptr_t collected_frames_count = 0;
|
||||
for (; (frame != NULL) && (collected_frames_count < count);
|
||||
frame = frames.NextFrame()) {
|
||||
@@ -611,9 +605,9 @@ intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
||||
continue;
|
||||
}
|
||||
code = frame->LookupDartCode();
|
||||
offset = Smi::New(frame->pc() - code.PayloadStart());
|
||||
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
|
||||
code_array.SetAt(array_offset, code);
|
||||
pc_offset_array.SetAt(array_offset, offset);
|
||||
pc_offset_array.SetUintPtr(array_offset * kWordSize, pc_offset);
|
||||
array_offset++;
|
||||
collected_frames_count++;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class StackTraceUtils : public AllStatic {
|
||||
|
||||
static void UnwindAwaiterChain(Zone* zone,
|
||||
const GrowableObjectArray& code_array,
|
||||
const GrowableObjectArray& pc_offset_array,
|
||||
GrowableArray<uword>* pc_offset_array,
|
||||
CallerClosureFinder* caller_closure_finder,
|
||||
const Closure& leaf_closure);
|
||||
|
||||
@@ -130,7 +130,7 @@ class StackTraceUtils : public AllStatic {
|
||||
static void CollectFramesLazy(
|
||||
Thread* thread,
|
||||
const GrowableObjectArray& code_array,
|
||||
const GrowableObjectArray& pc_offset_array,
|
||||
GrowableArray<uword>* pc_offset_array,
|
||||
int skip_frames,
|
||||
std::function<void(StackFrame*)>* on_sync_frames = nullptr,
|
||||
bool* has_async = nullptr);
|
||||
@@ -151,7 +151,7 @@ class StackTraceUtils : public AllStatic {
|
||||
/// Returns the number of frames collected.
|
||||
static intptr_t CollectFrames(Thread* thread,
|
||||
const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
const TypedData& pc_offset_array,
|
||||
intptr_t array_offset,
|
||||
intptr_t count,
|
||||
int skip_frames);
|
||||
|
||||
@@ -60,6 +60,30 @@ void StubCode::Init() {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
|
||||
entries_[i].code->set_object_pool(object_pool.ptr());
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
{
|
||||
// Set Function owner for UnknownDartCode stub so it pretends to
|
||||
// be a Dart code.
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const auto& signature = FunctionType::Handle(zone, FunctionType::New());
|
||||
auto& owner = Object::Handle(zone);
|
||||
owner = Object::void_class();
|
||||
ASSERT(!owner.IsNull());
|
||||
owner = Function::New(signature, Object::null_string(),
|
||||
UntaggedFunction::kRegularFunction,
|
||||
/*is_static=*/true,
|
||||
/*is_const=*/false,
|
||||
/*is_abstract=*/false,
|
||||
/*is_external=*/false,
|
||||
/*is_native=*/false, owner, TokenPosition::kNoSource);
|
||||
StubCode::UnknownDartCode().set_owner(owner);
|
||||
StubCode::UnknownDartCode().set_exception_handlers(
|
||||
Object::empty_exception_handlers());
|
||||
StubCode::UnknownDartCode().set_pc_descriptors(Object::empty_descriptors());
|
||||
ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
|
||||
}
|
||||
#endif // defined(DART_PRECOMPILER)
|
||||
}
|
||||
|
||||
#undef STUB_CODE_GENERATE
|
||||
|
||||
@@ -128,7 +128,8 @@ namespace dart {
|
||||
V(InstantiateTypeArguments) \
|
||||
V(InstantiateTypeArgumentsMayShareInstantiatorTA) \
|
||||
V(InstantiateTypeArgumentsMayShareFunctionTA) \
|
||||
V(NoSuchMethodDispatcher)
|
||||
V(NoSuchMethodDispatcher) \
|
||||
V(UnknownDartCode)
|
||||
|
||||
} // namespace dart
|
||||
|
||||
|
||||
Reference in New Issue
Block a user