witx-codegen 0.11.4

WITX code generator for WebAssembly guest modules.
Documentation
use std::io::Write;

use super::*;

impl AssemblyScriptGenerator {
    fn define_union_member_accessors<T: Write>(
        w: &mut PrettyWriter<T>,
        union_name: &str,
        i: usize,
        member: &ASUnionMember,
    ) -> Result<(), Error> {
        let member_type = member.type_.as_ref();
        match member_type {
            ASType::Void => {
                w.write_line(format!(
                    "static {}(): {} {{",
                    member.name.as_fn(),
                    union_name.as_type()
                ))?
                .indent()?
                .write_line(format!("return {}.new({});", union_name.as_type(), i))?
                .write_line("}")?
                .eob()?;

                w.write_line(format!("set{}(): void {{", member.name.as_fn_suffix()))?
                    .indent()?
                    .write_line(format!("this.tag = {};", i))?
                    .write_line("}")?
                    .eob()?;

                w.write_line(format!("is{}(): bool {{", member.name.as_fn_suffix()))?
                    .indent()?
                    .write_line(format!("return this.tag === {};", i))?
                    .write_line("}")?;
            }
            _ => {
                w.write_line(format!(
                    "static {}(val: {}): {} {{",
                    member.name.as_fn(),
                    member_type.as_lang(),
                    union_name.as_type()
                ))?;
                w.new_block().write_line(format!(
                    "return {}.new({}, val);",
                    union_name.as_type(),
                    i
                ))?;
                w.write_line("}")?.eob()?;

                w.write_line(format!(
                    "set{}(val: {}): void {{",
                    member.name.as_fn_suffix(),
                    member_type.as_lang()
                ))?;
                {
                    w.new_block()
                        .write_line(format!("this.tag = {};", i))?
                        .write_line("this.set(val);")?;
                }
                w.write_line("}")?.eob()?;

                w.write_line(format!("is{}(): bool {{", member.name.as_fn_suffix(),))?
                    .indent()?
                    .write_line(format!("return this.tag === {};", i))?
                    .write_line("}")?
                    .eob()?;

                if member_type.is_nullable() {
                    w.write_line(format!(
                        "get{}(): {} | null {{",
                        member.name.as_fn_suffix(),
                        member_type.as_lang()
                    ))?;
                } else {
                    w.write_line(format!(
                        "get{}(): {} {{",
                        member.name.as_fn_suffix(),
                        member_type.as_lang()
                    ))?;
                }
                {
                    let mut w = w.new_block();
                    if member_type.is_nullable() {
                        w.write_line(format!("if (this.tag !== {}) {{ return null; }}", i))?;
                    }
                    w.write_line(format!("return this.get<{}>();", member_type.as_lang()))?;
                }
                w.write_line("}")?;
            }
        }
        Ok(())
    }

    fn define_union_member<T: Write>(
        w: &mut PrettyWriter<T>,
        union_name: &str,
        i: usize,
        member: &ASUnionMember,
    ) -> Result<(), Error> {
        let member_type = member.type_.as_ref();
        match member_type {
            ASType::Void => {
                w.write_line(format!(
                    "// --- {}: (no associated content) if tag={}",
                    member.name.as_var(),
                    i
                ))?;
            }
            _ => {
                w.write_line(format!(
                    "// --- {}: {} if tag={}",
                    member.name.as_var(),
                    member_type.as_lang(),
                    i
                ))?;
            }
        }
        w.eob()?;
        Self::define_union_member_accessors(w, union_name, i, member)?;
        Ok(())
    }

    pub fn define_as_union<T: Write>(
        w: &mut PrettyWriter<T>,
        name: &str,
        union_: &ASUnion,
    ) -> Result<(), Error> {
        let tag_repr = union_.tag_repr.as_ref();
        w.write_line("// @ts-ignore: decorator")?
            .write_line("@unmanaged")?
            .write_line(format!("export class {} {{", name.as_type()))?;
        {
            let mut w = w.new_block();
            w.write_line(format!("tag: {};", tag_repr.as_lang()))?;
            let pad_len = union_.padding_after_tag;
            for i in 0..(pad_len & 1) {
                w.write_line(format!("private __pad8_{}: u8;", i))?;
            }
            for i in 0..(pad_len & 3) / 2 {
                w.write_line(format!("private __pad16_{}: u16;", i))?;
            }
            for i in 0..(pad_len & 7) / 4 {
                w.write_line(format!("private __pad32_{}: u32;", i))?;
            }
            for i in 0..pad_len / 8 {
                w.write_line(format!("private __pad64_{}: u64;", i))?;
            }
            w.eob()?;

            w.write_line(format!("constructor(tag: {}) {{", tag_repr.as_lang()))?;
            {
                let mut w = w.new_block();
                w.write_line("this.tag = tag;")?.write_line(format!(
                    "memory.fill(changetype<usize>(this) + {}, 0, {});",
                    union_.member_offset, union_.max_member_size
                ))?;
            }
            w.write_line("}")?.eob()?;

            w.write_line("// @ts-ignore: default")?.write_line(format!(
                "static new<T>(tag: {}, val: T = 0): {} {{",
                tag_repr.as_lang(),
                name.as_type()
            ))?;
            {
                let mut w = w.new_block();
                w.write_line(format!("let tu = new {}(tag);", name.as_type()))?
                    .write_line("tu.set(val);")?
                    .write_line("return tu;")?;
            }
            w.write_line("}")?.eob()?;

            w.write_line("get<T>(): T {")?;
            {
                let mut w = w.new_block();
                w.write_line("// @ts-ignore: cast")?
                    .write_line(format!(
                        "let valBuf = changetype<usize>(this) + {};",
                        union_.member_offset
                    ))?
                    .write_line("if (isReference<T>()) {")?;
                w.new_block().write_line("return changetype<T>(valBuf);")?;
                w.write_line("} else {")?;
                w.new_block().write_line("return load<T>(valBuf);")?;
                w.write_line("}")?;
            }
            w.write_line("}")?.eob()?;

            w.write_line("// @ts-ignore: default")?
                .write_line("set<T>(val: T = 0): void {")?;
            {
                let mut w = w.new_block();
                w.write_line("// @ts-ignore: cast")?
                    .write_line(format!(
                        "let valBuf = changetype<usize>(this) + {};",
                        union_.member_offset
                    ))?
                    .write_line(format!(
                        "memory.fill(valBuf, 0, {});",
                        union_.max_member_size
                    ))?
                    .write_line("if (isReference<T>()) {")?;
                w.new_block().write_line(
                    "(val !== null) && memory.copy(valBuf, changetype<usize>(val), offsetof<T>());",
                )?;
                w.write_line("} else {")?;
                w.new_block().write_line("store<T>(valBuf, val)")?;
                w.write_line("}")?;
            }
            w.write_line("}")?;

            for (i, member) in union_.members.iter().enumerate() {
                w.eob()?;
                Self::define_union_member(&mut w, name, i, member)?;
            }
        }
        w.write_line("}")?.eob()?;
        Ok(())
    }
}