summaryrefslogtreecommitdiff
path: root/src/wasm/source-map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/source-map.cpp')
-rw-r--r--src/wasm/source-map.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/wasm/source-map.cpp b/src/wasm/source-map.cpp
new file mode 100644
index 000000000..7ad26e898
--- /dev/null
+++ b/src/wasm/source-map.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2024 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "source-map.h"
+#include "support/colors.h"
+
+namespace wasm {
+
+std::vector<char> defaultEmptySourceMap;
+
+void MapParseException::dump(std::ostream& o) const {
+ Colors::magenta(o);
+ o << "[";
+ Colors::red(o);
+ o << "map parse exception: ";
+ Colors::green(o);
+ o << text;
+ Colors::magenta(o);
+ o << "]";
+ Colors::normal(o);
+}
+
+void SourceMapReader::readHeader(Module& wasm) {
+ assert(pos == 0);
+ if (buffer.empty()) {
+ return;
+ }
+
+ auto skipWhitespace = [&]() {
+ while (pos < buffer.size() && (buffer[pos] == ' ' || buffer[pos] == '\n')) {
+ ++pos;
+ }
+ };
+
+ auto findField = [&](const char* name) {
+ bool matching = false;
+ size_t len = strlen(name);
+ size_t index = 0;
+ while (1) {
+ char ch = get();
+ if (ch == '\"') {
+ if (matching) {
+ if (index == len) {
+ // We matched a terminating quote.
+ break;
+ }
+ matching = false;
+ } else {
+ // Beginning of a new potential match.
+ matching = true;
+ index = 0;
+ }
+ } else if (matching && name[index] == ch) {
+ ++index;
+ } else if (matching) {
+ matching = false;
+ }
+ }
+ skipWhitespace();
+ expect(':');
+ skipWhitespace();
+ return true;
+ };
+
+ auto readString = [&](std::string& str) {
+ std::vector<char> vec;
+ skipWhitespace();
+ expect('\"');
+ while (1) {
+ if (maybeGet('\"')) {
+ break;
+ }
+ vec.push_back(get());
+ }
+ skipWhitespace();
+ str = std::string(vec.begin(), vec.end());
+ };
+
+ if (!findField("sources")) {
+ throw MapParseException("cannot find the 'sources' field in map");
+ }
+
+ skipWhitespace();
+ expect('[');
+ if (!maybeGet(']')) {
+ do {
+ std::string file;
+ readString(file);
+ wasm.debugInfoFileNames.push_back(file);
+ } while (maybeGet(','));
+ expect(']');
+ }
+
+ if (findField("names")) {
+ skipWhitespace();
+ expect('[');
+ if (!maybeGet(']')) {
+ do {
+ std::string symbol;
+ readString(symbol);
+ wasm.debugInfoSymbolNames.push_back(symbol);
+ } while (maybeGet(','));
+ expect(']');
+ }
+ }
+
+ if (!findField("mappings")) {
+ throw MapParseException("cannot find the 'mappings' field in map");
+ }
+
+ expect('\"');
+ if (maybeGet('\"')) {
+ // There are no mappings.
+ location = 0;
+ return;
+ }
+
+ // Read the location of the first debug location.
+ location = readBase64VLQ();
+}
+
+std::optional<Function::DebugLocation>
+SourceMapReader::readDebugLocationAt(size_t currLocation) {
+ if (pos >= buffer.size()) {
+ return std::nullopt;
+ }
+
+ while (location && location <= currLocation) {
+ do {
+ char next = peek();
+ if (next == ',' || next == '\"') {
+ // This is a 1-length entry, so the next location has no debug info.
+ hasInfo = false;
+ break;
+ }
+
+ hasInfo = true;
+ file += readBase64VLQ();
+ line += readBase64VLQ();
+ col += readBase64VLQ();
+
+ next = peek();
+ if (next == ',' || next == '\"') {
+ hasSymbol = false;
+ break;
+ }
+
+ hasSymbol = true;
+ symbol += readBase64VLQ();
+
+ } while (false);
+
+ // Check whether there is another record to read the position for.
+ char next = get();
+ if (next == '\"') {
+ // End of records.
+ location = 0;
+ break;
+ }
+ if (next != ',') {
+ throw MapParseException("Expected delimiter");
+ }
+
+ // Set up for the next record.
+ location += readBase64VLQ();
+ }
+
+ if (!hasInfo) {
+ return std::nullopt;
+ }
+
+ auto sym = hasSymbol ? symbol : std::optional<uint32_t>{};
+ return Function::DebugLocation{file, line, col, sym};
+}
+
+int32_t SourceMapReader::readBase64VLQ() {
+ uint32_t value = 0;
+ uint32_t shift = 0;
+ while (1) {
+ auto ch = get();
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch < 'g')) {
+ // last number digit
+ uint32_t digit = ch < 'a' ? ch - 'A' : ch - 'a' + 26;
+ value |= digit << shift;
+ break;
+ }
+ if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && ch != '+' &&
+ ch != '/') {
+ throw MapParseException("invalid VLQ digit");
+ }
+ uint32_t digit =
+ ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31));
+ value |= digit << shift;
+ shift += 5;
+ }
+ return value & 1 ? -int32_t(value >> 1) : int32_t(value >> 1);
+}
+
+} // namespace wasm