Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2015 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/WasmBinaryToText.h"
20 :
21 : #include "jsnum.h"
22 : #include "jsprf.h"
23 :
24 : #include "vm/ArrayBufferObject.h"
25 : #include "vm/StringBuffer.h"
26 : #include "wasm/WasmAST.h"
27 : #include "wasm/WasmBinaryToAST.h"
28 : #include "wasm/WasmDebug.h"
29 : #include "wasm/WasmTextUtils.h"
30 : #include "wasm/WasmTypes.h"
31 :
32 : using namespace js;
33 : using namespace js::wasm;
34 :
35 : using mozilla::IsInfinite;
36 : using mozilla::IsNaN;
37 : using mozilla::IsNegativeZero;
38 :
39 : struct WasmRenderContext
40 : {
41 : JSContext* cx;
42 : AstModule* module;
43 : WasmPrintBuffer& buffer;
44 : GeneratedSourceMap* maybeSourceMap;
45 : uint32_t indent;
46 : uint32_t currentFuncIndex;
47 :
48 0 : WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer,
49 : GeneratedSourceMap* sourceMap)
50 0 : : cx(cx),
51 : module(module),
52 : buffer(buffer),
53 : maybeSourceMap(sourceMap),
54 : indent(0),
55 0 : currentFuncIndex(0)
56 0 : {}
57 :
58 0 : StringBuffer& sb() { return buffer.stringBuffer(); }
59 : };
60 :
61 : /*****************************************************************************/
62 : // utilities
63 :
64 : // Return true on purpose, so that we have a useful error message to provide to
65 : // the user.
66 : static bool
67 0 : Fail(WasmRenderContext& c, const char* msg)
68 : {
69 0 : c.buffer.stringBuffer().clear();
70 :
71 0 : return c.buffer.append("There was a problem when rendering the wasm text format: ") &&
72 0 : c.buffer.append(msg, strlen(msg)) &&
73 0 : c.buffer.append("\nYou should consider file a bug on Bugzilla in the "
74 : "Core:::JavaScript Engine::JIT component at "
75 0 : "https://bugzilla.mozilla.org/enter_bug.cgi.");
76 : }
77 :
78 : static bool
79 0 : RenderIndent(WasmRenderContext& c)
80 : {
81 0 : for (uint32_t i = 0; i < c.indent; i++) {
82 0 : if (!c.buffer.append(" "))
83 0 : return false;
84 : }
85 0 : return true;
86 : }
87 :
88 : static bool
89 0 : RenderInt32(WasmRenderContext& c, int32_t num)
90 : {
91 0 : return NumberValueToStringBuffer(c.cx, Int32Value(num), c.sb());
92 : }
93 :
94 : static bool
95 0 : RenderInt64(WasmRenderContext& c, int64_t num)
96 : {
97 0 : if (num < 0 && !c.buffer.append("-"))
98 0 : return false;
99 0 : if (!num)
100 0 : return c.buffer.append("0");
101 0 : return RenderInBase<10>(c.sb(), mozilla::Abs(num));
102 : }
103 :
104 : static bool
105 0 : RenderDouble(WasmRenderContext& c, double d)
106 : {
107 0 : if (IsNaN(d))
108 0 : return RenderNaN(c.sb(), d);
109 0 : if (IsNegativeZero(d))
110 0 : return c.buffer.append("-0");
111 0 : if (IsInfinite(d)) {
112 0 : if (d > 0)
113 0 : return c.buffer.append("infinity");
114 0 : return c.buffer.append("-infinity");
115 : }
116 0 : return NumberValueToStringBuffer(c.cx, DoubleValue(d), c.sb());
117 : }
118 :
119 : static bool
120 0 : RenderFloat32(WasmRenderContext& c, float f)
121 : {
122 0 : if (IsNaN(f))
123 0 : return RenderNaN(c.sb(), f);
124 0 : return RenderDouble(c, double(f));
125 : }
126 :
127 : static bool
128 0 : RenderEscapedString(WasmRenderContext& c, const AstName& s)
129 : {
130 0 : size_t length = s.length();
131 0 : const char16_t* p = s.begin();
132 0 : for (size_t i = 0; i < length; i++) {
133 0 : char16_t byte = p[i];
134 0 : switch (byte) {
135 : case '\n':
136 0 : if (!c.buffer.append("\\n"))
137 0 : return false;
138 0 : break;
139 : case '\r':
140 0 : if (!c.buffer.append("\\0d"))
141 0 : return false;
142 0 : break;
143 : case '\t':
144 0 : if (!c.buffer.append("\\t"))
145 0 : return false;
146 0 : break;
147 : case '\f':
148 0 : if (!c.buffer.append("\\0c"))
149 0 : return false;
150 0 : break;
151 : case '\b':
152 0 : if (!c.buffer.append("\\08"))
153 0 : return false;
154 0 : break;
155 : case '\\':
156 0 : if (!c.buffer.append("\\\\"))
157 0 : return false;
158 0 : break;
159 : case '"' :
160 0 : if (!c.buffer.append("\\\""))
161 0 : return false;
162 0 : break;
163 : case '\'':
164 0 : if (!c.buffer.append("\\'"))
165 0 : return false;
166 0 : break;
167 : default:
168 0 : if (byte >= 32 && byte < 127) {
169 0 : if (!c.buffer.append((char)byte))
170 0 : return false;
171 : } else {
172 0 : char digit1 = byte / 16, digit2 = byte % 16;
173 0 : if (!c.buffer.append("\\"))
174 0 : return false;
175 0 : if (!c.buffer.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10)))
176 0 : return false;
177 0 : if (!c.buffer.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10)))
178 0 : return false;
179 : }
180 0 : break;
181 : }
182 : }
183 0 : return true;
184 : }
185 :
186 : static bool
187 0 : RenderExprType(WasmRenderContext& c, ExprType type)
188 : {
189 0 : switch (type) {
190 0 : case ExprType::Void: return true; // ignoring void
191 0 : case ExprType::I32: return c.buffer.append("i32");
192 0 : case ExprType::I64: return c.buffer.append("i64");
193 0 : case ExprType::F32: return c.buffer.append("f32");
194 0 : case ExprType::F64: return c.buffer.append("f64");
195 : default:;
196 : }
197 :
198 0 : MOZ_CRASH("bad type");
199 : }
200 :
201 : static bool
202 0 : RenderValType(WasmRenderContext& c, ValType type)
203 : {
204 0 : return RenderExprType(c, ToExprType(type));
205 : }
206 :
207 : static bool
208 0 : RenderName(WasmRenderContext& c, const AstName& name)
209 : {
210 0 : return c.buffer.append(name.begin(), name.end());
211 : }
212 :
213 : static bool
214 0 : RenderRef(WasmRenderContext& c, const AstRef& ref)
215 : {
216 0 : if (ref.name().empty())
217 0 : return RenderInt32(c, ref.index());
218 :
219 0 : return RenderName(c, ref.name());
220 : }
221 :
222 : static bool
223 0 : RenderBlockNameAndSignature(WasmRenderContext& c, const AstName& name, ExprType type)
224 : {
225 0 : if (!name.empty()) {
226 0 : if (!c.buffer.append(' '))
227 0 : return false;
228 :
229 0 : if (!RenderName(c, name))
230 0 : return false;
231 : }
232 :
233 0 : if (!IsVoid(type)) {
234 0 : if (!c.buffer.append(' '))
235 0 : return false;
236 :
237 0 : if (!RenderExprType(c, type))
238 0 : return false;
239 : }
240 :
241 0 : return true;
242 : }
243 :
244 : static bool
245 : RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine = true);
246 :
247 : #define MAP_AST_EXPR(c, expr) \
248 : if (c.maybeSourceMap) { \
249 : uint32_t lineno = c.buffer.lineno(); \
250 : uint32_t column = c.buffer.column(); \
251 : if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset())) \
252 : return false; \
253 : }
254 :
255 : /*****************************************************************************/
256 : // binary format parsing and rendering
257 :
258 : static bool
259 0 : RenderNop(WasmRenderContext& c, AstNop& nop)
260 : {
261 0 : if (!RenderIndent(c))
262 0 : return false;
263 0 : MAP_AST_EXPR(c, nop);
264 0 : return c.buffer.append("nop");
265 : }
266 :
267 : static bool
268 0 : RenderDrop(WasmRenderContext& c, AstDrop& drop)
269 : {
270 0 : if (!RenderExpr(c, drop.value()))
271 0 : return false;
272 :
273 0 : if (!RenderIndent(c))
274 0 : return false;
275 0 : MAP_AST_EXPR(c, drop);
276 0 : return c.buffer.append("drop");
277 : }
278 :
279 : static bool
280 0 : RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable)
281 : {
282 0 : if (!RenderIndent(c))
283 0 : return false;
284 0 : MAP_AST_EXPR(c, unreachable);
285 0 : return c.buffer.append("unreachable");
286 : }
287 :
288 : static bool
289 0 : RenderCallArgs(WasmRenderContext& c, const AstExprVector& args)
290 : {
291 0 : for (uint32_t i = 0; i < args.length(); i++) {
292 0 : if (!RenderExpr(c, *args[i]))
293 0 : return false;
294 : }
295 :
296 0 : return true;
297 : }
298 :
299 : static bool
300 0 : RenderCall(WasmRenderContext& c, AstCall& call)
301 : {
302 0 : if (!RenderCallArgs(c, call.args()))
303 0 : return false;
304 :
305 0 : if (!RenderIndent(c))
306 0 : return false;
307 :
308 0 : MAP_AST_EXPR(c, call);
309 0 : if (call.op() == Op::Call) {
310 0 : if (!c.buffer.append("call "))
311 0 : return false;
312 : } else {
313 0 : return Fail(c, "unexpected operator");
314 : }
315 :
316 0 : return RenderRef(c, call.func());
317 : }
318 :
319 : static bool
320 0 : RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call)
321 : {
322 0 : if (!RenderCallArgs(c, call.args()))
323 0 : return false;
324 :
325 0 : if (!RenderExpr(c, *call.index()))
326 0 : return false;
327 :
328 0 : if (!RenderIndent(c))
329 0 : return false;
330 :
331 0 : MAP_AST_EXPR(c, call);
332 0 : if (!c.buffer.append("call_indirect "))
333 0 : return false;
334 0 : return RenderRef(c, call.sig());
335 : }
336 :
337 : static bool
338 0 : RenderConst(WasmRenderContext& c, AstConst& cst)
339 : {
340 0 : if (!RenderIndent(c))
341 0 : return false;
342 :
343 0 : MAP_AST_EXPR(c, cst);
344 0 : if (!RenderValType(c, cst.val().type()))
345 0 : return false;
346 0 : if (!c.buffer.append(".const "))
347 0 : return false;
348 :
349 0 : switch (ToExprType(cst.val().type())) {
350 : case ExprType::I32:
351 0 : return RenderInt32(c, (int32_t)cst.val().i32());
352 : case ExprType::I64:
353 0 : return RenderInt64(c, (int64_t)cst.val().i64());
354 : case ExprType::F32:
355 0 : return RenderFloat32(c, cst.val().f32());
356 : case ExprType::F64:
357 0 : return RenderDouble(c, cst.val().f64());
358 : default:
359 0 : break;
360 : }
361 :
362 0 : return false;
363 : }
364 :
365 : static bool
366 0 : RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl)
367 : {
368 0 : if (!RenderIndent(c))
369 0 : return false;
370 :
371 0 : MAP_AST_EXPR(c, gl);
372 0 : if (!c.buffer.append("get_local "))
373 0 : return false;
374 0 : return RenderRef(c, gl.local());
375 : }
376 :
377 : static bool
378 0 : RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl)
379 : {
380 0 : if (!RenderExpr(c, sl.value()))
381 0 : return false;
382 :
383 0 : if (!RenderIndent(c))
384 0 : return false;
385 :
386 0 : MAP_AST_EXPR(c, sl);
387 0 : if (!c.buffer.append("set_local "))
388 0 : return false;
389 0 : return RenderRef(c, sl.local());
390 : }
391 :
392 : static bool
393 0 : RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl)
394 : {
395 0 : if (!RenderExpr(c, tl.value()))
396 0 : return false;
397 :
398 0 : if (!RenderIndent(c))
399 0 : return false;
400 :
401 0 : MAP_AST_EXPR(c, tl);
402 0 : if (!c.buffer.append("tee_local "))
403 0 : return false;
404 0 : return RenderRef(c, tl.local());
405 : }
406 :
407 : static bool
408 0 : RenderGetGlobal(WasmRenderContext& c, AstGetGlobal& gg)
409 : {
410 0 : if (!RenderIndent(c))
411 0 : return false;
412 :
413 0 : MAP_AST_EXPR(c, gg);
414 0 : if (!c.buffer.append("get_global "))
415 0 : return false;
416 0 : return RenderRef(c, gg.global());
417 : }
418 :
419 : static bool
420 0 : RenderSetGlobal(WasmRenderContext& c, AstSetGlobal& sg)
421 : {
422 0 : if (!RenderExpr(c, sg.value()))
423 0 : return false;
424 :
425 0 : if (!RenderIndent(c))
426 0 : return false;
427 :
428 0 : MAP_AST_EXPR(c, sg);
429 0 : if (!c.buffer.append("set_global "))
430 0 : return false;
431 0 : return RenderRef(c, sg.global());
432 : }
433 :
434 : static bool
435 0 : RenderExprList(WasmRenderContext& c, const AstExprVector& exprs, uint32_t startAt = 0)
436 : {
437 0 : for (uint32_t i = startAt; i < exprs.length(); i++) {
438 0 : if (!RenderExpr(c, *exprs[i]))
439 0 : return false;
440 : }
441 0 : return true;
442 : }
443 :
444 : static bool
445 0 : RenderBlock(WasmRenderContext& c, AstBlock& block, bool isInline = false)
446 : {
447 0 : if (!isInline && !RenderIndent(c))
448 0 : return false;
449 :
450 0 : MAP_AST_EXPR(c, block);
451 0 : if (block.op() == Op::Block) {
452 0 : if (!c.buffer.append("block"))
453 0 : return false;
454 0 : } else if (block.op() == Op::Loop) {
455 0 : if (!c.buffer.append("loop"))
456 0 : return false;
457 : } else {
458 0 : return Fail(c, "unexpected block kind");
459 : }
460 :
461 0 : if (!RenderBlockNameAndSignature(c, block.name(), block.type()))
462 0 : return false;
463 :
464 0 : uint32_t startAtSubExpr = 0;
465 :
466 : // If there is a stack of blocks, print them all inline.
467 0 : if (block.op() == Op::Block &&
468 0 : block.exprs().length() &&
469 0 : block.exprs()[0]->kind() == AstExprKind::Block &&
470 0 : block.exprs()[0]->as<AstBlock>().op() == Op::Block)
471 : {
472 0 : if (!c.buffer.append(' '))
473 0 : return false;
474 :
475 : // Render the first inner expr (block) at the same indent level, but
476 : // next instructions one level further.
477 0 : if (!RenderBlock(c, block.exprs()[0]->as<AstBlock>(), /* isInline */ true))
478 0 : return false;
479 :
480 0 : startAtSubExpr = 1;
481 : }
482 :
483 0 : if (!c.buffer.append('\n'))
484 0 : return false;
485 :
486 0 : c.indent++;
487 0 : if (!RenderExprList(c, block.exprs(), startAtSubExpr))
488 0 : return false;
489 0 : c.indent--;
490 :
491 0 : return RenderIndent(c) &&
492 0 : c.buffer.append("end ") &&
493 0 : RenderName(c, block.name());
494 : }
495 :
496 : static bool
497 0 : RenderFirst(WasmRenderContext& c, AstFirst& first)
498 : {
499 0 : return RenderExprList(c, first.exprs());
500 : }
501 :
502 : static bool
503 0 : RenderCurrentMemory(WasmRenderContext& c, AstCurrentMemory& cm)
504 : {
505 0 : if (!RenderIndent(c))
506 0 : return false;
507 :
508 0 : return c.buffer.append("current_memory\n");
509 : }
510 :
511 : static bool
512 0 : RenderGrowMemory(WasmRenderContext& c, AstGrowMemory& gm)
513 : {
514 0 : if (!RenderExpr(c, *gm.operand()))
515 0 : return false;
516 :
517 0 : if (!RenderIndent(c))
518 0 : return false;
519 :
520 0 : MAP_AST_EXPR(c, gm);
521 0 : return c.buffer.append("grow_memory\n");
522 : }
523 :
524 : static bool
525 0 : RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& unary)
526 : {
527 0 : if (!RenderExpr(c, *unary.operand()))
528 0 : return false;
529 :
530 0 : if (!RenderIndent(c))
531 0 : return false;
532 :
533 0 : MAP_AST_EXPR(c, unary);
534 : const char* opStr;
535 0 : switch (unary.op()) {
536 0 : case Op::I32Eqz: opStr = "i32.eqz"; break;
537 0 : case Op::I32Clz: opStr = "i32.clz"; break;
538 0 : case Op::I32Ctz: opStr = "i32.ctz"; break;
539 0 : case Op::I32Popcnt: opStr = "i32.popcnt"; break;
540 0 : case Op::I64Clz: opStr = "i64.clz"; break;
541 0 : case Op::I64Ctz: opStr = "i64.ctz"; break;
542 0 : case Op::I64Popcnt: opStr = "i64.popcnt"; break;
543 0 : case Op::F32Abs: opStr = "f32.abs"; break;
544 0 : case Op::F32Neg: opStr = "f32.neg"; break;
545 0 : case Op::F32Ceil: opStr = "f32.ceil"; break;
546 0 : case Op::F32Floor: opStr = "f32.floor"; break;
547 0 : case Op::F32Sqrt: opStr = "f32.sqrt"; break;
548 0 : case Op::F32Trunc: opStr = "f32.trunc"; break;
549 0 : case Op::F32Nearest: opStr = "f32.nearest"; break;
550 0 : case Op::F64Abs: opStr = "f64.abs"; break;
551 0 : case Op::F64Neg: opStr = "f64.neg"; break;
552 0 : case Op::F64Ceil: opStr = "f64.ceil"; break;
553 0 : case Op::F64Floor: opStr = "f64.floor"; break;
554 0 : case Op::F64Nearest: opStr = "f64.nearest"; break;
555 0 : case Op::F64Sqrt: opStr = "f64.sqrt"; break;
556 0 : case Op::F64Trunc: opStr = "f64.trunc"; break;
557 0 : default: return Fail(c, "unexpected unary operator");
558 : }
559 :
560 0 : return c.buffer.append(opStr, strlen(opStr));
561 : }
562 :
563 : static bool
564 0 : RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& binary)
565 : {
566 0 : if (!RenderExpr(c, *binary.lhs()))
567 0 : return false;
568 0 : if (!RenderExpr(c, *binary.rhs()))
569 0 : return false;
570 :
571 0 : if (!RenderIndent(c))
572 0 : return false;
573 :
574 0 : MAP_AST_EXPR(c, binary);
575 : const char* opStr;
576 0 : switch (binary.op()) {
577 0 : case Op::I32Add: opStr = "i32.add"; break;
578 0 : case Op::I32Sub: opStr = "i32.sub"; break;
579 0 : case Op::I32Mul: opStr = "i32.mul"; break;
580 0 : case Op::I32DivS: opStr = "i32.div_s"; break;
581 0 : case Op::I32DivU: opStr = "i32.div_u"; break;
582 0 : case Op::I32RemS: opStr = "i32.rem_s"; break;
583 0 : case Op::I32RemU: opStr = "i32.rem_u"; break;
584 0 : case Op::I32And: opStr = "i32.and"; break;
585 0 : case Op::I32Or: opStr = "i32.or"; break;
586 0 : case Op::I32Xor: opStr = "i32.xor"; break;
587 0 : case Op::I32Shl: opStr = "i32.shl"; break;
588 0 : case Op::I32ShrS: opStr = "i32.shr_s"; break;
589 0 : case Op::I32ShrU: opStr = "i32.shr_u"; break;
590 0 : case Op::I32Rotl: opStr = "i32.rotl"; break;
591 0 : case Op::I32Rotr: opStr = "i32.rotr"; break;
592 0 : case Op::I64Add: opStr = "i64.add"; break;
593 0 : case Op::I64Sub: opStr = "i64.sub"; break;
594 0 : case Op::I64Mul: opStr = "i64.mul"; break;
595 0 : case Op::I64DivS: opStr = "i64.div_s"; break;
596 0 : case Op::I64DivU: opStr = "i64.div_u"; break;
597 0 : case Op::I64RemS: opStr = "i64.rem_s"; break;
598 0 : case Op::I64RemU: opStr = "i64.rem_u"; break;
599 0 : case Op::I64And: opStr = "i64.and"; break;
600 0 : case Op::I64Or: opStr = "i64.or"; break;
601 0 : case Op::I64Xor: opStr = "i64.xor"; break;
602 0 : case Op::I64Shl: opStr = "i64.shl"; break;
603 0 : case Op::I64ShrS: opStr = "i64.shr_s"; break;
604 0 : case Op::I64ShrU: opStr = "i64.shr_u"; break;
605 0 : case Op::I64Rotl: opStr = "i64.rotl"; break;
606 0 : case Op::I64Rotr: opStr = "i64.rotr"; break;
607 0 : case Op::F32Add: opStr = "f32.add"; break;
608 0 : case Op::F32Sub: opStr = "f32.sub"; break;
609 0 : case Op::F32Mul: opStr = "f32.mul"; break;
610 0 : case Op::F32Div: opStr = "f32.div"; break;
611 0 : case Op::F32Min: opStr = "f32.min"; break;
612 0 : case Op::F32Max: opStr = "f32.max"; break;
613 0 : case Op::F32CopySign: opStr = "f32.copysign"; break;
614 0 : case Op::F64Add: opStr = "f64.add"; break;
615 0 : case Op::F64Sub: opStr = "f64.sub"; break;
616 0 : case Op::F64Mul: opStr = "f64.mul"; break;
617 0 : case Op::F64Div: opStr = "f64.div"; break;
618 0 : case Op::F64Min: opStr = "f64.min"; break;
619 0 : case Op::F64Max: opStr = "f64.max"; break;
620 0 : case Op::F64CopySign: opStr = "f64.copysign"; break;
621 0 : default: return Fail(c, "unexpected binary operator");
622 : }
623 :
624 0 : return c.buffer.append(opStr, strlen(opStr));
625 : }
626 :
627 : static bool
628 0 : RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& ternary)
629 : {
630 0 : if (!RenderExpr(c, *ternary.op0()))
631 0 : return false;
632 0 : if (!RenderExpr(c, *ternary.op1()))
633 0 : return false;
634 0 : if (!RenderExpr(c, *ternary.op2()))
635 0 : return false;
636 :
637 0 : if (!RenderIndent(c))
638 0 : return false;
639 :
640 0 : MAP_AST_EXPR(c, ternary);
641 : const char* opStr;
642 0 : switch (ternary.op()) {
643 0 : case Op::Select: opStr = "select"; break;
644 0 : default: return Fail(c, "unexpected ternary operator");
645 : }
646 :
647 0 : return c.buffer.append(opStr, strlen(opStr));
648 : }
649 :
650 : static bool
651 0 : RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& comp)
652 : {
653 0 : if (!RenderExpr(c, *comp.lhs()))
654 0 : return false;
655 0 : if (!RenderExpr(c, *comp.rhs()))
656 0 : return false;
657 :
658 0 : if (!RenderIndent(c))
659 0 : return false;
660 :
661 0 : MAP_AST_EXPR(c, comp);
662 : const char* opStr;
663 0 : switch (comp.op()) {
664 0 : case Op::I32Eq: opStr = "i32.eq"; break;
665 0 : case Op::I32Ne: opStr = "i32.ne"; break;
666 0 : case Op::I32LtS: opStr = "i32.lt_s"; break;
667 0 : case Op::I32LtU: opStr = "i32.lt_u"; break;
668 0 : case Op::I32LeS: opStr = "i32.le_s"; break;
669 0 : case Op::I32LeU: opStr = "i32.le_u"; break;
670 0 : case Op::I32GtS: opStr = "i32.gt_s"; break;
671 0 : case Op::I32GtU: opStr = "i32.gt_u"; break;
672 0 : case Op::I32GeS: opStr = "i32.ge_s"; break;
673 0 : case Op::I32GeU: opStr = "i32.ge_u"; break;
674 0 : case Op::I64Eq: opStr = "i64.eq"; break;
675 0 : case Op::I64Ne: opStr = "i64.ne"; break;
676 0 : case Op::I64LtS: opStr = "i64.lt_s"; break;
677 0 : case Op::I64LtU: opStr = "i64.lt_u"; break;
678 0 : case Op::I64LeS: opStr = "i64.le_s"; break;
679 0 : case Op::I64LeU: opStr = "i64.le_u"; break;
680 0 : case Op::I64GtS: opStr = "i64.gt_s"; break;
681 0 : case Op::I64GtU: opStr = "i64.gt_u"; break;
682 0 : case Op::I64GeS: opStr = "i64.ge_s"; break;
683 0 : case Op::I64GeU: opStr = "i64.ge_u"; break;
684 0 : case Op::F32Eq: opStr = "f32.eq"; break;
685 0 : case Op::F32Ne: opStr = "f32.ne"; break;
686 0 : case Op::F32Lt: opStr = "f32.lt"; break;
687 0 : case Op::F32Le: opStr = "f32.le"; break;
688 0 : case Op::F32Gt: opStr = "f32.gt"; break;
689 0 : case Op::F32Ge: opStr = "f32.ge"; break;
690 0 : case Op::F64Eq: opStr = "f64.eq"; break;
691 0 : case Op::F64Ne: opStr = "f64.ne"; break;
692 0 : case Op::F64Lt: opStr = "f64.lt"; break;
693 0 : case Op::F64Le: opStr = "f64.le"; break;
694 0 : case Op::F64Gt: opStr = "f64.gt"; break;
695 0 : case Op::F64Ge: opStr = "f64.ge"; break;
696 0 : default: return Fail(c, "unexpected comparison operator");
697 : }
698 :
699 0 : return c.buffer.append(opStr, strlen(opStr));
700 : }
701 :
702 : static bool
703 0 : RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& conv)
704 : {
705 0 : if (!RenderExpr(c, *conv.operand()))
706 0 : return false;
707 :
708 0 : if (!RenderIndent(c))
709 0 : return false;
710 :
711 0 : MAP_AST_EXPR(c, conv);
712 : const char* opStr;
713 0 : switch (conv.op()) {
714 0 : case Op::I32WrapI64: opStr = "i32.wrap/i64"; break;
715 0 : case Op::I32TruncSF32: opStr = "i32.trunc_s/f32"; break;
716 0 : case Op::I32TruncUF32: opStr = "i32.trunc_u/f32"; break;
717 0 : case Op::I32ReinterpretF32: opStr = "i32.reinterpret/f32"; break;
718 0 : case Op::I32TruncSF64: opStr = "i32.trunc_s/f64"; break;
719 0 : case Op::I32TruncUF64: opStr = "i32.trunc_u/f64"; break;
720 0 : case Op::I64ExtendSI32: opStr = "i64.extend_s/i32"; break;
721 0 : case Op::I64ExtendUI32: opStr = "i64.extend_u/i32"; break;
722 0 : case Op::I64TruncSF32: opStr = "i64.trunc_s/f32"; break;
723 0 : case Op::I64TruncUF32: opStr = "i64.trunc_u/f32"; break;
724 0 : case Op::I64TruncSF64: opStr = "i64.trunc_s/f64"; break;
725 0 : case Op::I64TruncUF64: opStr = "i64.trunc_u/f64"; break;
726 0 : case Op::I64ReinterpretF64: opStr = "i64.reinterpret/f64"; break;
727 0 : case Op::F32ConvertSI32: opStr = "f32.convert_s/i32"; break;
728 0 : case Op::F32ConvertUI32: opStr = "f32.convert_u/i32"; break;
729 0 : case Op::F32ReinterpretI32: opStr = "f32.reinterpret/i32"; break;
730 0 : case Op::F32ConvertSI64: opStr = "f32.convert_s/i64"; break;
731 0 : case Op::F32ConvertUI64: opStr = "f32.convert_u/i64"; break;
732 0 : case Op::F32DemoteF64: opStr = "f32.demote/f64"; break;
733 0 : case Op::F64ConvertSI32: opStr = "f64.convert_s/i32"; break;
734 0 : case Op::F64ConvertUI32: opStr = "f64.convert_u/i32"; break;
735 0 : case Op::F64ConvertSI64: opStr = "f64.convert_s/i64"; break;
736 0 : case Op::F64ConvertUI64: opStr = "f64.convert_u/i64"; break;
737 0 : case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
738 0 : case Op::F64PromoteF32: opStr = "f64.promote/f32"; break;
739 0 : case Op::I32Eqz: opStr = "i32.eqz"; break;
740 0 : case Op::I64Eqz: opStr = "i64.eqz"; break;
741 0 : default: return Fail(c, "unexpected conversion operator");
742 : }
743 0 : return c.buffer.append(opStr, strlen(opStr));
744 : }
745 :
746 : static bool
747 0 : RenderIf(WasmRenderContext& c, AstIf& if_)
748 : {
749 0 : if (!RenderExpr(c, if_.cond()))
750 0 : return false;
751 :
752 0 : if (!RenderIndent(c))
753 0 : return false;
754 :
755 0 : MAP_AST_EXPR(c, if_);
756 0 : if (!c.buffer.append("if"))
757 0 : return false;
758 0 : if (!RenderBlockNameAndSignature(c, if_.name(), if_.type()))
759 0 : return false;
760 0 : if (!c.buffer.append('\n'))
761 0 : return false;
762 :
763 0 : c.indent++;
764 0 : if (!RenderExprList(c, if_.thenExprs()))
765 0 : return false;
766 0 : c.indent--;
767 :
768 0 : if (if_.hasElse()) {
769 0 : if (!RenderIndent(c))
770 0 : return false;
771 :
772 0 : if (!c.buffer.append("else\n"))
773 0 : return false;
774 :
775 0 : c.indent++;
776 0 : if (!RenderExprList(c, if_.elseExprs()))
777 0 : return false;
778 0 : c.indent--;
779 : }
780 :
781 0 : if (!RenderIndent(c))
782 0 : return false;
783 :
784 0 : return c.buffer.append("end");
785 : }
786 :
787 : static bool
788 0 : RenderLoadStoreBase(WasmRenderContext& c, const AstLoadStoreAddress& lsa)
789 : {
790 0 : return RenderExpr(c, lsa.base());
791 : }
792 :
793 : static bool
794 0 : RenderLoadStoreAddress(WasmRenderContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2)
795 : {
796 0 : if (lsa.offset() != 0) {
797 0 : if (!c.buffer.append(" offset="))
798 0 : return false;
799 0 : if (!RenderInt32(c, lsa.offset()))
800 0 : return false;
801 : }
802 :
803 0 : uint32_t alignLog2 = lsa.flags();
804 0 : if (defaultAlignLog2 != alignLog2) {
805 0 : if (!c.buffer.append(" align="))
806 0 : return false;
807 0 : if (!RenderInt32(c, 1 << alignLog2))
808 0 : return false;
809 : }
810 :
811 0 : return true;
812 : }
813 :
814 : static bool
815 0 : RenderLoad(WasmRenderContext& c, AstLoad& load)
816 : {
817 0 : if (!RenderLoadStoreBase(c, load.address()))
818 0 : return false;
819 :
820 0 : if (!RenderIndent(c))
821 0 : return false;
822 :
823 0 : MAP_AST_EXPR(c, load);
824 : uint32_t defaultAlignLog2;
825 0 : switch (load.op()) {
826 : case Op::I32Load8S:
827 0 : if (!c.buffer.append("i32.load8_s"))
828 0 : return false;
829 0 : defaultAlignLog2 = 0;
830 0 : break;
831 : case Op::I64Load8S:
832 0 : if (!c.buffer.append("i64.load8_s"))
833 0 : return false;
834 0 : defaultAlignLog2 = 0;
835 0 : break;
836 : case Op::I32Load8U:
837 0 : if (!c.buffer.append("i32.load8_u"))
838 0 : return false;
839 0 : defaultAlignLog2 = 0;
840 0 : break;
841 : case Op::I64Load8U:
842 0 : if (!c.buffer.append("i64.load8_u"))
843 0 : return false;
844 0 : defaultAlignLog2 = 0;
845 0 : break;
846 : case Op::I32Load16S:
847 0 : if (!c.buffer.append("i32.load16_s"))
848 0 : return false;
849 0 : defaultAlignLog2 = 1;
850 0 : break;
851 : case Op::I64Load16S:
852 0 : if (!c.buffer.append("i64.load16_s"))
853 0 : return false;
854 0 : defaultAlignLog2 = 1;
855 0 : break;
856 : case Op::I32Load16U:
857 0 : if (!c.buffer.append("i32.load16_u"))
858 0 : return false;
859 0 : defaultAlignLog2 = 1;
860 0 : break;
861 : case Op::I64Load16U:
862 0 : if (!c.buffer.append("i64.load16_u"))
863 0 : return false;
864 0 : defaultAlignLog2 = 1;
865 0 : break;
866 : case Op::I64Load32S:
867 0 : if (!c.buffer.append("i64.load32_s"))
868 0 : return false;
869 0 : defaultAlignLog2 = 2;
870 0 : break;
871 : case Op::I64Load32U:
872 0 : if (!c.buffer.append("i64.load32_u"))
873 0 : return false;
874 0 : defaultAlignLog2 = 2;
875 0 : break;
876 : case Op::I32Load:
877 0 : if (!c.buffer.append("i32.load"))
878 0 : return false;
879 0 : defaultAlignLog2 = 2;
880 0 : break;
881 : case Op::I64Load:
882 0 : if (!c.buffer.append("i64.load"))
883 0 : return false;
884 0 : defaultAlignLog2 = 3;
885 0 : break;
886 : case Op::F32Load:
887 0 : if (!c.buffer.append("f32.load"))
888 0 : return false;
889 0 : defaultAlignLog2 = 2;
890 0 : break;
891 : case Op::F64Load:
892 0 : if (!c.buffer.append("f64.load"))
893 0 : return false;
894 0 : defaultAlignLog2 = 3;
895 0 : break;
896 : default:
897 0 : return Fail(c, "unexpected load operator");
898 : }
899 :
900 0 : return RenderLoadStoreAddress(c, load.address(), defaultAlignLog2);
901 : }
902 :
903 : static bool
904 0 : RenderStore(WasmRenderContext& c, AstStore& store)
905 : {
906 0 : if (!RenderLoadStoreBase(c, store.address()))
907 0 : return false;
908 :
909 0 : if (!RenderExpr(c, store.value()))
910 0 : return false;
911 :
912 0 : if (!RenderIndent(c))
913 0 : return false;
914 :
915 0 : MAP_AST_EXPR(c, store);
916 : uint32_t defaultAlignLog2;
917 0 : switch (store.op()) {
918 : case Op::I32Store8:
919 0 : if (!c.buffer.append("i32.store8"))
920 0 : return false;
921 0 : defaultAlignLog2 = 0;
922 0 : break;
923 : case Op::I64Store8:
924 0 : if (!c.buffer.append("i64.store8"))
925 0 : return false;
926 0 : defaultAlignLog2 = 0;
927 0 : break;
928 : case Op::I32Store16:
929 0 : if (!c.buffer.append("i32.store16"))
930 0 : return false;
931 0 : defaultAlignLog2 = 1;
932 0 : break;
933 : case Op::I64Store16:
934 0 : if (!c.buffer.append("i64.store16"))
935 0 : return false;
936 0 : defaultAlignLog2 = 1;
937 0 : break;
938 : case Op::I64Store32:
939 0 : if (!c.buffer.append("i64.store32"))
940 0 : return false;
941 0 : defaultAlignLog2 = 2;
942 0 : break;
943 : case Op::I32Store:
944 0 : if (!c.buffer.append("i32.store"))
945 0 : return false;
946 0 : defaultAlignLog2 = 2;
947 0 : break;
948 : case Op::I64Store:
949 0 : if (!c.buffer.append("i64.store"))
950 0 : return false;
951 0 : defaultAlignLog2 = 3;
952 0 : break;
953 : case Op::F32Store:
954 0 : if (!c.buffer.append("f32.store"))
955 0 : return false;
956 0 : defaultAlignLog2 = 2;
957 0 : break;
958 : case Op::F64Store:
959 0 : if (!c.buffer.append("f64.store"))
960 0 : return false;
961 0 : defaultAlignLog2 = 3;
962 0 : break;
963 : default:
964 0 : return Fail(c, "unexpected store operator");
965 : }
966 :
967 0 : return RenderLoadStoreAddress(c, store.address(), defaultAlignLog2);
968 : }
969 :
970 : static bool
971 0 : RenderBranch(WasmRenderContext& c, AstBranch& branch)
972 : {
973 0 : Op op = branch.op();
974 0 : MOZ_ASSERT(op == Op::BrIf || op == Op::Br);
975 :
976 0 : if (op == Op::BrIf) {
977 0 : if (!RenderExpr(c, branch.cond()))
978 0 : return false;
979 : }
980 :
981 0 : if (branch.maybeValue()) {
982 0 : if (!RenderExpr(c, *(branch.maybeValue())))
983 0 : return false;
984 : }
985 :
986 0 : if (!RenderIndent(c))
987 0 : return false;
988 :
989 0 : MAP_AST_EXPR(c, branch);
990 0 : if (op == Op::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br "))
991 0 : return false;
992 :
993 0 : return RenderRef(c, branch.target());
994 : }
995 :
996 : static bool
997 0 : RenderBrTable(WasmRenderContext& c, AstBranchTable& table)
998 : {
999 0 : if (table.maybeValue()) {
1000 0 : if (!RenderExpr(c, *(table.maybeValue())))
1001 0 : return false;
1002 : }
1003 :
1004 : // Index
1005 0 : if (!RenderExpr(c, table.index()))
1006 0 : return false;
1007 :
1008 0 : if (!RenderIndent(c))
1009 0 : return false;
1010 :
1011 0 : MAP_AST_EXPR(c, table);
1012 0 : if (!c.buffer.append("br_table "))
1013 0 : return false;
1014 :
1015 0 : uint32_t tableLength = table.table().length();
1016 0 : for (uint32_t i = 0; i < tableLength; i++) {
1017 0 : if (!RenderRef(c, table.table()[i]))
1018 0 : return false;
1019 :
1020 0 : if (!c.buffer.append(" "))
1021 0 : return false;
1022 : }
1023 :
1024 0 : return RenderRef(c, table.def());
1025 : }
1026 :
1027 : static bool
1028 0 : RenderReturn(WasmRenderContext& c, AstReturn& ret)
1029 : {
1030 0 : if (ret.maybeExpr()) {
1031 0 : if (!RenderExpr(c, *(ret.maybeExpr())))
1032 0 : return false;
1033 : }
1034 :
1035 0 : if (!RenderIndent(c))
1036 0 : return false;
1037 :
1038 0 : MAP_AST_EXPR(c, ret);
1039 0 : return c.buffer.append("return");
1040 : }
1041 :
1042 : static bool
1043 0 : RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine /* = true */)
1044 : {
1045 0 : switch (expr.kind()) {
1046 : case AstExprKind::Drop:
1047 0 : if (!RenderDrop(c, expr.as<AstDrop>()))
1048 0 : return false;
1049 0 : break;
1050 : case AstExprKind::Nop:
1051 0 : if (!RenderNop(c, expr.as<AstNop>()))
1052 0 : return false;
1053 0 : break;
1054 : case AstExprKind::Unreachable:
1055 0 : if (!RenderUnreachable(c, expr.as<AstUnreachable>()))
1056 0 : return false;
1057 0 : break;
1058 : case AstExprKind::Call:
1059 0 : if (!RenderCall(c, expr.as<AstCall>()))
1060 0 : return false;
1061 0 : break;
1062 : case AstExprKind::CallIndirect:
1063 0 : if (!RenderCallIndirect(c, expr.as<AstCallIndirect>()))
1064 0 : return false;
1065 0 : break;
1066 : case AstExprKind::Const:
1067 0 : if (!RenderConst(c, expr.as<AstConst>()))
1068 0 : return false;
1069 0 : break;
1070 : case AstExprKind::GetLocal:
1071 0 : if (!RenderGetLocal(c, expr.as<AstGetLocal>()))
1072 0 : return false;
1073 0 : break;
1074 : case AstExprKind::SetLocal:
1075 0 : if (!RenderSetLocal(c, expr.as<AstSetLocal>()))
1076 0 : return false;
1077 0 : break;
1078 : case AstExprKind::GetGlobal:
1079 0 : if (!RenderGetGlobal(c, expr.as<AstGetGlobal>()))
1080 0 : return false;
1081 0 : break;
1082 : case AstExprKind::SetGlobal:
1083 0 : if (!RenderSetGlobal(c, expr.as<AstSetGlobal>()))
1084 0 : return false;
1085 0 : break;
1086 : case AstExprKind::TeeLocal:
1087 0 : if (!RenderTeeLocal(c, expr.as<AstTeeLocal>()))
1088 0 : return false;
1089 0 : break;
1090 : case AstExprKind::Block:
1091 0 : if (!RenderBlock(c, expr.as<AstBlock>()))
1092 0 : return false;
1093 0 : break;
1094 : case AstExprKind::If:
1095 0 : if (!RenderIf(c, expr.as<AstIf>()))
1096 0 : return false;
1097 0 : break;
1098 : case AstExprKind::UnaryOperator:
1099 0 : if (!RenderUnaryOperator(c, expr.as<AstUnaryOperator>()))
1100 0 : return false;
1101 0 : break;
1102 : case AstExprKind::BinaryOperator:
1103 0 : if (!RenderBinaryOperator(c, expr.as<AstBinaryOperator>()))
1104 0 : return false;
1105 0 : break;
1106 : case AstExprKind::TernaryOperator:
1107 0 : if (!RenderTernaryOperator(c, expr.as<AstTernaryOperator>()))
1108 0 : return false;
1109 0 : break;
1110 : case AstExprKind::ComparisonOperator:
1111 0 : if (!RenderComparisonOperator(c, expr.as<AstComparisonOperator>()))
1112 0 : return false;
1113 0 : break;
1114 : case AstExprKind::ConversionOperator:
1115 0 : if (!RenderConversionOperator(c, expr.as<AstConversionOperator>()))
1116 0 : return false;
1117 0 : break;
1118 : case AstExprKind::Load:
1119 0 : if (!RenderLoad(c, expr.as<AstLoad>()))
1120 0 : return false;
1121 0 : break;
1122 : case AstExprKind::Store:
1123 0 : if (!RenderStore(c, expr.as<AstStore>()))
1124 0 : return false;
1125 0 : break;
1126 : case AstExprKind::Branch:
1127 0 : if (!RenderBranch(c, expr.as<AstBranch>()))
1128 0 : return false;
1129 0 : break;
1130 : case AstExprKind::BranchTable:
1131 0 : if (!RenderBrTable(c, expr.as<AstBranchTable>()))
1132 0 : return false;
1133 0 : break;
1134 : case AstExprKind::Return:
1135 0 : if (!RenderReturn(c, expr.as<AstReturn>()))
1136 0 : return false;
1137 0 : break;
1138 : case AstExprKind::First:
1139 0 : newLine = false;
1140 0 : if (!RenderFirst(c, expr.as<AstFirst>()))
1141 0 : return false;
1142 0 : break;
1143 : case AstExprKind::CurrentMemory:
1144 0 : if (!RenderCurrentMemory(c, expr.as<AstCurrentMemory>()))
1145 0 : return false;
1146 0 : break;
1147 : case AstExprKind::GrowMemory:
1148 0 : if (!RenderGrowMemory(c, expr.as<AstGrowMemory>()))
1149 0 : return false;
1150 0 : break;
1151 : default:
1152 0 : MOZ_CRASH("Bad AstExprKind");
1153 : }
1154 :
1155 0 : return !newLine || c.buffer.append("\n");
1156 : }
1157 :
1158 : static bool
1159 0 : RenderSignature(WasmRenderContext& c, const AstSig& sig, const AstNameVector* maybeLocals = nullptr)
1160 : {
1161 0 : uint32_t paramsNum = sig.args().length();
1162 :
1163 0 : if (maybeLocals) {
1164 0 : for (uint32_t i = 0; i < paramsNum; i++) {
1165 0 : if (!c.buffer.append(" (param "))
1166 0 : return false;
1167 0 : const AstName& name = (*maybeLocals)[i];
1168 0 : if (!name.empty()) {
1169 0 : if (!RenderName(c, name))
1170 0 : return false;
1171 0 : if (!c.buffer.append(" "))
1172 0 : return false;
1173 : }
1174 0 : ValType arg = sig.args()[i];
1175 0 : if (!RenderValType(c, arg))
1176 0 : return false;
1177 0 : if (!c.buffer.append(")"))
1178 0 : return false;
1179 : }
1180 0 : } else if (paramsNum > 0) {
1181 0 : if (!c.buffer.append(" (param"))
1182 0 : return false;
1183 0 : for (uint32_t i = 0; i < paramsNum; i++) {
1184 0 : if (!c.buffer.append(" "))
1185 0 : return false;
1186 0 : ValType arg = sig.args()[i];
1187 0 : if (!RenderValType(c, arg))
1188 0 : return false;
1189 : }
1190 0 : if (!c.buffer.append(")"))
1191 0 : return false;
1192 : }
1193 0 : if (sig.ret() != ExprType::Void) {
1194 0 : if (!c.buffer.append(" (result "))
1195 0 : return false;
1196 0 : if (!RenderExprType(c, sig.ret()))
1197 0 : return false;
1198 0 : if (!c.buffer.append(")"))
1199 0 : return false;
1200 : }
1201 0 : return true;
1202 : }
1203 :
1204 : static bool
1205 0 : RenderTypeSection(WasmRenderContext& c, const AstModule::SigVector& sigs)
1206 : {
1207 0 : uint32_t numSigs = sigs.length();
1208 0 : if (!numSigs)
1209 0 : return true;
1210 :
1211 0 : for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
1212 0 : const AstSig* sig = sigs[sigIndex];
1213 0 : if (!RenderIndent(c))
1214 0 : return false;
1215 0 : if (!c.buffer.append("(type"))
1216 0 : return false;
1217 0 : if (!sig->name().empty()) {
1218 0 : if (!c.buffer.append(" "))
1219 0 : return false;
1220 0 : if (!RenderName(c, sig->name()))
1221 0 : return false;
1222 : }
1223 0 : if (!c.buffer.append(" (func"))
1224 0 : return false;
1225 0 : if (!RenderSignature(c, *sig))
1226 0 : return false;
1227 0 : if (!c.buffer.append("))\n"))
1228 0 : return false;
1229 : }
1230 0 : return true;
1231 : }
1232 :
1233 : static bool
1234 0 : RenderLimits(WasmRenderContext& c, const Limits& limits)
1235 : {
1236 0 : if (!RenderInt32(c, limits.initial))
1237 0 : return false;
1238 0 : if (limits.maximum) {
1239 0 : if (!c.buffer.append(" "))
1240 0 : return false;
1241 0 : if (!RenderInt32(c, *limits.maximum))
1242 0 : return false;
1243 : }
1244 0 : return true;
1245 : }
1246 :
1247 : static bool
1248 0 : RenderResizableTable(WasmRenderContext& c, const Limits& table)
1249 : {
1250 0 : if (!c.buffer.append("(table "))
1251 0 : return false;
1252 0 : if (!RenderLimits(c, table))
1253 0 : return false;
1254 0 : return c.buffer.append(" anyfunc)");
1255 : }
1256 :
1257 : static bool
1258 0 : RenderTableSection(WasmRenderContext& c, const AstModule& module)
1259 : {
1260 0 : if (!module.hasTable())
1261 0 : return true;
1262 0 : for (const AstResizable& table : module.tables()) {
1263 0 : if (table.imported)
1264 0 : continue;
1265 0 : if (!RenderIndent(c))
1266 0 : return false;
1267 0 : if (!RenderResizableTable(c, table.limits))
1268 0 : return false;
1269 0 : if (!c.buffer.append("\n"))
1270 0 : return false;
1271 : }
1272 0 : return true;
1273 : }
1274 :
1275 : static bool
1276 0 : RenderInlineExpr(WasmRenderContext& c, AstExpr& expr)
1277 : {
1278 0 : if (!c.buffer.append("("))
1279 0 : return false;
1280 :
1281 0 : uint32_t prevIndent = c.indent;
1282 0 : c.indent = 0;
1283 0 : if (!RenderExpr(c, expr, /* newLine */ false))
1284 0 : return false;
1285 0 : c.indent = prevIndent;
1286 :
1287 0 : return c.buffer.append(")");
1288 : }
1289 :
1290 : static bool
1291 0 : RenderElemSection(WasmRenderContext& c, const AstModule& module)
1292 : {
1293 0 : for (const AstElemSegment* segment : module.elemSegments()) {
1294 0 : if (!RenderIndent(c))
1295 0 : return false;
1296 0 : if (!c.buffer.append("(elem "))
1297 0 : return false;
1298 0 : if (!RenderInlineExpr(c, *segment->offset()))
1299 0 : return false;
1300 :
1301 0 : for (const AstRef& elem : segment->elems()) {
1302 0 : if (!c.buffer.append(" "))
1303 0 : return false;
1304 :
1305 0 : uint32_t index = elem.index();
1306 0 : AstName name = index < module.funcImportNames().length()
1307 0 : ? module.funcImportNames()[index]
1308 0 : : module.funcs()[index - module.funcImportNames().length()]->name();
1309 :
1310 0 : if (name.empty()) {
1311 0 : if (!RenderInt32(c, index))
1312 0 : return false;
1313 : } else {
1314 0 : if (!RenderName(c, name))
1315 0 : return false;
1316 : }
1317 : }
1318 :
1319 0 : if (!c.buffer.append(")\n"))
1320 0 : return false;
1321 : }
1322 :
1323 0 : return true;
1324 : }
1325 :
1326 : static bool
1327 0 : RenderGlobal(WasmRenderContext& c, const AstGlobal& glob, bool inImport = false)
1328 : {
1329 0 : if (!c.buffer.append("(global "))
1330 0 : return false;
1331 :
1332 0 : if (!inImport) {
1333 0 : if (!RenderName(c, glob.name()))
1334 0 : return false;
1335 0 : if (!c.buffer.append(" "))
1336 0 : return false;
1337 : }
1338 :
1339 0 : if (glob.isMutable()) {
1340 0 : if (!c.buffer.append("(mut "))
1341 0 : return false;
1342 0 : if (!RenderValType(c, glob.type()))
1343 0 : return false;
1344 0 : if (!c.buffer.append(")"))
1345 0 : return false;
1346 : } else {
1347 0 : if (!RenderValType(c, glob.type()))
1348 0 : return false;
1349 : }
1350 :
1351 0 : if (glob.hasInit()) {
1352 0 : if (!c.buffer.append(" "))
1353 0 : return false;
1354 0 : if (!RenderInlineExpr(c, glob.init()))
1355 0 : return false;
1356 : }
1357 :
1358 0 : if (!c.buffer.append(")"))
1359 0 : return false;
1360 :
1361 0 : return inImport || c.buffer.append("\n");
1362 : }
1363 :
1364 : static bool
1365 0 : RenderGlobalSection(WasmRenderContext& c, const AstModule& module)
1366 : {
1367 0 : if (module.globals().empty())
1368 0 : return true;
1369 :
1370 0 : for (const AstGlobal* global : module.globals()) {
1371 0 : if (!RenderIndent(c))
1372 0 : return false;
1373 0 : if (!RenderGlobal(c, *global))
1374 0 : return false;
1375 : }
1376 :
1377 0 : return true;
1378 : }
1379 :
1380 : static bool
1381 0 : RenderResizableMemory(WasmRenderContext& c, const Limits& memory)
1382 : {
1383 0 : if (!c.buffer.append("(memory "))
1384 0 : return false;
1385 :
1386 0 : Limits resizedMemory = memory;
1387 :
1388 0 : MOZ_ASSERT(resizedMemory.initial % PageSize == 0);
1389 0 : resizedMemory.initial /= PageSize;
1390 :
1391 0 : if (resizedMemory.maximum) {
1392 0 : MOZ_ASSERT(*resizedMemory.maximum % PageSize == 0);
1393 0 : *resizedMemory.maximum /= PageSize;
1394 : }
1395 :
1396 0 : if (!RenderLimits(c, resizedMemory))
1397 0 : return false;
1398 :
1399 0 : return c.buffer.append(")");
1400 : }
1401 :
1402 : static bool
1403 0 : RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module)
1404 : {
1405 0 : if (!RenderIndent(c))
1406 0 : return false;
1407 0 : if (!c.buffer.append("(import "))
1408 0 : return false;
1409 0 : if (!RenderName(c, import.name()))
1410 0 : return false;
1411 0 : if (!c.buffer.append(" \""))
1412 0 : return false;
1413 :
1414 0 : const AstName& moduleName = import.module();
1415 0 : if (!RenderEscapedString(c, moduleName))
1416 0 : return false;
1417 :
1418 0 : if (!c.buffer.append("\" \""))
1419 0 : return false;
1420 :
1421 0 : const AstName& fieldName = import.field();
1422 0 : if (!RenderEscapedString(c, fieldName))
1423 0 : return false;
1424 :
1425 0 : if (!c.buffer.append("\" "))
1426 0 : return false;
1427 :
1428 0 : switch (import.kind()) {
1429 : case DefinitionKind::Function: {
1430 0 : if (!c.buffer.append("(func"))
1431 0 : return false;
1432 0 : const AstSig* sig = module.sigs()[import.funcSig().index()];
1433 0 : if (!RenderSignature(c, *sig))
1434 0 : return false;
1435 0 : if (!c.buffer.append(")"))
1436 0 : return false;
1437 0 : break;
1438 : }
1439 : case DefinitionKind::Table: {
1440 0 : if (!RenderResizableTable(c, import.limits()))
1441 0 : return false;
1442 0 : break;
1443 : }
1444 : case DefinitionKind::Memory: {
1445 0 : if (!RenderResizableMemory(c, import.limits()))
1446 0 : return false;
1447 0 : break;
1448 : }
1449 : case DefinitionKind::Global: {
1450 0 : const AstGlobal& glob = import.global();
1451 0 : if (!RenderGlobal(c, glob, /* inImport */ true))
1452 0 : return false;
1453 0 : break;
1454 : }
1455 : }
1456 :
1457 0 : return c.buffer.append(")\n");
1458 : }
1459 :
1460 : static bool
1461 0 : RenderImportSection(WasmRenderContext& c, const AstModule& module)
1462 : {
1463 0 : for (AstImport* import : module.imports()) {
1464 0 : if (!RenderImport(c, *import, module))
1465 0 : return false;
1466 : }
1467 0 : return true;
1468 : }
1469 :
1470 : static bool
1471 0 : RenderExport(WasmRenderContext& c, AstExport& export_,
1472 : const AstModule::NameVector& funcImportNames,
1473 : const AstModule::FuncVector& funcs)
1474 : {
1475 0 : if (!RenderIndent(c))
1476 0 : return false;
1477 0 : if (!c.buffer.append("(export \""))
1478 0 : return false;
1479 0 : if (!RenderEscapedString(c, export_.name()))
1480 0 : return false;
1481 0 : if (!c.buffer.append("\" "))
1482 0 : return false;
1483 :
1484 0 : switch (export_.kind()) {
1485 : case DefinitionKind::Function: {
1486 0 : uint32_t index = export_.ref().index();
1487 0 : AstName name = index < funcImportNames.length()
1488 0 : ? funcImportNames[index]
1489 0 : : funcs[index - funcImportNames.length()]->name();
1490 0 : if (name.empty()) {
1491 0 : if (!RenderInt32(c, index))
1492 0 : return false;
1493 : } else {
1494 0 : if (!RenderName(c, name))
1495 0 : return false;
1496 : }
1497 0 : break;
1498 : }
1499 : case DefinitionKind::Table: {
1500 0 : if (!c.buffer.append("table"))
1501 0 : return false;
1502 0 : break;
1503 : }
1504 : case DefinitionKind::Memory: {
1505 0 : if (!c.buffer.append("memory"))
1506 0 : return false;
1507 0 : break;
1508 : }
1509 : case DefinitionKind::Global: {
1510 0 : if (!c.buffer.append("global"))
1511 0 : return false;
1512 0 : if (!RenderRef(c, export_.ref()))
1513 0 : return false;
1514 0 : break;
1515 : }
1516 : }
1517 :
1518 0 : return c.buffer.append(")\n");
1519 : }
1520 :
1521 : static bool
1522 0 : RenderExportSection(WasmRenderContext& c, const AstModule::ExportVector& exports,
1523 : const AstModule::NameVector& funcImportNames,
1524 : const AstModule::FuncVector& funcs)
1525 : {
1526 0 : uint32_t numExports = exports.length();
1527 0 : for (uint32_t i = 0; i < numExports; i++) {
1528 0 : if (!RenderExport(c, *exports[i], funcImportNames, funcs))
1529 0 : return false;
1530 : }
1531 0 : return true;
1532 : }
1533 :
1534 : static bool
1535 0 : RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::SigVector& sigs)
1536 : {
1537 0 : const AstSig* sig = sigs[func.sig().index()];
1538 :
1539 0 : uint32_t argsNum = sig->args().length();
1540 0 : uint32_t localsNum = func.vars().length();
1541 0 : if (localsNum > 0) {
1542 0 : if (!RenderIndent(c))
1543 0 : return false;
1544 0 : for (uint32_t i = 0; i < localsNum; i++) {
1545 0 : if (!c.buffer.append("(local "))
1546 0 : return false;
1547 0 : const AstName& name = func.locals()[argsNum + i];
1548 0 : if (!name.empty()) {
1549 0 : if (!RenderName(c, name))
1550 0 : return false;
1551 0 : if (!c.buffer.append(" "))
1552 0 : return false;
1553 : }
1554 0 : ValType local = func.vars()[i];
1555 0 : if (!RenderValType(c, local))
1556 0 : return false;
1557 0 : if (!c.buffer.append(") "))
1558 0 : return false;
1559 : }
1560 0 : if (!c.buffer.append("\n"))
1561 0 : return false;
1562 : }
1563 :
1564 :
1565 0 : uint32_t exprsNum = func.body().length();
1566 0 : for (uint32_t i = 0; i < exprsNum; i++) {
1567 0 : if (!RenderExpr(c, *func.body()[i]))
1568 0 : return false;
1569 : }
1570 :
1571 0 : if (c.maybeSourceMap) {
1572 0 : if (!c.maybeSourceMap->exprlocs().emplaceBack(c.buffer.lineno(), c.buffer.column(), func.endOffset()))
1573 0 : return false;
1574 : }
1575 :
1576 0 : return true;
1577 : }
1578 :
1579 : static bool
1580 0 : RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs,
1581 : const AstModule::SigVector& sigs)
1582 : {
1583 0 : uint32_t numFuncBodies = funcs.length();
1584 0 : for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
1585 0 : AstFunc* func = funcs[funcIndex];
1586 0 : uint32_t sigIndex = func->sig().index();
1587 0 : AstSig* sig = sigs[sigIndex];
1588 :
1589 0 : if (!RenderIndent(c))
1590 0 : return false;
1591 0 : if (!c.buffer.append("(func "))
1592 0 : return false;
1593 0 : if (!func->name().empty()) {
1594 0 : if (!RenderName(c, func->name()))
1595 0 : return false;
1596 : }
1597 :
1598 0 : if (!RenderSignature(c, *sig, &(func->locals())))
1599 0 : return false;
1600 0 : if (!c.buffer.append("\n"))
1601 0 : return false;
1602 :
1603 0 : c.currentFuncIndex = funcIndex;
1604 :
1605 0 : c.indent++;
1606 0 : if (!RenderFunctionBody(c, *func, sigs))
1607 0 : return false;
1608 0 : c.indent--;
1609 0 : if (!RenderIndent(c))
1610 0 : return false;
1611 0 : if (!c.buffer.append(")\n"))
1612 0 : return false;
1613 : }
1614 :
1615 0 : return true;
1616 : }
1617 :
1618 : static bool
1619 0 : RenderMemorySection(WasmRenderContext& c, const AstModule& module)
1620 : {
1621 0 : if (!module.hasMemory())
1622 0 : return true;
1623 :
1624 0 : for (const AstResizable& memory : module.memories()) {
1625 0 : if (memory.imported)
1626 0 : continue;
1627 0 : if (!RenderIndent(c))
1628 0 : return false;
1629 0 : if (!RenderResizableMemory(c, memory.limits))
1630 0 : return false;
1631 0 : if (!c.buffer.append("\n"))
1632 0 : return false;
1633 : }
1634 :
1635 0 : return true;
1636 : }
1637 :
1638 : static bool
1639 0 : RenderDataSection(WasmRenderContext& c, const AstModule& module)
1640 : {
1641 0 : uint32_t numSegments = module.dataSegments().length();
1642 0 : if (!numSegments)
1643 0 : return true;
1644 :
1645 0 : for (const AstDataSegment* seg : module.dataSegments()) {
1646 0 : if (!RenderIndent(c))
1647 0 : return false;
1648 0 : if (!c.buffer.append("(data "))
1649 0 : return false;
1650 0 : if (!RenderInlineExpr(c, *seg->offset()))
1651 0 : return false;
1652 0 : if (!c.buffer.append("\n"))
1653 0 : return false;
1654 :
1655 0 : c.indent++;
1656 0 : for (const AstName& fragment : seg->fragments()) {
1657 0 : if (!RenderIndent(c))
1658 0 : return false;
1659 0 : if (!c.buffer.append("\""))
1660 0 : return false;
1661 0 : if (!RenderEscapedString(c, fragment))
1662 0 : return false;
1663 0 : if (!c.buffer.append("\"\n"))
1664 0 : return false;
1665 : }
1666 0 : c.indent--;
1667 :
1668 0 : if (!RenderIndent(c))
1669 0 : return false;
1670 0 : if (!c.buffer.append(")\n"))
1671 0 : return false;
1672 : }
1673 :
1674 0 : return true;
1675 : }
1676 :
1677 : static bool
1678 0 : RenderStartSection(WasmRenderContext& c, AstModule& module)
1679 : {
1680 0 : if (!module.hasStartFunc())
1681 0 : return true;
1682 :
1683 0 : if (!RenderIndent(c))
1684 0 : return false;
1685 0 : if (!c.buffer.append("(start "))
1686 0 : return false;
1687 0 : if (!RenderRef(c, module.startFunc().func()))
1688 0 : return false;
1689 0 : if (!c.buffer.append(")\n"))
1690 0 : return false;
1691 :
1692 0 : return true;
1693 : }
1694 :
1695 : static bool
1696 0 : RenderModule(WasmRenderContext& c, AstModule& module)
1697 : {
1698 0 : if (!c.buffer.append("(module\n"))
1699 0 : return false;
1700 :
1701 0 : c.indent++;
1702 :
1703 0 : if (!RenderTypeSection(c, module.sigs()))
1704 0 : return false;
1705 :
1706 0 : if (!RenderImportSection(c, module))
1707 0 : return false;
1708 :
1709 0 : if (!RenderTableSection(c, module))
1710 0 : return false;
1711 :
1712 0 : if (!RenderMemorySection(c, module))
1713 0 : return false;
1714 :
1715 0 : if (!RenderGlobalSection(c, module))
1716 0 : return false;
1717 :
1718 0 : if (!RenderExportSection(c, module.exports(), module.funcImportNames(), module.funcs()))
1719 0 : return false;
1720 :
1721 0 : if (!RenderStartSection(c, module))
1722 0 : return false;
1723 :
1724 0 : if (!RenderElemSection(c, module))
1725 0 : return false;
1726 :
1727 0 : if (!RenderCodeSection(c, module.funcs(), module.sigs()))
1728 0 : return false;
1729 :
1730 0 : if (!RenderDataSection(c, module))
1731 0 : return false;
1732 :
1733 0 : c.indent--;
1734 :
1735 0 : if (!c.buffer.append(")"))
1736 0 : return false;
1737 :
1738 0 : return true;
1739 : }
1740 :
1741 : #undef MAP_AST_EXPR
1742 :
1743 : /*****************************************************************************/
1744 : // Top-level functions
1745 :
1746 : bool
1747 0 : wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
1748 : GeneratedSourceMap* sourceMap /* = nullptr */)
1749 : {
1750 0 : LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
1751 :
1752 : AstModule* module;
1753 0 : if (!BinaryToAst(cx, bytes, length, lifo, &module))
1754 0 : return false;
1755 :
1756 0 : WasmPrintBuffer buf(buffer);
1757 0 : WasmRenderContext c(cx, module, buf, sourceMap);
1758 :
1759 0 : if (!RenderModule(c, *module)) {
1760 0 : if (!cx->isExceptionPending())
1761 0 : ReportOutOfMemory(cx);
1762 0 : return false;
1763 : }
1764 :
1765 0 : return true;
1766 : }
|