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/WasmBinaryToExperimentalText.h"
20
21 #include "mozilla/CheckedInt.h"
22
23 #include "jsnum.h"
24 #include "jsprf.h"
25
26 #include "vm/ArrayBufferObject.h"
27 #include "vm/StringBuffer.h"
28 #include "wasm/WasmAST.h"
29 #include "wasm/WasmBinaryToAST.h"
30 #include "wasm/WasmTextUtils.h"
31 #include "wasm/WasmTypes.h"
32
33 using namespace js;
34 using namespace js::wasm;
35
36 using mozilla::CheckedInt;
37 using mozilla::IsInfinite;
38 using mozilla::IsNaN;
39 using mozilla::IsNegativeZero;
40
41 enum PrintOperatorPrecedence
42 {
43 ExpressionPrecedence = 0,
44 AssignmentPrecedence = 1,
45 StoreOperatorPrecedence = 1,
46 BitwiseOrPrecedence = 4,
47 BitwiseXorPrecedence = 5,
48 BitwiseAndPrecedence = 6,
49 EqualityPrecedence = 7,
50 ComparisonPrecedence = 8,
51 BitwiseShiftPrecedence = 9,
52 AdditionPrecedence = 10,
53 MultiplicationPrecedence = 11,
54 NegatePrecedence = 12,
55 EqzPrecedence = 12,
56 OperatorPrecedence = 15,
57 LoadOperatorPrecedence = 15,
58 CallPrecedence = 15,
59 GroupPrecedence = 16,
60 };
61
62 struct WasmPrintContext
63 {
64 JSContext* cx;
65 AstModule* module;
66 WasmPrintBuffer& buffer;
67 const ExperimentalTextFormatting& f;
68 GeneratedSourceMap* maybeSourceMap;
69 uint32_t indent;
70
71 uint32_t currentFuncIndex;
72 PrintOperatorPrecedence currentPrecedence;
73
WasmPrintContextWasmPrintContext74 WasmPrintContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer,
75 const ExperimentalTextFormatting& f, GeneratedSourceMap* wasmSourceMap_)
76 : cx(cx),
77 module(module),
78 buffer(buffer),
79 f(f),
80 maybeSourceMap(wasmSourceMap_),
81 indent(0),
82 currentFuncIndex(0),
83 currentPrecedence(PrintOperatorPrecedence::ExpressionPrecedence)
84 {}
85
sbWasmPrintContext86 StringBuffer& sb() { return buffer.stringBuffer(); }
87 };
88
89 /*****************************************************************************/
90 // utilities
91
92 static bool
IsDropValueExpr(AstExpr & expr)93 IsDropValueExpr(AstExpr& expr)
94 {
95 // Based on AST information, determines if the expression does not return a value.
96 // TODO infer presence of a return value for rest kinds of expressions from
97 // the function return type.
98 switch (expr.kind()) {
99 case AstExprKind::Branch:
100 return !expr.as<AstBranch>().maybeValue();
101 case AstExprKind::BranchTable:
102 return !expr.as<AstBranchTable>().maybeValue();
103 case AstExprKind::If:
104 return !expr.as<AstIf>().hasElse();
105 case AstExprKind::Nop:
106 case AstExprKind::Drop:
107 case AstExprKind::Unreachable:
108 case AstExprKind::Return:
109 case AstExprKind::SetLocal:
110 case AstExprKind::Store:
111 return true;
112 default:
113 return false;
114 }
115 }
116
117 static bool
PrintIndent(WasmPrintContext & c)118 PrintIndent(WasmPrintContext& c)
119 {
120 for (uint32_t i = 0; i < c.indent; i++) {
121 if (!c.buffer.append(" "))
122 return false;
123 }
124 return true;
125 }
126
127 static bool
PrintInt32(WasmPrintContext & c,int32_t num,bool printSign=false)128 PrintInt32(WasmPrintContext& c, int32_t num, bool printSign = false)
129 {
130 // Negative sign will be printed, printing '+' for non-negative values.
131 if (printSign && num >= 0) {
132 if (!c.buffer.append("+"))
133 return false;
134 }
135 return NumberValueToStringBuffer(c.cx, Int32Value(num), c.buffer.stringBuffer());
136 }
137
138 static bool
PrintInt64(WasmPrintContext & c,int64_t num)139 PrintInt64(WasmPrintContext& c, int64_t num)
140 {
141 if (num < 0 && !c.buffer.append("-"))
142 return false;
143 if (!num)
144 return c.buffer.append("0");
145
146 uint64_t abs = mozilla::Abs(num);
147 uint64_t n = abs;
148 uint64_t pow = 1;
149 while (n) {
150 pow *= 10;
151 n /= 10;
152 }
153 pow /= 10;
154
155 n = abs;
156 while (pow) {
157 if (!c.buffer.append((char16_t)(u'0' + n / pow)))
158 return false;
159 n -= (n / pow) * pow;
160 pow /= 10;
161 }
162
163 return true;
164 }
165
166 static bool
PrintDouble(WasmPrintContext & c,RawF64 num)167 PrintDouble(WasmPrintContext& c, RawF64 num)
168 {
169 double d = num.fp();
170 if (IsNegativeZero(d))
171 return c.buffer.append("-0.0");
172 if (IsNaN(d))
173 return RenderNaN(c.sb(), num);
174 if (IsInfinite(d)) {
175 if (d > 0)
176 return c.buffer.append("infinity");
177 return c.buffer.append("-infinity");
178 }
179
180 uint32_t startLength = c.buffer.length();
181 if (!NumberValueToStringBuffer(c.cx, DoubleValue(d), c.buffer.stringBuffer()))
182 return false;
183 MOZ_ASSERT(startLength < c.buffer.length());
184
185 // Checking if we need to end number with '.0'.
186 for (uint32_t i = c.buffer.length() - 1; i >= startLength; i--) {
187 char16_t ch = c.buffer.getChar(i);
188 if (ch == '.' || ch == 'e')
189 return true;
190 }
191 return c.buffer.append(".0");
192 }
193
194 static bool
PrintFloat32(WasmPrintContext & c,RawF32 num)195 PrintFloat32(WasmPrintContext& c, RawF32 num)
196 {
197 float f = num.fp();
198 if (IsNaN(f))
199 return RenderNaN(c.sb(), num) && c.buffer.append(".f");
200 return PrintDouble(c, RawF64(double(f))) &&
201 c.buffer.append("f");
202 }
203
204 static bool
PrintEscapedString(WasmPrintContext & c,const AstName & s)205 PrintEscapedString(WasmPrintContext& c, const AstName& s)
206 {
207 size_t length = s.length();
208 const char16_t* p = s.begin();
209 for (size_t i = 0; i < length; i++) {
210 char16_t byte = p[i];
211 switch (byte) {
212 case '\n':
213 if (!c.buffer.append("\\n"))
214 return false;
215 break;
216 case '\r':
217 if (!c.buffer.append("\\0d"))
218 return false;
219 break;
220 case '\t':
221 if (!c.buffer.append("\\t"))
222 return false;
223 break;
224 case '\f':
225 if (!c.buffer.append("\\0c"))
226 return false;
227 break;
228 case '\b':
229 if (!c.buffer.append("\\08"))
230 return false;
231 break;
232 case '\\':
233 if (!c.buffer.append("\\\\"))
234 return false;
235 break;
236 case '"' :
237 if (!c.buffer.append("\\\""))
238 return false;
239 break;
240 case '\'':
241 if (!c.buffer.append("\\'"))
242 return false;
243 break;
244 default:
245 if (byte >= 32 && byte < 127) {
246 if (!c.buffer.append((char)byte))
247 return false;
248 } else {
249 char digit1 = byte / 16, digit2 = byte % 16;
250 if (!c.buffer.append("\\"))
251 return false;
252 if (!c.buffer.append((char)(digit1 < 10 ? digit1 + '0' : digit1 - 10 + 'a')))
253 return false;
254 if (!c.buffer.append((char)(digit2 < 10 ? digit2 + '0' : digit2 - 10 + 'a')))
255 return false;
256 }
257 break;
258 }
259 }
260 return true;
261 }
262
263 static bool
PrintExprType(WasmPrintContext & c,ExprType type)264 PrintExprType(WasmPrintContext& c, ExprType type)
265 {
266 switch (type) {
267 case ExprType::Void: return true; // ignoring void
268 case ExprType::I32: return c.buffer.append("i32");
269 case ExprType::I64: return c.buffer.append("i64");
270 case ExprType::F32: return c.buffer.append("f32");
271 case ExprType::F64: return c.buffer.append("f64");
272 default:;
273 }
274
275 MOZ_CRASH("bad type");
276 }
277
278 static bool
PrintValType(WasmPrintContext & c,ValType type)279 PrintValType(WasmPrintContext& c, ValType type)
280 {
281 return PrintExprType(c, ToExprType(type));
282 }
283
284 static bool
PrintName(WasmPrintContext & c,const AstName & name)285 PrintName(WasmPrintContext& c, const AstName& name)
286 {
287 return c.buffer.append(name.begin(), name.end());
288 }
289
290 static bool
PrintRef(WasmPrintContext & c,const AstRef & ref)291 PrintRef(WasmPrintContext& c, const AstRef& ref)
292 {
293 if (ref.name().empty())
294 return PrintInt32(c, ref.index());
295
296 return PrintName(c, ref.name());
297 }
298
299 static bool
300 PrintExpr(WasmPrintContext& c, AstExpr& expr);
301
302 static bool
PrintBlockLevelExpr(WasmPrintContext & c,AstExpr & expr,bool isLast)303 PrintBlockLevelExpr(WasmPrintContext& c, AstExpr& expr, bool isLast)
304 {
305 if (!PrintIndent(c))
306 return false;
307 if (!PrintExpr(c, expr))
308 return false;
309 if (!isLast || IsDropValueExpr(expr)) {
310 if (!c.buffer.append(';'))
311 return false;
312 }
313 return c.buffer.append('\n');
314 }
315
316 /*****************************************************************************/
317 // binary format parsing and rendering
318
319 static bool
PrintNop(WasmPrintContext & c)320 PrintNop(WasmPrintContext& c)
321 {
322 return c.buffer.append("nop");
323 }
324
325 static bool
PrintDrop(WasmPrintContext & c,AstDrop & drop)326 PrintDrop(WasmPrintContext& c, AstDrop& drop)
327 {
328 if (!PrintExpr(c, drop.value()))
329 return false;
330
331 return true;
332 }
333
334 static bool
PrintUnreachable(WasmPrintContext & c,AstUnreachable & unreachable)335 PrintUnreachable(WasmPrintContext& c, AstUnreachable& unreachable)
336 {
337 return c.buffer.append("unreachable");
338 }
339
340 static bool
PrintCallArgs(WasmPrintContext & c,const AstExprVector & args)341 PrintCallArgs(WasmPrintContext& c, const AstExprVector& args)
342 {
343 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
344 c.currentPrecedence = ExpressionPrecedence;
345
346 if (!c.buffer.append("("))
347 return false;
348 for (uint32_t i = 0; i < args.length(); i++) {
349 if (!PrintExpr(c, *args[i]))
350 return false;
351 if (i + 1 == args.length())
352 break;
353 if (!c.buffer.append(", "))
354 return false;
355 }
356 if (!c.buffer.append(")"))
357 return false;
358
359 c.currentPrecedence = lastPrecedence;
360 return true;
361 }
362
363 static bool
PrintCall(WasmPrintContext & c,AstCall & call)364 PrintCall(WasmPrintContext& c, AstCall& call)
365 {
366 if (call.op() == Op::Call) {
367 if (!c.buffer.append("call "))
368 return false;
369 } else {
370 return false;
371 }
372
373 if (!PrintRef(c, call.func()))
374 return false;
375
376 if (!c.buffer.append(" "))
377 return false;
378
379 if (!PrintCallArgs(c, call.args()))
380 return false;
381
382 return true;
383 }
384
385 static bool
PrintCallIndirect(WasmPrintContext & c,AstCallIndirect & call)386 PrintCallIndirect(WasmPrintContext& c, AstCallIndirect& call)
387 {
388 if (!c.buffer.append("call_indirect "))
389 return false;
390 if (!PrintRef(c, call.sig()))
391 return false;
392
393 if (!c.buffer.append(' '))
394 return false;
395
396 if (!PrintCallArgs(c, call.args()))
397 return false;
398
399 if (!c.buffer.append(" ["))
400 return false;
401
402 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
403 c.currentPrecedence = ExpressionPrecedence;
404
405 if (!PrintExpr(c, *call.index()))
406 return false;
407
408 c.currentPrecedence = lastPrecedence;
409
410 if (!c.buffer.append(']'))
411 return false;
412 return true;
413 }
414
415 static bool
PrintConst(WasmPrintContext & c,AstConst & cst)416 PrintConst(WasmPrintContext& c, AstConst& cst)
417 {
418 switch (ToExprType(cst.val().type())) {
419 case ExprType::I32:
420 if (!PrintInt32(c, (uint32_t)cst.val().i32()))
421 return false;
422 break;
423 case ExprType::I64:
424 if (!PrintInt64(c, (uint32_t)cst.val().i64()))
425 return false;
426 if (!c.buffer.append("i64"))
427 return false;
428 break;
429 case ExprType::F32:
430 if (!PrintFloat32(c, cst.val().f32()))
431 return false;
432 break;
433 case ExprType::F64:
434 if (!PrintDouble(c, cst.val().f64()))
435 return false;
436 break;
437 default:
438 return false;
439 }
440 return true;
441 }
442
443 static bool
PrintGetLocal(WasmPrintContext & c,AstGetLocal & gl)444 PrintGetLocal(WasmPrintContext& c, AstGetLocal& gl)
445 {
446 if (!PrintRef(c, gl.local()))
447 return false;
448 return true;
449 }
450
451 static bool
PrintSetLocal(WasmPrintContext & c,AstSetLocal & sl)452 PrintSetLocal(WasmPrintContext& c, AstSetLocal& sl)
453 {
454 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
455
456 if (!c.f.reduceParens || lastPrecedence > AssignmentPrecedence) {
457 if (!c.buffer.append("("))
458 return false;
459 }
460
461 if (!PrintRef(c, sl.local()))
462 return false;
463 if (!c.buffer.append(" = "))
464 return false;
465
466 c.currentPrecedence = AssignmentPrecedence;
467
468 if (!PrintExpr(c, sl.value()))
469 return false;
470
471 if (!c.f.reduceParens || lastPrecedence > AssignmentPrecedence) {
472 if (!c.buffer.append(")"))
473 return false;
474 }
475
476 c.currentPrecedence = lastPrecedence;
477 return true;
478 }
479
480 static bool
PrintTeeLocal(WasmPrintContext & c,AstTeeLocal & sl)481 PrintTeeLocal(WasmPrintContext& c, AstTeeLocal& sl)
482 {
483 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
484
485 if (!c.f.reduceParens || lastPrecedence > AssignmentPrecedence) {
486 if (!c.buffer.append("("))
487 return false;
488 }
489
490 if (!PrintRef(c, sl.local()))
491 return false;
492 if (!c.buffer.append(" = "))
493 return false;
494
495 c.currentPrecedence = AssignmentPrecedence;
496
497 if (!PrintExpr(c, sl.value()))
498 return false;
499
500 if (!c.f.reduceParens || lastPrecedence > AssignmentPrecedence) {
501 if (!c.buffer.append(")"))
502 return false;
503 }
504
505 c.currentPrecedence = lastPrecedence;
506 return true;
507 }
508
509 static bool
PrintGetGlobal(WasmPrintContext & c,AstGetGlobal & gg)510 PrintGetGlobal(WasmPrintContext& c, AstGetGlobal& gg)
511 {
512 return PrintRef(c, gg.global());
513 }
514
515 static bool
PrintSetGlobal(WasmPrintContext & c,AstSetGlobal & sg)516 PrintSetGlobal(WasmPrintContext& c, AstSetGlobal& sg)
517 {
518 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
519
520 if (!c.f.reduceParens || lastPrecedence > AssignmentPrecedence) {
521 if (!c.buffer.append("("))
522 return false;
523 }
524
525 if (!PrintRef(c, sg.global()))
526 return false;
527 if (!c.buffer.append(" = "))
528 return false;
529
530 c.currentPrecedence = AssignmentPrecedence;
531
532 if (!PrintExpr(c, sg.value()))
533 return false;
534
535 if (!c.f.reduceParens || lastPrecedence > AssignmentPrecedence) {
536 if (!c.buffer.append(")"))
537 return false;
538 }
539
540 c.currentPrecedence = lastPrecedence;
541 return true;
542 }
543
544 static bool
PrintExprList(WasmPrintContext & c,const AstExprVector & exprs,uint32_t startFrom=0)545 PrintExprList(WasmPrintContext& c, const AstExprVector& exprs, uint32_t startFrom = 0)
546 {
547 for (uint32_t i = startFrom; i < exprs.length(); i++) {
548 if (!PrintBlockLevelExpr(c, *exprs[i], i + 1 == exprs.length()))
549 return false;
550 }
551 return true;
552 }
553
554 static bool
PrintGroupedBlock(WasmPrintContext & c,AstBlock & block)555 PrintGroupedBlock(WasmPrintContext& c, AstBlock& block)
556 {
557 uint32_t skip = 0;
558 if (block.exprs().length() > 0 &&
559 block.exprs()[0]->kind() == AstExprKind::Block) {
560 if (!PrintGroupedBlock(c, *static_cast<AstBlock*>(block.exprs()[0])))
561 return false;
562 skip = 1;
563 }
564 c.indent++;
565 if (!PrintExprList(c, block.exprs(), skip))
566 return false;
567 c.indent--;
568 if (!PrintIndent(c))
569 return false;
570
571 // If no br/br_if/br_table refer this block, use some non-existent label.
572 if (block.name().empty())
573 return c.buffer.append("$label:\n");
574
575 if (!PrintName(c, block.name()))
576 return false;
577 if (!c.buffer.append(":\n"))
578 return false;
579 return true;
580 }
581
582 static bool
PrintBlockName(WasmPrintContext & c,const AstName & name)583 PrintBlockName(WasmPrintContext& c, const AstName& name) {
584 if (name.empty())
585 return true;
586
587 if (!PrintIndent(c))
588 return false;
589 if (!PrintName(c, name))
590 return false;
591 return c.buffer.append(":\n");
592 }
593
594 static bool
PrintBlock(WasmPrintContext & c,AstBlock & block)595 PrintBlock(WasmPrintContext& c, AstBlock& block)
596 {
597 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
598 if (block.op() == Op::Block) {
599 if (!c.buffer.append("{\n"))
600 return false;
601 } else if (block.op() == Op::Loop) {
602 if (!c.buffer.append("loop"))
603 return false;
604 if (!block.name().empty()) {
605 if (!c.buffer.append(" "))
606 return false;
607 if (!PrintName(c, block.name()))
608 return false;
609 }
610 if (!c.buffer.append(" {\n"))
611 return false;
612 } else
613 return false;
614
615 c.currentPrecedence = ExpressionPrecedence;
616
617 bool skip = 0;
618 if (c.f.groupBlocks && block.op() == Op::Block &&
619 block.exprs().length() > 0 && block.exprs()[0]->kind() == AstExprKind::Block)
620 {
621 AstBlock* innerBlock = static_cast<AstBlock*>(block.exprs()[0]);
622 if (innerBlock->op() == Op::Block) {
623 if (!PrintGroupedBlock(c, *innerBlock))
624 return false;
625 skip = 1;
626 if (block.exprs().length() == 1 && block.name().empty()) {
627 // Special case to resolve ambiguity in parsing of optional end block label.
628 if (!PrintIndent(c))
629 return false;
630 if (!c.buffer.append("$exit$:\n"))
631 return false;
632 }
633 }
634 }
635
636 c.indent++;
637 if (!PrintExprList(c, block.exprs(), skip))
638 return false;
639 c.indent--;
640 c.currentPrecedence = lastPrecedence;
641
642 if (block.op() != Op::Loop) {
643 if (!PrintBlockName(c, block.name()))
644 return false;
645 }
646
647 if (!PrintIndent(c))
648 return false;
649
650 return c.buffer.append("}");
651 }
652
653 static bool
PrintUnaryOperator(WasmPrintContext & c,AstUnaryOperator & unary)654 PrintUnaryOperator(WasmPrintContext& c, AstUnaryOperator& unary)
655 {
656 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
657
658 const char* opStr;
659 const char* prefixStr = nullptr;
660 PrintOperatorPrecedence precedence = OperatorPrecedence;
661 switch (unary.op()) {
662 case Op::I32Clz: opStr = "i32.clz"; break;
663 case Op::I32Ctz: opStr = "i32.ctz"; break;
664 case Op::I32Popcnt: opStr = "i32.popcnt"; break;
665 case Op::I64Clz: opStr = "i64.clz"; break;
666 case Op::I64Ctz: opStr = "i64.ctz"; break;
667 case Op::I64Popcnt: opStr = "i64.popcnt"; break;
668 case Op::F32Abs: opStr = "f32.abs"; break;
669 case Op::F32Neg: opStr = "f32.neg"; prefixStr = "-"; precedence = NegatePrecedence; break;
670 case Op::F32Ceil: opStr = "f32.ceil"; break;
671 case Op::F32Floor: opStr = "f32.floor"; break;
672 case Op::F32Sqrt: opStr = "f32.sqrt"; break;
673 case Op::F32Trunc: opStr = "f32.trunc"; break;
674 case Op::F32Nearest: opStr = "f32.nearest"; break;
675 case Op::F64Abs: opStr = "f64.abs"; break;
676 case Op::F64Neg: opStr = "f64.neg"; prefixStr = "-"; precedence = NegatePrecedence; break;
677 case Op::F64Ceil: opStr = "f64.ceil"; break;
678 case Op::F64Floor: opStr = "f64.floor"; break;
679 case Op::F64Sqrt: opStr = "f64.sqrt"; break;
680 default: return false;
681 }
682
683 if (c.f.allowAsciiOperators && prefixStr) {
684 if (!c.f.reduceParens || lastPrecedence > precedence) {
685 if (!c.buffer.append("("))
686 return false;
687 }
688
689 c.currentPrecedence = precedence;
690 if (!c.buffer.append(prefixStr, strlen(prefixStr)))
691 return false;
692 if (!PrintExpr(c, *unary.operand()))
693 return false;
694
695 if (!c.f.reduceParens || lastPrecedence > precedence) {
696 if (!c.buffer.append(")"))
697 return false;
698 }
699 } else {
700 if (!c.buffer.append(opStr, strlen(opStr)))
701 return false;
702 if (!c.buffer.append("("))
703 return false;
704
705 c.currentPrecedence = ExpressionPrecedence;
706 if (!PrintExpr(c, *unary.operand()))
707 return false;
708
709 if (!c.buffer.append(")"))
710 return false;
711 }
712 c.currentPrecedence = lastPrecedence;
713
714 return true;
715 }
716
717 static bool
PrintBinaryOperator(WasmPrintContext & c,AstBinaryOperator & binary)718 PrintBinaryOperator(WasmPrintContext& c, AstBinaryOperator& binary)
719 {
720 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
721
722 const char* opStr;
723 const char* infixStr = nullptr;
724 PrintOperatorPrecedence precedence;
725 switch (binary.op()) {
726 case Op::I32Add: opStr = "i32.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
727 case Op::I32Sub: opStr = "i32.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
728 case Op::I32Mul: opStr = "i32.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
729 case Op::I32DivS: opStr = "i32.div_s"; infixStr = "/s"; precedence = MultiplicationPrecedence; break;
730 case Op::I32DivU: opStr = "i32.div_u"; infixStr = "/u"; precedence = MultiplicationPrecedence; break;
731 case Op::I32RemS: opStr = "i32.rem_s"; infixStr = "%s"; precedence = MultiplicationPrecedence; break;
732 case Op::I32RemU: opStr = "i32.rem_u"; infixStr = "%u"; precedence = MultiplicationPrecedence; break;
733 case Op::I32And: opStr = "i32.and"; infixStr = "&"; precedence = BitwiseAndPrecedence; break;
734 case Op::I32Or: opStr = "i32.or"; infixStr = "|"; precedence = BitwiseOrPrecedence; break;
735 case Op::I32Xor: opStr = "i32.xor"; infixStr = "^"; precedence = BitwiseXorPrecedence; break;
736 case Op::I32Shl: opStr = "i32.shl"; infixStr = "<<"; precedence = BitwiseShiftPrecedence; break;
737 case Op::I32ShrS: opStr = "i32.shr_s"; infixStr = ">>s"; precedence = BitwiseShiftPrecedence; break;
738 case Op::I32ShrU: opStr = "i32.shr_u"; infixStr = ">>u"; precedence = BitwiseShiftPrecedence; break;
739 case Op::I64Add: opStr = "i64.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
740 case Op::I64Sub: opStr = "i64.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
741 case Op::I64Mul: opStr = "i64.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
742 case Op::I64DivS: opStr = "i64.div_s"; infixStr = "/s"; precedence = MultiplicationPrecedence; break;
743 case Op::I64DivU: opStr = "i64.div_u"; infixStr = "/u"; precedence = MultiplicationPrecedence; break;
744 case Op::I64RemS: opStr = "i64.rem_s"; infixStr = "%s"; precedence = MultiplicationPrecedence; break;
745 case Op::I64RemU: opStr = "i64.rem_u"; infixStr = "%u"; precedence = MultiplicationPrecedence; break;
746 case Op::I64And: opStr = "i64.and"; infixStr = "&"; precedence = BitwiseAndPrecedence; break;
747 case Op::I64Or: opStr = "i64.or"; infixStr = "|"; precedence = BitwiseOrPrecedence; break;
748 case Op::I64Xor: opStr = "i64.xor"; infixStr = "^"; precedence = BitwiseXorPrecedence; break;
749 case Op::I64Shl: opStr = "i64.shl"; infixStr = "<<"; precedence = BitwiseShiftPrecedence; break;
750 case Op::I64ShrS: opStr = "i64.shr_s"; infixStr = ">>s"; precedence = BitwiseShiftPrecedence; break;
751 case Op::I64ShrU: opStr = "i64.shr_u"; infixStr = ">>u"; precedence = BitwiseShiftPrecedence; break;
752 case Op::F32Add: opStr = "f32.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
753 case Op::F32Sub: opStr = "f32.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
754 case Op::F32Mul: opStr = "f32.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
755 case Op::F32Div: opStr = "f32.div"; infixStr = "/"; precedence = MultiplicationPrecedence; break;
756 case Op::F32Min: opStr = "f32.min"; precedence = OperatorPrecedence; break;
757 case Op::F32Max: opStr = "f32.max"; precedence = OperatorPrecedence; break;
758 case Op::F32CopySign: opStr = "f32.copysign"; precedence = OperatorPrecedence; break;
759 case Op::F64Add: opStr = "f64.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
760 case Op::F64Sub: opStr = "f64.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
761 case Op::F64Mul: opStr = "f64.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
762 case Op::F64Div: opStr = "f64.div"; infixStr = "/"; precedence = MultiplicationPrecedence; break;
763 case Op::F64Min: opStr = "f64.min"; precedence = OperatorPrecedence; break;
764 case Op::F64Max: opStr = "f64.max"; precedence = OperatorPrecedence; break;
765 case Op::F64CopySign: opStr = "f64.copysign"; precedence = OperatorPrecedence; break;
766 default: return false;
767 }
768
769 if (c.f.allowAsciiOperators && infixStr) {
770 if (!c.f.reduceParens || lastPrecedence > precedence) {
771 if (!c.buffer.append("("))
772 return false;
773 }
774
775 c.currentPrecedence = precedence;
776 if (!PrintExpr(c, *binary.lhs()))
777 return false;
778 if (!c.buffer.append(" "))
779 return false;
780 if (!c.buffer.append(infixStr, strlen(infixStr)))
781 return false;
782 if (!c.buffer.append(" "))
783 return false;
784 // case of A / (B / C)
785 c.currentPrecedence = (PrintOperatorPrecedence)(precedence + 1);
786
787 if (!PrintExpr(c, *binary.rhs()))
788 return false;
789 if (!c.f.reduceParens || lastPrecedence > precedence) {
790 if (!c.buffer.append(")"))
791 return false;
792 }
793 } else {
794 if (!c.buffer.append(opStr, strlen(opStr)))
795 return false;
796 if (!c.buffer.append("("))
797 return false;
798
799 c.currentPrecedence = ExpressionPrecedence;
800 if (!PrintExpr(c, *binary.lhs()))
801 return false;
802 if (!c.buffer.append(", "))
803 return false;
804 if (!PrintExpr(c, *binary.rhs()))
805 return false;
806
807 if (!c.buffer.append(")"))
808 return false;
809 }
810 c.currentPrecedence = lastPrecedence;
811
812 return true;
813 }
814
815 static bool
PrintTernaryOperator(WasmPrintContext & c,AstTernaryOperator & ternary)816 PrintTernaryOperator(WasmPrintContext& c, AstTernaryOperator& ternary)
817 {
818 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
819
820 const char* opStr;
821 switch (ternary.op()) {
822 case Op::Select: opStr = "select"; break;
823 default: return false;
824 }
825
826 if (!c.buffer.append(opStr, strlen(opStr)))
827 return false;
828 if (!c.buffer.append("("))
829 return false;
830
831 c.currentPrecedence = ExpressionPrecedence;
832 if (!PrintExpr(c, *ternary.op0()))
833 return false;
834 if (!c.buffer.append(", "))
835 return false;
836 if (!PrintExpr(c, *ternary.op1()))
837 return false;
838 if (!c.buffer.append(", "))
839 return false;
840 if (!PrintExpr(c, *ternary.op2()))
841 return false;
842
843 if (!c.buffer.append(")"))
844 return false;
845 c.currentPrecedence = lastPrecedence;
846
847 return true;
848 }
849
850 static bool
PrintComparisonOperator(WasmPrintContext & c,AstComparisonOperator & comp)851 PrintComparisonOperator(WasmPrintContext& c, AstComparisonOperator& comp)
852 {
853 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
854
855 const char* opStr;
856 const char* infixStr = nullptr;
857 PrintOperatorPrecedence precedence;
858 switch (comp.op()) {
859 case Op::I32Eq: opStr = "i32.eq"; infixStr = "=="; precedence = EqualityPrecedence; break;
860 case Op::I32Ne: opStr = "i32.ne"; infixStr = "!="; precedence = EqualityPrecedence; break;
861 case Op::I32LtS: opStr = "i32.lt_s"; infixStr = "<s"; precedence = ComparisonPrecedence; break;
862 case Op::I32LtU: opStr = "i32.lt_u"; infixStr = "<u"; precedence = ComparisonPrecedence; break;
863 case Op::I32LeS: opStr = "i32.le_s"; infixStr = "<=s"; precedence = ComparisonPrecedence; break;
864 case Op::I32LeU: opStr = "i32.le_u"; infixStr = "<=u"; precedence = ComparisonPrecedence; break;
865 case Op::I32GtS: opStr = "i32.gt_s"; infixStr = ">s"; precedence = ComparisonPrecedence; break;
866 case Op::I32GtU: opStr = "i32.gt_u"; infixStr = ">u"; precedence = ComparisonPrecedence; break;
867 case Op::I32GeS: opStr = "i32.ge_s"; infixStr = ">=s"; precedence = ComparisonPrecedence; break;
868 case Op::I32GeU: opStr = "i32.ge_u"; infixStr = ">=u"; precedence = ComparisonPrecedence; break;
869 case Op::I64Eq: opStr = "i64.eq"; infixStr = "=="; precedence = EqualityPrecedence; break;
870 case Op::I64Ne: opStr = "i64.ne"; infixStr = "!="; precedence = EqualityPrecedence; break;
871 case Op::I64LtS: opStr = "i64.lt_s"; infixStr = "<s"; precedence = ComparisonPrecedence; break;
872 case Op::I64LtU: opStr = "i64.lt_u"; infixStr = "<u"; precedence = ComparisonPrecedence; break;
873 case Op::I64LeS: opStr = "i64.le_s"; infixStr = "<=s"; precedence = ComparisonPrecedence; break;
874 case Op::I64LeU: opStr = "i64.le_u"; infixStr = "<=u"; precedence = ComparisonPrecedence; break;
875 case Op::I64GtS: opStr = "i64.gt_s"; infixStr = ">s"; precedence = ComparisonPrecedence; break;
876 case Op::I64GtU: opStr = "i64.gt_u"; infixStr = ">u"; precedence = ComparisonPrecedence; break;
877 case Op::I64GeS: opStr = "i64.ge_s"; infixStr = ">=s"; precedence = ComparisonPrecedence; break;
878 case Op::I64GeU: opStr = "i64.ge_u"; infixStr = ">=u"; precedence = ComparisonPrecedence; break;
879 case Op::F32Eq: opStr = "f32.eq"; infixStr = "=="; precedence = EqualityPrecedence; break;
880 case Op::F32Ne: opStr = "f32.ne"; infixStr = "!="; precedence = EqualityPrecedence; break;
881 case Op::F32Lt: opStr = "f32.lt"; infixStr = "<"; precedence = ComparisonPrecedence; break;
882 case Op::F32Le: opStr = "f32.le"; infixStr = "<="; precedence = ComparisonPrecedence; break;
883 case Op::F32Gt: opStr = "f32.gt"; infixStr = ">"; precedence = ComparisonPrecedence; break;
884 case Op::F32Ge: opStr = "f32.ge"; infixStr = ">="; precedence = ComparisonPrecedence; break;
885 case Op::F64Eq: opStr = "f64.eq"; infixStr = "=="; precedence = ComparisonPrecedence; break;
886 case Op::F64Ne: opStr = "f64.ne"; infixStr = "!="; precedence = EqualityPrecedence; break;
887 case Op::F64Lt: opStr = "f64.lt"; infixStr = "<"; precedence = EqualityPrecedence; break;
888 case Op::F64Le: opStr = "f64.le"; infixStr = "<="; precedence = ComparisonPrecedence; break;
889 case Op::F64Gt: opStr = "f64.gt"; infixStr = ">"; precedence = ComparisonPrecedence; break;
890 case Op::F64Ge: opStr = "f64.ge"; infixStr = ">="; precedence = ComparisonPrecedence; break;
891 default: return false;
892 }
893
894 if (c.f.allowAsciiOperators && infixStr) {
895 if (!c.f.reduceParens || lastPrecedence > precedence) {
896 if (!c.buffer.append("("))
897 return false;
898 }
899 c.currentPrecedence = precedence;
900 if (!PrintExpr(c, *comp.lhs()))
901 return false;
902 if (!c.buffer.append(" "))
903 return false;
904 if (!c.buffer.append(infixStr, strlen(infixStr)))
905 return false;
906 if (!c.buffer.append(" "))
907 return false;
908 // case of A == (B == C)
909 c.currentPrecedence = (PrintOperatorPrecedence)(precedence + 1);
910 if (!PrintExpr(c, *comp.rhs()))
911 return false;
912 if (!c.f.reduceParens || lastPrecedence > precedence) {
913 if (!c.buffer.append(")"))
914 return false;
915 }
916 } else {
917 if (!c.buffer.append(opStr, strlen(opStr)))
918 return false;
919 c.currentPrecedence = ExpressionPrecedence;
920 if (!c.buffer.append("("))
921 return false;
922 if (!PrintExpr(c, *comp.lhs()))
923 return false;
924 if (!c.buffer.append(", "))
925 return false;
926 if (!PrintExpr(c, *comp.rhs()))
927 return false;
928 if (!c.buffer.append(")"))
929 return false;
930 }
931 c.currentPrecedence = lastPrecedence;
932
933 return true;
934 }
935
936 static bool
PrintConversionOperator(WasmPrintContext & c,AstConversionOperator & conv)937 PrintConversionOperator(WasmPrintContext& c, AstConversionOperator& conv)
938 {
939 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
940
941 const char* opStr;
942 const char* prefixStr = nullptr;
943 PrintOperatorPrecedence precedence = ExpressionPrecedence;
944 switch (conv.op()) {
945 case Op::I32Eqz: opStr = "i32.eqz"; prefixStr = "!"; precedence = EqzPrecedence; break;
946 case Op::I32WrapI64: opStr = "i32.wrap/i64"; break;
947 case Op::I32TruncSF32: opStr = "i32.trunc_s/f32"; break;
948 case Op::I32TruncUF32: opStr = "i32.trunc_u/f32"; break;
949 case Op::I32ReinterpretF32: opStr = "i32.reinterpret/f32"; break;
950 case Op::I32TruncSF64: opStr = "i32.trunc_s/f64"; break;
951 case Op::I32TruncUF64: opStr = "i32.trunc_u/f64"; break;
952 case Op::I64Eqz: opStr = "i64.eqz"; prefixStr = "!"; precedence = EqzPrecedence; break;
953 case Op::I64ExtendSI32: opStr = "i64.extend_s/i32"; break;
954 case Op::I64ExtendUI32: opStr = "i64.extend_u/i32"; break;
955 case Op::I64TruncSF32: opStr = "i64.trunc_s/f32"; break;
956 case Op::I64TruncUF32: opStr = "i64.trunc_u/f32"; break;
957 case Op::I64TruncSF64: opStr = "i64.trunc_s/f64"; break;
958 case Op::I64TruncUF64: opStr = "i64.trunc_u/f64"; break;
959 case Op::I64ReinterpretF64: opStr = "i64.reinterpret/f64"; break;
960 case Op::F32ConvertSI32: opStr = "f32.convert_s/i32"; break;
961 case Op::F32ConvertUI32: opStr = "f32.convert_u/i32"; break;
962 case Op::F32ReinterpretI32: opStr = "f32.reinterpret/i32"; break;
963 case Op::F32ConvertSI64: opStr = "f32.convert_s/i64"; break;
964 case Op::F32ConvertUI64: opStr = "f32.convert_u/i64"; break;
965 case Op::F32DemoteF64: opStr = "f32.demote/f64"; break;
966 case Op::F64ConvertSI32: opStr = "f64.convert_s/i32"; break;
967 case Op::F64ConvertUI32: opStr = "f64.convert_u/i32"; break;
968 case Op::F64ConvertSI64: opStr = "f64.convert_s/i64"; break;
969 case Op::F64ConvertUI64: opStr = "f64.convert_u/i64"; break;
970 case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
971 case Op::F64PromoteF32: opStr = "f64.promote/f32"; break;
972 default: return false;
973 }
974
975 if (c.f.allowAsciiOperators && prefixStr) {
976 if (!c.f.reduceParens || lastPrecedence > precedence) {
977 if (!c.buffer.append("("))
978 return false;
979 }
980
981 c.currentPrecedence = precedence;
982 if (!c.buffer.append(prefixStr, strlen(prefixStr)))
983 return false;
984 if (!PrintExpr(c, *conv.operand()))
985 return false;
986
987 if (!c.f.reduceParens || lastPrecedence > precedence) {
988 if (!c.buffer.append(")"))
989 return false;
990 }
991 } else {
992 if (!c.buffer.append(opStr, strlen(opStr)))
993 return false;
994 if (!c.buffer.append("("))
995 return false;
996
997 c.currentPrecedence = ExpressionPrecedence;
998 if (!PrintExpr(c, *conv.operand()))
999 return false;
1000
1001 if (!c.buffer.append(")"))
1002 return false;
1003 }
1004 c.currentPrecedence = lastPrecedence;
1005
1006 return true;
1007 }
1008
1009 static bool
PrintIf(WasmPrintContext & c,AstIf & if_)1010 PrintIf(WasmPrintContext& c, AstIf& if_)
1011 {
1012 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
1013
1014 c.currentPrecedence = ExpressionPrecedence;
1015 if (!c.buffer.append("if ("))
1016 return false;
1017 if (!PrintExpr(c, if_.cond()))
1018 return false;
1019
1020 if (!c.buffer.append(") {\n"))
1021 return false;
1022
1023 c.indent++;
1024 if (!PrintExprList(c, if_.thenExprs()))
1025 return false;
1026 c.indent--;
1027
1028 if (!PrintBlockName(c, if_.name()))
1029 return false;
1030
1031 if (if_.hasElse()) {
1032 if (!PrintIndent(c))
1033 return false;
1034 if (!c.buffer.append("} else {\n"))
1035 return false;
1036
1037 c.indent++;
1038 if (!PrintExprList(c, if_.elseExprs()))
1039 return false;
1040 c.indent--;
1041 if (!PrintBlockName(c, if_.name()))
1042 return false;
1043 }
1044
1045 if (!PrintIndent(c))
1046 return false;
1047
1048 c.currentPrecedence = lastPrecedence;
1049
1050 return c.buffer.append("}");
1051 }
1052
1053 static bool
PrintLoadStoreAddress(WasmPrintContext & c,const AstLoadStoreAddress & lsa,uint32_t defaultAlignLog2)1054 PrintLoadStoreAddress(WasmPrintContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2)
1055 {
1056 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
1057
1058 c.currentPrecedence = ExpressionPrecedence;
1059
1060 if (!c.buffer.append("["))
1061 return false;
1062 if (!PrintExpr(c, lsa.base()))
1063 return false;
1064
1065 if (lsa.offset() != 0) {
1066 if (!c.buffer.append(", "))
1067 return false;
1068 if (!PrintInt32(c, lsa.offset(), true))
1069 return false;
1070 }
1071 if (!c.buffer.append("]"))
1072 return false;
1073
1074 uint32_t alignLog2 = lsa.flags();
1075 if (defaultAlignLog2 != alignLog2) {
1076 if (!c.buffer.append(", align="))
1077 return false;
1078 if (!PrintInt32(c, 1 << alignLog2))
1079 return false;
1080 }
1081
1082 c.currentPrecedence = lastPrecedence;
1083 return true;
1084 }
1085
1086 static bool
PrintLoad(WasmPrintContext & c,AstLoad & load)1087 PrintLoad(WasmPrintContext& c, AstLoad& load)
1088 {
1089 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
1090
1091 c.currentPrecedence = LoadOperatorPrecedence;
1092 if (!c.f.reduceParens || lastPrecedence > LoadOperatorPrecedence) {
1093 if (!c.buffer.append("("))
1094 return false;
1095 }
1096
1097 uint32_t defaultAlignLog2;
1098 switch (load.op()) {
1099 case Op::I32Load8S:
1100 if (!c.buffer.append("i32:8s"))
1101 return false;
1102 defaultAlignLog2 = 0;
1103 break;
1104 case Op::I64Load8S:
1105 if (!c.buffer.append("i64:8s"))
1106 return false;
1107 defaultAlignLog2 = 0;
1108 break;
1109 case Op::I32Load8U:
1110 if (!c.buffer.append("i32:8u"))
1111 return false;
1112 defaultAlignLog2 = 0;
1113 break;
1114 case Op::I64Load8U:
1115 if (!c.buffer.append("i64:8u"))
1116 return false;
1117 defaultAlignLog2 = 0;
1118 break;
1119 case Op::I32Load16S:
1120 if (!c.buffer.append("i32:16s"))
1121 return false;
1122 defaultAlignLog2 = 1;
1123 break;
1124 case Op::I64Load16S:
1125 if (!c.buffer.append("i64:16s"))
1126 return false;
1127 defaultAlignLog2 = 1;
1128 break;
1129 case Op::I32Load16U:
1130 if (!c.buffer.append("i32:16u"))
1131 return false;
1132 defaultAlignLog2 = 1;
1133 break;
1134 case Op::I64Load16U:
1135 if (!c.buffer.append("i64:16u"))
1136 return false;
1137 defaultAlignLog2 = 1;
1138 break;
1139 case Op::I64Load32S:
1140 if (!c.buffer.append("i64:32s"))
1141 return false;
1142 defaultAlignLog2 = 2;
1143 break;
1144 case Op::I64Load32U:
1145 if (!c.buffer.append("i64:32u"))
1146 return false;
1147 defaultAlignLog2 = 2;
1148 break;
1149 case Op::I32Load:
1150 if (!c.buffer.append("i32"))
1151 return false;
1152 defaultAlignLog2 = 2;
1153 break;
1154 case Op::I64Load:
1155 if (!c.buffer.append("i64"))
1156 return false;
1157 defaultAlignLog2 = 3;
1158 break;
1159 case Op::F32Load:
1160 if (!c.buffer.append("f32"))
1161 return false;
1162 defaultAlignLog2 = 2;
1163 break;
1164 case Op::F64Load:
1165 if (!c.buffer.append("f64"))
1166 return false;
1167 defaultAlignLog2 = 3;
1168 break;
1169 default:
1170 return false;
1171 }
1172
1173 if (!PrintLoadStoreAddress(c, load.address(), defaultAlignLog2))
1174 return false;
1175
1176 if (!c.f.reduceParens || lastPrecedence > LoadOperatorPrecedence) {
1177 if (!c.buffer.append(")"))
1178 return false;
1179 }
1180 c.currentPrecedence = lastPrecedence;
1181
1182 return true;
1183 }
1184
1185 static bool
PrintStore(WasmPrintContext & c,AstStore & store)1186 PrintStore(WasmPrintContext& c, AstStore& store)
1187 {
1188 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
1189
1190 c.currentPrecedence = StoreOperatorPrecedence;
1191 if (!c.f.reduceParens || lastPrecedence > StoreOperatorPrecedence) {
1192 if (!c.buffer.append("("))
1193 return false;
1194 }
1195
1196 uint32_t defaultAlignLog2;
1197 switch (store.op()) {
1198 case Op::I32Store8:
1199 if (!c.buffer.append("i32:8"))
1200 return false;
1201 defaultAlignLog2 = 0;
1202 break;
1203 case Op::I64Store8:
1204 if (!c.buffer.append("i64:8"))
1205 return false;
1206 defaultAlignLog2 = 0;
1207 break;
1208 case Op::I32Store16:
1209 if (!c.buffer.append("i32:16"))
1210 return false;
1211 defaultAlignLog2 = 1;
1212 break;
1213 case Op::I64Store16:
1214 if (!c.buffer.append("i64:16"))
1215 return false;
1216 defaultAlignLog2 = 1;
1217 break;
1218 case Op::I64Store32:
1219 if (!c.buffer.append("i64:32"))
1220 return false;
1221 defaultAlignLog2 = 2;
1222 break;
1223 case Op::I32Store:
1224 if (!c.buffer.append("i32"))
1225 return false;
1226 defaultAlignLog2 = 2;
1227 break;
1228 case Op::I64Store:
1229 if (!c.buffer.append("i64"))
1230 return false;
1231 defaultAlignLog2 = 3;
1232 break;
1233 case Op::F32Store:
1234 if (!c.buffer.append("f32"))
1235 return false;
1236 defaultAlignLog2 = 2;
1237 break;
1238 case Op::F64Store:
1239 if (!c.buffer.append("f64"))
1240 return false;
1241 defaultAlignLog2 = 3;
1242 break;
1243 default:
1244 return false;
1245 }
1246
1247 if (!PrintLoadStoreAddress(c, store.address(), defaultAlignLog2))
1248 return false;
1249
1250 if (!c.buffer.append(" = "))
1251 return false;
1252
1253 if (!PrintExpr(c, store.value()))
1254 return false;
1255
1256 if (!c.f.reduceParens || lastPrecedence > StoreOperatorPrecedence) {
1257 if (!c.buffer.append(")"))
1258 return false;
1259 }
1260
1261 c.currentPrecedence = lastPrecedence;
1262 return true;
1263 }
1264
1265 static bool
PrintBranch(WasmPrintContext & c,AstBranch & branch)1266 PrintBranch(WasmPrintContext& c, AstBranch& branch)
1267 {
1268 Op op = branch.op();
1269 MOZ_ASSERT(op == Op::BrIf || op == Op::Br);
1270
1271 if (op == Op::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br "))
1272 return false;
1273
1274 if (op == Op::BrIf || branch.maybeValue()) {
1275 if (!c.buffer.append('('))
1276 return false;
1277 }
1278
1279 if (op == Op::BrIf) {
1280 if (!PrintExpr(c, branch.cond()))
1281 return false;
1282 }
1283
1284 if (branch.maybeValue()) {
1285 if (!c.buffer.append(", "))
1286 return false;
1287
1288 if (!PrintExpr(c, *(branch.maybeValue())))
1289 return false;
1290 }
1291
1292 if (op == Op::BrIf || branch.maybeValue()) {
1293 if (!c.buffer.append(") "))
1294 return false;
1295 }
1296
1297 if (!PrintRef(c, branch.target()))
1298 return false;
1299
1300 return true;
1301 }
1302
1303 static bool
PrintBrTable(WasmPrintContext & c,AstBranchTable & table)1304 PrintBrTable(WasmPrintContext& c, AstBranchTable& table)
1305 {
1306 if (!c.buffer.append("br_table "))
1307 return false;
1308
1309 if (!c.buffer.append('('))
1310 return false;
1311
1312 // Index
1313 if (!PrintExpr(c, table.index()))
1314 return false;
1315
1316 if (table.maybeValue()) {
1317 if (!c.buffer.append(", "))
1318 return false;
1319
1320 if (!PrintExpr(c, *(table.maybeValue())))
1321 return false;
1322 }
1323
1324 if (!c.buffer.append(") "))
1325 return false;
1326
1327 uint32_t tableLength = table.table().length();
1328 if (tableLength > 0) {
1329 if (!c.buffer.append("["))
1330 return false;
1331 for (uint32_t i = 0; i < tableLength; i++) {
1332 if (!PrintRef(c, table.table()[i]))
1333 return false;
1334 if (i + 1 == tableLength)
1335 break;
1336 if (!c.buffer.append(", "))
1337 return false;
1338 }
1339 if (!c.buffer.append("], "))
1340 return false;
1341 }
1342
1343 if (!PrintRef(c, table.def()))
1344 return false;
1345
1346 return true;
1347 }
1348
1349 static bool
PrintReturn(WasmPrintContext & c,AstReturn & ret)1350 PrintReturn(WasmPrintContext& c, AstReturn& ret)
1351 {
1352 if (!c.buffer.append("return"))
1353 return false;
1354
1355 if (ret.maybeExpr()) {
1356 if (!c.buffer.append(" "))
1357 return false;
1358 if (!PrintExpr(c, *(ret.maybeExpr())))
1359 return false;
1360 }
1361
1362 return true;
1363 }
1364
1365 static bool
PrintFirst(WasmPrintContext & c,AstFirst & first)1366 PrintFirst(WasmPrintContext& c, AstFirst& first)
1367 {
1368 if (!c.buffer.append("first("))
1369 return false;
1370
1371 for (uint32_t i = 0; i < first.exprs().length(); i++) {
1372 if (!PrintExpr(c, *first.exprs()[i]))
1373 return false;
1374 if (i + 1 == first.exprs().length())
1375 break;
1376 if (!c.buffer.append(", "))
1377 return false;
1378 }
1379
1380 if (!c.buffer.append(")"))
1381 return false;
1382
1383 return true;
1384 }
1385
1386 static bool
PrintCurrentMemory(WasmPrintContext & c,AstCurrentMemory & cm)1387 PrintCurrentMemory(WasmPrintContext& c, AstCurrentMemory& cm)
1388 {
1389 return c.buffer.append("current_memory");
1390 }
1391
1392 static bool
PrintGrowMemory(WasmPrintContext & c,AstGrowMemory & gm)1393 PrintGrowMemory(WasmPrintContext& c, AstGrowMemory& gm)
1394 {
1395 if (!c.buffer.append("grow_memory("))
1396 return false;
1397
1398 PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
1399 c.currentPrecedence = ExpressionPrecedence;
1400
1401 if (!PrintExpr(c, *gm.operand()))
1402 return false;
1403
1404 if (!c.buffer.append(")"))
1405 return false;
1406
1407 c.currentPrecedence = lastPrecedence;
1408 return true;
1409 }
1410
1411 static bool
PrintExpr(WasmPrintContext & c,AstExpr & expr)1412 PrintExpr(WasmPrintContext& c, AstExpr& expr)
1413 {
1414 if (c.maybeSourceMap) {
1415 uint32_t lineno = c.buffer.lineno();
1416 uint32_t column = c.buffer.column();
1417 if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset()))
1418 return false;
1419 }
1420
1421 switch (expr.kind()) {
1422 case AstExprKind::Nop:
1423 return PrintNop(c);
1424 case AstExprKind::Drop:
1425 return PrintDrop(c, expr.as<AstDrop>());
1426 case AstExprKind::Unreachable:
1427 return PrintUnreachable(c, expr.as<AstUnreachable>());
1428 case AstExprKind::Call:
1429 return PrintCall(c, expr.as<AstCall>());
1430 case AstExprKind::CallIndirect:
1431 return PrintCallIndirect(c, expr.as<AstCallIndirect>());
1432 case AstExprKind::Const:
1433 return PrintConst(c, expr.as<AstConst>());
1434 case AstExprKind::GetLocal:
1435 return PrintGetLocal(c, expr.as<AstGetLocal>());
1436 case AstExprKind::SetLocal:
1437 return PrintSetLocal(c, expr.as<AstSetLocal>());
1438 case AstExprKind::TeeLocal:
1439 return PrintTeeLocal(c, expr.as<AstTeeLocal>());
1440 case AstExprKind::GetGlobal:
1441 return PrintGetGlobal(c, expr.as<AstGetGlobal>());
1442 case AstExprKind::SetGlobal:
1443 return PrintSetGlobal(c, expr.as<AstSetGlobal>());
1444 case AstExprKind::Block:
1445 return PrintBlock(c, expr.as<AstBlock>());
1446 case AstExprKind::If:
1447 return PrintIf(c, expr.as<AstIf>());
1448 case AstExprKind::UnaryOperator:
1449 return PrintUnaryOperator(c, expr.as<AstUnaryOperator>());
1450 case AstExprKind::BinaryOperator:
1451 return PrintBinaryOperator(c, expr.as<AstBinaryOperator>());
1452 case AstExprKind::TernaryOperator:
1453 return PrintTernaryOperator(c, expr.as<AstTernaryOperator>());
1454 case AstExprKind::ComparisonOperator:
1455 return PrintComparisonOperator(c, expr.as<AstComparisonOperator>());
1456 case AstExprKind::ConversionOperator:
1457 return PrintConversionOperator(c, expr.as<AstConversionOperator>());
1458 case AstExprKind::Load:
1459 return PrintLoad(c, expr.as<AstLoad>());
1460 case AstExprKind::Store:
1461 return PrintStore(c, expr.as<AstStore>());
1462 case AstExprKind::Branch:
1463 return PrintBranch(c, expr.as<AstBranch>());
1464 case AstExprKind::BranchTable:
1465 return PrintBrTable(c, expr.as<AstBranchTable>());
1466 case AstExprKind::Return:
1467 return PrintReturn(c, expr.as<AstReturn>());
1468 case AstExprKind::First:
1469 return PrintFirst(c, expr.as<AstFirst>());
1470 case AstExprKind::CurrentMemory:
1471 return PrintCurrentMemory(c, expr.as<AstCurrentMemory>());
1472 case AstExprKind::GrowMemory:
1473 return PrintGrowMemory(c, expr.as<AstGrowMemory>());
1474 case AstExprKind::Pop:
1475 return true;
1476 }
1477
1478 MOZ_CRASH("Bad AstExprKind");
1479 }
1480
1481 static bool
PrintSignature(WasmPrintContext & c,const AstSig & sig,const AstNameVector * maybeLocals=nullptr)1482 PrintSignature(WasmPrintContext& c, const AstSig& sig, const AstNameVector* maybeLocals = nullptr)
1483 {
1484 uint32_t paramsNum = sig.args().length();
1485
1486 if (!c.buffer.append("("))
1487 return false;
1488 if (maybeLocals) {
1489 for (uint32_t i = 0; i < paramsNum; i++) {
1490 const AstName& name = (*maybeLocals)[i];
1491 if (!name.empty()) {
1492 if (!PrintName(c, name))
1493 return false;
1494 if (!c.buffer.append(": "))
1495 return false;
1496 }
1497 ValType arg = sig.args()[i];
1498 if (!PrintValType(c, arg))
1499 return false;
1500 if (i + 1 == paramsNum)
1501 break;
1502 if (!c.buffer.append(", "))
1503 return false;
1504 }
1505 } else if (paramsNum > 0) {
1506 for (uint32_t i = 0; i < paramsNum; i++) {
1507 ValType arg = sig.args()[i];
1508 if (!PrintValType(c, arg))
1509 return false;
1510 if (i + 1 == paramsNum)
1511 break;
1512 if (!c.buffer.append(", "))
1513 return false;
1514 }
1515 }
1516 if (!c.buffer.append(") : ("))
1517 return false;
1518 if (sig.ret() != ExprType::Void) {
1519 if (!PrintExprType(c, sig.ret()))
1520 return false;
1521 }
1522 if (!c.buffer.append(")"))
1523 return false;
1524 return true;
1525 }
1526
1527 static bool
PrintTypeSection(WasmPrintContext & c,const AstModule::SigVector & sigs)1528 PrintTypeSection(WasmPrintContext& c, const AstModule::SigVector& sigs)
1529 {
1530 uint32_t numSigs = sigs.length();
1531 if (!numSigs)
1532 return true;
1533
1534 for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
1535 const AstSig* sig = sigs[sigIndex];
1536 if (!PrintIndent(c))
1537 return false;
1538 if (!c.buffer.append("type "))
1539 return false;
1540 if (!sig->name().empty()) {
1541 if (!PrintName(c, sig->name()))
1542 return false;
1543 if (!c.buffer.append(" of "))
1544 return false;
1545 }
1546 if (!c.buffer.append("function "))
1547 return false;
1548 if (!PrintSignature(c, *sig))
1549 return false;
1550 if (!c.buffer.append(";\n"))
1551 return false;
1552 }
1553
1554 if (!c.buffer.append("\n"))
1555 return false;
1556
1557 return true;
1558 }
1559
1560 static bool
PrintTableSection(WasmPrintContext & c,const AstModule & module)1561 PrintTableSection(WasmPrintContext& c, const AstModule& module)
1562 {
1563 if (module.elemSegments().empty())
1564 return true;
1565
1566 const AstElemSegment& segment = *module.elemSegments()[0];
1567
1568 if (!c.buffer.append("table ["))
1569 return false;
1570
1571 for (uint32_t i = 0; i < segment.elems().length(); i++) {
1572 const AstRef& elem = segment.elems()[i];
1573 uint32_t index = elem.index();
1574 AstName name = index < module.funcImportNames().length()
1575 ? module.funcImportNames()[index]
1576 : module.funcs()[index - module.funcImportNames().length()]->name();
1577 if (name.empty()) {
1578 if (!PrintInt32(c, index))
1579 return false;
1580 } else {
1581 if (!PrintName(c, name))
1582 return false;
1583 }
1584 if (i + 1 == segment.elems().length())
1585 break;
1586 if (!c.buffer.append(", "))
1587 return false;
1588 }
1589
1590 if (!c.buffer.append("];\n\n"))
1591 return false;
1592
1593 return true;
1594 }
1595
1596 static bool
PrintImport(WasmPrintContext & c,AstImport & import,const AstModule::SigVector & sigs)1597 PrintImport(WasmPrintContext& c, AstImport& import, const AstModule::SigVector& sigs)
1598 {
1599 const AstSig* sig = sigs[import.funcSig().index()];
1600 if (!PrintIndent(c))
1601 return false;
1602 if (!c.buffer.append("import "))
1603 return false;
1604 if (!c.buffer.append("\""))
1605 return false;
1606
1607 const AstName& fieldName = import.field();
1608 if (!PrintEscapedString(c, fieldName))
1609 return false;
1610
1611 if (!c.buffer.append("\" as "))
1612 return false;
1613
1614 if (!PrintName(c, import.name()))
1615 return false;
1616
1617 if (!c.buffer.append(" from \""))
1618 return false;
1619
1620 const AstName& moduleName = import.module();
1621 if (!PrintEscapedString(c, moduleName))
1622 return false;
1623
1624 if (!c.buffer.append("\" typeof function "))
1625 return false;
1626
1627 if (!PrintSignature(c, *sig))
1628 return false;
1629 if (!c.buffer.append(";\n"))
1630 return false;
1631
1632 return true;
1633 }
1634
1635
1636 static bool
PrintImportSection(WasmPrintContext & c,const AstModule::ImportVector & imports,const AstModule::SigVector & sigs)1637 PrintImportSection(WasmPrintContext& c, const AstModule::ImportVector& imports, const AstModule::SigVector& sigs)
1638 {
1639 uint32_t numImports = imports.length();
1640
1641 for (uint32_t i = 0; i < numImports; i++) {
1642 if (!PrintImport(c, *imports[i], sigs))
1643 return false;
1644 }
1645
1646 if (numImports) {
1647 if (!c.buffer.append("\n"))
1648 return false;
1649 }
1650
1651 return true;
1652 }
1653
1654 static bool
PrintExport(WasmPrintContext & c,AstExport & export_,const AstModule::NameVector & funcImportNames,const AstModule::FuncVector & funcs)1655 PrintExport(WasmPrintContext& c, AstExport& export_,
1656 const AstModule::NameVector& funcImportNames,
1657 const AstModule::FuncVector& funcs)
1658 {
1659 if (!PrintIndent(c))
1660 return false;
1661 if (!c.buffer.append("export "))
1662 return false;
1663 if (export_.kind() == DefinitionKind::Memory) {
1664 if (!c.buffer.append("memory"))
1665 return false;
1666 } else {
1667 uint32_t index = export_.ref().index();
1668 AstName name = index < funcImportNames.length()
1669 ? funcImportNames[index]
1670 : funcs[index - funcImportNames.length()]->name();
1671 if (name.empty()) {
1672 if (!PrintInt32(c, index))
1673 return false;
1674 } else {
1675 if (!PrintName(c, name))
1676 return false;
1677 }
1678 }
1679 if (!c.buffer.append(" as \""))
1680 return false;
1681 if (!PrintEscapedString(c, export_.name()))
1682 return false;
1683 if (!c.buffer.append("\";\n"))
1684 return false;
1685
1686 return true;
1687 }
1688
1689 static bool
PrintExportSection(WasmPrintContext & c,const AstModule::ExportVector & exports,const AstModule::NameVector & funcImportNames,const AstModule::FuncVector & funcs)1690 PrintExportSection(WasmPrintContext& c, const AstModule::ExportVector& exports,
1691 const AstModule::NameVector& funcImportNames,
1692 const AstModule::FuncVector& funcs)
1693 {
1694 uint32_t numExports = exports.length();
1695 for (uint32_t i = 0; i < numExports; i++) {
1696 if (!PrintExport(c, *exports[i], funcImportNames, funcs))
1697 return false;
1698 }
1699 if (numExports) {
1700 if (!c.buffer.append("\n"))
1701 return false;
1702 }
1703 return true;
1704 }
1705
1706 static bool
PrintFunctionBody(WasmPrintContext & c,AstFunc & func,const AstModule::SigVector & sigs)1707 PrintFunctionBody(WasmPrintContext& c, AstFunc& func, const AstModule::SigVector& sigs)
1708 {
1709 const AstSig* sig = sigs[func.sig().index()];
1710 c.indent++;
1711
1712 size_t startExprIndex = c.maybeSourceMap ? c.maybeSourceMap->exprlocs().length() : 0;
1713 uint32_t startLineno = c.buffer.lineno();
1714
1715 uint32_t argsNum = sig->args().length();
1716 uint32_t localsNum = func.vars().length();
1717 if (localsNum > 0) {
1718 if (!PrintIndent(c))
1719 return false;
1720 if (!c.buffer.append("var "))
1721 return false;
1722 for (uint32_t i = 0; i < localsNum; i++) {
1723 const AstName& name = func.locals()[argsNum + i];
1724 if (!name.empty()) {
1725 if (!PrintName(c, name))
1726 return false;
1727 if (!c.buffer.append(": "))
1728 return false;
1729 }
1730 ValType local = func.vars()[i];
1731 if (!PrintValType(c, local))
1732 return false;
1733 if (i + 1 == localsNum)
1734 break;
1735 if (!c.buffer.append(", "))
1736 return false;
1737 }
1738 if (!c.buffer.append(";\n"))
1739 return false;
1740 }
1741
1742
1743 uint32_t exprsNum = func.body().length();
1744 for (uint32_t i = 0; i < exprsNum; i++) {
1745 if (!PrintBlockLevelExpr(c, *func.body()[i], i + 1 == exprsNum))
1746 return false;
1747 }
1748
1749 c.indent--;
1750
1751 size_t endExprIndex = c.maybeSourceMap ? c.maybeSourceMap->exprlocs().length() : 0;
1752 uint32_t endLineno = c.buffer.lineno();
1753
1754 if (c.maybeSourceMap) {
1755 if (!c.maybeSourceMap->functionlocs().emplaceBack(startExprIndex, endExprIndex, startLineno, endLineno))
1756 return false;
1757 }
1758 return true;
1759 }
1760
1761 static bool
PrintCodeSection(WasmPrintContext & c,const AstModule::FuncVector & funcs,const AstModule::SigVector & sigs)1762 PrintCodeSection(WasmPrintContext& c, const AstModule::FuncVector& funcs, const AstModule::SigVector& sigs)
1763 {
1764 uint32_t numFuncBodies = funcs.length();
1765 for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
1766 AstFunc* func = funcs[funcIndex];
1767 uint32_t sigIndex = func->sig().index();
1768 AstSig* sig = sigs[sigIndex];
1769
1770 if (!PrintIndent(c))
1771 return false;
1772 if (!c.buffer.append("function "))
1773 return false;
1774 if (!func->name().empty()) {
1775 if (!PrintName(c, func->name()))
1776 return false;
1777 }
1778
1779 if (!PrintSignature(c, *sig, &(func->locals())))
1780 return false;
1781 if (!c.buffer.append(" {\n"))
1782 return false;
1783
1784 c.currentFuncIndex = funcIndex;
1785
1786 if (!PrintFunctionBody(c, *func, sigs))
1787 return false;
1788
1789 if (!PrintIndent(c))
1790 return false;
1791 if (!c.buffer.append("}\n\n"))
1792 return false;
1793 }
1794
1795 return true;
1796 }
1797
1798 static bool
PrintDataSection(WasmPrintContext & c,const AstModule & module)1799 PrintDataSection(WasmPrintContext& c, const AstModule& module)
1800 {
1801 if (!module.hasMemory())
1802 return true;
1803
1804 MOZ_ASSERT(module.memories().length() == 1, "NYI: several memories");
1805
1806 if (!PrintIndent(c))
1807 return false;
1808 if (!c.buffer.append("memory "))
1809 return false;
1810
1811 const Limits& memory = module.memories()[0].limits;
1812 MOZ_ASSERT(memory.initial % PageSize == 0);
1813 if (!PrintInt32(c, memory.initial / PageSize))
1814 return false;
1815
1816 if (memory.maximum) {
1817 MOZ_ASSERT(*memory.maximum % PageSize == 0);
1818 if (!c.buffer.append(", "))
1819 return false;
1820 if (!PrintInt32(c, *memory.maximum / PageSize))
1821 return false;
1822 }
1823
1824 c.indent++;
1825
1826 uint32_t numSegments = module.dataSegments().length();
1827 if (!numSegments) {
1828 if (!c.buffer.append(" {}\n\n"))
1829 return false;
1830 return true;
1831 }
1832 if (!c.buffer.append(" {\n"))
1833 return false;
1834
1835 for (uint32_t i = 0; i < numSegments; i++) {
1836 const AstDataSegment* segment = module.dataSegments()[i];
1837 if (!PrintIndent(c))
1838 return false;
1839 if (!c.buffer.append("segment "))
1840 return false;
1841 if (!PrintInt32(c, segment->offset()->as<AstConst>().val().i32()))
1842 return false;
1843 if (!c.buffer.append("\n"))
1844 return false;
1845
1846 c.indent++;
1847 for (const AstName& fragment : segment->fragments()) {
1848 if (!PrintIndent(c))
1849 return false;
1850 if (!c.buffer.append("\""))
1851 return false;
1852 if (!PrintEscapedString(c, fragment))
1853 return false;
1854 if (!c.buffer.append("\"\n"))
1855 return false;
1856 }
1857 c.indent--;
1858
1859 if (!PrintIndent(c))
1860 return false;
1861 if (!c.buffer.append(";\n"))
1862 return false;
1863 }
1864
1865 c.indent--;
1866 if (!c.buffer.append("}\n\n"))
1867 return false;
1868
1869 return true;
1870 }
1871
1872 static bool
PrintModule(WasmPrintContext & c,AstModule & module)1873 PrintModule(WasmPrintContext& c, AstModule& module)
1874 {
1875 if (!PrintTypeSection(c, module.sigs()))
1876 return false;
1877
1878 if (!PrintImportSection(c, module.imports(), module.sigs()))
1879 return false;
1880
1881 if (!PrintTableSection(c, module))
1882 return false;
1883
1884 if (!PrintExportSection(c, module.exports(), module.funcImportNames(), module.funcs()))
1885 return false;
1886
1887 if (!PrintCodeSection(c, module.funcs(), module.sigs()))
1888 return false;
1889
1890 if (!PrintDataSection(c, module))
1891 return false;
1892
1893 return true;
1894 }
1895
1896 /*****************************************************************************/
1897 // Top-level functions
1898
1899 bool
BinaryToExperimentalText(JSContext * cx,const uint8_t * bytes,size_t length,StringBuffer & buffer,const ExperimentalTextFormatting & formatting,GeneratedSourceMap * sourceMap)1900 wasm::BinaryToExperimentalText(JSContext* cx, const uint8_t* bytes, size_t length,
1901 StringBuffer& buffer, const ExperimentalTextFormatting& formatting,
1902 GeneratedSourceMap* sourceMap)
1903 {
1904
1905 LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
1906
1907 AstModule* module;
1908 if (!BinaryToAst(cx, bytes, length, lifo, &module))
1909 return false;
1910
1911 WasmPrintBuffer buf(buffer);
1912 WasmPrintContext c(cx, module, buf, formatting, sourceMap);
1913
1914 if (!PrintModule(c, *module)) {
1915 if (!cx->isExceptionPending())
1916 ReportOutOfMemory(cx);
1917 return false;
1918 }
1919
1920 return true;
1921 }
1922
1923