1 module tagion.wasm.WasmBase;
2 
3 import std.bitmanip : Endian, binpeek = peek, binread = read, binwrite = write;
4 import std.conv : emplace, to;
5 import std.exception : assumeUnique, assumeWontThrow;
6 import std.format;
7 import std.meta : AliasSeq;
8 import std.range.primitives : isInputRange;
9 import std.stdio;
10 import std.traits : ConstOf, EnumMembers, ForeachType, Unqual, getUDAs, isAssociativeArray, isFunctionPointer;
11 import std.typecons : Tuple;
12 import std.uni : toLower;
13 import tagion.wasm.WasmException;
14 
15 import LEB128 = tagion.utils.LEB128;
16 
17 enum VerboseMode {
18     NONE,
19     STANDARD
20 }
21 
22 @safe struct Verbose {
23     VerboseMode mode;
24     string indent;
25     File fout;
26     enum INDENT = "  ";
27     enum WIDTH = 16;
28 
29     void opCall(Args...)(string fmt, lazy Args args) {
30         if (mode !is VerboseMode.NONE) {
31             fout.write(indent);
32             fout.writefln(fmt, args);
33         }
34     }
35 
36     void print(Args...)(string fmt, lazy Args args) {
37         if (mode !is VerboseMode.NONE) {
38             fout.writef(fmt, args);
39         }
40     }
41 
42     void println(Args...)(string fmt, lazy Args args) {
43         if (mode !is VerboseMode.NONE) {
44             fout.writefln(fmt, args);
45         }
46     }
47 
48     void down() nothrow {
49         if (mode !is VerboseMode.NONE) {
50             indent ~= INDENT;
51         }
52     }
53 
54     void up() nothrow {
55         if (mode !is VerboseMode.NONE) {
56             if (indent.length >= INDENT.length) {
57                 indent.length -= INDENT.length;
58             }
59         }
60     }
61 
62     void hex(const size_t index, const(ubyte[]) data) {
63         if (mode !is VerboseMode.NONE) {
64             size_t _index = index;
65             foreach (const i, d; data) {
66                 if (i % WIDTH is 0) {
67                     if (i !is 0) {
68                         fout.writeln("");
69                     }
70                     fout.writef("%s%06X", indent, _index);
71                 }
72                 fout.writef(" %02X", d);
73                 _index++;
74             }
75             fout.writeln("");
76         }
77     }
78 
79     void ln() {
80         if (mode !is VerboseMode.NONE) {
81             fout.writeln("");
82         }
83     }
84 
85 }
86 
87 static Verbose verbose;
88 
89 static this() {
90     verbose.fout = stdout;
91 }
92 
93 enum Section : ubyte {
94     CUSTOM = 0,
95     TYPE = 1,
96     IMPORT = 2,
97     FUNCTION = 3,
98     TABLE = 4,
99     MEMORY = 5,
100     GLOBAL = 6,
101     EXPORT = 7,
102     START = 8,
103     ELEMENT = 9,
104     CODE = 10,
105     DATA = 11,
106 }
107 
108 enum IRType {
109     CODE, /// Simple instruction with no argument
110     BLOCK, /// Block instruction
111     //    BLOCK_IF,      /// Block for [IF] ELSE END
112     //   BLOCK_ELSE,    /// Block for IF [ELSE] END
113     BRANCH, /// Branch jump instruction
114     BRANCH_IF, /// Conditional branch jump instruction
115     BRANCH_TABLE, /// Branch table jump instruction
116     CALL, /// Subroutine call
117     CALL_INDIRECT, /// Indirect subroutine call
118     LOCAL, /// Local register storage instruction
119     GLOBAL, /// Global register storage instruction
120     MEMORY, /// Memory instruction
121     MEMOP, /// Memory management instruction
122     CONST, /// Constant argument
123     END, /// Block end instruction
124     PREFIX, /// Prefix for two byte extension
125     SYMBOL, /// This is extra instruction which does not have an equivalent wasm opcode
126     ILLEGAL, /// Illegal instructions
127 }
128 
129 immutable illegalInstr = Instr("illegal", "illegal", 0, IRType.ILLEGAL);
130 
131 struct Instr {
132     string name; /// Instruction name
133     string wast; /// Wast name 
134     uint cost;
135     IRType irtype;
136     uint pops; // Number of pops from the stack
137     uint push; // Number of valus pushed
138     bool extend; // Extended
139 }
140 
141 enum ubyte[] magic = [0x00, 0x61, 0x73, 0x6D];
142 enum ubyte[] wasm_version = [0x01, 0x00, 0x00, 0x00];
143 enum IR : ubyte {
144     // dfmt off
145         @Instr("unreachable", "unreachable", 1, IRType.CODE)               UNREACHABLE         = 0x00, ///  unreachable
146         @Instr("nop", "nop", 1, IRType.CODE)                       NOP                 = 0x01, ///  nop
147         @Instr("block", "block", 0, IRType.BLOCK)                    BLOCK               = 0x02, ///  block rt:blocktype (in:instr) * end
148         @Instr("loop", "loop", 0, IRType.BLOCK)                     LOOP                = 0x03, ///  loop rt:blocktype (in:instr) * end
149         @Instr("if", "if", 1, IRType.BLOCK, 1)                    IF                  = 0x04, /++     if rt:blocktype (in:instr) *rt in * else ? end
150                                                                                         if rt:blocktype (in1:instr) *rt in * 1 else (in2:instr) * end
151                                                                                         +/
152         @Instr("else", "else", 0, IRType.END)                       ELSE                = 0x05, ///  else
153         @Instr("end", "end", 0, IRType.END)                        END                 = 0x0B, ///  end
154         @Instr("br", "br", 1, IRType.BRANCH, 1)                      BR                  = 0x0C, ///  br l:labelidx
155         @Instr("br_if", "br_if", 1, IRType.BRANCH_IF, 1)                BR_IF               = 0x0D, ///  br_if l:labelidx
156         @Instr("br_table", "br_table", 1, IRType.BRANCH_TABLE, 1)       BR_TABLE            = 0x0E, ///  br_table l:vec(labelidx) * lN:labelidx
157         @Instr("return", "return", 1, IRType.CODE, 1)                    RETURN              = 0x0F, ///  return
158         @Instr("call", "call", 1, IRType.CALL)                      CALL                = 0x10, ///  call x:funcidx
159         @Instr("call_indirect", "call_indirect", 1, IRType.CALL_INDIRECT, 1) CALL_INDIRECT       = 0x11, ///  call_indirect x:typeidx 0x00
160         @Instr("drop", "drop", 1, IRType.CODE, 1)                   DROP                = 0x1A, ///  drop
161         @Instr("select", "select", 1, IRType.CODE, 3, 1)              SELECT              = 0x1B, ///  select
162         @Instr("local.get", "get_local", 1, IRType.LOCAL, 0, 1)          LOCAL_GET           = 0x20, ///  local.get x:localidx
163         @Instr("local.set", "set_local", 1, IRType.LOCAL, 1)             LOCAL_SET           = 0x21, ///  local.set x:localidx
164         @Instr("local.tee", "tee_local", 1, IRType.LOCAL, 1, 1)          LOCAL_TEE           = 0x22, ///  local.tee x:localidx
165         @Instr("global.get", "get_global", 1, IRType.GLOBAL, 1, 0)        GLOBAL_GET          = 0x23, ///  global.get x:globalidx
166         @Instr("global.set", "set_global", 1, IRType.GLOBAL, 0, 1)        GLOBAL_SET          = 0x24, ///  global.set x:globalidx
167 
168         @Instr("i32.load", "i32.load", 2, IRType.MEMORY, 1, 1)          I32_LOAD            = 0x28, ///  i32.load     m:memarg
169         @Instr("i64.load", "i64.load", 2, IRType.MEMORY, 1, 1)          I64_LOAD            = 0x29, ///  i64.load     m:memarg
170         @Instr("f32.load", "f32.load", 2, IRType.MEMORY, 1, 1)          F32_LOAD            = 0x2A, ///  f32.load     m:memarg
171         @Instr("f64.load", "f64.load", 2, IRType.MEMORY, 1, 1)          F64_LOAD            = 0x2B, ///  f64.load     m:memarg
172         @Instr("i32.load8_s", "i32.load8_s", 2, IRType.MEMORY, 1, 1)       I32_LOAD8_S         = 0x2C, ///  i32.load8_s  m:memarg
173         @Instr("i32.load8_u", "i32.load8_u", 2, IRType.MEMORY, 1, 1)       I32_LOAD8_U         = 0x2D, ///  i32.load8_u  m:memarg
174         @Instr("i32.load16_s", "i32.load16_s", 2, IRType.MEMORY, 1, 1)      I32_LOAD16_S        = 0x2E, ///  i32.load16_s m:memarg
175         @Instr("i32.load16_u", "i32.load16_u", 2, IRType.MEMORY, 1, 1)      I32_LOAD16_U        = 0x2F, ///  i32.load16_u m:memarg
176         @Instr("i64.load8_s", "i64.load8_s", 2, IRType.MEMORY, 1, 1)       I64_LOAD8_S         = 0x30, ///  i64.load8_s  m:memarg
177         @Instr("i64.load8_u", "i64.load8_u", 2, IRType.MEMORY, 1, 1)       I64_LOAD8_U         = 0x31, ///  i64.load8_u  m:memarg
178         @Instr("i64.load16_s", "i64.load16_s", 2, IRType.MEMORY, 1, 1)      I64_LOAD16_S        = 0x32, ///  i64.load16_s m:memarg
179         @Instr("i64.load16_u", "i64.load16_u", 2, IRType.MEMORY, 1, 1)      I64_LOAD16_U        = 0x33, ///  i64.load16_u m:memarg
180         @Instr("i64.load32_s", "i64.load32_s", 2, IRType.MEMORY, 1, 1)      I64_LOAD32_S        = 0x34, ///  i64.load32_s m:memarg
181         @Instr("i64.load32_u", "i64.load32_u", 2, IRType.MEMORY, 1, 1)      I64_LOAD32_U        = 0x35, ///  i64.load32_u m:memarg
182         @Instr("i32.store", "i32.store", 2, IRType.MEMORY, 2)            I32_STORE           = 0x36, ///  i32.store    m:memarg
183         @Instr("i64.store", "i64.store", 2, IRType.MEMORY, 2)            I64_STORE           = 0x37, ///  i64.store    m:memarg
184         @Instr("f32.store", "f32.store", 2, IRType.MEMORY, 2)            F32_STORE           = 0x38, ///  f32.store    m:memarg
185         @Instr("f64.store", "f64.store", 2, IRType.MEMORY, 2)            F64_STORE           = 0x39, ///  f64.store    m:memarg
186         @Instr("i32.store8", "i32.store8", 2, IRType.MEMORY, 2)           I32_STORE8          = 0x3A, ///  i32.store8   m:memarg
187         @Instr("i32.store16", "i32.store16", 2, IRType.MEMORY, 2)          I32_STORE16         = 0x3B, ///  i32.store16  m:memarg
188         @Instr("i64.store8", "i64.store8", 2, IRType.MEMORY, 2)           I64_STORE8          = 0x3C, ///  i64.store8   m:memarg
189         @Instr("i64.store16", "i64.store16", 2, IRType.MEMORY, 2)          I64_STORE16         = 0x3D, ///  i64.store16  m:memarg
190         @Instr("i64.store32", "i64.store32", 2, IRType.MEMORY, 2)          I64_STORE32         = 0x3E, ///  i64.store32  m:memarg
191         @Instr("memory.size", "memory_size", 7, IRType.MEMOP, 0, 2)        MEMORY_SIZE         = 0x3F, ///  memory.size  0x00
192         @Instr("memory.grow", "grow_memory", 7, IRType.MEMOP, 1, 2)        MEMORY_GROW         = 0x40, ///  memory.grow  0x00
193 
194         @Instr("i32.const", "i32.const", 1, IRType.CONST, 0, 1)          I32_CONST           = 0x41, ///  i32.const n:i32
195         @Instr("i64.const", "i64.const", 1, IRType.CONST, 0, 1)          I64_CONST           = 0x42, ///  i64.const n:i64
196         @Instr("f32.const", "f32.const", 1, IRType.CONST, 0, 1)          F32_CONST           = 0x43, ///  f32.const z:f32
197         @Instr("f64.const", "f64.const", 1, IRType.CONST, 0, 1)          F64_CONST           = 0x44, ///  f64.const z:f64
198 
199         @Instr("i32.eqz", "i32.eqz", 1, IRType.CODE, 1)                I32_EQZ             = 0x45, ///  i32.eqz
200         @Instr("i32.eq", "i32.eq", 1, IRType.CODE, 2, 1)                 I32_EQ              = 0x46, ///  i32.eq
201         @Instr("i32.ne", "i32.ne", 1, IRType.CODE, 2, 1)                 I32_NE              = 0x47, ///  i32.ne
202         @Instr("i32.lt_s", "i32.lt_s", 1, IRType.CODE, 2, 1)            I32_LT_S            = 0x48, ///  i32.lt_s
203         @Instr("i32.lt_u", "i32.lt_u", 1, IRType.CODE, 2, 1)            I32_LT_U            = 0x49, ///  i32.lt_u
204         @Instr("i32.gt_s", "i32.gt_s", 1, IRType.CODE, 2, 1)            I32_GT_S            = 0x4A, ///  i32.gt_s
205         @Instr("i32.gt_u", "i32.gt_u", 1, IRType.CODE, 2, 1)            I32_GT_U            = 0x4B, ///  i32.gt_u
206         @Instr("i32.le_s", "i32.le_s", 1, IRType.CODE, 2, 1)            I32_LE_S            = 0x4C, ///  i32.le_s
207         @Instr("i32.le_u", "i32.le_u", 1, IRType.CODE, 2, 1)            I32_LE_U            = 0x4D, ///  i32.le_u
208         @Instr("i32.ge_s", "i32.ge_s", 1, IRType.CODE, 2, 1)            I32_GE_S            = 0x4E, ///  i32.ge_s
209         @Instr("i32.ge_u", "i32.ge_u", 1, IRType.CODE, 2, 1)            I32_GE_U            = 0x4F, ///  i32.ge_u
210 
211         @Instr("i64.eqz", "i64.eqz", 1, IRType.CODE, 2, 1)             I64_EQZ             = 0x50, ///  i64.eqz
212         @Instr("i64.eq", "i64.eq", 1, IRType.CODE, 2, 1)              I64_EQ              = 0x51, ///  i64.eq
213         @Instr("i64.ne", "i64.ne", 1, IRType.CODE, 2, 1)              I64_NE              = 0x52, ///  i64.ne
214         @Instr("i64.lt_s", "i64.lt_s", 1, IRType.CODE, 2, 1)                  I64_LT_S            = 0x53, ///  i64.lt_s
215 
216         @Instr("i64.lt_u", "i64.lt_u", 1, IRType.CODE, 2, 1)            I64_LT_U            = 0x54, ///  i64.lt_u
217         @Instr("i64.gt_s", "i64.gt_s", 1, IRType.CODE, 2, 1)            I64_GT_S            = 0x55, ///  i64.gt_s
218         @Instr("i64.gt_u", "i64.gt_u", 1, IRType.CODE, 2, 1)            I64_GT_U            = 0x56, ///  i64.gt_u
219         @Instr("i64.le_s", "i64.le_s", 1, IRType.CODE, 2, 1)            I64_LE_S            = 0x57, ///  i64.le_s
220         @Instr("i64.le_u", "i64.le_u", 1, IRType.CODE, 2, 1)            I64_LE_U            = 0x58, ///  i64.le_u
221         @Instr("i64.ge_s", "i64.ge_s", 1, IRType.CODE, 2, 1)            I64_GE_S            = 0x59, ///  i64.ge_s
222         @Instr("i64.ge_u", "i64.ge_u", 1, IRType.CODE, 2, 1)            I64_GE_U            = 0x5A, ///  i64.ge_u
223 
224         @Instr("f32.eq", "f32.eq", 1, IRType.CODE, 2, 1)                 F32_EQ              = 0x5B, ///  f32.eq
225         @Instr("f32.ne", "f32.ne", 1, IRType.CODE, 2, 1)                 F32_NE              = 0x5C, ///  f32.ne
226         @Instr("f32.lt", "f32.lt", 1, IRType.CODE, 2, 1)                 F32_LT              = 0x5D, ///  f32.lt
227         @Instr("f32.gt", "f32.gt", 1, IRType.CODE, 2, 1)                 F32_GT              = 0x5E, ///  f32.gt
228         @Instr("f32.le", "f32.le", 1, IRType.CODE, 2, 1)                 F32_LE              = 0x5F, ///  f32.le
229         @Instr("f32.ge", "f32.ge", 1, IRType.CODE, 2, 1)                 F32_GE              = 0x60, ///  f32.ge
230 
231         @Instr("f64.eq", "f64.eq", 1, IRType.CODE, 2, 1)                 F64_EQ              = 0x61, ///  f64.eq
232         @Instr("f64.ne", "f64.ne", 1, IRType.CODE, 2, 1)                 F64_NE              = 0x62, ///  f64.ne
233         @Instr("f64.lt", "f64.lt", 1, IRType.CODE, 2, 1)                 F64_LT              = 0x63, ///  f64.lt
234         @Instr("f64.gt", "f64.gt", 1, IRType.CODE, 2, 1)                 F64_GT              = 0x64, ///  f64.gt
235         @Instr("f64.le", "f64.le", 1, IRType.CODE, 2, 1)                 F64_LE              = 0x65, ///  f64.le
236         @Instr("f64.ge", "f64.ge", 1, IRType.CODE, 2, 1)                 F64_GE              = 0x66, ///  f64.ge
237 
238             // instructions
239         @Instr("i32.clz", "i32.clz", 1, IRType.CODE, 1, 1)             I32_CLZ             = 0x67, ///  i32.clz
240         @Instr("i32.ctz", "i32.ctz", 1, IRType.CODE, 1, 1)             I32_CTZ             = 0x68, ///  i32.ctz
241         @Instr("i32.popcnt", "i32.popcnt", 1, IRType.CODE, 1, 1)          I32_POPCNT          = 0x69, ///  i32.popcnt
242         @Instr("i32.add", "i32.add", 1, IRType.CODE, 2, 1)             I32_ADD             = 0x6A, ///  i32.add
243         @Instr("i32.sub", "i32.sub", 1, IRType.CODE, 2, 1)             I32_SUB             = 0x6B, ///  i32.sub
244         @Instr("i32.mul", "i32.mul", 1, IRType.CODE, 2, 1)             I32_MUL             = 0x6C, ///  i32.mul
245         @Instr("i32.div_s", "i32.div_s", 1, IRType.CODE, 2, 1)           I32_DIV_S           = 0x6D, ///  i32.div_s
246         @Instr("i32.div_u", "i32.div_u", 1, IRType.CODE, 2, 1)           I32_DIV_U           = 0x6E, ///  i32.div_u
247         @Instr("i32.rem_s", "i32.rem_s", 1, IRType.CODE, 2, 1)           I32_REM_S           = 0x6F, ///  i32.rem_s
248         @Instr("i32.rem_u", "i32.rem_u", 1, IRType.CODE, 2, 1)           I32_REM_U           = 0x70, ///  i32.rem_u
249         @Instr("i32.and", "i32.and", 1, IRType.CODE, 2, 1)             I32_AND             = 0x71, ///  i32.and
250         @Instr("i32.or", "i32.or", 1, IRType.CODE, 2, 1)              I32_OR              = 0x72, ///  i32.or
251         @Instr("i32.xor", "i32.xor", 1, IRType.CODE, 2, 1)             I32_XOR             = 0x73, ///  i32.xor
252         @Instr("i32.shl", "i32.shl", 1, IRType.CODE, 2, 1)             I32_SHL             = 0x74, ///  i32.shl
253         @Instr("i32.shr_s", "i32.shr_s", 1, IRType.CODE, 2, 1)           I32_SHR_S           = 0x75, ///  i32.shr_s
254         @Instr("i32.shr_u", "i32.shr_u", 1, IRType.CODE, 2, 1)           I32_SHR_U           = 0x76, ///  i32.shr_u
255         @Instr("i32.rotl", "i32.rotl", 1, IRType.CODE, 2, 1)            I32_ROTL            = 0x77, ///  i32.rotl
256         @Instr("i32.rotr", "i32.rotr", 1, IRType.CODE, 2, 1)            I32_ROTR            = 0x78, ///  i32.rotr
257 
258         @Instr("i64.clz", "i64.clz", 1, IRType.CODE, 1, 1)             I64_CLZ             = 0x79, ///  i64.clz
259         @Instr("i64.ctz", "i64.ctz", 1, IRType.CODE, 1, 1)             I64_CTZ             = 0x7A, ///  i64.ctz
260         @Instr("i64.popcnt", "i64.popcnt", 1, IRType.CODE, 1, 1)          I64_POPCNT          = 0x7B, ///  i64.popcnt
261         @Instr("i64.add", "i64.add", 1, IRType.CODE, 2, 1)             I64_ADD             = 0x7C, ///  i64.add
262         @Instr("i64.sub", "i64.sub", 1, IRType.CODE, 2, 1)             I64_SUB             = 0x7D, ///  i64.sub
263         @Instr("i64.mul", "i64.mul", 1, IRType.CODE, 2, 1)             I64_MUL             = 0x7E, ///  i64.mul
264         @Instr("i64.div_s", "i64.div_s", 1, IRType.CODE, 2, 1)           I64_DIV_S           = 0x7F, ///  i64.div_s
265         @Instr("i64.div_u", "i64.div_u", 1, IRType.CODE, 2, 1)           I64_DIV_U           = 0x80, ///  i64.div_u
266         @Instr("i64.rem_s", "i64.rem_s", 1, IRType.CODE, 2, 1)           I64_REM_S           = 0x81, ///  i64.rem_s
267         @Instr("i64.rem_u", "i64.rem_u", 1, IRType.CODE, 2, 1)           I64_REM_U           = 0x82, ///  i64.rem_u
268         @Instr("i64.and", "i64.and", 1, IRType.CODE, 2, 1)             I64_AND             = 0x83, ///  i64.and
269         @Instr("i64.or", "i64.or", 1, IRType.CODE, 2, 1)              I64_OR              = 0x84, ///  i64.or
270         @Instr("i64.xor", "i64.xor", 1, IRType.CODE, 2, 1)             I64_XOR             = 0x85, ///  i64.xor
271         @Instr("i64.shl", "i64.shl", 1, IRType.CODE, 2, 1)             I64_SHL             = 0x86, ///  i64.shl
272         @Instr("i64.shr_s", "i64.shr_s", 1, IRType.CODE, 2, 1)           I64_SHR_S           = 0x87, ///  i64.shr_s
273         @Instr("i64.shr_u", "i64.shr_u", 1, IRType.CODE, 2, 1)           I64_SHR_U           = 0x88, ///  i64.shr_u
274         @Instr("i64.rotl", "i64.rotl", 1, IRType.CODE, 2, 1)            I64_ROTL            = 0x89, ///  i64.rotl
275         @Instr("i64.rotr", "i64.rotr", 1, IRType.CODE, 2, 1)            I64_ROTR            = 0x8A, ///  i64.rotr
276 
277         @Instr("f32.abs", "f32.abs", 1, IRType.CODE, 1, 1)             F32_ABS             = 0x8B, ///  f32.abs
278         @Instr("f32.neg", "f32.neg", 1, IRType.CODE, 1, 1)             F32_NEG             = 0x8C, ///  f32.neg
279         @Instr("f32.ceil", "f32.ceil", 1, IRType.CODE, 1, 1)            F32_CEIL            = 0x8D, ///  f32.ceil
280         @Instr("f32.floor", "f32.floor", 1, IRType.CODE, 1, 1)           F32_FLOOR           = 0x8E, ///  f32.floor
281         @Instr("f32.trunc", "f32.trunc", 1, IRType.CODE, 1, 1)           F32_TRUNC           = 0x8F, ///  f32.trunc
282         @Instr("f32.nearest", "f32.nearest", 1, IRType.CODE, 1, 1)         F32_NEAREST         = 0x90, ///  f32.nearest
283         @Instr("f32.sqrt", "f32.sqrt", 3, IRType.CODE, 1, 1)            F32_SQRT            = 0x91, ///  f32.sqrt
284         @Instr("f32.add", "f32.add", 3, IRType.CODE, 2, 1)             F32_ADD             = 0x92, ///  f32.add
285         @Instr("f32.sub", "f32.sub", 3, IRType.CODE, 2, 1)             F32_SUB             = 0x93, ///  f32.sub
286         @Instr("f32.mul", "f32.mul", 3, IRType.CODE, 2, 1)             F32_MUL             = 0x94, ///  f32.mul
287         @Instr("f32.div", "f32.div", 3, IRType.CODE, 2, 1)             F32_DIV             = 0x95, ///  f32.div
288         @Instr("f32.min", "f32.min", 1, IRType.CODE, 2, 1)             F32_MIN             = 0x96, ///  f32.min
289         @Instr("f32.max", "f32.max", 1, IRType.CODE, 2, 1)             F32_MAX             = 0x97, ///  f32.max
290         @Instr("f32.copysign", "f32.copysign", 1, IRType.CODE, 2, 1)        F32_COPYSIGN        = 0x98, ///  f32.copysign
291 
292         @Instr("f64.abs", "f64.abs", 1, IRType.CODE, 1, 1)             F64_ABS             = 0x99, ///  f64.abs
293         @Instr("f64.neg", "f64.neg", 1, IRType.CODE, 1, 1)             F64_NEG             = 0x9A, ///  f64.neg
294         @Instr("f64.ceil", "f64.ceil", 1, IRType.CODE, 1, 1)            F64_CEIL            = 0x9B, ///  f64.ceil
295         @Instr("f64.floor", "f64.floor", 1, IRType.CODE, 1, 1)           F64_FLOOR           = 0x9C, ///  f64.floor
296         @Instr("f64.trunc", "f64.trunc", 1, IRType.CODE, 1, 1)           F64_TRUNC           = 0x9D, ///  f64.trunc
297         @Instr("f64.nearest", "f64.nearest", 1, IRType.CODE, 1, 1)         F64_NEAREST         = 0x9E, ///  f64.nearest
298         @Instr("f64.sqrt", "f64.sqrt", 3, IRType.CODE, 1, 1)            F64_SQRT            = 0x9F, ///  f64.sqrt
299         @Instr("f64.add", "f64.add", 3, IRType.CODE, 2, 1)             F64_ADD             = 0xA0, ///  f64.add
300         @Instr("f64.sub", "f64.sub", 3, IRType.CODE, 2, 1)             F64_SUB             = 0xA1, ///  f64.sub
301         @Instr("f64.mul", "f64.mul", 3, IRType.CODE, 2, 1)             F64_MUL             = 0xA2, ///  f64.mul
302         @Instr("f64.div", "f64.div", 3, IRType.CODE, 2, 1)             F64_DIV             = 0xA3, ///  f64.div
303         @Instr("f64.min", "f64.min", 1, IRType.CODE, 2, 1)             F64_MIN             = 0xA4, ///  f64.min
304         @Instr("f64.max", "f64.max", 1, IRType.CODE, 2, 1)             F64_MAX             = 0xA5, ///  f64.max
305         @Instr("f64.copysign", "f64.copysign", 1, IRType.CODE, 2, 1)        F64_COPYSIGN        = 0xA6, ///  f64.copysign
306 
307         @Instr("i32.wrap_i64", "i32.wrap/i64", 1, IRType.CODE, 1, 1)        I32_WRAP_I64        = 0xA7, ///  i32.wrap_i64
308         @Instr("i32.trunc_f32_s", "i32.trunc_s/f32", 1, IRType.CODE, 1, 1)     I32_TRUNC_F32_S     = 0xA8, ///  i32.trunc_f32_s
309         @Instr("i32.trunc_f32_u", "i32.trunc_u/f32", 1, IRType.CODE, 1, 1)     I32_TRUNC_F32_U     = 0xA9, ///  i32.trunc_f32_u
310         @Instr("i32.trunc_f64_s", "i32.trunc_s/f64", 1, IRType.CODE, 1, 1)     I32_TRUNC_F64_S     = 0xAA, ///  i32.trunc_f64_s
311         @Instr("i32.trunc_f64_u", "i32.trunc_u/f64", 1, IRType.CODE, 1, 1)     I32_TRUNC_F64_U     = 0xAB, ///  i32.trunc_f64_u
312         @Instr("i64.extend_i32_s", "i64.extend_s/i32", 1, IRType.CODE, 1, 1)    I64_EXTEND_I32_S    = 0xAC, ///  i64.extend_i32_s
313         @Instr("i64.extend_i32_u", "i64.extend_u/i32", 1, IRType.CODE, 1, 1)    I64_EXTEND_I32_U    = 0xAD, ///  i64.extend_i32_u
314         @Instr("i32.extend8_s", "i32.extend8_s", 1, IRType.CODE, 1, 1)       I32_EXTEND8_S       = 0xC0, ///  i32.extend8_s
315         @Instr("i32.extend16_s", "i32.extend16_s", 1, IRType.CODE, 1, 1)      I32_EXTEND16_S      = 0xC1, ///  i32.extend16_s
316         @Instr("i64.extend8_s", "i64.extend8_s", 1, IRType.CODE, 1, 1)       I64_EXTEND8_S       = 0xC2, ///  i64.extend8_s
317         @Instr("i64.extend16_s", "i64.extend16_s", 1, IRType.CODE, 1, 1)      I64_EXTEND16_S      = 0xC3, ///  i64.extend16_s
318         @Instr("i64.extend32_s", "i64.extend32_s", 1, IRType.CODE, 1, 1)      I64_EXTEND32_S     = 0xC4, ///  i64.extend32_s
319         @Instr("i64.trunc_f32_s", "i64.trunc_s/f32", 1, IRType.CODE, 1, 1)     I64_TRUNC_F32_S     = 0xAE, ///  i64.trunc_f32_s
320         @Instr("i64.trunc_f32_u", "i64.trunc_u/f32", 1, IRType.CODE, 1, 1)     I64_TRUNC_F32_U     = 0xAF, ///  i64.trunc_f32_u
321         @Instr("i64.trunc_f64_s", "i64.trunc_s/f64", 1, IRType.CODE, 1, 1)     I64_TRUNC_F64_S     = 0xB0, ///  i64.trunc_f64_s
322         @Instr("i64.trunc_f64_u", "i64.trunc_u/f64", 1, IRType.CODE, 1, 1)     I64_TRUNC_F64_U     = 0xB1, ///  i64.trunc_f64_u
323         @Instr("f32.convert_i32_s", "f32.convert_s/i32", 1, IRType.CODE, 1, 1)   F32_CONVERT_I32_S   = 0xB2, ///  f32.convert_i32_s
324         @Instr("f32.convert_i32_u", "f32.convert_u/i32", 1, IRType.CODE, 1, 1)   F32_CONVERT_I32_U   = 0xB3, ///  f32.convert_i32_u
325         @Instr("f32.convert_i64_s", "f32.convert_s/i64", 1, IRType.CODE, 1, 1)   F32_CONVERT_I64_S   = 0xB4, ///  f32.convert_i64_s
326         @Instr("f32.convert_i64_u", "f32.convert_u/i64", 1, IRType.CODE, 1, 1)   F32_CONVERT_I64_U   = 0xB5, ///  f32.convert_i64_u
327         @Instr("f32.demote_f64", "f32.demote/f64", 1, IRType.CODE, 1, 1)      F32_DEMOTE_F64      = 0xB6, ///  f32.demote_f64
328         @Instr("f64.convert_i32_s", "f64.convert_s/i32", 1, IRType.CODE, 1, 1)   F64_CONVERT_I32_S   = 0xB7, ///  f64.convert_i32_s
329         @Instr("f64.convert_i32_u", "f64.convert_u/i32", 1, IRType.CODE, 1, 1)   F64_CONVERT_I32_U   = 0xB8, ///  f64.convert_i32_u
330         @Instr("f64.convert_i64_s", "f64.convert_s/i64", 1, IRType.CODE, 1, 1)   F64_CONVERT_I64_S   = 0xB9, ///  f64.convert_i64_s
331         @Instr("f64.convert_i64_u", "f64.convert_u/i64", 1, IRType.CODE, 1, 1)   F64_CONVERT_I64_U   = 0xBA, ///  f64.convert_i64_u
332         @Instr("f64.promote_f32", "f64.promote/f32", 1, IRType.CODE, 1, 1)     F64_PROMOTE_F32     = 0xBB, ///  f64.promote_f32
333         @Instr("i32.reinterpret_f32", "i32.reinterpret/f32", 1, IRType.CODE, 1, 1) I32_REINTERPRET_F32 = 0xBC, ///  i32.reinterpret_f32
334         @Instr("i64.reinterpret_f64", "i64.reinterpret/f64", 1, IRType.CODE, 1, 1) I64_REINTERPRET_F64 = 0xBD, ///  i64.reinterpret_f64
335         @Instr("f32.reinterpret_i32", "f32.reinterpret/i32", 1, IRType.CODE, 1, 1) F32_REINTERPRET_I32 = 0xBE, ///  f32.reinterpret_i32
336         @Instr("f64.reinterpret_i64", "f64.reinterpret/i64", 1, IRType.CODE, 1, 1) F64_REINTERPRET_I64 = 0xBF, ///  f64.reinterpret_i64
337         @Instr("truct_sat", "truct_sat", 1, IRType.CODE, 1, 1, true)     TRUNC_SAT           = 0xFC, ///  TYPE.truct_sat_TYPE_SIGN
338             // dfmt on
339 
340 }
341 
342 Instr getInstr(IR ir)() {
343     enum code = format!q{enum result = getUDAs!(%s, Instr)[0];}(ir.stringof);
344     mixin(code);
345     return result;
346 }
347 
348 static unittest {
349     enum InstrUnreachable = Instr("unreachable", "unreachable", 1, IRType.CODE);
350     static assert(getInstr!(IR.UNREACHABLE) == InstrUnreachable);
351     enum ir = IR.UNREACHABLE;
352     static assert(getInstr!(ir) == InstrUnreachable);
353 }
354 
355 shared static immutable(Instr[IR]) instrTable;
356 shared static immutable(IR[string]) irLookupTable;
357 shared static immutable(Instr[string]) instrWastLookup;
358 
359 enum PseudoWastInstr {
360     invoke = "invoke",
361     if_else = "if_else",
362     call_import = "call_import",
363     local = "local",
364     label = "label",
365     tableswitch = "tableswitch",
366     table = "table",
367     case_ = "case",
368     memory_size = "memory_size",
369 }
370 
371 protected immutable(Instr[IR]) generate_instrTable() {
372     Instr[IR] result;
373     with (IR) {
374         static foreach (E; EnumMembers!IR) {
375             {
376                 enum code = format!q{result[%1$s]=getUDAs!(%1$s, Instr)[0];}(E.stringof);
377                 mixin(code);
378             }
379         }
380     }
381     return assumeUnique(result);
382 }
383 
384 shared static this() {
385     instrTable = generate_instrTable;
386     immutable(IR[string]) generateLookupTable() {
387         IR[string] result;
388         foreach (ir, ref instr; instrTable) {
389             result[instr.name] = ir;
390         }
391         return assumeUnique(result);
392     }
393 
394     irLookupTable = generateLookupTable;
395 
396     immutable(Instr[string]) generated_instrWastLookup() {
397         Instr[string] result;
398         static foreach (ir; EnumMembers!IR) {
399             {
400                 enum instr = getInstr!ir;
401                 result[instr.wast] = instr;
402             }
403         }
404         void setPseudo(const PseudoWastInstr pseudo, const IRType ir_type, const uint pushs = 0, const uint pops = 0) {
405             result[pseudo] = Instr("<" ~ pseudo ~ ">", pseudo, uint.max, ir_type, pops, pushs);
406         }
407 
408         setPseudo(PseudoWastInstr.invoke, IRType.CALL, 0, 1);
409         setPseudo(PseudoWastInstr.if_else, IRType.BRANCH, 3, 1);
410         setPseudo(PseudoWastInstr.local, IRType.SYMBOL, 0, uint.max);
411         setPseudo(PseudoWastInstr.label, IRType.SYMBOL, 1, uint.max);
412         setPseudo(PseudoWastInstr.call_import, IRType.CALL);
413         setPseudo(PseudoWastInstr.tableswitch, IRType.SYMBOL, uint.max, uint.max);
414         setPseudo(PseudoWastInstr.table, IRType.SYMBOL, uint.max);
415         setPseudo(PseudoWastInstr.case_, IRType.SYMBOL, uint.max, 1);
416 
417         result["i32.select"] = instrTable[IR.SELECT];
418         result["i64.select"] = instrTable[IR.SELECT];
419         result["f32.select"] = instrTable[IR.SELECT];
420         result["f64.select"] = instrTable[IR.SELECT];
421 
422         return assumeUnique(result);
423     }
424 
425     instrWastLookup = generated_instrWastLookup;
426 }
427 
428 enum IR_TRUNC_SAT : ubyte {
429     @Instr("i32.trunc_sat_f32_s", "i32.trunc_sat_f32_s", 3, IRType.CODE, 1, 1) I32_F32_S,
430     @Instr("i32.trunc_sat_f32_u", "i32.trunc_sat_f32_u", 3, IRType.CODE, 1, 1) I32_F32_U,
431     @Instr("i32.trunc_sat_f64_s", "i32.trunc_sat_f64_s", 3, IRType.CODE, 1, 1) I32_F64_S,
432     @Instr("i32.trunc_sat_f64_u", "i32.trunc_sat_f64_u", 3, IRType.CODE, 1, 1) I32_F64_U,
433     @Instr("i64.trunc_sat_f32_s", "i64.trunc_sat_f32_s", 3, IRType.CODE, 1, 1) I64_F32_S,
434     @Instr("i64.trunc_sat_f32_u", "i64.trunc_sat_f32_u", 3, IRType.CODE, 1, 1) I64_F32_U,
435     @Instr("i64.trunc_sat_f64_s", "i64.trunc_sat_f64_s", 3, IRType.CODE, 1, 1) I64_F64_S,
436     @Instr("i64.trunc_sat_f64_u", "i64.trunc_sat_f64_u", 3, IRType.CODE, 1, 1) I64_F64_U,
437 }
438 
439 version (none) {
440     shared static immutable(string[IR_TRUNC_SAT]) trunc_sat_mnemonic;
441 
442     shared static this() {
443         with (IR_TRUNC_SAT) {
444             trunc_sat_mnemonic = [
445                 I32_F32_S: "i32.trunc_sat_f32_s",
446                 I32_F32_U: "i32.trunc_sat_f32_u",
447                 I32_F64_S: "i32.trunc_sat_f64_s",
448                 I32_F64_U: "i32.trunc_sat_f64_u",
449                 I64_F32_S: "i64.trunc_sat_f32_s",
450                 I64_F32_U: "i64.trunc_sat_f32_u",
451                 I64_F64_S: "i64.trunc_sat_f64_s",
452                 I64_F64_U: "i64.trunc_sat_f64_u",
453             ];
454         }
455     }
456 }
457 
458 unittest {
459     size_t i;
460     foreach (E; EnumMembers!IR) {
461         assert(E in instrTable);
462         i++;
463     }
464     assert(i == instrTable.length);
465 }
466 
467 enum Limits : ubyte {
468     INFINITE = 0x00, ///  n:u32       ⇒ {min n, max ε}
469     RANGE = 0x01, /// n:u32 m:u32  ⇒ {min n, max m}
470 }
471 
472 enum Mutable : ubyte {
473     CONST = 0x00,
474     VAR = 0x01,
475 }
476 
477 enum Types : ubyte {
478     EMPTY = 0x40, /// Empty block
479     @("func") FUNC = 0x60, /// functype
480     @("funcref") FUNCREF = 0x70, /// funcref
481     @("i32") I32 = 0x7F, /// i32 valtype
482     @("i64") I64 = 0x7E, /// i64 valtype
483     @("f32") F32 = 0x7D, /// f32 valtype
484     @("f64") F64 = 0x7C, /// f64 valtype
485 }
486 
487 template toWasmType(T) {
488     static if (is(T == int)) {
489         enum toWasmType = Types.I32;
490     }
491     else static if (is(T == long)) {
492         enum toWasmType = Types.I64;
493     }
494     else static if (is(T == float)) {
495         enum toWasmType = Types.F32;
496     }
497     else static if (is(T == double)) {
498         enum toWasmType = Types.F64;
499     }
500     else static if (isFunctionPointer!T) {
501         enum toWasmType = Types.FUNCREF;
502     }
503     else {
504         enum toWasmType = Types.EMPTY;
505     }
506 }
507 
508 unittest {
509     static assert(toWasmType!int  is Types.I32);
510     static assert(toWasmType!void  is Types.EMPTY);
511 }
512 
513 template toDType(Types t) {
514     static if (t is Types.I32) {
515         alias toDType = int;
516     }
517     else static if (t is Types.I64) {
518         alias toDType = long;
519     }
520     else static if (t is Types.F32) {
521         alias toDType = float;
522     }
523     else static if (t is Types.F64) {
524         alias toDType = double;
525     }
526     else static if (t is Types.FUNCREF) {
527         alias toDType = void*;
528     }
529     else {
530         alias toDType = void;
531     }
532 }
533 
534 @safe static string typesName(const Types type) pure {
535     import std.conv : to;
536     import std.uni : toLower;
537 
538     final switch (type) {
539         static foreach (E; EnumMembers!Types) {
540     case E:
541             return toLower(E.to!string);
542         }
543     }
544 }
545 
546 @safe static Types getType(const string name) pure {
547     import std.traits;
548 
549     switch (name) {
550         static foreach (E; EnumMembers!Types) {
551             static if (hasUDA!(E, string)) {
552     case getUDAs!(E, string)[0]:
553                 return E;
554             }
555         }
556     default:
557         return Types.EMPTY;
558     }
559 }
560 
561 @safe
562 unittest {
563     assert("f32".getType == Types.F32);
564     assert("empty".getType == Types.EMPTY);
565     assert("not-valid".getType == Types.EMPTY);
566 
567 }
568 
569 enum IndexType : ubyte {
570     @("func") FUNC = 0x00, /// func x:typeidx
571     @("table") TABLE = 0x01, /// func  tt:tabletype
572     @("memory") MEMORY = 0x02, /// mem mt:memtype
573     @("global") GLOBAL = 0x03, /// global gt:globaltype
574 }
575 
576 @safe static string indexName(const IndexType idx) pure {
577     import std.conv : to;
578     import std.uni : toLower;
579 
580     final switch (idx) {
581         foreach (E; EnumMembers!IndexType) {
582     case E:
583             return toLower(E.to!string);
584         }
585     }
586 }
587 
588 T decode(T)(immutable(ubyte[]) data, ref size_t index) pure {
589     size_t byte_size;
590     const leb128_index = LEB128.decode!T(data[index .. $]);
591     scope (exit) {
592         index += leb128_index.size;
593     }
594     return leb128_index.value;
595 }
596 
597 alias u32 = decode!uint;
598 alias u64 = decode!ulong;
599 alias i32 = decode!int;
600 alias i64 = decode!long;
601 
602 string secname(immutable Section s) @safe {
603     import std.exception : assumeUnique;
604 
605     return assumeUnique(format("%s_sec", toLower(s.to!string)));
606 }
607 
608 alias SectionsT(SectionType) = AliasSeq!(
609         SectionType.Custom,
610         SectionType.Type,
611         SectionType.Import,
612         SectionType.Function,
613         SectionType.Table,
614         SectionType.Memory,
615         SectionType.Global,
616         SectionType.Export,
617         SectionType.Start,
618         SectionType.Element,
619         SectionType.Code,
620         SectionType.Data,
621 );
622 
623 protected string GenerateInterfaceModule(T...)() {
624     import std.array : join;
625 
626     string[] result;
627     static foreach (i, E; EnumMembers!Section) {
628         result ~= format(q{alias SecType_%s=T[Section.%s];}, i, E);
629         result ~= format(q{void %s(ref ConstOf!(SecType_%s) sec);}, secname(E), i);
630     }
631     return result.join("\n");
632 }
633 
634 interface InterfaceModuleT(T...) {
635     enum code = GenerateInterfaceModule!(T)();
636     mixin(code);
637 }
638 
639 version (none) bool isWasmModule(alias M)() @safe if (is(M == struct) || is(M == class)) {
640     import std.algorithm;
641 
642     enum all_members = [__traits(allMembers, M)];
643     return [EnumMembers!Section]
644         .map!(sec => sec.secname)
645         .all!(name => all_members.canFind(name));
646 }
647 
648 @safe struct WasmArg {
649     protected {
650         Types _type;
651         union {
652             @(Types.I32) int i32;
653             @(Types.I64) long i64;
654             @(Types.F32) float f32;
655             @(Types.F64) double f64;
656         }
657     }
658 
659     static WasmArg undefine() pure nothrow {
660         WasmArg result;
661         result._type = Types.EMPTY;
662         return result;
663     }
664 
665     void opAssign(T)(T x) nothrow {
666         alias BaseT = Unqual!T;
667         static if (is(BaseT == int) || is(BaseT == uint)) {
668             _type = Types.I32;
669             i32 = cast(int) x;
670         }
671         else static if (is(BaseT == long) || is(BaseT == ulong)) {
672             _type = Types.I64;
673             i64 = cast(long) x;
674         }
675         else static if (is(BaseT == float)) {
676             _type = Types.F32;
677             f32 = x;
678         }
679         else static if (is(BaseT == double)) {
680             _type = Types.F64;
681             f64 = x;
682         }
683         else static if (is(BaseT == WasmArg)) {
684             emplace!WasmArg(&this, x);
685         }
686         else {
687             static assert(0, format("Type %s is not supported by WasmArg", T.stringof));
688         }
689     }
690 
691     T get(T)() const {
692         alias BaseT = Unqual!T;
693         static if (is(BaseT == int) || is(BaseT == uint)) {
694             check(_type is Types.I32, format("Wrong to type %s execpted %s", _type, Types.I32));
695             return cast(T) i32;
696         }
697         else static if (is(BaseT == long) || is(BaseT == ulong)) {
698             check(_type is Types.I64, format("Wrong to type %s execpted %s", _type, Types.I64));
699             return cast(T) i64;
700         }
701         else static if (is(BaseT == float)) {
702             check(_type is Types.F32, format("Wrong to type %s execpted %s", _type, Types.F32));
703             return f32;
704         }
705         else static if (is(BaseT == double)) {
706             check(_type is Types.F64, format("Wrong to type %s execpted %s", _type, Types.F64));
707             return f64;
708         }
709     }
710 
711     @property Types type() const pure nothrow {
712         return _type;
713     }
714 
715 }
716 
717 static assert(isInputRange!ExprRange);
718 @safe struct ExprRange {
719     immutable(ubyte[]) data;
720 
721     protected {
722         size_t _index;
723         int _level;
724         IRElement current;
725         WasmException wasm_exception;
726     }
727 
728     const(WasmException) exception() const pure nothrow @nogc {
729         return wasm_exception;
730     }
731 
732     struct IRElement {
733         IR code;
734         int level;
735         private {
736             WasmArg _warg;
737             WasmArg[] _wargs;
738             const(Types)[] _types;
739         }
740 
741         enum unreachable = IRElement(IR.UNREACHABLE);
742 
743         const(WasmArg) warg() const pure nothrow {
744             return _warg;
745         }
746 
747         const(WasmArg[]) wargs() const pure nothrow {
748             return _wargs;
749         }
750 
751         const(Types[]) types() const pure nothrow {
752             return _types;
753         }
754 
755     }
756 
757     this(immutable(ubyte[]) data) pure {
758         this.data = data;
759         set_front(current, _index);
760     }
761 
762     @safe protected void set_front(ref scope IRElement elm, ref size_t index) pure {
763         @trusted void set(ref WasmArg warg, const Types type) pure nothrow {
764             with (Types) {
765                 switch (type) {
766                 case I32:
767                     warg = i32(data, index);
768                     break;
769                 case I64:
770                     warg = i64(data, index);
771                     break;
772                 case F32:
773                     warg = data.binpeek!(float, Endian.littleEndian)(&index);
774                     break;
775                 case F64:
776                     warg = data.binpeek!(double, Endian.littleEndian)(&index);
777                     break;
778                 default:
779                     assumeWontThrow({
780                         wasm_exception = new WasmException(format(
781                             "Assembler argument type not vaild as an argument %s", type));
782                     });
783                 }
784             }
785         }
786 
787         if (index < data.length) {
788             elm.code = cast(IR) data[index];
789             elm._types = null;
790             const instr = instrTable.get(elm.code, illegalInstr);
791             index += IR.sizeof;
792             with (IRType) {
793                 final switch (instr.irtype) {
794                 case CODE:
795                     break;
796                 case PREFIX:
797                     elm._warg = int(data[index]); // Extened insruction
798                     index += ubyte.sizeof;
799                     break;
800                 case BLOCK:
801                     elm._types = [cast(Types) data[index]];
802                     index += Types.sizeof;
803                     _level++;
804                     break;
805                 case BRANCH:
806                 case BRANCH_IF:
807                     // branchidx
808                     set(elm._warg, Types.I32);
809                     _level++;
810                     break;
811                 case BRANCH_TABLE:
812                     //size_t vec_size;
813                     const len = u32(data, index) + 1;
814                     elm._wargs = new WasmArg[len];
815                     foreach (ref a; elm._wargs) {
816                         a = u32(data, index);
817                     }
818                     break;
819                 case CALL:
820                     // callidx
821                     set(elm._warg, Types.I32);
822                     break;
823                 case CALL_INDIRECT:
824                     // typeidx
825                     set(elm._warg, Types.I32);
826                     scope (exit) {
827                         index += ubyte.sizeof;
828                     }
829                     if (!(data[index] == 0x00)) {
830                         throw new WasmException("call_indirect should end with 0x00");
831                     }
832                     break;
833                 case LOCAL, GLOBAL:
834                     // localidx globalidx
835                     set(elm._warg, Types.I32);
836                     break;
837                 case MEMORY:
838                     // offset
839                     elm._wargs = new WasmArg[2];
840                     set(elm._wargs[0], Types.I32);
841                     // align
842                     set(elm._wargs[1], Types.I32);
843                     break;
844                 case MEMOP:
845                     index++;
846                     break;
847                 case CONST:
848                     with (IR) {
849                         switch (elm.code) {
850                         case I32_CONST:
851                             set(elm._warg, Types.I32);
852                             break;
853                         case I64_CONST:
854                             set(elm._warg, Types.I64);
855                             break;
856                         case F32_CONST:
857                             set(elm._warg, Types.F32);
858                             break;
859                         case F64_CONST:
860                             set(elm._warg, Types.F64);
861                             break;
862                         default:
863                             throw new WasmException(format("Instruction %s is not a const", elm.code));
864                         }
865                     }
866                     break;
867                 case END:
868                     _level--;
869                     break;
870                 case ILLEGAL:
871 
872                     throw new WasmException(format("%s:Illegal opcode %02X", __FUNCTION__, elm.code));
873                     break;
874                 case SYMBOL:
875                     assert(0, "Is a symbol and it does not have an equivalent opcode");
876                 }
877 
878             }
879         }
880         else {
881             if (index == data.length) {
882                 index++;
883             }
884             elm.code = IR.UNREACHABLE;
885         }
886     }
887 
888     pure nothrow {
889         const(size_t) index() const {
890             return _index;
891         }
892 
893         const(IRElement) front() const {
894             return current;
895         }
896 
897         bool empty() const {
898             return _index > data.length || (wasm_exception !is null);
899         }
900 
901     }
902     void popFront() pure {
903         set_front(current, _index);
904     }
905 
906 }