witx_codegen/assemblyscript/
union.rs

1use std::io::Write;
2
3use super::*;
4
5impl AssemblyScriptGenerator {
6    fn define_union_member_accessors<T: Write>(
7        w: &mut PrettyWriter<T>,
8        union_name: &str,
9        i: usize,
10        member: &ASUnionMember,
11    ) -> Result<(), Error> {
12        let member_type = member.type_.as_ref();
13        match member_type {
14            ASType::Void => {
15                w.write_line(format!(
16                    "static {}(): {} {{",
17                    member.name.as_fn(),
18                    union_name.as_type()
19                ))?
20                .indent()?
21                .write_line(format!("return {}.new({});", union_name.as_type(), i))?
22                .write_line("}")?
23                .eob()?;
24
25                w.write_line(format!("set{}(): void {{", member.name.as_fn_suffix()))?
26                    .indent()?
27                    .write_line(format!("this.tag = {};", i))?
28                    .write_line("}")?
29                    .eob()?;
30
31                w.write_line(format!("is{}(): bool {{", member.name.as_fn_suffix()))?
32                    .indent()?
33                    .write_line(format!("return this.tag === {};", i))?
34                    .write_line("}")?;
35            }
36            _ => {
37                w.write_line(format!(
38                    "static {}(val: {}): {} {{",
39                    member.name.as_fn(),
40                    member_type.as_lang(),
41                    union_name.as_type()
42                ))?;
43                w.new_block().write_line(format!(
44                    "return {}.new({}, val);",
45                    union_name.as_type(),
46                    i
47                ))?;
48                w.write_line("}")?.eob()?;
49
50                w.write_line(format!(
51                    "set{}(val: {}): void {{",
52                    member.name.as_fn_suffix(),
53                    member_type.as_lang()
54                ))?;
55                {
56                    w.new_block()
57                        .write_line(format!("this.tag = {};", i))?
58                        .write_line("this.set(val);")?;
59                }
60                w.write_line("}")?.eob()?;
61
62                w.write_line(format!("is{}(): bool {{", member.name.as_fn_suffix(),))?
63                    .indent()?
64                    .write_line(format!("return this.tag === {};", i))?
65                    .write_line("}")?
66                    .eob()?;
67
68                if member_type.is_nullable() {
69                    w.write_line(format!(
70                        "get{}(): {} | null {{",
71                        member.name.as_fn_suffix(),
72                        member_type.as_lang()
73                    ))?;
74                } else {
75                    w.write_line(format!(
76                        "get{}(): {} {{",
77                        member.name.as_fn_suffix(),
78                        member_type.as_lang()
79                    ))?;
80                }
81                {
82                    let mut w = w.new_block();
83                    if member_type.is_nullable() {
84                        w.write_line(format!("if (this.tag !== {}) {{ return null; }}", i))?;
85                    }
86                    w.write_line(format!("return this.get<{}>();", member_type.as_lang()))?;
87                }
88                w.write_line("}")?;
89            }
90        }
91        Ok(())
92    }
93
94    fn define_union_member<T: Write>(
95        w: &mut PrettyWriter<T>,
96        union_name: &str,
97        i: usize,
98        member: &ASUnionMember,
99    ) -> Result<(), Error> {
100        let member_type = member.type_.as_ref();
101        match member_type {
102            ASType::Void => {
103                w.write_line(format!(
104                    "// --- {}: (no associated content) if tag={}",
105                    member.name.as_var(),
106                    i
107                ))?;
108            }
109            _ => {
110                w.write_line(format!(
111                    "// --- {}: {} if tag={}",
112                    member.name.as_var(),
113                    member_type.as_lang(),
114                    i
115                ))?;
116            }
117        }
118        w.eob()?;
119        Self::define_union_member_accessors(w, union_name, i, member)?;
120        Ok(())
121    }
122
123    pub fn define_as_union<T: Write>(
124        w: &mut PrettyWriter<T>,
125        name: &str,
126        union_: &ASUnion,
127    ) -> Result<(), Error> {
128        let tag_repr = union_.tag_repr.as_ref();
129        w.write_line("// @ts-ignore: decorator")?
130            .write_line("@unmanaged")?
131            .write_line(format!("export class {} {{", name.as_type()))?;
132        {
133            let mut w = w.new_block();
134            w.write_line(format!("tag: {};", tag_repr.as_lang()))?;
135            let pad_len = union_.padding_after_tag;
136            for i in 0..(pad_len & 1) {
137                w.write_line(format!("private __pad8_{}: u8;", i))?;
138            }
139            for i in 0..(pad_len & 3) / 2 {
140                w.write_line(format!("private __pad16_{}: u16;", i))?;
141            }
142            for i in 0..(pad_len & 7) / 4 {
143                w.write_line(format!("private __pad32_{}: u32;", i))?;
144            }
145            for i in 0..pad_len / 8 {
146                w.write_line(format!("private __pad64_{}: u64;", i))?;
147            }
148            w.eob()?;
149
150            w.write_line(format!("constructor(tag: {}) {{", tag_repr.as_lang()))?;
151            {
152                let mut w = w.new_block();
153                w.write_line("this.tag = tag;")?.write_line(format!(
154                    "memory.fill(changetype<usize>(this) + {}, 0, {});",
155                    union_.member_offset, union_.max_member_size
156                ))?;
157            }
158            w.write_line("}")?.eob()?;
159
160            w.write_line("// @ts-ignore: default")?.write_line(format!(
161                "static new<T>(tag: {}, val: T = 0): {} {{",
162                tag_repr.as_lang(),
163                name.as_type()
164            ))?;
165            {
166                let mut w = w.new_block();
167                w.write_line(format!("let tu = new {}(tag);", name.as_type()))?
168                    .write_line("tu.set(val);")?
169                    .write_line("return tu;")?;
170            }
171            w.write_line("}")?.eob()?;
172
173            w.write_line("get<T>(): T {")?;
174            {
175                let mut w = w.new_block();
176                w.write_line("// @ts-ignore: cast")?
177                    .write_line(format!(
178                        "let valBuf = changetype<usize>(this) + {};",
179                        union_.member_offset
180                    ))?
181                    .write_line("if (isReference<T>()) {")?;
182                w.new_block().write_line("return changetype<T>(valBuf);")?;
183                w.write_line("} else {")?;
184                w.new_block().write_line("return load<T>(valBuf);")?;
185                w.write_line("}")?;
186            }
187            w.write_line("}")?.eob()?;
188
189            w.write_line("// @ts-ignore: default")?
190                .write_line("set<T>(val: T = 0): void {")?;
191            {
192                let mut w = w.new_block();
193                w.write_line("// @ts-ignore: cast")?
194                    .write_line(format!(
195                        "let valBuf = changetype<usize>(this) + {};",
196                        union_.member_offset
197                    ))?
198                    .write_line(format!(
199                        "memory.fill(valBuf, 0, {});",
200                        union_.max_member_size
201                    ))?
202                    .write_line("if (isReference<T>()) {")?;
203                w.new_block().write_line(
204                    "(val !== null) && memory.copy(valBuf, changetype<usize>(val), offsetof<T>());",
205                )?;
206                w.write_line("} else {")?;
207                w.new_block().write_line("store<T>(valBuf, val)")?;
208                w.write_line("}")?;
209            }
210            w.write_line("}")?;
211
212            for (i, member) in union_.members.iter().enumerate() {
213                w.eob()?;
214                Self::define_union_member(&mut w, name, i, member)?;
215            }
216        }
217        w.write_line("}")?.eob()?;
218        Ok(())
219    }
220}