1 // WebAssembly C++ API
2
3 #ifndef __WASM_HH
4 #define __WASM_HH
5
6 #include <cassert>
7 #include <cstddef>
8 #include <cstdint>
9 #include <cstring>
10 #include <memory>
11 #include <limits>
12 #include <string>
13
14
15 ///////////////////////////////////////////////////////////////////////////////
16 // Auxiliaries
17
18 // Machine types
19
20 static_assert(sizeof(float) == sizeof(int32_t), "incompatible float type");
21 static_assert(sizeof(double) == sizeof(int64_t), "incompatible double type");
22 static_assert(sizeof(intptr_t) == sizeof(int32_t) ||
23 sizeof(intptr_t) == sizeof(int64_t), "incompatible pointer type");
24
25 using byte_t = char;
26 using float32_t = float;
27 using float64_t = double;
28
29
30 namespace wasm {
31
32 // Vectors
33
34 template<class T>
35 class vec {
36 static const size_t invalid_size = SIZE_MAX;
37
38 size_t size_;
39 std::unique_ptr<T[]> data_;
40
41 #ifdef WASM_API_DEBUG
42 void make_data();
43 void free_data();
44 #else
make_data()45 void make_data() {}
free_data()46 void free_data() {}
47 #endif
48
vec(size_t size)49 vec(size_t size) : vec(size, size ? new(std::nothrow) T[size] : nullptr) {
50 make_data();
51 }
52
vec(size_t size,T * data)53 vec(size_t size, T* data) : size_(size), data_(data) {
54 assert(!!size_ == !!data_ || size_ == invalid_size);
55 }
56
57 public:
58 using elem_type = T;
59
vec(vec<T> && that)60 vec(vec<T>&& that) : vec(that.size_, that.data_.release()) {}
61
~vec()62 ~vec() {
63 free_data();
64 }
65
operator bool() const66 operator bool() const {
67 return bool(size_ != invalid_size);
68 }
69
size() const70 auto size() const -> size_t {
71 return size_;
72 }
73
get() const74 auto get() const -> const T* {
75 return data_.get();
76 }
77
get()78 auto get() -> T* {
79 return data_.get();
80 }
81
release()82 auto release() -> T* {
83 return data_.release();
84 }
85
reset()86 void reset() {
87 free_data();
88 size_ = invalid_size;
89 data_.reset();
90 }
91
reset(vec & that)92 void reset(vec& that) {
93 free_data();
94 size_ = that.size_;
95 data_.reset(that.data_.release());
96 }
97
operator =(vec && that)98 auto operator=(vec&& that) -> vec& {
99 reset(that);
100 return *this;
101 }
102
operator [](size_t i)103 auto operator[](size_t i) -> T& {
104 assert(i < size_);
105 return data_[i];
106 }
107
operator [](size_t i) const108 auto operator[](size_t i) const -> const T& {
109 assert(i < size_);
110 return data_[i];
111 }
112
copy() const113 auto copy() const -> vec {
114 auto v = vec(size_);
115 if (v) for (size_t i = 0; i < size_; i++) v.data_[i] = data_[i];
116 return v;
117 }
118
119 // TODO: This can't be used for e.g. vec<Val>
deep_copy() const120 auto deep_copy() const -> vec {
121 auto v = vec(size_);
122 if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i]->copy();
123 return v;
124 }
125
make_uninitialized(size_t size=0)126 static auto make_uninitialized(size_t size = 0) -> vec {
127 return vec(size);
128 }
129
make(size_t size,T init[])130 static auto make(size_t size, T init[]) -> vec {
131 auto v = vec(size);
132 if (v) for (size_t i = 0; i < size; ++i) v.data_[i] = std::move(init[i]);
133 return v;
134 }
135
make(std::string s)136 static auto make(std::string s) -> vec<char> {
137 auto v = vec(s.length() + 1);
138 if (v) std::strcpy(v.get(), s.data());
139 return v;
140 }
141
142 // TODO(mvsc): MVSC requires this special case:
make()143 static auto make() -> vec {
144 return vec(0);
145 }
146
147 template<class... Ts>
make(Ts &&...args)148 static auto make(Ts&&... args) -> vec {
149 T data[] = { std::move(args)... };
150 return make(sizeof...(Ts), data);
151 }
152
adopt(size_t size,T data[])153 static auto adopt(size_t size, T data[]) -> vec {
154 return vec(size, data);
155 }
156
invalid()157 static auto invalid() -> vec {
158 return vec(invalid_size, nullptr);
159 }
160 };
161
162
163 // Ownership
164
165 template<class T> using own = std::unique_ptr<T>;
166 template<class T> using ownvec = vec<own<T>>;
167
168 template<class T>
make_own(T * x)169 auto make_own(T* x) -> own<T> { return own<T>(x); }
170
171
172 ///////////////////////////////////////////////////////////////////////////////
173 // Runtime Environment
174
175 // Configuration
176
177 class Config {
178 public:
179 Config() = delete;
180 ~Config();
181 void operator delete(void*);
182
183 static auto make() -> own<Config>;
184
185 // Implementations may provide custom methods for manipulating Configs.
186 };
187
188
189 // Engine
190
191 class Engine {
192 public:
193 Engine() = delete;
194 ~Engine();
195 void operator delete(void*);
196
197 static auto make(own<Config>&& = Config::make()) -> own<Engine>;
198 };
199
200
201 // Store
202
203 class Store {
204 public:
205 Store() = delete;
206 ~Store();
207 void operator delete(void*);
208
209 static auto make(Engine*) -> own<Store>;
210 };
211
212
213 ///////////////////////////////////////////////////////////////////////////////
214 // Type Representations
215
216 // Type attributes
217
218 enum Mutability : uint8_t { CONST, VAR };
219
220 struct Limits {
221 uint32_t min;
222 uint32_t max;
223
Limitswasm::Limits224 Limits(uint32_t min, uint32_t max = std::numeric_limits<uint32_t>::max()) :
225 min(min), max(max) {}
226 };
227
228
229 // Value Types
230
231 enum ValKind : uint8_t {
232 I32, I64, F32, F64,
233 ANYREF = 128, FUNCREF,
234 };
235
is_num(ValKind k)236 inline bool is_num(ValKind k) { return k < ANYREF; }
is_ref(ValKind k)237 inline bool is_ref(ValKind k) { return k >= ANYREF; }
238
239
240 class ValType {
241 public:
242 ValType() = delete;
243 ~ValType();
244 void operator delete(void*);
245
246 static auto make(ValKind) -> own<ValType>;
247 auto copy() const -> own<ValType>;
248
249 auto kind() const -> ValKind;
is_num() const250 auto is_num() const -> bool { return wasm::is_num(kind()); }
is_ref() const251 auto is_ref() const -> bool { return wasm::is_ref(kind()); }
252 };
253
254
255 // External Types
256
257 enum ExternKind : uint8_t {
258 EXTERN_FUNC, EXTERN_GLOBAL, EXTERN_TABLE, EXTERN_MEMORY
259 };
260
261 class FuncType;
262 class GlobalType;
263 class TableType;
264 class MemoryType;
265
266 class ExternType {
267 public:
268 ExternType() = delete;
269 ~ExternType();
270 void operator delete(void*);
271
272 auto copy() const-> own<ExternType>;
273
274 auto kind() const -> ExternKind;
275
276 auto func() -> FuncType*;
277 auto global() -> GlobalType*;
278 auto table() -> TableType*;
279 auto memory() -> MemoryType*;
280
281 auto func() const -> const FuncType*;
282 auto global() const -> const GlobalType*;
283 auto table() const -> const TableType*;
284 auto memory() const -> const MemoryType*;
285 };
286
287
288 // Function Types
289
290 class FuncType : public ExternType {
291 public:
292 FuncType() = delete;
293 ~FuncType();
294
295 static auto make(
296 ownvec<ValType>&& params = ownvec<ValType>::make(),
297 ownvec<ValType>&& results = ownvec<ValType>::make()
298 ) -> own<FuncType>;
299
300 auto copy() const -> own<FuncType>;
301
302 auto params() const -> const ownvec<ValType>&;
303 auto results() const -> const ownvec<ValType>&;
304 };
305
306
307 // Global Types
308
309 class GlobalType : public ExternType {
310 public:
311 GlobalType() = delete;
312 ~GlobalType();
313
314 static auto make(own<ValType>&&, Mutability) -> own<GlobalType>;
315 auto copy() const -> own<GlobalType>;
316
317 auto content() const -> const ValType*;
318 auto mutability() const -> Mutability;
319 };
320
321
322 // Table Types
323
324 class TableType : public ExternType {
325 public:
326 TableType() = delete;
327 ~TableType();
328
329 static auto make(own<ValType>&&, Limits) -> own<TableType>;
330 auto copy() const -> own<TableType>;
331
332 auto element() const -> const ValType*;
333 auto limits() const -> const Limits&;
334 };
335
336
337 // Memory Types
338
339 class MemoryType : public ExternType {
340 public:
341 MemoryType() = delete;
342 ~MemoryType();
343
344 static auto make(Limits) -> own<MemoryType>;
345 auto copy() const -> own<MemoryType>;
346
347 auto limits() const -> const Limits&;
348 };
349
350
351 // Import Types
352
353 using Name = vec<byte_t>;
354
355 class ImportType {
356 public:
357 ImportType() = delete;
358 ~ImportType();
359 void operator delete(void*);
360
361 static auto make(Name&& module, Name&& name, own<ExternType>&&) ->
362 own<ImportType>;
363 auto copy() const -> own<ImportType>;
364
365 auto module() const -> const Name&;
366 auto name() const -> const Name&;
367 auto type() const -> const ExternType*;
368 };
369
370
371 // Export Types
372
373 class ExportType {
374 public:
375 ExportType() = delete;
376 ~ExportType();
377 void operator delete(void*);
378
379 static auto make(Name&&, own<ExternType>&&) -> own<ExportType>;
380 auto copy() const -> own<ExportType>;
381
382 auto name() const -> const Name&;
383 auto type() const -> const ExternType*;
384 };
385
386
387 ///////////////////////////////////////////////////////////////////////////////
388 // Runtime Objects
389
390 // References
391
392 class Ref {
393 public:
394 Ref() = delete;
395 ~Ref();
396 void operator delete(void*);
397
398 auto copy() const -> own<Ref>;
399 auto same(const Ref*) const -> bool;
400
401 auto get_host_info() const -> void*;
402 void set_host_info(void* info, void (*finalizer)(void*) = nullptr);
403 };
404
405
406 // Values
407
408 class Val {
409 ValKind kind_;
410 union impl {
411 int32_t i32;
412 int64_t i64;
413 float32_t f32;
414 float64_t f64;
415 Ref* ref;
416 } impl_;
417
Val(ValKind kind,impl impl)418 Val(ValKind kind, impl impl) : kind_(kind), impl_(impl) {}
419
420 public:
Val()421 Val() : kind_(ANYREF) { impl_.ref = nullptr; }
Val(int32_t i)422 Val(int32_t i) : kind_(I32) { impl_.i32 = i; }
Val(int64_t i)423 Val(int64_t i) : kind_(I64) { impl_.i64 = i; }
Val(float32_t z)424 Val(float32_t z) : kind_(F32) { impl_.f32 = z; }
Val(float64_t z)425 Val(float64_t z) : kind_(F64) { impl_.f64 = z; }
Val(own<Ref> && r)426 Val(own<Ref>&& r) : kind_(ANYREF) { impl_.ref = r.release(); }
427
Val(Val && that)428 Val(Val&& that) : kind_(that.kind_), impl_(that.impl_) {
429 if (is_ref()) that.impl_.ref = nullptr;
430 }
431
~Val()432 ~Val() {
433 reset();
434 }
435
is_num() const436 auto is_num() const -> bool { return wasm::is_num(kind_); }
is_ref() const437 auto is_ref() const -> bool { return wasm::is_ref(kind_); }
438
i32(int32_t x)439 static auto i32(int32_t x) -> Val { return Val(x); }
i64(int64_t x)440 static auto i64(int64_t x) -> Val { return Val(x); }
f32(float32_t x)441 static auto f32(float32_t x) -> Val { return Val(x); }
f64(float64_t x)442 static auto f64(float64_t x) -> Val { return Val(x); }
ref(own<Ref> && x)443 static auto ref(own<Ref>&& x) -> Val { return Val(std::move(x)); }
444 template<class T> inline static auto make(T x) -> Val;
445 template<class T> inline static auto make(own<T>&& x) -> Val;
446
reset()447 void reset() {
448 if (is_ref() && impl_.ref) {
449 delete impl_.ref;
450 impl_.ref = nullptr;
451 }
452 }
453
reset(Val & that)454 void reset(Val& that) {
455 reset();
456 kind_ = that.kind_;
457 impl_ = that.impl_;
458 if (is_ref()) that.impl_.ref = nullptr;
459 }
460
operator =(Val && that)461 auto operator=(Val&& that) -> Val& {
462 reset(that);
463 return *this;
464 }
465
kind() const466 auto kind() const -> ValKind { return kind_; }
i32() const467 auto i32() const -> int32_t { assert(kind_ == I32); return impl_.i32; }
i64() const468 auto i64() const -> int64_t { assert(kind_ == I64); return impl_.i64; }
f32() const469 auto f32() const -> float32_t { assert(kind_ == F32); return impl_.f32; }
f64() const470 auto f64() const -> float64_t { assert(kind_ == F64); return impl_.f64; }
ref() const471 auto ref() const -> Ref* { assert(is_ref()); return impl_.ref; }
472 template<class T> inline auto get() const -> T;
473
release_ref()474 auto release_ref() -> own<Ref> {
475 assert(is_ref());
476 auto ref = impl_.ref;
477 impl_.ref = nullptr;
478 return own<Ref>(ref);
479 }
480
copy() const481 auto copy() const -> Val {
482 if (is_ref() && impl_.ref != nullptr) {
483 // TODO(mvsc): MVSC cannot handle this:
484 // impl impl = {.ref = impl_.ref->copy().release()};
485 impl impl;
486 impl.ref = impl_.ref->copy().release();
487 return Val(kind_, impl);
488 } else {
489 return Val(kind_, impl_);
490 }
491 }
492 };
493
494
make(int32_t x)495 template<> inline auto Val::make<int32_t>(int32_t x) -> Val { return Val(x); }
make(int64_t x)496 template<> inline auto Val::make<int64_t>(int64_t x) -> Val { return Val(x); }
make(float32_t x)497 template<> inline auto Val::make<float32_t>(float32_t x) -> Val { return Val(x); }
make(float64_t x)498 template<> inline auto Val::make<float64_t>(float64_t x) -> Val { return Val(x); }
make(own<Ref> && x)499 template<> inline auto Val::make<Ref>(own<Ref>&& x) -> Val {
500 return Val(std::move(x));
501 }
502
make(uint32_t x)503 template<> inline auto Val::make<uint32_t>(uint32_t x) -> Val {
504 return Val(static_cast<int32_t>(x));
505 }
make(uint64_t x)506 template<> inline auto Val::make<uint64_t>(uint64_t x) -> Val {
507 return Val(static_cast<int64_t>(x));
508 }
509
get() const510 template<> inline auto Val::get<int32_t>() const -> int32_t { return i32(); }
get() const511 template<> inline auto Val::get<int64_t>() const -> int64_t { return i64(); }
get() const512 template<> inline auto Val::get<float32_t>() const -> float32_t { return f32(); }
get() const513 template<> inline auto Val::get<float64_t>() const -> float64_t { return f64(); }
get() const514 template<> inline auto Val::get<Ref*>() const -> Ref* { return ref(); }
515
get() const516 template<> inline auto Val::get<uint32_t>() const -> uint32_t {
517 return static_cast<uint32_t>(i32());
518 }
get() const519 template<> inline auto Val::get<uint64_t>() const -> uint64_t {
520 return static_cast<uint64_t>(i64());
521 }
522
523
524 // Traps
525
526 using Message = vec<byte_t>; // null terminated
527
528 class Instance;
529
530 class Frame {
531 public:
532 Frame() = delete;
533 ~Frame();
534 void operator delete(void*);
535
536 auto copy() const -> own<Frame>;
537
538 auto instance() const -> Instance*;
539 auto func_index() const -> uint32_t;
540 auto func_offset() const -> size_t;
541 auto module_offset() const -> size_t;
542 };
543
544 class Trap : public Ref {
545 public:
546 Trap() = delete;
547 ~Trap();
548
549 static auto make(Store*, const Message& msg) -> own<Trap>;
550 auto copy() const -> own<Trap>;
551
552 auto message() const -> Message;
553 auto origin() const -> own<Frame>; // may be null
554 auto trace() const -> ownvec<Frame>; // may be empty, origin first
555 };
556
557
558 // Shared objects
559
560 template<class T>
561 class Shared {
562 public:
563 Shared() = delete;
564 ~Shared();
565 void operator delete(void*);
566 };
567
568
569 // Modules
570
571 class Module : public Ref {
572 public:
573 Module() = delete;
574 ~Module();
575
576 static auto validate(Store*, const vec<byte_t>& binary) -> bool;
577 static auto make(Store*, const vec<byte_t>& binary) -> own<Module>;
578 auto copy() const -> own<Module>;
579
580 auto imports() const -> ownvec<ImportType>;
581 auto exports() const -> ownvec<ExportType>;
582
583 auto share() const -> own<Shared<Module>>;
584 static auto obtain(Store*, const Shared<Module>*) -> own<Module>;
585
586 auto serialize() const -> vec<byte_t>;
587 static auto deserialize(Store*, const vec<byte_t>&) -> own<Module>;
588 };
589
590
591 // Foreign Objects
592
593 class Foreign : public Ref {
594 public:
595 Foreign() = delete;
596 ~Foreign();
597
598 static auto make(Store*) -> own<Foreign>;
599 auto copy() const -> own<Foreign>;
600 };
601
602
603 // Externals
604
605 class Func;
606 class Global;
607 class Table;
608 class Memory;
609
610 class Extern : public Ref {
611 public:
612 Extern() = delete;
613 ~Extern();
614
615 auto copy() const -> own<Extern>;
616
617 auto kind() const -> ExternKind;
618 auto type() const -> own<ExternType>;
619
620 auto func() -> Func*;
621 auto global() -> Global*;
622 auto table() -> Table*;
623 auto memory() -> Memory*;
624
625 auto func() const -> const Func*;
626 auto global() const -> const Global*;
627 auto table() const -> const Table*;
628 auto memory() const -> const Memory*;
629 };
630
631
632 // Function Instances
633
634 class Func : public Extern {
635 public:
636 Func() = delete;
637 ~Func();
638
639 using callback = auto (*)(const Val[], Val[]) -> own<Trap>;
640 using callback_with_env = auto (*)(void*, const Val[], Val[]) -> own<Trap>;
641
642 static auto make(Store*, const FuncType*, callback) -> own<Func>;
643 static auto make(Store*, const FuncType*, callback_with_env,
644 void*, void (*finalizer)(void*) = nullptr) -> own<Func>;
645 auto copy() const -> own<Func>;
646
647 auto type() const -> own<FuncType>;
648 auto param_arity() const -> size_t;
649 auto result_arity() const -> size_t;
650
651 auto call(const Val[] = nullptr, Val[] = nullptr) const -> own<Trap>;
652 };
653
654
655 // Global Instances
656
657 class Global : public Extern {
658 public:
659 Global() = delete;
660 ~Global();
661
662 static auto make(Store*, const GlobalType*, const Val&) -> own<Global>;
663 auto copy() const -> own<Global>;
664
665 auto type() const -> own<GlobalType>;
666 auto get() const -> Val;
667 void set(const Val&);
668 };
669
670
671 // Table Instances
672
673 class Table : public Extern {
674 public:
675 Table() = delete;
676 ~Table();
677
678 using size_t = uint32_t;
679
680 static auto make(
681 Store*, const TableType*, const Ref* init = nullptr) -> own<Table>;
682 auto copy() const -> own<Table>;
683
684 auto type() const -> own<TableType>;
685 auto get(size_t index) const -> own<Ref>;
686 auto set(size_t index, const Ref*) -> bool;
687 auto size() const -> size_t;
688 auto grow(size_t delta, const Ref* init = nullptr) -> bool;
689 };
690
691
692 // Memory Instances
693
694 class Memory : public Extern {
695 public:
696 Memory() = delete;
697 ~Memory();
698
699 static auto make(Store*, const MemoryType*) -> own<Memory>;
700 auto copy() const -> own<Memory>;
701
702 using pages_t = uint32_t;
703
704 static const size_t page_size = 0x10000;
705
706 auto type() const -> own<MemoryType>;
707 auto data() const -> byte_t*;
708 auto data_size() const -> size_t;
709 auto size() const -> pages_t;
710 auto grow(pages_t delta) -> bool;
711 };
712
713
714 // Module Instances
715
716 class Instance : public Ref {
717 public:
718 Instance() = delete;
719 ~Instance();
720
721 static auto make(
722 Store*, const Module*, const Extern* const[], own<Trap>* = nullptr
723 ) -> own<Instance>;
724 auto copy() const -> own<Instance>;
725
726 auto exports() const -> ownvec<Extern>;
727 };
728
729
730 ///////////////////////////////////////////////////////////////////////////////
731
732 } // namespace wasm
733
734 #endif // #ifdef __WASM_HH
735