DelSkayn / rquickjs

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

Defining static method without constructor #216

Closed stevefan1999-personal closed 9 months ago

stevefan1999-personal commented 9 months ago

Currently this doesn't work:

#[rquickjs::methods]
impl TcpListenerWrapper {

    #[qjs(get, enumerable)]
    pub fn local_addr(&self) -> rquickjs::Result<SocketAddrWrapper> {
        Ok(self.deref().local_addr()?.into())
    }

    pub async fn accept(
        self,
        ctx: Ctx<'_>,
    ) -> rquickjs::Result<List<(TcpStreamWrapper, SocketAddrWrapper)>> {
        let (stream, addr) = self
            .deref()
            .accept()
            .with_cancellation(&ctx.worlds_end())
            .await??;
        let stream = Arc::new(RwLock::new(stream));
        Ok(List((stream.into(), addr.into())))
    }

    #[qjs(static)]
    pub async fn listen(addr: String, ctx: Ctx<'_>) -> rquickjs::Result<Self> {
        let listener = TcpListener::bind(addr)
            .with_cancellation(&ctx.worlds_end())
            .await??;
        Ok(Arc::new(listener).into())
    }
}

With:

import { TcpListener } from 'den:networking'

const socket = await TcpListener.listen("localhost:8080") // Error: Could not find export 'listen' in module 'den:networking'

It seems like I don't have an option to set the listen method to the exported class prototype, unless I construct it first. Also it is quite counterintuitive to see that static method needs a constructor first.

DelSkayn commented 9 months ago

Static methods in JavaScript are methods which are defined on the constructor instead of the prototype. So if a class doesn't have a constructor it would not make sense for it to have static methods.

In JavaScript a class which does not define a constructor will still have one, it will just return an empty object. In rust we don't always have a way to default initialize an object so I don't create a constructor object if there isn't one defined.

The above rust code should have given a warning that static methods don't work without a constructor.

stevefan1999-personal commented 9 months ago

Static methods in JavaScript are methods which are defined on the constructor instead of the prototype. So if a class doesn't have a constructor it would not make sense for it to have static methods.

In JavaScript a class which does not define a constructor will still have one, it will just return an empty object. In rust we don't always have a way to default initialize an object so I don't create a constructor object if there isn't one defined.

The above rust code should have given a warning that static methods don't work without a constructor.

What would be the alternative? So if I want to have:

class TcpListener {
  static connect(...) { ... }
}

How do I express this in rquickjs?

I quickly added an empty constructor:

#[rquickjs::methods]
impl TcpListenerWrapper {
    #[qjs(constructor)]
    pub fn new() {
    }

    #[qjs(static)]
    pub async fn listen(addr: String, ctx: Ctx<'_>) -> rquickjs::Result<Self> {
        let listener = TcpListener::bind(addr)
            .with_cancellation(&ctx.worlds_end())
            .await??;
        Ok(Arc::new(listener).into())
    }
}

No diceIt's working. Thanks for the tip!