[wasm-c-api] Roll dc8cc29: Implement stack trace API

Change-Id: Ic5145b7ba15ae58d15e2cc4511afc2f8c6d42ea0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1741654
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63132}
This commit is contained in:
Jakob Kummerow 2019-08-08 17:59:35 +02:00 committed by Commit Bot
parent c9f9d1b0b4
commit 5e46b285df
15 changed files with 326 additions and 7 deletions

View File

@ -925,6 +925,7 @@ extern class StackFrameInfo extends Struct {
type_name: String | Null | Undefined;
eval_origin: String | Null | Undefined;
wasm_module_name: String | Null | Undefined;
wasm_instance: WasmInstanceObject | Null | Undefined;
flag: Smi;
}

View File

@ -314,6 +314,10 @@ Handle<Object> StackFrameBase::GetWasmModuleName() {
return isolate_->factory()->undefined_value();
}
Handle<Object> StackFrameBase::GetWasmInstance() {
return isolate_->factory()->undefined_value();
}
int StackFrameBase::GetScriptId() const {
if (!HasScript()) return kNone;
return GetScript()->id();
@ -580,6 +584,8 @@ Handle<Object> WasmStackFrame::GetWasmModuleName() {
return module_name;
}
Handle<Object> WasmStackFrame::GetWasmInstance() { return wasm_instance_; }
int WasmStackFrame::GetPosition() const {
return IsInterpreted()
? offset_

View File

@ -73,6 +73,7 @@ class StackFrameBase {
virtual Handle<Object> GetTypeName() = 0;
virtual Handle<Object> GetEvalOrigin();
virtual Handle<Object> GetWasmModuleName();
virtual Handle<Object> GetWasmInstance();
// Returns the script ID if one is attached, -1 otherwise.
int GetScriptId() const;
@ -170,12 +171,13 @@ class WasmStackFrame : public StackFrameBase {
Handle<Object> GetMethodName() override { return Null(); }
Handle<Object> GetTypeName() override { return Null(); }
Handle<Object> GetWasmModuleName() override;
Handle<Object> GetWasmInstance() override;
int GetPosition() const override;
int GetLineNumber() override { return wasm_func_index_; }
int GetColumnNumber() override;
int GetPromiseIndex() const override { return kNone; }
int GetPromiseIndex() const override { return GetPosition(); }
bool IsNative() override { return false; }
bool IsToplevel() override { return false; }

View File

@ -3708,6 +3708,7 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
Handle<Object> type_name = undefined_value();
Handle<Object> eval_origin = frame->GetEvalOrigin();
Handle<Object> wasm_module_name = frame->GetWasmModuleName();
Handle<Object> wasm_instance = frame->GetWasmInstance();
// MethodName and TypeName are expensive to look up, so they are only
// included when they are strictly needed by the stack trace
@ -3743,6 +3744,7 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
info->set_type_name(*type_name);
info->set_eval_origin(*eval_origin);
info->set_wasm_module_name(*wasm_module_name);
info->set_wasm_instance(*wasm_instance);
info->set_is_eval(frame->IsEval());
info->set_is_constructor(is_constructor);

View File

@ -28,6 +28,8 @@ SMI_ACCESSORS(StackFrameInfo, line_number, kLineNumberOffset)
SMI_ACCESSORS(StackFrameInfo, column_number, kColumnNumberOffset)
SMI_ACCESSORS(StackFrameInfo, script_id, kScriptIdOffset)
SMI_ACCESSORS(StackFrameInfo, promise_all_index, kPromiseAllIndexOffset)
SMI_ACCESSORS_CHECKED(StackFrameInfo, function_offset, kPromiseAllIndexOffset,
is_wasm())
ACCESSORS(StackFrameInfo, script_name, Object, kScriptNameOffset)
ACCESSORS(StackFrameInfo, script_name_or_source_url, Object,
kScriptNameOrSourceUrlOffset)
@ -36,6 +38,7 @@ ACCESSORS(StackFrameInfo, method_name, Object, kMethodNameOffset)
ACCESSORS(StackFrameInfo, type_name, Object, kTypeNameOffset)
ACCESSORS(StackFrameInfo, eval_origin, Object, kEvalOriginOffset)
ACCESSORS(StackFrameInfo, wasm_module_name, Object, kWasmModuleNameOffset)
ACCESSORS(StackFrameInfo, wasm_instance, Object, kWasmInstanceOffset)
SMI_ACCESSORS(StackFrameInfo, flag, kFlagOffset)
BOOL_ACCESSORS(StackFrameInfo, flag, is_eval, kIsEvalBit)
BOOL_ACCESSORS(StackFrameInfo, flag, is_constructor, kIsConstructorBit)

View File

@ -51,6 +51,12 @@ int StackTraceFrame::GetPromiseAllIndex(Handle<StackTraceFrame> frame) {
return GetFrameInfo(frame)->promise_all_index();
}
// static
int StackTraceFrame::GetFunctionOffset(Handle<StackTraceFrame> frame) {
DCHECK(IsWasm(frame));
return GetFrameInfo(frame)->function_offset();
}
// static
Handle<Object> StackTraceFrame::GetFileName(Handle<StackTraceFrame> frame) {
auto name = GetFrameInfo(frame)->script_name();
@ -95,6 +101,13 @@ Handle<Object> StackTraceFrame::GetWasmModuleName(
return handle(module, frame->GetIsolate());
}
// static
Handle<WasmInstanceObject> StackTraceFrame::GetWasmInstance(
Handle<StackTraceFrame> frame) {
Object instance = GetFrameInfo(frame)->wasm_instance();
return handle(WasmInstanceObject::cast(instance), frame->GetIsolate());
}
// static
bool StackTraceFrame::IsEval(Handle<StackTraceFrame> frame) {
return GetFrameInfo(frame)->is_eval();

View File

@ -14,6 +14,7 @@ namespace v8 {
namespace internal {
class FrameArray;
class WasmInstanceObject;
class StackFrameInfo : public Struct {
public:
@ -22,6 +23,8 @@ class StackFrameInfo : public Struct {
DECL_INT_ACCESSORS(column_number)
DECL_INT_ACCESSORS(script_id)
DECL_INT_ACCESSORS(promise_all_index)
// Wasm frames only: function_offset instead of promise_all_index.
DECL_INT_ACCESSORS(function_offset)
DECL_ACCESSORS(script_name, Object)
DECL_ACCESSORS(script_name_or_source_url, Object)
DECL_ACCESSORS(function_name, Object)
@ -29,6 +32,7 @@ class StackFrameInfo : public Struct {
DECL_ACCESSORS(type_name, Object)
DECL_ACCESSORS(eval_origin, Object)
DECL_ACCESSORS(wasm_module_name, Object)
DECL_ACCESSORS(wasm_instance, Object)
DECL_BOOLEAN_ACCESSORS(is_eval)
DECL_BOOLEAN_ACCESSORS(is_constructor)
DECL_BOOLEAN_ACCESSORS(is_wasm)
@ -90,6 +94,7 @@ class StackTraceFrame : public Struct {
static int GetOneBasedColumnNumber(Handle<StackTraceFrame> frame);
static int GetScriptId(Handle<StackTraceFrame> frame);
static int GetPromiseAllIndex(Handle<StackTraceFrame> frame);
static int GetFunctionOffset(Handle<StackTraceFrame> frame);
static Handle<Object> GetFileName(Handle<StackTraceFrame> frame);
static Handle<Object> GetScriptNameOrSourceUrl(Handle<StackTraceFrame> frame);
@ -98,6 +103,8 @@ class StackTraceFrame : public Struct {
static Handle<Object> GetTypeName(Handle<StackTraceFrame> frame);
static Handle<Object> GetEvalOrigin(Handle<StackTraceFrame> frame);
static Handle<Object> GetWasmModuleName(Handle<StackTraceFrame> frame);
static Handle<WasmInstanceObject> GetWasmInstance(
Handle<StackTraceFrame> frame);
static bool IsEval(Handle<StackTraceFrame> frame);
static bool IsConstructor(Handle<StackTraceFrame> frame);

View File

@ -29,6 +29,7 @@
#include "include/libplatform/libplatform.h"
#include "src/api/api-inl.h"
#include "src/compiler/wasm-compiler.h"
#include "src/objects/stack-frame-info-inl.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/module-instantiate.h"
#include "src/wasm/wasm-arguments.h"
@ -309,6 +310,10 @@ auto Store::make(Engine*) -> own<Store*> {
// and hence must not be called by anything reachable via this file.
store->context()->Enter();
isolate->SetData(0, store.get());
// We want stack traces for traps.
constexpr int kStackLimit = 10;
isolate->SetCaptureStackTraceForUncaughtExceptions(true, kStackLimit,
v8::StackTrace::kOverview);
return make_own(seal<Store>(store.release()));
}
@ -771,6 +776,52 @@ void Ref::set_host_info(void* info, void (*finalizer)(void*)) {
///////////////////////////////////////////////////////////////////////////////
// Runtime Objects
// Frames
namespace {
struct FrameImpl {
FrameImpl(own<Instance*>&& instance, uint32_t func_index, size_t func_offset,
size_t module_offset)
: instance(std::move(instance)),
func_index(func_index),
func_offset(func_offset),
module_offset(module_offset) {}
~FrameImpl() {}
own<Instance*> instance;
uint32_t func_index;
size_t func_offset;
size_t module_offset;
};
} // namespace
template <>
struct implement<Frame> {
using type = FrameImpl;
};
Frame::~Frame() { impl(this)->~FrameImpl(); }
void Frame::operator delete(void* p) { ::operator delete(p); }
own<Frame*> Frame::copy() const {
auto self = impl(this);
return own<Frame*>(seal<Frame>(
new (std::nothrow) FrameImpl(self->instance->copy(), self->func_index,
self->func_offset, self->module_offset)));
}
Instance* Frame::instance() const { return impl(this)->instance.get(); }
uint32_t Frame::func_index() const { return impl(this)->func_index; }
size_t Frame::func_offset() const { return impl(this)->func_offset; }
size_t Frame::module_offset() const { return impl(this)->module_offset; }
// Traps
template <>
@ -806,6 +857,56 @@ auto Trap::message() const -> Message {
return vec<byte_t>::adopt(length, utf8.release());
}
namespace {
own<Instance*> GetInstance(StoreImpl* store,
i::Handle<i::WasmInstanceObject> instance);
own<Frame*> CreateFrameFromInternal(i::Handle<i::FixedArray> frames, int index,
i::Isolate* isolate, StoreImpl* store) {
i::Handle<i::StackTraceFrame> frame(i::StackTraceFrame::cast(frames->get(0)),
isolate);
i::Handle<i::WasmInstanceObject> instance =
i::StackTraceFrame::GetWasmInstance(frame);
uint32_t func_index = i::StackTraceFrame::GetLineNumber(frame);
size_t func_offset = i::StackTraceFrame::GetFunctionOffset(frame);
size_t module_offset = i::StackTraceFrame::GetColumnNumber(frame);
return own<Frame*>(seal<Frame>(new (std::nothrow) FrameImpl(
GetInstance(store, instance), func_index, func_offset, module_offset)));
}
} // namespace
own<Frame*> Trap::origin() const {
i::Isolate* isolate = impl(this)->isolate();
i::HandleScope handle_scope(isolate);
i::Handle<i::JSMessageObject> message =
isolate->CreateMessage(impl(this)->v8_object(), nullptr);
i::Handle<i::FixedArray> frames(i::FixedArray::cast(message->stack_frames()),
isolate);
DCHECK_GT(frames->length(), 0);
return CreateFrameFromInternal(frames, 0, isolate, impl(this)->store());
}
vec<Frame*> Trap::trace() const {
i::Isolate* isolate = impl(this)->isolate();
i::HandleScope handle_scope(isolate);
i::Handle<i::JSMessageObject> message =
isolate->CreateMessage(impl(this)->v8_object(), nullptr);
i::Handle<i::FixedArray> frames(i::FixedArray::cast(message->stack_frames()),
isolate);
int num_frames = frames->length();
DCHECK_GT(num_frames, 0);
vec<Frame*> result = vec<Frame*>::make_uninitialized(num_frames);
for (int i = 0; i < num_frames; i++) {
result[i] =
CreateFrameFromInternal(frames, i, isolate, impl(this)->store());
}
return result;
}
// Foreign Objects
template <>
@ -1832,6 +1933,15 @@ auto Instance::make(Store* store_abs, const Module* module_abs,
return implement<Instance>::type::make(store, instance_obj);
}
namespace {
own<Instance*> GetInstance(StoreImpl* store,
i::Handle<i::WasmInstanceObject> instance) {
return implement<Instance>::type::make(store, instance);
}
} // namespace
auto Instance::exports() const -> vec<Extern*> {
const implement<Instance>::type* instance = impl(this);
StoreImpl* store = instance->store();
@ -2536,6 +2646,30 @@ void wasm_val_copy(wasm_val_t* out, const wasm_val_t* v) {
///////////////////////////////////////////////////////////////////////////////
// Runtime Objects
// Frames
WASM_DEFINE_OWN(frame, wasm::Frame)
WASM_DEFINE_VEC(frame, wasm::Frame, *)
wasm_frame_t* wasm_frame_copy(const wasm_frame_t* frame) {
return release(frame->copy());
}
wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame);
// Defined below along with wasm_instance_t.
uint32_t wasm_frame_func_index(const wasm_frame_t* frame) {
return reveal(frame)->func_index();
}
size_t wasm_frame_func_offset(const wasm_frame_t* frame) {
return reveal(frame)->func_offset();
}
size_t wasm_frame_module_offset(const wasm_frame_t* frame) {
return reveal(frame)->module_offset();
}
// Traps
WASM_DEFINE_REF(trap, wasm::Trap)
@ -2549,6 +2683,14 @@ void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) {
*out = release(reveal(trap)->message());
}
wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) {
return release(reveal(trap)->origin());
}
void wasm_trap_trace(const wasm_trap_t* trap, wasm_frame_vec_t* out) {
*out = release(reveal(trap)->trace());
}
// Foreign Objects
WASM_DEFINE_REF(foreign, wasm::Foreign)
@ -2829,6 +2971,10 @@ void wasm_instance_exports(const wasm_instance_t* instance,
*out = release(instance->exports());
}
wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) {
return hide(reveal(frame)->instance());
}
#undef WASM_DEFINE_OWN
#undef WASM_DEFINE_VEC_BASE
#undef WASM_DEFINE_VEC_PLAIN

View File

@ -267,7 +267,7 @@ size_t GetFunctionOffset(i::Isolate* isolate, const uint8_t* buffer,
kAllWasmFeatures, buffer, buffer + size, false, ModuleOrigin::kWasmOrigin,
isolate->counters(), isolate->wasm_engine()->allocator());
CHECK(result.ok());
const WasmFunction* func = &result.value()->functions[1];
const WasmFunction* func = &result.value()->functions[index];
return func->code.offset();
}

View File

@ -4,12 +4,18 @@
#include "test/wasm-api-tests/wasm-api-test.h"
#include "src/execution/isolate.h"
#include "src/wasm/c-api.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include <iostream>
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Frame;
using ::wasm::Message;
namespace {
@ -34,7 +40,9 @@ TEST_F(WasmCapiTest, Traps) {
uint32_t callback_index = builder()->AddImport(CStrVector("callback"), &sig);
byte code[] = {WASM_CALL_FUNCTION0(callback_index)};
AddExportedFunction(CStrVector("callback"), code, sizeof(code), &sig);
byte code2[] = {WASM_UNREACHABLE, WASM_I32V_1(1)};
// The first constant is a 4-byte dummy so that the {unreachable} trap
// has a more interesting offset.
byte code2[] = {WASM_I32V_3(0), WASM_UNREACHABLE, WASM_I32V_1(1)};
AddExportedFunction(CStrVector("unreachable"), code2, sizeof(code2), &sig);
own<FuncType*> func_type = FuncType::make(
@ -44,15 +52,53 @@ TEST_F(WasmCapiTest, Traps) {
Extern* imports[] = {cpp_callback.get()};
Instantiate(imports);
// Use internal machinery to parse the module to find the function offsets.
// This makes the test more robust than hardcoding them.
i::Isolate* isolate =
reinterpret_cast<::wasm::StoreImpl*>(store())->i_isolate();
ModuleResult result = DecodeWasmModule(
kAllWasmFeatures, wire_bytes()->begin(), wire_bytes()->end(), false,
ModuleOrigin::kWasmOrigin, isolate->counters(),
isolate->wasm_engine()->allocator());
ASSERT_TRUE(result.ok());
const WasmFunction* func1 = &result.value()->functions[1];
const WasmFunction* func2 = &result.value()->functions[2];
const uint32_t func1_offset = func1->code.offset();
const uint32_t func2_offset = func2->code.offset();
Func* cpp_trapping_func = GetExportedFunction(0);
own<Trap*> cpp_trap = cpp_trapping_func->call();
EXPECT_NE(nullptr, cpp_trap.get());
ExpectMessage("Uncaught Error: callback abort", cpp_trap->message());
own<Frame*> frame = cpp_trap->origin();
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(1u, frame->func_index());
EXPECT_EQ(1u, frame->func_offset());
EXPECT_EQ(func1_offset + frame->func_offset(), frame->module_offset());
vec<Frame*> trace = cpp_trap->trace();
EXPECT_EQ(1u, trace.size());
frame = trace[0].move();
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(1u, frame->func_index());
EXPECT_EQ(1u, frame->func_offset());
EXPECT_EQ(func1_offset + frame->func_offset(), frame->module_offset());
Func* wasm_trapping_func = GetExportedFunction(1);
own<Trap*> wasm_trap = wasm_trapping_func->call();
EXPECT_NE(nullptr, wasm_trap.get());
ExpectMessage("Uncaught RuntimeError: unreachable", wasm_trap->message());
frame = wasm_trap->origin();
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(2u, frame->func_index());
EXPECT_EQ(5u, frame->func_offset());
EXPECT_EQ(func2_offset + frame->func_offset(), frame->module_offset());
trace = wasm_trap->trace();
EXPECT_EQ(1u, trace.size());
frame = trace[0].move();
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(2u, frame->func_index());
EXPECT_EQ(5u, frame->func_offset());
EXPECT_EQ(func2_offset + frame->func_offset(), frame->module_offset());
}
} // namespace wasm

View File

@ -53,6 +53,7 @@ class WasmCapiTest : public ::testing::Test {
WasmCapiTest()
: Test(),
zone_(&allocator_, ZONE_NAME),
wire_bytes_(&zone_),
builder_(&zone_),
exports_(vec<Extern*>::make()),
wasm_i_i_sig_(1, 1, wasm_i_i_sig_types_) {
@ -64,11 +65,11 @@ class WasmCapiTest : public ::testing::Test {
}
void Compile() {
ZoneBuffer buffer(&zone_);
builder_.WriteTo(&buffer);
size_t size = buffer.end() - buffer.begin();
builder_.WriteTo(&wire_bytes_);
size_t size = wire_bytes_.end() - wire_bytes_.begin();
vec<byte_t> binary = vec<byte_t>::make(
size, reinterpret_cast<byte_t*>(const_cast<byte*>(buffer.begin())));
size,
reinterpret_cast<byte_t*>(const_cast<byte*>(wire_bytes_.begin())));
module_ = Module::make(store_.get(), binary);
DCHECK_NE(module_.get(), nullptr);
@ -136,7 +137,9 @@ class WasmCapiTest : public ::testing::Test {
Engine* engine() { return engine_.get(); }
Store* store() { return store_.get(); }
Module* module() { return module_.get(); }
Instance* instance() { return instance_.get(); }
const vec<Extern*>& exports() { return exports_; }
ZoneBuffer* wire_bytes() { return &wire_bytes_; }
FunctionSig* wasm_i_i_sig() { return &wasm_i_i_sig_; }
FuncType* cpp_i_i_sig() { return cpp_i_i_sig_.get(); }
@ -144,6 +147,7 @@ class WasmCapiTest : public ::testing::Test {
private:
AccountingAllocator allocator_;
Zone zone_;
ZoneBuffer wire_bytes_;
WasmModuleBuilder builder_;
own<Engine*> engine_;
own<Store*> store_;

View File

@ -20,6 +20,16 @@ own wasm_trap_t* fail_callback(
}
void print_frame(wasm_frame_t* frame) {
printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n",
wasm_frame_instance(frame),
wasm_frame_module_offset(frame),
wasm_frame_func_index(frame),
wasm_frame_func_offset(frame)
);
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
@ -104,6 +114,27 @@ int main(int argc, const char* argv[]) {
wasm_trap_message(trap, &message);
printf("> %s\n", message.data);
printf("Printing origin...\n");
own wasm_frame_t* frame = wasm_trap_origin(trap);
if (frame) {
print_frame(frame);
wasm_frame_delete(frame);
} else {
printf("> Empty origin.\n");
}
printf("Printing trace...\n");
own wasm_frame_vec_t trace;
wasm_trap_trace(trap, &trace);
if (trace.size > 0) {
for (size_t i = 0; i < trace.size; ++i) {
print_frame(trace.data[i]);
}
} else {
printf("> Empty trace.\n");
}
wasm_frame_vec_delete(&trace);
wasm_trap_delete(trap);
wasm_name_delete(&message);
}

View File

@ -17,6 +17,14 @@ auto fail_callback(
}
void print_frame(const wasm::Frame* frame) {
std::cout << "> " << frame->instance();
std::cout << " @ 0x" << std::hex << frame->module_offset();
std::cout << " = " << frame->func_index();
std::cout << ".0x" << std::hex << frame->func_offset() << std::endl;
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
@ -85,6 +93,24 @@ void run() {
std::cout << "Printing message..." << std::endl;
std::cout << "> " << trap->message().get() << std::endl;
std::cout << "Printing origin..." << std::endl;
auto frame = trap->origin();
if (frame) {
print_frame(frame.get());
} else {
std::cout << "> Empty origin." << std::endl;
}
std::cout << "Printing trace..." << std::endl;
auto trace = trap->trace();
if (trace.size() > 0) {
for (size_t i = 0; i < trace.size(); ++i) {
print_frame(trace[i].get());
}
} else {
std::cout << "> Empty trace." << std::endl;
}
}
// Shut down.

View File

@ -345,6 +345,18 @@ WASM_DECLARE_VEC(val, )
WASM_DECLARE_REF_BASE(ref)
// Frames
WASM_DECLARE_OWN(frame)
WASM_DECLARE_VEC(frame, *)
own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*);
struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*);
uint32_t wasm_frame_func_index(const wasm_frame_t*);
size_t wasm_frame_func_offset(const wasm_frame_t*);
size_t wasm_frame_module_offset(const wasm_frame_t*);
// Traps
typedef wasm_name_t wasm_message_t; // null terminated
@ -354,6 +366,8 @@ WASM_DECLARE_REF(trap)
own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*);
void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out);
own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*);
void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out);
// Foreign Objects

View File

@ -584,6 +584,22 @@ template<> inline auto Val::get<uint64_t>() const -> uint64_t {
using Message = vec<byte_t>; // null terminated
class Instance;
class Frame {
public:
Frame() = delete;
~Frame();
void operator delete(void*);
auto copy() const -> own<Frame*>;
auto instance() const -> Instance*;
auto func_index() const -> uint32_t;
auto func_offset() const -> size_t;
auto module_offset() const -> size_t;
};
class Trap : public Ref {
public:
Trap() = delete;
@ -593,6 +609,8 @@ public:
auto copy() const -> own<Trap*>;
auto message() const -> Message;
auto origin() const -> own<Frame*>; // may be null
auto trace() const -> vec<Frame*>; // may be empty, origin first
};