This is the 16th day of my participation in the genwen Challenge

preface

As we know, the code has to go through compression, beautification, confusion and other steps before it goes online, and the code mom doesn’t know it after it goes online. This is understandable, in order to prevent others to read your source code to find your vulnerabilities to attack your web page.

The problem is, if your code gets bugged online, you don’t even know what went wrong. At this point, we need a code restoration tool to help us restore the code to find the error.

This artifact is our main character today, the Source Map. Today we’re going to talk about how it restores our code.

Where is the source map

Usually, when generating code with the WebPack build, you can configure DevTool to generate the Source map so that the.js.map file is found in the last dist generated.

Take this list.js for example


const a = 111;

console.log(a);
Copy the code

Generated dist file

/ * * * /1:
/ * * * /(function(module.exports,__webpack_require__)){
    module.exports=__webpack_require__("779063cf0aeaac0d5b8");

/ * * * * /}),
/ * * * /"779063cf0aeaac0d5b8":
/ * * * * /(function(module.exports){
    var a=111;
    console.log(a);
/ * * * * /})
/ * * * * /}),
/ / #
 sourceMappingURL=list.cle192cf.js.map
Copy the code

There is a line of code that references the js.map file. When we open the file, we can see that the generated map file looks like this


{"version":3."file":"vote/list/list.c1e192cf.js"."sources": ["webpack:///webpack/bootstrap"."webpack:///./src/pages/vote/list/list.js"]."sourcesContent": [" \t// The module cache\n \tvar installedModules = {}; \n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports; \n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t}; \n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__); \n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true; \n\n \t\t// Return the exports of the module\n \t\treturn module.exports; \n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules; \n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules; \n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(! __webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter }); \n \t\t}\n \t}; \n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol ! == 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); \n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true }); \n \t}; \n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value); \n \t\tif(mode & 8) return value; \n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; \n \t\tvar ns = Object.create(null); \n \t\t__webpack_require__.r(ns); \n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value }); \n \t\tif(mode & 2 && typeof value ! = 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); \n \t\treturn ns; \n \t}; \n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ? \n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; }; \n \t\t__webpack_require__.d(getter, 'a', getter); \n \t\treturn getter; \n \t}; \n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; \n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/mpres/zh_CN/htmledition/pages/\"; \n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1); \n"."var a = 111; \nconsole.log(a);"]."mappings":"; AAAA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA; AACA;;;;;;;;;;;;;;;; AClFA; AACA;;;; A"."sourceRoot":""}
Copy the code

This is a list of the following fields:

{
  "version": 3.// The source map version
  "file": "".// The converted file name
  "source": [].// The source file code
  "names": [].// All variables and attribute names before conversion
  "mapping": "" // A string to record location information
}
Copy the code

What is really used for positioning is the information in the mapping field. How does the Source Map restore the code

Since the above example is too complicated, let’s use a simple example to illustrate.

The source code

/ * comment * /
var name = "abc";
Copy the code

Compressed code

var name="abc";
//# sourceMappingURL=a.js.map
Copy the code

The corresponding source – the map

{
  "version":3."sources": ["a.js"]."names": ["name"]."mappings":"; AACA,IAAIA,KAAO"."file":"a.js"."sourcesContent": ["/* comment */\nvar name = \" ABC \";"]}Copy the code

Now let’s look at this; AACA,IAAIA, what KAAO is saying.

Where semicolon; Represents a blank line. Comma, represents a position.

AACA indicates the location of VAR, which is encoded by VLQ and base64. VLQ is not related to base64, we know it is a way of encoding.

As an example, let’s take a look at the process and result of 188 encoding by VLQ and Base64.

First of all, the binary representation of 188 is 10111100, which can not meet the requirements of VLQ 6 bytes, so here we split it into two parts, and then switch.

And then I have 1 in front of 1100, because I have another block behind it. Add a zero at the end, because 188 is a positive number. The first section eventually turns out to be 111000.

If we look at 1011, we just have to put two zeros in front of it. The first 0 means there are no more blocks behind it, and the second 0 means there are less than five left zeros. The second paragraph eventually turns out to be 001011.

111000 corresponds to VLQ which is 56 and then corresponds to base64 which is 4.

And 001011 corresponding to VLQ is 11, in the corresponding base64 is L.

AACA going back is going the other way.

It’s a little tricky, and I don’t think you want to do it, so let’s just use a library VLQ to help convert it.

The print result is as follows:

You can see that AACA resolves to 0010. Among them,

First, which indicates the number of columns in which the position is located.

The second, indicating which file the location belongs to in the Sources property.

The third bit, indicating which line of code this position belongs to before the conversion.

The fourth bit, which indicates the number of columns in the code before the transformation.

Two things to note here:

1. Positions are based on 0.

2. Calculate the position relative to the previous position.

So this 0010, here represents the 0 column of the compressed var code, corresponding to the 0 row 0 column of the 0 source file.

Similarly, the second IAAIA turns to 4004, which is the position relative to the previous character, so we need to add it up, which is 4014. Name is in column 4 of the compressed code, corresponding to line 1 and column 4 of the 0th source file.

The third KAAO is 5007, which is 90111, indicating that ABC is the 9th column of the transformed code, corresponding to line 1 and column 11 of the 0th source file.

Here’s the scipt. Js code before the conversion

This is the compiled code for scipt-transpiled. Js

This is the Source map file

This mapping corresponds to the transformed code looking like this:

You can analyze this example for yourself.

That’s all for today’s Source Map.

Reference Documents:

medium.com/@trungutt/y…