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