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 using namespace v8;
18 using namespace v8::internal;
19 using namespace v8::internal::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 wasm::PrintWasmText(const WasmModule *module,
36 const ModuleWireBytes &wire_bytes, uint32_t func_index,
37 std::ostream &os,
38 debug::WasmDisassembly::OffsetTable *offset_table) {
39 DCHECK_NOT_NULL(module);
40 DCHECK_GT(module->functions.size(), func_index);
41 const WasmFunction *fun = &module->functions[func_index];
42
43 AccountingAllocator allocator;
44 Zone zone(&allocator, ZONE_NAME);
45 int line_nr = 0;
46 int control_depth = 1;
47
48 // Print the function signature.
49 os << "func";
50 WasmName fun_name = wire_bytes.GetNameOrNull(fun);
51 if (IsValidFunctionName(fun_name)) {
52 os << " $";
53 os.write(fun_name.start(), fun_name.length());
54 }
55 size_t param_count = fun->sig->parameter_count();
56 if (param_count) {
57 os << " (param";
58 for (size_t i = 0; i < param_count; ++i)
59 os << ' ' << WasmOpcodes::TypeName(fun->sig->GetParam(i));
60 os << ')';
61 }
62 size_t return_count = fun->sig->return_count();
63 if (return_count) {
64 os << " (result";
65 for (size_t i = 0; i < return_count; ++i)
66 os << ' ' << WasmOpcodes::TypeName(fun->sig->GetReturn(i));
67 os << ')';
68 }
69 os << "\n";
70 ++line_nr;
71
72 // Print the local declarations.
73 BodyLocalDecls decls(&zone);
74 Vector<const byte> func_bytes = wire_bytes.GetFunctionBytes(fun);
75 BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
76 DCHECK_LT(func_bytes.begin(), i.pc());
77 if (!decls.type_list.empty()) {
78 os << "(local";
79 for (const ValueType &v : decls.type_list) {
80 os << ' ' << WasmOpcodes::TypeName(v);
81 }
82 os << ")\n";
83 ++line_nr;
84 }
85
86 for (; i.has_next(); i.next()) {
87 WasmOpcode opcode = i.current();
88 if (opcode == kExprElse || opcode == kExprEnd) --control_depth;
89
90 DCHECK_LE(0, control_depth);
91 const int kMaxIndentation = 64;
92 int indentation = std::min(kMaxIndentation, 2 * control_depth);
93 if (offset_table) {
94 offset_table->emplace_back(i.pc_offset(), line_nr, indentation);
95 }
96
97 // 64 whitespaces
98 const char padding[kMaxIndentation + 1] =
99 " ";
100 os.write(padding, indentation);
101
102 switch (opcode) {
103 case kExprLoop:
104 case kExprIf:
105 case kExprBlock:
106 case kExprTry: {
107 BlockTypeOperand operand(&i, i.pc());
108 os << WasmOpcodes::OpcodeName(opcode);
109 for (unsigned i = 0; i < operand.arity; i++) {
110 os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
111 }
112 control_depth++;
113 break;
114 }
115 case kExprBr:
116 case kExprBrIf: {
117 BreakDepthOperand operand(&i, i.pc());
118 os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.depth;
119 break;
120 }
121 case kExprElse:
122 os << "else";
123 control_depth++;
124 break;
125 case kExprEnd:
126 os << "end";
127 break;
128 case kExprBrTable: {
129 BranchTableOperand operand(&i, i.pc());
130 BranchTableIterator iterator(&i, operand);
131 os << "br_table";
132 while (iterator.has_next()) os << ' ' << iterator.next();
133 break;
134 }
135 case kExprCallIndirect: {
136 CallIndirectOperand operand(&i, i.pc());
137 DCHECK_EQ(0, operand.table_index);
138 os << "call_indirect " << operand.index;
139 break;
140 }
141 case kExprCallFunction: {
142 CallFunctionOperand operand(&i, i.pc());
143 os << "call " << operand.index;
144 break;
145 }
146 case kExprGetLocal:
147 case kExprSetLocal:
148 case kExprTeeLocal:
149 case kExprCatch: {
150 LocalIndexOperand operand(&i, i.pc());
151 os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
152 break;
153 }
154 case kExprGetGlobal:
155 case kExprSetGlobal: {
156 GlobalIndexOperand operand(&i, i.pc());
157 os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
158 break;
159 }
160 #define CASE_CONST(type, str, cast_type) \
161 case kExpr##type##Const: { \
162 Imm##type##Operand operand(&i, i.pc()); \
163 os << #str ".const " << static_cast<cast_type>(operand.value); \
164 break; \
165 }
166 CASE_CONST(I32, i32, int32_t)
167 CASE_CONST(I64, i64, int64_t)
168 CASE_CONST(F32, f32, float)
169 CASE_CONST(F64, f64, double)
170
171 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
172 FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
173 FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
174 MemoryAccessOperand operand(&i, i.pc(), kMaxUInt32);
175 os << WasmOpcodes::OpcodeName(opcode) << " offset=" << operand.offset
176 << " align=" << (1ULL << operand.alignment);
177 break;
178 }
179
180 FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
181 case kExprUnreachable:
182 case kExprNop:
183 case kExprReturn:
184 case kExprMemorySize:
185 case kExprGrowMemory:
186 case kExprDrop:
187 case kExprSelect:
188 case kExprThrow:
189 os << WasmOpcodes::OpcodeName(opcode);
190 break;
191
192 // This group is just printed by their internal opcode name, as they
193 // should never be shown to end-users.
194 FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
195 // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
196 // they are publicly available.
197 FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
198 FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
199 FOREACH_ATOMIC_OPCODE(CASE_OPCODE)
200 os << WasmOpcodes::OpcodeName(opcode);
201 break;
202
203 default:
204 UNREACHABLE();
205 break;
206 }
207 os << '\n';
208 ++line_nr;
209 }
210 DCHECK_EQ(0, control_depth);
211 DCHECK(i.ok());
212 }
213