@@ -153,11 +153,25 @@ pub fn to_js_error(cx: Ctx, e: Error) -> JSError {
153153
154154/// Converts the JavaScript value to a string, replacing any invalid UTF-8 sequences with the
155155/// Unicode replacement character (U+FFFD).
156+ ///
157+ /// Returns [`JSError::Exception`] if the engine runs out of memory while
158+ /// converting the string; the out-of-memory exception is left pending on the
159+ /// context.
156160// TODO: Upstream this?
157- pub fn to_string_lossy < ' js > ( cx : & Ctx < ' js > , string : & JSString < ' js > , error : JSError ) -> String {
161+ pub fn to_string_lossy < ' js > (
162+ cx : & Ctx < ' js > ,
163+ string : & JSString < ' js > ,
164+ error : JSError ,
165+ ) -> rquickjs:: Result < String > {
158166 let mut len: qjs:: size_t = 0 ;
159167 let ptr =
160168 unsafe { qjs:: JS_ToCStringLen2 ( cx. as_raw ( ) . as_ptr ( ) , & mut len, string. as_raw ( ) , false ) } ;
169+ if ptr. is_null ( ) {
170+ // The conversion only fails when the engine cannot allocate the
171+ // buffer for the converted copy, in which case it has thrown an
172+ // out-of-memory exception.
173+ return Err ( JSError :: Exception ) ;
174+ }
161175 let buffer = unsafe { std:: slice:: from_raw_parts ( ptr as * const u8 , len as usize ) } ;
162176
163177 // The error here *must* be a Utf8 error; the `JSString::to_string()` may
@@ -203,7 +217,10 @@ pub fn to_string_lossy<'js>(cx: &Ctx<'js>, string: &JSString<'js>, error: JSErro
203217 Err ( e) => utf8_error = e,
204218 }
205219 }
206- res
220+ // The engine allocates a new reference-counted string for the converted
221+ // copy on every call; release it now that `res` owns the decoded contents.
222+ unsafe { qjs:: JS_FreeCString ( cx. as_raw ( ) . as_ptr ( ) , ptr) } ;
223+ Ok ( res)
207224}
208225
209226/// Retrieves the string representation of a JavaScript value.
@@ -212,17 +229,16 @@ pub fn val_to_string<'js>(this: &Ctx<'js>, val: Value<'js>) -> Result<String> {
212229 if let Some ( description) = symbol. description ( ) ?. into_string ( ) {
213230 let description = description
214231 . to_string ( )
215- . unwrap_or_else ( |e| to_string_lossy ( this, & description, e) ) ;
232+ . or_else ( |e| to_string_lossy ( this, & description, e) ) ? ;
216233 Ok ( format ! ( "Symbol({description})" ) )
217234 } else {
218235 Ok ( "Symbol()" . into ( ) )
219236 }
220237 } else {
221- let stringified = <convert:: Coerced < JSString > >:: from_js ( this, val) . map ( |string| {
222- string
223- . to_string ( )
224- . unwrap_or_else ( |e| to_string_lossy ( this, & string. 0 , e) )
225- } ) ?;
238+ let string = <convert:: Coerced < JSString > >:: from_js ( this, val) ?;
239+ let stringified = string
240+ . to_string ( )
241+ . or_else ( |e| to_string_lossy ( this, & string. 0 , e) ) ?;
226242 Ok ( stringified)
227243 }
228244}
0 commit comments