• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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