| // Copyright 2019 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/wasm/wasm-module-sourcemap.h" |
| |
| #include <algorithm> |
| |
| #include "include/v8.h" |
| #include "src/api/api.h" |
| #include "src/base/vlq-base64.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| |
| WasmModuleSourceMap::WasmModuleSourceMap(v8::Isolate* v8_isolate, |
| v8::Local<v8::String> src_map_str) { |
| v8::HandleScope scope(v8_isolate); |
| v8::Local<v8::Context> context = v8::Context::New(v8_isolate); |
| |
| v8::Local<v8::Value> src_map_value; |
| if (!v8::JSON::Parse(context, src_map_str).ToLocal(&src_map_value)) return; |
| v8::Local<v8::Object> src_map_obj = |
| v8::Local<v8::Object>::Cast(src_map_value); |
| |
| v8::Local<v8::Value> version_value, sources_value, mappings_value; |
| bool has_valid_version = |
| src_map_obj |
| ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "version")) |
| .ToLocal(&version_value) && |
| version_value->IsUint32(); |
| uint32_t version = 0; |
| if (!has_valid_version || !version_value->Uint32Value(context).To(&version) || |
| version != 3u) |
| return; |
| |
| bool has_valid_sources = |
| src_map_obj |
| ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "sources")) |
| .ToLocal(&sources_value) && |
| sources_value->IsArray(); |
| if (!has_valid_sources) return; |
| |
| v8::Local<v8::Object> sources_arr = |
| v8::Local<v8::Object>::Cast(sources_value); |
| v8::Local<v8::Value> sources_len_value; |
| if (!sources_arr |
| ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "length")) |
| .ToLocal(&sources_len_value)) |
| return; |
| uint32_t sources_len = 0; |
| if (!sources_len_value->Uint32Value(context).To(&sources_len)) return; |
| |
| for (uint32_t i = 0; i < sources_len; ++i) { |
| v8::Local<v8::Value> file_name_value; |
| if (!sources_arr->Get(context, i).ToLocal(&file_name_value) || |
| !file_name_value->IsString()) |
| return; |
| v8::Local<v8::String> file_name = |
| v8::Local<v8::String>::Cast(file_name_value); |
| auto file_name_sz = file_name->Utf8Length(v8_isolate); |
| std::unique_ptr<char[]> file_name_buf(new char[file_name_sz + 1]); |
| file_name->WriteUtf8(v8_isolate, file_name_buf.get()); |
| file_name_buf.get()[file_name_sz] = '\0'; |
| filenames.emplace_back(file_name_buf.get()); |
| } |
| |
| bool has_valid_mappings = |
| src_map_obj |
| ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "mappings")) |
| .ToLocal(&mappings_value) && |
| mappings_value->IsString(); |
| if (!has_valid_mappings) return; |
| |
| v8::Local<v8::String> mappings = v8::Local<v8::String>::Cast(mappings_value); |
| int mappings_sz = mappings->Utf8Length(v8_isolate); |
| std::unique_ptr<char[]> mappings_buf(new char[mappings_sz + 1]); |
| mappings->WriteUtf8(v8_isolate, mappings_buf.get()); |
| mappings_buf.get()[mappings_sz] = '\0'; |
| |
| valid_ = DecodeMapping(mappings_buf.get()); |
| } |
| |
| size_t WasmModuleSourceMap::GetSourceLine(size_t wasm_offset) const { |
| std::vector<std::size_t>::const_iterator up = |
| std::upper_bound(offsets.begin(), offsets.end(), wasm_offset); |
| CHECK_NE(offsets.begin(), up); |
| size_t source_idx = up - offsets.begin() - 1; |
| return source_row[source_idx]; |
| } |
| |
| std::string WasmModuleSourceMap::GetFilename(size_t wasm_offset) const { |
| std::vector<size_t>::const_iterator up = |
| std::upper_bound(offsets.begin(), offsets.end(), wasm_offset); |
| CHECK_NE(offsets.begin(), up); |
| size_t offset_idx = up - offsets.begin() - 1; |
| size_t filename_idx = file_idxs[offset_idx]; |
| return filenames[filename_idx]; |
| } |
| |
| bool WasmModuleSourceMap::HasSource(size_t start, size_t end) const { |
| return start <= *(offsets.end() - 1) && end > *offsets.begin(); |
| } |
| |
| bool WasmModuleSourceMap::HasValidEntry(size_t start, size_t addr) const { |
| std::vector<size_t>::const_iterator up = |
| std::upper_bound(offsets.begin(), offsets.end(), addr); |
| if (up == offsets.begin()) return false; |
| size_t offset_idx = up - offsets.begin() - 1; |
| size_t entry_offset = offsets[offset_idx]; |
| if (entry_offset < start) return false; |
| return true; |
| } |
| |
| bool WasmModuleSourceMap::DecodeMapping(const std::string& s) { |
| size_t pos = 0, gen_col = 0, file_idx = 0, ori_line = 0; |
| int32_t qnt = 0; |
| |
| while (pos < s.size()) { |
| // Skip redundant commas. |
| if (s[pos] == ',') { |
| ++pos; |
| continue; |
| } |
| if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == |
| std::numeric_limits<int32_t>::min()) |
| return false; |
| gen_col += qnt; |
| if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == |
| std::numeric_limits<int32_t>::min()) |
| return false; |
| file_idx += qnt; |
| if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == |
| std::numeric_limits<int32_t>::min()) |
| return false; |
| ori_line += qnt; |
| // Column number in source file is always 0 in source map generated by |
| // Emscripten. We just decode this value without further usage of it. |
| if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == |
| std::numeric_limits<int32_t>::min()) |
| return false; |
| |
| if (pos < s.size() && s[pos] != ',') return false; |
| pos++; |
| |
| file_idxs.push_back(file_idx); |
| source_row.push_back(ori_line); |
| offsets.push_back(gen_col); |
| } |
| return true; |
| } |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |