1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/wasm/wasm-text.h"
6 
7 #include "src/debug/interface-types.h"
8 #include "src/objects-inl.h"
9 #include "src/ostreams.h"
10 #include "src/vector.h"
11 #include "src/wasm/function-body-decoder-impl.h"
12 #include "src/wasm/function-body-decoder.h"
13 #include "src/wasm/wasm-module.h"
14 #include "src/wasm/wasm-opcodes.h"
15 #include "src/zone/zone.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace wasm {
20 
21 namespace {
IsValidFunctionName(const Vector<const char> & name)22 bool IsValidFunctionName(const Vector<const char> &name) {
23   if (name.is_empty()) return false;
24   const char *special_chars = "_.+-*/\\^~=<>!?@#$%&|:'`";
25   for (char c : name) {
26     bool valid_char = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
27                       (c >= 'A' && c <= 'Z') || strchr(special_chars, c);
28     if (!valid_char) return false;
29   }
30   return true;
31 }
32 
33 }  // namespace
34 
PrintWasmText(const WasmModule * module,const ModuleWireBytes & wire_bytes,uint32_t func_index,std::ostream & os,debug::WasmDisassembly::OffsetTable * offset_table)35 void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
36                    uint32_t func_index, std::ostream& os,
37                    debug::WasmDisassembly::OffsetTable* offset_table) {
38   DCHECK_NOT_NULL(module);
39   DCHECK_GT(module->functions.size(), func_index);
40   const WasmFunction *fun = &module->functions[func_index];
41 
42   AccountingAllocator allocator;
43   Zone zone(&allocator, ZONE_NAME);
44   int line_nr = 0;
45   int control_depth = 1;
46 
47   // Print the function signature.
48   os << "func";
49   WasmName fun_name = wire_bytes.GetNameOrNull(fun, module);
50   if (IsValidFunctionName(fun_name)) {
51     os << " $";
52     os.write(fun_name.start(), fun_name.length());
53   }
54   if (fun->sig->parameter_count()) {
55     os << " (param";
56     for (auto param : fun->sig->parameters())
57       os << ' ' << ValueTypes::TypeName(param);
58     os << ')';
59   }
60   if (fun->sig->return_count()) {
61     os << " (result";
62     for (auto ret : fun->sig->returns()) os << ' ' << ValueTypes::TypeName(ret);
63     os << ')';
64   }
65   os << "\n";
66   ++line_nr;
67 
68   // Print the local declarations.
69   BodyLocalDecls decls(&zone);
70   Vector<const byte> func_bytes = wire_bytes.GetFunctionBytes(fun);
71   BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
72   DCHECK_LT(func_bytes.begin(), i.pc());
73   if (!decls.type_list.empty()) {
74     os << "(local";
75     for (const ValueType &v : decls.type_list) {
76       os << ' ' << ValueTypes::TypeName(v);
77     }
78     os << ")\n";
79     ++line_nr;
80   }
81 
82   for (; i.has_next(); i.next()) {
83     WasmOpcode opcode = i.current();
84     if (opcode == kExprElse || opcode == kExprEnd) --control_depth;
85 
86     DCHECK_LE(0, control_depth);
87     const int kMaxIndentation = 64;
88     int indentation = std::min(kMaxIndentation, 2 * control_depth);
89     if (offset_table) {
90       offset_table->emplace_back(i.pc_offset(), line_nr, indentation);
91     }
92 
93     // 64 whitespaces
94     const char padding[kMaxIndentation + 1] =
95         "                                                                ";
96     os.write(padding, indentation);
97 
98     switch (opcode) {
99       case kExprLoop:
100       case kExprIf:
101       case kExprBlock:
102       case kExprTry: {
103         BlockTypeImmediate<Decoder::kNoValidate> imm(&i, i.pc());
104         os << WasmOpcodes::OpcodeName(opcode);
105         if (imm.type == kWasmVar) {
106           os << " (type " << imm.sig_index << ")";
107         } else if (imm.out_arity() > 0) {
108           os << " " << ValueTypes::TypeName(imm.out_type(0));
109         }
110         control_depth++;
111         break;
112       }
113       case kExprBr:
114       case kExprBrIf: {
115         BreakDepthImmediate<Decoder::kNoValidate> imm(&i, i.pc());
116         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth;
117         break;
118       }
119       case kExprElse:
120         os << "else";
121         control_depth++;
122         break;
123       case kExprEnd:
124         os << "end";
125         break;
126       case kExprBrTable: {
127         BranchTableImmediate<Decoder::kNoValidate> imm(&i, i.pc());
128         BranchTableIterator<Decoder::kNoValidate> iterator(&i, imm);
129         os << "br_table";
130         while (iterator.has_next()) os << ' ' << iterator.next();
131         break;
132       }
133       case kExprCallIndirect: {
134         CallIndirectImmediate<Decoder::kNoValidate> imm(&i, i.pc());
135         DCHECK_EQ(0, imm.table_index);
136         os << "call_indirect " << imm.sig_index;
137         break;
138       }
139       case kExprCallFunction: {
140         CallFunctionImmediate<Decoder::kNoValidate> imm(&i, i.pc());
141         os << "call " << imm.index;
142         break;
143       }
144       case kExprGetLocal:
145       case kExprSetLocal:
146       case kExprTeeLocal: {
147         LocalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
148         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
149         break;
150       }
151       case kExprThrow:
152       case kExprCatch: {
153         ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
154         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
155         break;
156       }
157       case kExprGetGlobal:
158       case kExprSetGlobal: {
159         GlobalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
160         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
161         break;
162       }
163 #define CASE_CONST(type, str, cast_type)                        \
164   case kExpr##type##Const: {                                    \
165     Imm##type##Immediate<Decoder::kNoValidate> imm(&i, i.pc()); \
166     os << #str ".const " << static_cast<cast_type>(imm.value);  \
167     break;                                                      \
168   }
169         CASE_CONST(I32, i32, int32_t)
170         CASE_CONST(I64, i64, int64_t)
171         CASE_CONST(F32, f32, float)
172         CASE_CONST(F64, f64, double)
173 #undef CASE_CONST
174 
175 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
176         FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
177         FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
178           MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
179                                                           kMaxUInt32);
180           os << WasmOpcodes::OpcodeName(opcode) << " offset=" << imm.offset
181              << " align=" << (1ULL << imm.alignment);
182           break;
183         }
184 
185         FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
186       case kExprUnreachable:
187       case kExprNop:
188       case kExprReturn:
189       case kExprMemorySize:
190       case kExprGrowMemory:
191       case kExprDrop:
192       case kExprSelect:
193         os << WasmOpcodes::OpcodeName(opcode);
194         break;
195       case kAtomicPrefix: {
196         WasmOpcode atomic_opcode = i.prefixed_opcode();
197         switch (atomic_opcode) {
198           FOREACH_ATOMIC_OPCODE(CASE_OPCODE) {
199             MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
200                                                             kMaxUInt32);
201             os << WasmOpcodes::OpcodeName(atomic_opcode)
202                << " offset=" << imm.offset
203                << " align=" << (1ULL << imm.alignment);
204             break;
205           }
206           default:
207             UNREACHABLE();
208             break;
209         }
210         break;
211       }
212 
213         // This group is just printed by their internal opcode name, as they
214         // should never be shown to end-users.
215         FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
216         // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
217         // they are publicly available.
218         FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
219         FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
220         FOREACH_SIMD_MASK_OPERAND_OPCODE(CASE_OPCODE)
221         FOREACH_SIMD_MEM_OPCODE(CASE_OPCODE)
222         os << WasmOpcodes::OpcodeName(opcode);
223         break;
224 #undef CASE_OPCODE
225 
226       default:
227         UNREACHABLE();
228         break;
229     }
230     os << '\n';
231     ++line_nr;
232   }
233   DCHECK_EQ(0, control_depth);
234   DCHECK(i.ok());
235 }
236 
237 }  // namespace wasm
238 }  // namespace internal
239 }  // namespace v8
240