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(kAllWasmFeatures, &i,
104 i.pc());
105 os << WasmOpcodes::OpcodeName(opcode);
106 if (imm.type == kWasmVar) {
107 os << " (type " << imm.sig_index << ")";
108 } else if (imm.out_arity() > 0) {
109 os << " " << ValueTypes::TypeName(imm.out_type(0));
110 }
111 control_depth++;
112 break;
113 }
114 case kExprBr:
115 case kExprBrIf: {
116 BreakDepthImmediate<Decoder::kNoValidate> imm(&i, i.pc());
117 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth;
118 break;
119 }
120 case kExprElse:
121 os << "else";
122 control_depth++;
123 break;
124 case kExprEnd:
125 os << "end";
126 break;
127 case kExprBrTable: {
128 BranchTableImmediate<Decoder::kNoValidate> imm(&i, i.pc());
129 BranchTableIterator<Decoder::kNoValidate> iterator(&i, imm);
130 os << "br_table";
131 while (iterator.has_next()) os << ' ' << iterator.next();
132 break;
133 }
134 case kExprCallIndirect: {
135 CallIndirectImmediate<Decoder::kNoValidate> imm(&i, i.pc());
136 DCHECK_EQ(0, imm.table_index);
137 os << "call_indirect " << imm.sig_index;
138 break;
139 }
140 case kExprCallFunction: {
141 CallFunctionImmediate<Decoder::kNoValidate> imm(&i, i.pc());
142 os << "call " << imm.index;
143 break;
144 }
145 case kExprGetLocal:
146 case kExprSetLocal:
147 case kExprTeeLocal: {
148 LocalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
149 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
150 break;
151 }
152 case kExprThrow:
153 case kExprCatch: {
154 ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
155 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
156 break;
157 }
158 case kExprGetGlobal:
159 case kExprSetGlobal: {
160 GlobalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
161 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
162 break;
163 }
164 #define CASE_CONST(type, str, cast_type) \
165 case kExpr##type##Const: { \
166 Imm##type##Immediate<Decoder::kNoValidate> imm(&i, i.pc()); \
167 os << #str ".const " << static_cast<cast_type>(imm.value); \
168 break; \
169 }
170 CASE_CONST(I32, i32, int32_t)
171 CASE_CONST(I64, i64, int64_t)
172 CASE_CONST(F32, f32, float)
173 CASE_CONST(F64, f64, double)
174 #undef CASE_CONST
175
176 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
177 FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
178 FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
179 MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
180 kMaxUInt32);
181 os << WasmOpcodes::OpcodeName(opcode) << " offset=" << imm.offset
182 << " align=" << (1ULL << imm.alignment);
183 break;
184 }
185
186 FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
187 case kExprUnreachable:
188 case kExprNop:
189 case kExprReturn:
190 case kExprMemorySize:
191 case kExprGrowMemory:
192 case kExprDrop:
193 case kExprSelect:
194 os << WasmOpcodes::OpcodeName(opcode);
195 break;
196 case kAtomicPrefix: {
197 WasmOpcode atomic_opcode = i.prefixed_opcode();
198 switch (atomic_opcode) {
199 FOREACH_ATOMIC_OPCODE(CASE_OPCODE) {
200 MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
201 kMaxUInt32);
202 os << WasmOpcodes::OpcodeName(atomic_opcode)
203 << " offset=" << imm.offset
204 << " align=" << (1ULL << imm.alignment);
205 break;
206 }
207 default:
208 UNREACHABLE();
209 break;
210 }
211 break;
212 }
213
214 // This group is just printed by their internal opcode name, as they
215 // should never be shown to end-users.
216 FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
217 // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
218 // they are publicly available.
219 FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
220 FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
221 FOREACH_SIMD_MASK_OPERAND_OPCODE(CASE_OPCODE)
222 FOREACH_SIMD_MEM_OPCODE(CASE_OPCODE)
223 os << WasmOpcodes::OpcodeName(opcode);
224 break;
225 #undef CASE_OPCODE
226
227 default:
228 UNREACHABLE();
229 break;
230 }
231 os << '\n';
232 ++line_nr;
233 }
234 DCHECK_EQ(0, control_depth);
235 DCHECK(i.ok());
236 }
237
238 } // namespace wasm
239 } // namespace internal
240 } // namespace v8
241