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 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 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