Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upSafe, straightforward API for capturing JS stacks #381
Conversation
…stacks
| // do we want to do the actual logic of conversion in this method or maybe in constructor and just return cached result? | ||
| unsafe { | ||
| let stack_handle = self.stack.handle(); | ||
| let nullptr = 0 as *const ::std::os::raw::c_char; |
This comment has been minimized.
This comment has been minimized.
emilio
Nov 25, 2017
Member
nit: rust has ptr::null() and is_null, so you should use them instead probably.
|
Good work! |
| } | ||
|
|
||
| impl<'a> CapturedJSStack<'a> { | ||
| pub fn new(cx: *mut JSContext, mut guard: RootedGuard<'a, *mut JSObject>, max_frame_count: u32) -> Option<Self> { // or maybe another name, for example "capture"? |
This comment has been minimized.
This comment has been minimized.
jdm
Nov 27, 2017
Member
This needs to be unsafe, because it accepts a raw pointer argument. I think new is a fine name. However, let's make max_frame_count an Option value and use unwrap_or(0) to make it easier to use.
|
|
||
| impl<'a> CapturedJSStack<'a> { | ||
| pub fn new(cx: *mut JSContext, mut guard: RootedGuard<'a, *mut JSObject>, max_frame_count: u32) -> Option<Self> { // or maybe another name, for example "capture"? | ||
| let obj_handle = guard.handle_mut(); // if nullptr, return None |
This comment has been minimized.
This comment has been minimized.
| // TODO where errors can occur? some example of non trivial error handling? | ||
| // TODO how to write tests for this code? Including the problem of obtaining some stack trace | ||
| // do we want to do the actual logic of conversion in this method or maybe in constructor and just return cached result? | ||
| unsafe { // since almost everything here is unsafe, is it okay to wrap it like this? |
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| // wanted somehow to just get the utf-16 string and use String::from_utf16_lossy | ||
| // but JS_GetTwoByteStringCharsAndLength requires some object of strange type (AutoCheckCannotGC) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
mrowqa
Nov 28, 2017
Author
Contributor
Are you sure about passing null pointer to the JS_GetTwoByteStringCharsAndLength? In C++ it requires a reference: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_Reference/JS_GetLatin1StringCharsAndLength. Maybe it doesn't have any internal fields, but isn't it undefined behavior?
I read the comment before definition (https://dxr.mozilla.org/mozilla-central/source/js/public/GCAPI.h?q=AutoCheckCannotGC&redirect_type=direct#852-876), but I am still not sure when we can use it. Does it mean that we can use it when we have rooted all the objects whose handles we are passing to the function?
This comment has been minimized.
This comment has been minimized.
KiChjang
Nov 28, 2017
Member
If you look at where the nogc parameter is actually used in the function body, you'll see that it's passed as a parameter to twoByteChars, and that function doesn't do anything with it, so I'm quite confident that there's no UB going on here.
In fact, the comment before the definition of AutoCheckCannotGC states that this is only used for static rooting hazard analysis, so I don't see how it can go wrong when you send in a null pointer.
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| let mut size = 0 as usize; | ||
| while *str_contents.offset(size as isize) != 0 { // what is some better way of calculating length on raw pointers? |
This comment has been minimized.
This comment has been minimized.
| // do we want to do the actual logic of conversion in this method or maybe in constructor and just return cached result? | ||
| unsafe { // since almost everything here is unsafe, is it okay to wrap it like this? | ||
| let stack_handle = self.stack.handle(); | ||
| rooted!(in(self.cx) let mut js_string = JS_NewStringCopyZ(self.cx, ptr::null())); |
This comment has been minimized.
This comment has been minimized.
| }) | ||
| } | ||
|
|
||
| pub fn as_string(&self, indent: usize) -> Option<String> { |
This comment has been minimized.
This comment has been minimized.
| #[macro_export] | ||
| macro_rules! capture_stack { | ||
| ($cx:expr, $max_frame_count:expr) => { | ||
| rooted!(in($cx) let mut __obj = JS_NewPlainObject($cx)); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
mrowqa
Nov 28, 2017
Author
Contributor
Right now, we can't. Please take a look at the definition of the macro: https://github.com/servo/rust-mozjs/blob/master/src/rust.rs#L454-L464.
Should I expand it and if yes, would you prefer to do it in this PR or separate one? Another problem is that JS_NewPlainObject returns *mut JSObject and JS_NewCopyStringZ, which I used somewhere else, returns *mut JSString. So, what should be the type of the new binding created within the macro?
This comment has been minimized.
This comment has been minimized.
jdm
Nov 28, 2017
Member
Oh, my mistake. It should be possible to use a null pointer instead, though.
This comment has been minimized.
This comment has been minimized.
mrowqa
Nov 28, 2017
Author
Contributor
So, if some function expects a mutable handle for an output argument, we can just pass a handle to a null pointer? Did I get it right?
This comment has been minimized.
This comment has been minimized.
jdm
Nov 28, 2017
Member
Yes. The mutable handle allows the function to initialize it with a meaningful value.
| } | ||
|
|
||
| pub fn as_string(&self, indent: usize) -> Option<String> { | ||
| // TODO where errors can occur? some example of non trivial error handling? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
mrowqa
Nov 28, 2017
•
Author
Contributor
Isn't it guaranted by CaptureCurrentStack after returning true? Is it bad to assume that it will not change since we own the RootedGuard to it?
This comment has been minimized.
This comment has been minimized.
jdm
Nov 28, 2017
Member
Yes, but if a consumer does not use the macros and overwrites the value in the Rooted<*mut JSObject> before calling as_string, it won't work. This is probably an instance where we would get a false value from the code in as_string.
|
|
||
| pub fn as_string(&self, indent: usize) -> Option<String> { | ||
| // TODO where errors can occur? some example of non trivial error handling? | ||
| // TODO how to write tests for this code? Including the problem of obtaining some stack trace |
This comment has been minimized.
This comment has been minimized.
jdm
Nov 27, 2017
Member
We can create an automated test that evaluates some JS that creates several JS stack frames (by defining and invoking functions), then invokes a function that calls into Rust and creates a stack trace. https://github.com/servo/rust-mozjs/blob/master/tests/callback.rs is a good model for this.
|
Now it should be almost complete. When I use |
|
@jdm I have just removed the commented out code with So, since I am really confused what is going on, I have left the version with JS_EncodeStringToUTF8 since it works as expected. The drawback of this is that we have this one extra We can merge this, if it is okay with you. And I am willing to learn more about what is going on internally, what these functions are supposed to do and so on :) |
|
@jdm Travis failed to have installed nightly toolchain, but the code actually works and passes the tests. |
|
With respect to the unexpected output of JS_GetTwoByteStringCharsAndLength, that sounds like what the code at https://github.com/Mrowqa/rust-mozjs/blob/e654bb6c238b659e85ac087adf6568eda20e8648/src/conversions.rs#L510-L513 is intended to address. Perhaps we can try extracting https://github.com/Mrowqa/rust-mozjs/blob/e654bb6c238b659e85ac087adf6568eda20e8648/src/conversions.rs#L511-L519 into a method that we can reuse here? |
|
|
|
@bors-servo: r+ |
|
|
Safe, straightforward API for capturing JS stacks PR for servo/servo#14987 Can someone take a look if it goes in the right direction and also look through my comments? Maybe, I have left some "stupid questions", but I wanted to post my changes and get feedback before weekend - I work from Europe, so here is middle of the night. CC: @jdm <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-mozjs/381) <!-- Reviewable:end -->
|
|
mrowqa commentedNov 24, 2017
•
edited by larsbergstrom
PR for servo/servo#14987
Can someone take a look if it goes in the right direction and also look through my comments? Maybe, I have left some "stupid questions", but I wanted to post my changes and get feedback before weekend - I work from Europe, so here is middle of the night.
CC: @jdm
This change is