microsoft / deoptexplorer-vscode

A VS Code extension to visualize deoptimizations in your JavaScript and TypeScript code running in V8 (i.e., NodeJS, Edge, Chrome, etc.).
MIT License
754 stars 9 forks source link

Inline source maps is broken because URL encoding #27

Closed eddyw closed 1 year ago

eddyw commented 1 year ago

Description

It seems inlined source maps (e.g: with tsc --inlineSourceMaps) aren't loaded because the function that looks if it matches the RegEx does sourceMappingURL.toString() which returns it URL encoded so it never matches the RegEx.

This can be fixed by using sourceMappingURL.path instead and changing slightly the RegEx to exclude the schema:

- const sourceMapDataUrlRegExp = /^data:application\/json[^,]+base64,(?<data>.*)/;
+ const sourceMapDataUrlRegExp = /^application\/json[^,]+base64,(?<data>.*)/;

export function getInlineSourceMapData(sourceMappingURL: Uri): string | undefined {
-    const match = sourceMappingURL.scheme === "data" ? sourceMapDataUrlRegExp.exec(sourceMappingURL.toString()) : undefined;
+    const match = sourceMappingURL.scheme === "data" ? sourceMapDataUrlRegExp.exec(sourceMappingURL.path) : undefined;
    if (match?.groups) {
        return Buffer.from(match.groups.data, "base64").toString();
    }
}

This works. Well kinda. On a very small test it works (a ts file imports another and does some shitty stuff so it deopts). Then loading the extension, it shows files as .ts and maps to original source.

However, I tried on a larger project (+100k LoC) and (after long 5min) it does seem to work for many cases but it doesn't expand ICs Tree when clicking on some items. I get "Condition not met" and for the life of me I cannot figure it out.

I don't have any experience with vscode extensions and I installed this a few hours ago. If some kind soul can provide any pointers or an idea of what I can be missing, I'd be happy to open a PR 😅

Other things I tried that I thought made sense

In _getExistingSourceMapWorker, add a return:

            if (data !== undefined) {
                try {
                    sourceMap = new SourceMap(resolved, data, resolved);
                }
                catch (e) {
                    this._sourceMaps.set(resolved, "no-sourcemap");
                    throw e;
                }
                this._sourceMaps.set(resolved, sourceMap);

+                return sourceMap;
            }

Then in getSourceMapAsync early return because it seems it'll try to load the source map again?

        let sourceMap = this._getExistingSourceMapWorker(resolved);
+        if (sourceMap instanceof SourceMap) {
+            return sourceMap;
+        }

        if (sourceMap === "no-sourcemap") {
            return "no-sourcemap";
        }

        if (sourceMap instanceof Uri) {
            const data = await tryReadFileAsync(sourceMap);