v2 / vlib / v / gen / wasm / serialise / serialise.v
455 lines · 388 sloc · 8.1 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2023 l-m.dev. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module serialise
5
6import v.ast
7// import v.eval
8import math.bits
9import strconv
10
11@[noinit]
12pub struct Pool {
13mut:
14 table &ast.Table
15 // eval eval.Eval
16 structs map[ast.Type]StructInfo
17 strings []StringInfo // string intern
18pub:
19 null_terminated bool
20 intern_strings bool
21 store_relocs bool
22pub mut:
23 buf []u8
24 relocs []Reloc
25 highest_alignment int
26}
27
28struct StringInfo {
29 pos int
30 len int
31}
32
33pub struct StructInfo {
34pub mut:
35 offsets []int
36}
37
38pub struct Reloc {
39pub:
40 pos int
41 offset int
42}
43
44pub fn (mut p Pool) type_struct_info(typ ast.Type) ?StructInfo {
45 ts := p.table.sym(typ)
46
47 if ts.info !is ast.Struct {
48 return none
49 }
50
51 if typ.idx() in p.structs {
52 return p.structs[typ.idx()]
53 }
54
55 // will cache inside `p.structs`
56 p.type_size(typ)
57 return p.structs[typ.idx()]
58}
59
60pub fn (mut p Pool) type_size(typ ast.Type) (int, int) {
61 if typ.nr_muls() > 0 {
62 return p.table.pointer_size, p.table.pointer_size
63 }
64
65 ts := p.table.sym(typ)
66 if ts.size != -1 && typ.idx() in p.structs {
67 return ts.size, ts.align
68 }
69
70 if ts.info is ast.Enum {
71 return p.table.type_size(ts.info.typ)
72 }
73
74 if ts.info !is ast.Struct {
75 return p.table.type_size(typ)
76 }
77
78 ti := ts.info as ast.Struct
79
80 // code borrowed from native, inserted in wasm, and now here!
81
82 mut strc := StructInfo{}
83 mut size := 0
84 mut align := 1
85 for f in ti.fields {
86 f_size, f_align := p.type_size(f.typ)
87 if f_size == 0 {
88 strc.offsets << 0
89 continue
90 }
91 padding := (f_align - size % f_align) % f_align
92 strc.offsets << size + padding
93 size += f_size + padding
94 if f_align > align {
95 align = f_align
96 }
97 }
98 size = (size + align - 1) / align * align
99 p.structs[typ.idx()] = strc
100
101 mut ts_ := p.table.sym(typ)
102 ts_.size = size
103 ts_.align = align
104
105 return size, align
106}
107
108@[params]
109pub struct PoolOpts {
110pub:
111 null_terminated bool = true
112 intern_strings bool = true
113 store_relocs bool = true
114}
115
116pub fn new_pool(table &ast.Table, opts PoolOpts) Pool {
117 return Pool{
118 table: table
119 null_terminated: opts.null_terminated
120 intern_strings: opts.intern_strings
121 store_relocs: opts.store_relocs
122 }
123}
124
125fn (mut p Pool) zero_fill(size int) {
126 // TODO: eventually support a way to utilise a BSS section
127
128 for i := 0; i < size; i++ {
129 p.buf << 0
130 }
131}
132
133fn (mut p Pool) alignment(align int) int {
134 if align > p.highest_alignment {
135 p.highest_alignment = align
136 }
137 padding := (align - p.buf.len % align) % align
138 p.zero_fill(padding)
139 pos := p.buf.len
140 return pos
141}
142
143/*
144fn (mut p Pool) append_struct(init ast.StructInit) ?int {
145 old_len := p.buf.len
146
147 size, align := p.type_size(v.typ)
148 ts := g.table.sym(v.typ)
149 ts_info := ts.info as ast.Struct
150
151 pos := p.alignment(align)
152
153 if init.fields.len == 0 && !(ts_info.fields.any(it.has_default_expr)) {
154 for i := 0 ; i < size ; i++ {
155 p.buf << 0
156 }
157 return pos
158 }
159
160 /* for i, f in ts_info.fields {
161 field_to_be_set := init.fields.map(it.name).filter(f.name)
162 } */
163
164 /* for i, f in ts_info.fields {
165 field_to_be_set := init.fields.map(it.name).contains(f.name)
166
167 if !field_to_be_set {
168 offset := g.structs[v.typ.idx()].offsets[i]
169 offset_var := g.offset(v, f.typ, offset)
170
171 fsize, _ := g.get_type_size_align(f.typ)
172
173 if f.has_default_expr {
174 g.expr(f.default_expr, f.typ)
175 g.set(offset_var)
176 } else {
177 g.zero_fill(offset_var, fsize)
178 }
179 }
180 }
181
182 for f in init.fields {
183 field := ts.find_field(f.name) or {
184 g.w_error('could not find field `${f.name}` on init')
185 }
186
187 offset := g.structs[v.typ.idx()].offsets[field.i]
188 offset_var := g.offset(v, f.expected_type, offset)
189
190 g.expr(f.expr, f.expected_type)
191 g.set(offset_var)
192 } */
193
194 return pos
195}*/
196
197pub fn eval_escape_codes_raw(str string) !string {
198 mut buffer := []u8{}
199
200 mut i := 0
201 for i < str.len {
202 if str[i] != `\\` {
203 buffer << str[i]
204 i++
205 continue
206 }
207
208 // skip \
209 i++
210 match str[i] {
211 `\\`, `'`, `"` {
212 buffer << str[i]
213 i++
214 }
215 `a`, `b`, `f` {
216 buffer << str[i] - u8(90)
217 i++
218 }
219 `n` {
220 buffer << `\n`
221 i++
222 }
223 `r` {
224 buffer << `\r`
225 i++
226 }
227 `t` {
228 buffer << `\t`
229 i++
230 }
231 `u` {
232 i++
233 utf8 := strconv.parse_int(str[i..i + 4], 16, 16) or {
234 return error('invalid \\u escape code (${str[i..i + 4]})')
235 }
236 i += 4
237 buffer << u8(utf8)
238 buffer << u8(utf8 >> 8)
239 }
240 `v` {
241 buffer << `\v`
242 i++
243 }
244 `x` {
245 i++
246 c := strconv.parse_int(str[i..i + 2], 16, 8) or {
247 return error('invalid \\x escape code (${str[i..i + 2]})')
248 }
249 i += 2
250 buffer << u8(c)
251 }
252 `0`...`7` {
253 c := strconv.parse_int(str[i..i + 3], 8, 8) or {
254 return error('invalid escape code \\${str[i..i + 3]}')
255 }
256 i += 3
257 buffer << u8(c)
258 }
259 else {
260 return error('invalid escape code \\${str[i]}')
261 }
262 }
263 }
264
265 return buffer.bytestr()
266}
267
268pub fn eval_escape_codes(str_lit ast.StringLiteral) !string {
269 if str_lit.is_raw {
270 return str_lit.val
271 }
272
273 return eval_escape_codes_raw(str_lit.val)
274}
275
276pub fn (mut p Pool) append_string(val string) int {
277 data := val.bytes()
278
279 if p.intern_strings {
280 for str in p.strings {
281 if data.len > str.len || (p.null_terminated && data.len != str.len) {
282 continue
283 }
284
285 // TODO: aggressive string interning if `p.null_terminated`
286 if p.buf[str.pos..str.pos + data.len] == data {
287 return str.pos
288 }
289 }
290 }
291
292 pos := p.buf.len
293 p.buf << data
294 if p.null_terminated {
295 p.buf << 0
296 }
297
298 p.strings << StringInfo{
299 pos: pos
300 len: data.len
301 }
302
303 return pos
304}
305
306pub fn (mut p Pool) append(init ast.Expr, typ ast.Type) (int, bool) {
307 match init {
308 ast.BoolLiteral {
309 pos := p.buf.len
310 p.buf << u8(init.val)
311 return pos, true
312 }
313 ast.FloatLiteral {
314 assert typ.is_pure_float()
315
316 mut pos := 0
317 if typ == ast.f32_type {
318 pos = p.alignment(4)
319 p.u32(bits.f32_bits(init.val.f32()))
320 } else {
321 pos = p.alignment(8)
322 p.u64(bits.f64_bits(init.val.f64()))
323 }
324
325 return pos, true
326 }
327 ast.IntegerLiteral {
328 assert typ.is_pure_int()
329
330 size, align := p.table.type_size(typ)
331 pos := p.alignment(align)
332
333 match size {
334 1 {
335 p.u8(u8(init.val.i8()))
336 }
337 2 {
338 p.u16(u16(init.val.i16()))
339 }
340 4 {
341 p.u32(u32(init.val.int()))
342 }
343 8 {
344 p.u64(u64(init.val.i64()))
345 }
346 else {}
347 }
348
349 return pos, true
350 }
351 ast.CharLiteral {
352 // 3 extra bytes for improved program correctness, thank me later
353 rne := u32(eval_escape_codes_raw(init.val) or { panic('Pool.append: ${err}') }.runes()[0])
354 pos := p.alignment(4)
355 p.u32(rne)
356
357 return pos, true
358 }
359 ast.StringLiteral {
360 val := eval_escape_codes(init) or { panic('Pool.append: ${err}') }
361 str_pos := p.append_string(val)
362
363 if typ != ast.string_type {
364 // c'str'
365 return str_pos, true
366 }
367
368 _, align := p.type_size(ast.string_type)
369 tss := p.table.sym(ast.string_type).info as ast.Struct
370 pos := p.alignment(align)
371
372 for field in tss.fields {
373 match field.name {
374 'str' {
375 p.ptr(str_pos)
376 }
377 'len' {
378 p.u32(u32(val.len))
379 }
380 'is_lit' {
381 p.u32(1)
382 }
383 else {
384 panic('ast.string: field `${field.name}` is unknown')
385 }
386 }
387 }
388
389 return pos, true
390 }
391 else {
392 size, align := p.type_size(typ)
393 pos := p.alignment(align)
394 p.zero_fill(size)
395 return pos, false
396 }
397 }
398}
399
400fn (mut p Pool) u64(v u64) {
401 p.buf << u8(v)
402 p.buf << u8(v >> u64(8))
403 p.buf << u8(v >> u64(16))
404 p.buf << u8(v >> u64(24))
405 p.buf << u8(v >> u64(32))
406 p.buf << u8(v >> u64(40))
407 p.buf << u8(v >> u64(48))
408 p.buf << u8(v >> u64(56))
409}
410
411fn (mut p Pool) u32(v u32) {
412 p.buf << u8(v)
413 p.buf << u8(v >> u32(8))
414 p.buf << u8(v >> u32(16))
415 p.buf << u8(v >> u32(24))
416}
417
418fn (mut p Pool) u16(v u16) {
419 p.buf << u8(v)
420 p.buf << u8(v >> u32(8))
421}
422
423fn (mut p Pool) u8(v u8) {
424 p.buf << v
425}
426
427fn (mut p Pool) ptr(offset int) int {
428 assert p.table.pointer_size in [1, 2, 4, 8]
429 pos := p.buf.len // p.alignment(p.table.pointer_size)
430
431 if p.store_relocs {
432 p.relocs << Reloc{
433 pos: pos
434 offset: offset
435 }
436 }
437
438 match p.table.pointer_size {
439 1 {
440 p.u8(u8(offset))
441 }
442 2 {
443 p.u16(u16(offset))
444 }
445 4 {
446 p.u32(u32(offset))
447 }
448 8 {
449 p.u64(u64(offset))
450 }
451 else {}
452 }
453
454 return pos
455}
456