Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register custom Class #113

Open
jameslahm opened this issue Jul 21, 2021 · 8 comments
Open

Register custom Class #113

jameslahm opened this issue Jul 21, 2021 · 8 comments
Assignees

Comments

@jameslahm
Copy link
Member

We could expose an API, for example, register_class to register a custom class in the global object.

@jameslahm jameslahm self-assigned this Jul 21, 2021
@andrieshiemstra
Copy link
Contributor

Would be very helpfull to me also for registering classes like Request and Response for fetch api

@playXE
Copy link
Collaborator

playXE commented Jul 21, 2021

Yeah it would be nice. I think we need lots of APIs to make it easy to use Starlight. Like add builder like API for adding new properties to objects or easier API to declare accessors and methods on the objects.

@jameslahm
Copy link
Member Author

Yeah it would be nice. I think we need lots of APIs to make it easy to use Starlight. Like add builder like API for adding new properties to objects or easier API to declare accessors and methods on the objects.

I am going to work on this. 🤣

@jameslahm
Copy link
Member Author

We now support register custom class, and also snapshot for custom class

use std::mem::ManuallyDrop;

use starlight::{
    def_native_method, def_native_property, define_jsclass,
    jsrt::VM_NATIVE_REFERENCES,
    prelude::*,
    prelude::{JsClass, Options},
    vm::context::Context,
    Platform,
};

pub struct Person {
    age: i32,
}

pub fn person_constructor(ctx: GcPointer<Context>, args: &Arguments) -> Result<JsValue, JsValue> {
    let mut res = 0;
    if args.size() != 0 {
        res = args.at(0).to_int32(ctx)?;
    }
    Ok(JsValue::new(Person::new(ctx, res)))
}

impl Person {
    pub fn new(ctx: GcPointer<Context>, age: i32) -> GcPointer<JsObject> {
        let structure = ctx.global_data().get_structure("Person".intern()).unwrap();
        let obj = JsObject::new(ctx, &structure, Self::class(), ObjectTag::Ordinary);
        *obj.data::<Self>() = ManuallyDrop::new(Self { age });
        obj
    }

    pub fn say_hello(ctx: GcPointer<Context>, args: &Arguments) -> Result<JsValue, JsValue> {
        let mut obj = args.this.to_object(ctx).unwrap();
        let person = obj.as_data::<Person>();
        println!("Hello {}", person.age);
        Ok(JsValue::UNDEFINED)
    }
}

impl JsClass for Person {
    fn class() -> &'static starlight::prelude::Class {
        define_jsclass!(Person, Person)
    }

    fn init(mut ctx: GcPointer<Context>) -> Result<(), JsValue> {
        let obj_proto = ctx.global_data().get_object_prototype();
        let structure = Structure::new_unique_indexed(ctx, Some(obj_proto), false);
        let mut proto = JsObject::new(ctx, &structure, Self::class(), ObjectTag::Ordinary);

        let structure = Structure::new_indexed(ctx, Some(proto), false);
        let mut constructor = JsNativeFunction::new(ctx, "Person".intern(), person_constructor, 1);

        def_native_property!(ctx, constructor, prototype, proto)?;
        def_native_property!(ctx, proto, constructor, constructor)?;
        def_native_method!(ctx, proto, sayHello, Person::say_hello, 0)?;

        ctx.register_structure("Person".intern(), structure);

        let mut global_object = ctx.global_object();
        def_native_property!(ctx, global_object, Person, constructor)?;

        Ok(())
    }
}

fn main() {
    Platform::initialize();
    let mut runtime = Platform::new_runtime(Options::default(), None);
    let mut ctx = runtime.new_context();

    ctx.register_class::<Person>().unwrap();

    match ctx.eval("let person = new Person(10);person.sayHello()") {
        Err(e) => {
            println!("{}", e.to_string(ctx).unwrap());
        }
        _ => {}
    }

    unsafe {
        VM_NATIVE_REFERENCES.push(Person::say_hello as _);
        VM_NATIVE_REFERENCES.push(person_constructor as _);
        VM_NATIVE_REFERENCES.push(Person::class() as *const _ as _);
    }

    let buf = Snapshot::take_context(false, &mut runtime, ctx, |_, _| {}).buffer;
    let mut ctx = Deserializer::deserialize_context(&mut runtime, false, &buf);
    match ctx.eval("let person = new Person(10);person.sayHello()") {
        Err(e) => {
            println!("{}", e.to_string(ctx).unwrap());
        }
        _ => {}
    }
}

@jameslahm
Copy link
Member Author

but it's still a little complex now. I am going to make it simpler.

@andrieshiemstra
Copy link
Contributor

Please also consider how to remove a custom class.

E.g. when registering a custom class for a native module, e.g. import('my_native_mysql_con') i want to register custom Connection/Resultset classes but i also want to remove them when the module is no longer needed.

Keep up the great work so far!

@jameslahm
Copy link
Member Author

Please also consider how to remove a custom class.

E.g. when registering a custom class for a native module, e.g. import('my_native_mysql_con') i want to register custom Connection/Resultset classes but i also want to remove them when the module is no longer needed.

Keep up the great work so far!

That'll be wonderful. I'll try ~

@jameslahm
Copy link
Member Author

It's very simple to register custom class now

pub struct Person {
    age: i32,
}

impl ClassConstructor for Person {
    fn constructor(
        _ctx: GcPointer<Context>,
        args: &starlight::prelude::Arguments<'_>,
    ) -> Result<Self, JsValue> {
        Ok(Person {
            age: args.at(0).get_int32(),
        })
    }
}

impl Person {
    pub fn say_hello(ctx: GcPointer<Context>, args: &Arguments) -> Result<JsValue, JsValue> {
        let mut obj = args.this.to_object(ctx).unwrap();
        let person = obj.as_data::<Person>();
        println!("Hello {}", person.age);
        Ok(JsValue::UNDEFINED)
    }
}

impl JsClass for Person {
    fn class() -> &'static starlight::prelude::Class {
        define_jsclass!(Person, Person)
    }

    fn init(builder: &mut ClassBuilder) -> Result<(), JsValue> {
        builder.method("sayHello", Person::say_hello, 0)?;
        Ok(())
    }
}

fn main() {
    Platform::initialize();
    let mut runtime = Platform::new_runtime(Options::default(), None);
    let mut ctx = runtime.new_context();

    ctx.register_class::<Person>().unwrap();

    match ctx.eval("let person = new Person(10);person.sayHello()") {
        Err(e) => {
            println!("{}", e.to_string(ctx).unwrap());
        }
        _ => {}
    }
    unsafe {
        VM_NATIVE_REFERENCES.push(Person::say_hello as _);
    }
    let buf = Snapshot::take_context(false, &mut runtime, ctx, |_, _| {}).buffer;
    let mut ctx = Deserializer::deserialize_context(&mut runtime, false, &buf);
    match ctx.eval("let person = new Person(10);person.sayHello()") {
        Err(e) => {
            println!("{}", e.to_string(ctx).unwrap());
        }
        _ => {}
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants