boa icon indicating copy to clipboard operation
boa copied to clipboard

`toString` missing on `TryIntoJs`

Open xbwwj opened this issue 5 months ago • 1 comments

Describe the bug

toString method is missing from value constructed with TryIntoJs

To Reproduce

Minimal example:

use boa_engine::{Context, IntoJsFunctionCopied, Source, js_string, value::TryIntoJs};

fn main() {
    let mut context = Context::default();
    let get_foo_bar = get_foo_bar.into_js_function_copied(&mut context);
    context
        .register_global_builtin_callable(js_string!("getFooBar"), 0, get_foo_bar)
        .unwrap();
    let value = context.eval(Source::from_bytes("getFooBar()")).unwrap();
    println!(
        "{}",
        value
            .to_string(&mut context)
            .unwrap()
            .to_std_string_escaped()
    );
}

#[derive(TryIntoJs)]
struct FooBar {
    foo: usize,
    bar: usize,
}

fn get_foo_bar() -> FooBar {
    FooBar { foo: 0, bar: 1 }
}

The error message:

thread 'main' panicked at delight_cli/src/main.rs:47:14:
called `Result::unwrap()` on an `Err` value: JsError { inner: Native(JsNativeError { kind: Type, message: "cannot convert object to primitive value", cause: None, .. }), backtrace: None }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Expected behavior

It works without throwing if using js_object:

fn get_foo_bar(context: &mut Context) -> JsObject {
    // FooBar { foo: 0, bar: 1 }
    js_object!({"foo": 0, "bar": 1}, context)
}
// [object Object]

Build environment

  • OS: NixOS Linux
  • Version: 25.05
  • Target triple: x86_64-unknown-linux-gnu
  • Rustc version: rustc 1.88.0 (6b00bc388 2025-06-23)

Additional context

Using the git version of boa:

[[package]]
name = "boa_engine"
version = "0.20.0"
source = "git+https://github.com/boa-dev/boa#488ef539f70c7e80d86b568f5d60e6822eb8f261"
...

xbwwj avatar Aug 03 '25 08:08 xbwwj

I tried replicating this and got the same result. With some additional print statements I also was able to show this:

sfb: Source { reader: UTF8Input { input: Bytes { inner: [103, 101, 116, 70, 111, 111, 66, 97, 114, 40, 41] } }, path: None } value Ok(JsValue(Object(("boa_engine::object::jsobject::ErasedObjectData") 0x55F1A854D6F8)))

I am not sure what this means but hopefully this info helps the next person to touch this issue.

The prints in question:

fn main() {
    let mut context = Context::default();
    let get_foo_bar = get_foo_bar.into_js_function_copied(&mut context);
    context
        .register_global_builtin_callable(js_string!("getFooBar"), 0, get_foo_bar)
        .unwrap();
    let sfb = Source::from_bytes("getFooBar()");
    println!("sfb: {:?}", sfb);
    let value = context.eval(sfb).unwrap();
    println!(
        "{}",
        value
            .to_string(&mut context)
            .unwrap()
            .to_std_string_escaped()
    );
}

AdithyaLaakso avatar Aug 13 '25 21:08 AdithyaLaakso