From e2252267d4221fa048f6d01e58b0cfeb472eab6b Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Thu, 7 Oct 2021 08:50:17 +0200 Subject: [PATCH 1/4] feat: v8::StackTrace::CurrentStackTrace() bindings --- src/binding.cc | 5 +++++ src/exception.rs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/binding.cc b/src/binding.cc index 40f215e081..d87d23515c 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1658,6 +1658,11 @@ const v8::Value* v8__ReturnValue__Get(const v8::ReturnValue& self) { return local_to_ptr(self.Get()); } +// Note: StackTraceOptions is deprecated, kDetailed is always used +const v8::StackTrace* v8__StackTrace__CurrentStackTrace(v8::Isolate* isolate, int frame_limit) { + return local_to_ptr(v8::StackTrace::CurrentStackTrace(isolate, frame_limit)); +} + int v8__StackTrace__GetFrameCount(const v8::StackTrace& self) { return self.GetFrameCount(); } diff --git a/src/exception.rs b/src/exception.rs index 9606c2c06c..3c2bffecfc 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -32,6 +32,10 @@ extern "C" { fn v8__Message__IsOpaque(this: *const Message) -> bool; fn v8__Message__GetStackTrace(this: *const Message) -> *const StackTrace; + fn v8__StackTrace__CurrentStackTrace( + isolate: *mut Isolate, + frame_limit: int, + ) -> *const StackTrace; fn v8__StackTrace__GetFrameCount(this: *const StackTrace) -> int; fn v8__StackTrace__GetFrame( this: *const StackTrace, @@ -67,6 +71,18 @@ extern "C" { } impl StackTrace { + /// Grab a snapshot of the current JavaScript execution stack. + pub fn current_stack_trace<'s>( + scope: &mut HandleScope<'s>, + frame_limit: i32, + ) -> Option> { + unsafe { + scope.cast_local(|sd| { + v8__StackTrace__CurrentStackTrace(sd.get_isolate_ptr(), frame_limit) + }) + } + } + /// Returns the number of StackFrames. pub fn get_frame_count(&self) -> usize { unsafe { v8__StackTrace__GetFrameCount(self) as usize } From 052b35af83a4f16120b74d7cca13878ca9d577c9 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Tue, 28 Dec 2021 12:59:05 +0100 Subject: [PATCH 2/4] fmt c++ v8__StackTrace__CurrentStackTrace --- src/binding.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/binding.cc b/src/binding.cc index d87d23515c..6150af600d 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1659,7 +1659,8 @@ const v8::Value* v8__ReturnValue__Get(const v8::ReturnValue& self) { } // Note: StackTraceOptions is deprecated, kDetailed is always used -const v8::StackTrace* v8__StackTrace__CurrentStackTrace(v8::Isolate* isolate, int frame_limit) { +const v8::StackTrace* v8__StackTrace__CurrentStackTrace(v8::Isolate* isolate, + int frame_limit) { return local_to_ptr(v8::StackTrace::CurrentStackTrace(isolate, frame_limit)); } From 77288e91ea453d8c269887b6d16842d8caeb10a3 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Tue, 28 Dec 2021 13:18:51 +0100 Subject: [PATCH 3/4] usize frame_limit cast to C++ int --- src/exception.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/exception.rs b/src/exception.rs index 3c2bffecfc..7fa65c9d14 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,5 +1,7 @@ #![allow(non_snake_case)] +use std::convert::TryInto; + use crate::isolate::Isolate; use crate::support::int; use crate::Context; @@ -74,8 +76,9 @@ impl StackTrace { /// Grab a snapshot of the current JavaScript execution stack. pub fn current_stack_trace<'s>( scope: &mut HandleScope<'s>, - frame_limit: i32, + frame_limit: usize, ) -> Option> { + let frame_limit = frame_limit.try_into().ok()?; unsafe { scope.cast_local(|sd| { v8__StackTrace__CurrentStackTrace(sd.get_isolate_ptr(), frame_limit) From 399dd0c1a8a76ba3a557fcc136cd6d97095f0804 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Tue, 28 Dec 2021 16:18:46 +0100 Subject: [PATCH 4/4] test --- tests/test_api.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/test_api.rs b/tests/test_api.rs index 8154dd7e95..ffb249a1d7 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -5675,3 +5675,50 @@ fn backing_store_from_empty_boxed_slice() { .make_shared(); let _ = v8::ArrayBuffer::with_backing_store(&mut scope, &store); } + +#[test] +fn current_stack_trace() { + // Setup isolate + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + // A simple JS-facing function that returns its call depth, max of 5 + fn call_depth( + scope: &mut v8::HandleScope, + _args: v8::FunctionCallbackArguments, + mut rv: v8::ReturnValue, + ) { + let stack = v8::StackTrace::current_stack_trace(scope, 5).unwrap(); + let count = stack.get_frame_count(); + rv.set(v8::Integer::new(scope, count as i32).into()) + } + + let key = v8::String::new(scope, "callDepth").unwrap(); + let tmpl = v8::FunctionTemplate::new(scope, call_depth); + let func = tmpl.get_function(scope).unwrap(); + let global = context.global(scope); + global.set(scope, key.into(), func.into()); + + let top_level = eval(scope, "callDepth()") + .unwrap() + .uint32_value(scope) + .unwrap(); + assert_eq!(top_level, 1); + + let nested = eval(scope, "(_ => (_ => callDepth())())()") + .unwrap() + .uint32_value(scope) + .unwrap(); + assert_eq!(nested, 3); + + let too_deep = eval( + scope, + "(_ => (_ => (_ => (_ => (_ => (_ => (_ => callDepth())())())())())())())()", + ) + .unwrap() + .uint32_value(scope) + .unwrap(); + assert_eq!(too_deep, 5); +}