V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
wasm-text.cc
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 {
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 
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 kExprMemoryGrow:
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
Definition: libplatform.h:13