1// Copyright 2019 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-module-sourcemap.h" 6 7#include <algorithm> 8 9#include "include/v8-context.h" 10#include "include/v8-json.h" 11#include "include/v8-local-handle.h" 12#include "include/v8-object.h" 13#include "include/v8-primitive.h" 14#include "src/api/api.h" 15#include "src/base/vlq-base64.h" 16 17namespace v8 { 18 19class String; 20 21namespace internal { 22namespace wasm { 23 24WasmModuleSourceMap::WasmModuleSourceMap(v8::Isolate* v8_isolate, 25 v8::Local<v8::String> src_map_str) { 26 v8::HandleScope scope(v8_isolate); 27 v8::Local<v8::Context> context = v8::Context::New(v8_isolate); 28 29 v8::Local<v8::Value> src_map_value; 30 if (!v8::JSON::Parse(context, src_map_str).ToLocal(&src_map_value)) return; 31 v8::Local<v8::Object> src_map_obj = 32 v8::Local<v8::Object>::Cast(src_map_value); 33 34 v8::Local<v8::Value> version_value, sources_value, mappings_value; 35 bool has_valid_version = 36 src_map_obj 37 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "version")) 38 .ToLocal(&version_value) && 39 version_value->IsUint32(); 40 uint32_t version = 0; 41 if (!has_valid_version || !version_value->Uint32Value(context).To(&version) || 42 version != 3u) 43 return; 44 45 bool has_valid_sources = 46 src_map_obj 47 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "sources")) 48 .ToLocal(&sources_value) && 49 sources_value->IsArray(); 50 if (!has_valid_sources) return; 51 52 v8::Local<v8::Object> sources_arr = 53 v8::Local<v8::Object>::Cast(sources_value); 54 v8::Local<v8::Value> sources_len_value; 55 if (!sources_arr 56 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "length")) 57 .ToLocal(&sources_len_value)) 58 return; 59 uint32_t sources_len = 0; 60 if (!sources_len_value->Uint32Value(context).To(&sources_len)) return; 61 62 for (uint32_t i = 0; i < sources_len; ++i) { 63 v8::Local<v8::Value> file_name_value; 64 if (!sources_arr->Get(context, i).ToLocal(&file_name_value) || 65 !file_name_value->IsString()) 66 return; 67 v8::Local<v8::String> file_name = 68 v8::Local<v8::String>::Cast(file_name_value); 69 auto file_name_sz = file_name->Utf8Length(v8_isolate); 70 std::unique_ptr<char[]> file_name_buf(new char[file_name_sz + 1]); 71 file_name->WriteUtf8(v8_isolate, file_name_buf.get()); 72 file_name_buf.get()[file_name_sz] = '\0'; 73 filenames.emplace_back(file_name_buf.get()); 74 } 75 76 bool has_valid_mappings = 77 src_map_obj 78 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "mappings")) 79 .ToLocal(&mappings_value) && 80 mappings_value->IsString(); 81 if (!has_valid_mappings) return; 82 83 v8::Local<v8::String> mappings = v8::Local<v8::String>::Cast(mappings_value); 84 int mappings_sz = mappings->Utf8Length(v8_isolate); 85 std::unique_ptr<char[]> mappings_buf(new char[mappings_sz + 1]); 86 mappings->WriteUtf8(v8_isolate, mappings_buf.get()); 87 mappings_buf.get()[mappings_sz] = '\0'; 88 89 valid_ = DecodeMapping(mappings_buf.get()); 90} 91 92size_t WasmModuleSourceMap::GetSourceLine(size_t wasm_offset) const { 93 std::vector<std::size_t>::const_iterator up = 94 std::upper_bound(offsets.begin(), offsets.end(), wasm_offset); 95 CHECK_NE(offsets.begin(), up); 96 size_t source_idx = up - offsets.begin() - 1; 97 return source_row[source_idx]; 98} 99 100std::string WasmModuleSourceMap::GetFilename(size_t wasm_offset) const { 101 std::vector<size_t>::const_iterator up = 102 std::upper_bound(offsets.begin(), offsets.end(), wasm_offset); 103 CHECK_NE(offsets.begin(), up); 104 size_t offset_idx = up - offsets.begin() - 1; 105 size_t filename_idx = file_idxs[offset_idx]; 106 return filenames[filename_idx]; 107} 108 109bool WasmModuleSourceMap::HasSource(size_t start, size_t end) const { 110 return start <= *(offsets.end() - 1) && end > *offsets.begin(); 111} 112 113bool WasmModuleSourceMap::HasValidEntry(size_t start, size_t addr) const { 114 std::vector<size_t>::const_iterator up = 115 std::upper_bound(offsets.begin(), offsets.end(), addr); 116 if (up == offsets.begin()) return false; 117 size_t offset_idx = up - offsets.begin() - 1; 118 size_t entry_offset = offsets[offset_idx]; 119 if (entry_offset < start) return false; 120 return true; 121} 122 123bool WasmModuleSourceMap::DecodeMapping(const std::string& s) { 124 size_t pos = 0, gen_col = 0, file_idx = 0, ori_line = 0; 125 int32_t qnt = 0; 126 127 while (pos < s.size()) { 128 // Skip redundant commas. 129 if (s[pos] == ',') { 130 ++pos; 131 continue; 132 } 133 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == 134 std::numeric_limits<int32_t>::min()) 135 return false; 136 gen_col += qnt; 137 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == 138 std::numeric_limits<int32_t>::min()) 139 return false; 140 file_idx += qnt; 141 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == 142 std::numeric_limits<int32_t>::min()) 143 return false; 144 ori_line += qnt; 145 // Column number in source file is always 0 in source map generated by 146 // Emscripten. We just decode this value without further usage of it. 147 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == 148 std::numeric_limits<int32_t>::min()) 149 return false; 150 151 if (pos < s.size() && s[pos] != ',') return false; 152 pos++; 153 154 file_idxs.push_back(file_idx); 155 source_row.push_back(ori_line); 156 offsets.push_back(gen_col); 157 } 158 return true; 159} 160 161} // namespace wasm 162} // namespace internal 163} // namespace v8 164