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