Skip to content

Question about proper way to marshal multi-parameter type constructors #719

Closed
@tyler-conrad

Description

I've been trying to marshall a type constructor with 2 parameters and it appears that the stack is not aligning correctly. I'm probably not using the Getable and Pushable traits correctly, any pointers would be great. Here is some example code outlining the issue:

#[macro_use]
extern crate gluon_codegen;

#[macro_use]
extern crate gluon_vm;

use gluon::{
    base::types::ArcType,
    import::add_extern_module,
    new_vm,
    vm::{
        api::{ActiveThread, Getable, Pushable, UserdataValue, ValueRef, VmType},
        internal::Value,
        thread::Thread,
        ExternModule, Result, Variants,
    },
    Compiler,
};

#[derive(Debug, Clone)]
struct ExternalValue(f64);

#[derive(Debug, Clone, Userdata)]
struct ValueWrapper(ExternalValue);

impl ValueWrapper {
    fn new(_: ()) -> ValueWrapper {
        ValueWrapper(ExternalValue(0.0))
    }

    fn load(vm: &Thread) -> Result<ExternModule> {
        vm.register_type::<ValueWrapper>("Value", &[])?;

        ExternModule::new(
            vm,
            record! {
                type Value => ValueWrapper,
                new => primitive!(1, ValueWrapper::new),
            },
        )
    }
}

#[derive(Debug)]
enum ExternalEnumOne {
    One,
    Two(f64, f64),
}

#[derive(Debug)]
struct EnumOneWrapper(ExternalEnumOne);

impl VmType for EnumOneWrapper {
    type Type = Self;

    fn make_type(vm: &Thread) -> ArcType {
        vm.find_type_info("example.types.EnumOne")
            .expect("Could not find type EnumOneWrapper")
            .into_type()
    }
}

impl<'vm> Pushable<'vm> for EnumOneWrapper {
    fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
        match self.0 {
            ExternalEnumOne::One => context.push(Value::tag(0)),
            ExternalEnumOne::Two(one, two) => {
                one.push(context)?;
                two.push(context)?;
                let thread = context.thread();
                context.context().push_new_data(thread, 1, 2).unwrap();
            }
        };
        Ok(())
    }
}

impl<'vm, 'value> Getable<'vm, 'value> for EnumOneWrapper {
    fn from_value(_vm: &'vm Thread, data: Variants<'value>) -> EnumOneWrapper {
        match data.as_ref() {
            ValueRef::Data(data) => match data.tag() {
                0 => EnumOneWrapper(ExternalEnumOne::One),
                1 => EnumOneWrapper(ExternalEnumOne::Two(
                    match data.get(0).unwrap() {
                        ValueRef::Float(value) => value,
                        _ => panic!("Failed to marshall ExternalEnumOne::Two"),
                    },
                    match data.get(1).unwrap() {
                        ValueRef::Float(value) => value,
                        _ => panic!("Failed to marshall ExternalEnumOne::Two"),
                    },
                )),
                _ => panic!("Invalid VmTag for EnumOneWrapper."),
            },
            _ => panic!("EnumOneWrapper is not a complex type."),
        }
    }
}

#[derive(Debug)]
enum ExternalEnumTwo {
    A(ExternalEnumOne),
    B {
        value: ExternalValue,
        enum_one: ExternalEnumOne,
    },
}

#[derive(Debug)]
struct EnumTwoWrapper(ExternalEnumTwo);

impl EnumTwoWrapper {
    fn id(self) -> EnumTwoWrapper {
        self
    }

    fn load(vm: &Thread) -> Result<ExternModule> {
        ExternModule::new(
            vm,
            record! {
                id => primitive!(1, EnumTwoWrapper::id),
            },
        )
    }
}

impl VmType for EnumTwoWrapper {
    type Type = Self;

    fn make_type(vm: &Thread) -> ArcType {
        vm.find_type_info("example.types.EnumTwo")
            .expect("Could not find type EnumTwoWrapper")
            .into_type()
    }
}

impl<'vm> Pushable<'vm> for EnumTwoWrapper {
    fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
        match self.0 {
            ExternalEnumTwo::A(value) => {
                EnumOneWrapper(value).push(context)?;
                let thread = context.thread();
                context.context().push_new_data(thread, 0, 1).unwrap();
            }
            ExternalEnumTwo::B { value, enum_one } => {
                (record! {
                    value => ValueWrapper(value),
                    enum_one => EnumOneWrapper(enum_one)
                })
                .push(context)?;
                let thread = context.thread();
                context.context().push_new_data(thread, 1, 1).unwrap();
            }
        };
        Ok(())
    }
}

impl<'vm, 'value> Getable<'vm, 'value> for EnumTwoWrapper {
    fn from_value(vm: &'vm Thread, data: Variants<'value>) -> EnumTwoWrapper {
        match data.as_ref() {
            ValueRef::Data(data) => match data.tag() {
                0 => EnumTwoWrapper(ExternalEnumTwo::A(
                    EnumOneWrapper::from_value(vm, data.get_variant(0).unwrap()).0,
                )),
                1 => match data.get(0).unwrap() {
                    ValueRef::Data(data) => {
                        let UserdataValue(ValueWrapper(external_value)) =
                            <UserdataValue<ValueWrapper>>::from_value(
                                vm,
                                data.lookup_field(vm, "value").unwrap(),
                            );
                        EnumTwoWrapper(ExternalEnumTwo::B {
                            value: external_value,
                            enum_one: EnumOneWrapper::from_value(
                                vm,
                                data.lookup_field(vm, "enum_one").unwrap(),
                            )
                            .0,
                        })
                    }
                    _ => panic!("Unable to marshall ExternalEnumTwo::B."),
                },
                _ => panic!("Invalid VmTag for EnumTwoWrapper."),
            },
            _ => panic!("EnumTwoWrapper is not a complex type."),
        }
    }
}

fn main() {
    let vm = new_vm();
    let mut compiler = Compiler::new();

    add_extern_module(&vm, "example.value", ValueWrapper::load);

    let src = r#"
let { Value } = import! example.value

type EnumOne = | One | Two Float Float
type EnumTwo = | A EnumOne | B { value: Value, enum_one: EnumOne }

{
    EnumOne,
    EnumTwo,
}
"#;
    compiler.load_script(&vm, "example.types", src).unwrap();
    add_extern_module(&vm, "example.enum_two.prim", EnumTwoWrapper::load);

    let script1 = r#"
let { EnumOne, EnumTwo } = import! example.types
let { id } = import! example.enum_two.prim
id (A One)
"#;
    let (value, _) = compiler
        .run_expr::<EnumTwoWrapper>(&vm, "example1", script1)
        .unwrap();
    println!("{:#?}", value);

    let script2 = r#"
let { new } = import! example.value
let { EnumOne, EnumTwo } = import! example.types
let { id } = import! example.enum_two.prim
id (B { value = new (), enum_one = One })
"#;
    let (value, _) = compiler
        .run_expr::<EnumTwoWrapper>(&vm, "example2", script2)
        .unwrap();
    println!("{:#?}", value);

    let script3 = r#"
let { new } = import! example.value
let { EnumOne, EnumTwo } = import! example.types
let { id } = import! example.enum_two.prim
id (B { value = new (), enum_one = Two 0.0 0.0 })
"#;
    let (value, _) = compiler
        .run_expr::<EnumTwoWrapper>(&vm, "example3", script3)
        .unwrap();
    println!("{:#?}", value);
}

I get the error 'ValueRef is not an Userdata' and it looks like one of the arguments passed to the 'Two' constructor is causing a misalignment of the stack. I modified gluon locally to print out the data that is trying to get interpreted as Userdata and it shows Float(0.0). Here is the output of running the script:

EnumTwoWrapper(
    A(
        One
    )
)
EnumTwoWrapper(
    B {
        value: ExternalValue(
            0.0
        ),
        enum_one: One
    }
)
thread 'main' panicked at 'ValueRef is not an Userdata: Float(
    0.0
)', /home/tyler/Desktop/vulkan/gluon/vm/src/api/mod.rs:629:22
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at src/libstd/sys_common/backtrace.rs:59
             at src/libstd/panicking.rs:211
   3: std::panicking::default_hook
             at src/libstd/panicking.rs:227
   4: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:491
   5: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:398
   6: std::panicking::begin_panic_fmt
             at src/libstd/panicking.rs:353
   7: <&'value T as gluon_vm::api::Getable<'vm, 'value>>::from_value
             at ./<::std::macros::panic macros>:8
   8: <gluon_vm::api::UserdataValue<T> as gluon_vm::api::Getable<'vm, 'value>>::from_value
             at /home/tyler/Desktop/vulkan/gluon/vm/src/api/mod.rs:601
   9: <marshall_test::EnumTwoWrapper as gluon_vm::api::Getable<'vm, 'value>>::from_value
             at src/main.rs:169
  10: gluon::Compiler::run_expr::{{closure}}
             at /home/tyler/Desktop/vulkan/gluon/src/lib.rs:602
  11: <futures::future::and_then::AndThen<A, B, F> as futures::future::Future>::poll::{{closure}}::{{closure}}
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/future/and_then.rs:34
  12: <core::result::Result<T, E>>::map
             at /rustc/9fda7c2237db910e41d6a712e9a2139b352e558b/src/libcore/result.rs:468
  13: <futures::future::and_then::AndThen<A, B, F> as futures::future::Future>::poll::{{closure}}
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/future/and_then.rs:33
  14: <futures::future::chain::Chain<A, B, C>>::poll
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/future/chain.rs:39
  15: <futures::future::and_then::AndThen<A, B, F> as futures::future::Future>::poll
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/future/and_then.rs:32
  16: <futures::task_impl::Spawn<T>>::poll_future_notify::{{closure}}
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/mod.rs:329
  17: <futures::task_impl::Spawn<T>>::enter::{{closure}}
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/mod.rs:399
  18: futures::task_impl::std::set
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/std/mod.rs:78
  19: <futures::task_impl::Spawn<T>>::enter
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/mod.rs:399
  20: <futures::task_impl::Spawn<T>>::poll_fn_notify
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/mod.rs:291
  21: <futures::task_impl::Spawn<T>>::poll_future_notify
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/mod.rs:329
  22: futures::task_impl::std::<impl futures::task_impl::Spawn<F>>::wait_future::{{closure}}
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/std/mod.rs:231
  23: futures::task_impl::std::ThreadNotify::with_current::{{closure}}
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/std/mod.rs:478
  24: <std::thread::local::LocalKey<T>>::try_with
             at /rustc/9fda7c2237db910e41d6a712e9a2139b352e558b/src/libstd/thread/local.rs:309
  25: <std::thread::local::LocalKey<T>>::with
             at /rustc/9fda7c2237db910e41d6a712e9a2139b352e558b/src/libstd/thread/local.rs:255
  26: futures::task_impl::std::ThreadNotify::with_current
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/std/mod.rs:478
  27: futures::task_impl::std::<impl futures::task_impl::Spawn<F>>::wait_future
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/task_impl/std/mod.rs:228
  28: futures::future::Future::wait
             at /home/tyler/.cargo/registry/src/github.yoyoweb123.workers.dev-1ecc6299db9ec823/futures-0.1.26/src/future/mod.rs:299
  29: gluon::Compiler::run_expr
             at /home/tyler/Desktop/vulkan/gluon/src/lib.rs:598
  30: marshall_test::main
             at src/main.rs:238
  31: std::rt::lang_start::{{closure}}
             at /rustc/9fda7c2237db910e41d6a712e9a2139b352e558b/src/libstd/rt.rs:74
  32: std::panicking::try::do_call
             at src/libstd/rt.rs:59
             at src/libstd/panicking.rs:310
  33: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:102
  34: std::rt::lang_start_internal
             at src/libstd/panicking.rs:289
             at src/libstd/panic.rs:398
             at src/libstd/rt.rs:58
  35: std::rt::lang_start
             at /rustc/9fda7c2237db910e41d6a712e9a2139b352e558b/src/libstd/rt.rs:74
  36: main
  37: __libc_start_main
  38: _start

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions