DelSkayn / rquickjs

High level bindings to the quickjs javascript engine
MIT License
434 stars 59 forks source link

call the `Object.keys` function with rust struct ,retrun a empty array #277

Closed LXS2000 closed 3 months ago

LXS2000 commented 3 months ago
#[rquickjs::class(rename_all="camelCase")]
#[derive(Debug, Trace, Clone)]
pub struct UriParser {
    #[qjs(get, set, enumerable, configurable)]
    pub scheme: String,
    #[qjs(get, set, enumerable, configurable)]
    pub authority: String,
    #[qjs(get, set, enumerable, configurable)]
    pub host: String,
    #[qjs(get, set, enumerable, configurable)]
    pub port: u16,
    #[qjs(get, set, enumerable, configurable)]
    pub path: String,
    #[qjs(get, set, enumerable, configurable)]
    pub params: HashMap<String, String>,
}

is the struct as above definition has any problem? in addition:

let uri=new UriParser('http://xxx.com');
uri.host="aaa";
uri.host==="aaa";//true
//But in the rust environment, the value of host property  has not changed and has always been 'xxx.com'
DelSkayn commented 3 months ago

This behaviour happens because the properties defined with the class attribute are defined on the prototype of the class not on the instance.

You can kinda think of the definition in javascript as being:

class UriParser{
    get scheme(){
        //impl
    }
    set scheme(v){
        //impl
    }
    //etc..
}

Properties defined on the prototype of an object don't show up in Object.keys.

With regards to setters not working, I can't seem to replicate the problem. For me the following code runs without problem:

use rquickjs::{
    class::{OwnedBorrow, Trace},
    CatchResultExt, Class, Context, Runtime,
};
use std::collections::HashMap;

#[rquickjs::class(rename_all = "camelCase")]
#[derive(Debug, Trace, Clone, Default)]
pub struct UriParser {
    #[qjs(get, set, enumerable, configurable)]
    pub scheme: String,
    #[qjs(get, set, enumerable, configurable)]
    pub authority: String,
    #[qjs(get, set, enumerable, configurable)]
    pub host: String,
    #[qjs(get, set, enumerable, configurable)]
    pub port: u16,
    #[qjs(get, set, enumerable, configurable)]
    pub path: String,
    #[qjs(get, set, enumerable, configurable)]
    pub params: HashMap<String, String>,
}

#[rquickjs::methods]
impl UriParser {
    #[qjs(constructor)]
    pub fn new() -> Self {
        // just a simple impl
        Default::default()
    }
}

fn main() {
    let rt = Runtime::new().unwrap();
    let ctx = Context::full(&rt).unwrap();

    ctx.with(|ctx| {
        Class::<UriParser>::define(&ctx.globals()).unwrap();

        ctx.eval::<(), _>(
            r#"
                globalThis.test = new UriParser();
                test.host = "foo";
            "#,
        )
        .catch(&ctx)
        .unwrap();

        let uri = ctx
            .globals()
            .get::<_, OwnedBorrow<UriParser>>("test")
            .catch(&ctx)
            .unwrap();
        assert_eq!(uri.host, "foo")
    });
}