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