Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a decoding algorithm for index maps #132

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
71 changes: 68 additions & 3 deletions source-map.bs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ spec:url; type:dfn; for:/; text:url
spec:infra; type:dfn;
text:list
for:list; text:for each
for:list; text:is not empty
</pre>
<pre class="anchors">
urlPrefix:https://tc39.es/ecma262/#; type:dfn; spec:ecmascript
Expand Down Expand Up @@ -410,7 +411,8 @@ following steps:
1. Let |jsonMap| be the result of [=parse a JSON string to an Infra value|parsing a JSON string to
an Infra value=] |str|.
1. If |jsonMap| is not a [=/map=], report an error and abort these steps.
1. [=Decode a source map=] given |jsonMap| and |baseURL|, and return its result if any.
1. If |jsonMap|[`"sections"`] [=map/exists=], [=decode an index source map=] given |jsonMap| and |baseURL|, and return its result if any.
1. Otherwise, [=decode a source map=] given |jsonMap| and |baseURL|, and return its result if any.

To <dfn export>decode a source map</dfn> given a [=string=]-keyed [=/map=] |jsonMap| and a [=/URL=]
|baseURL|, run the following steps:
Expand Down Expand Up @@ -834,16 +836,79 @@ the file format is JSON with a top-level object. It shares the [=json/version=]

Section objects have the following fields:

* <dfn dfn-for="index-map"><code>offset</code></dfn> is an object with two fields, `line` and `column`,
* <code>offset</code> is an object with two fields, `line` and `column`,
that represent the offset into generated code that the referenced source map
represents.

* <dfn dfn-for="index-map"><code>map</code></dfn> is an embedded complete source map object.
* <code>map</code> is an embedded complete source map object.
An embedded map does not inherit any values from the containing index map.

The sections must be sorted by starting position and the represented sections
must not overlap.

## Decoding an Index Map ## {#decoding-index-map}

To <dfn export>decode an index source map</dfn> given a [=string=]-keyed [=/map=] |jsonMap| and a [=/URL=]
|baseURL|, run the following steps:
1. <p>Assert: |jsonMap|[`"sections"`] [=map/exists=].</p>
1. If |jsonMap|[`"sections"`] is not a [=list=], report an error.
1. If |jsonMap|[`"version"`] does not [=map/exist=] or |jsonMap|[`"version"`] is not 3,
[=optionally report an error=].
1. Let |sourceMap| be a new [=decoded source map=].
1. Set |sourceMap|'s [=decoded source map/file=] to [=optionally get a string=] `"file"` from |jsonMap|.
1. Let |previousOffset| be null.
1. Let |previousLastMapping| be null.
1. [=For each=] |section| of |jsonMap|[`"sections"`]:
1. If |section| is not a [=/map=], [=optionally report an error=] and continue with the next iteration.
1. If |section|[`"offset"`] does not [=map/exist=], then report an error.
1. Let |offsetLine| be |section|[`"offset"`][`"line"`].
1. Let |offsetColumn| be |section|[`"offset"`][`"column"`].
1. If |offsetLine| is not a number, [=optionally report an error=].
1. If |offsetColumn| is not a number, [=optionally report an error=].
takikawa marked this conversation as resolved.
Show resolved Hide resolved
1. If |previousOffset| is not null,
1. If |previousLastMapping| is not null,
1. If |offsetLine| is less than |previousLastMapping|'s [=decoded mapping/generatedLine=], [=optionally report an error=].
Copy link
Collaborator Author

@takikawa takikawa Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these range checks be "less than or equal to" to be strict inequality rather than allowing equality?

1. If |offsetLine| is equal to |previousLastMapping|'s [=decoded mapping/generatedLine=] and |offsetColumn| is less than |previousLastMapping|'s [=decoded mapping/generatedColumn=], [=optionally report an error=].
1. Otherwise,
1. If |offsetLine| is less than |previousOffset|[`"line"`], [=optionally report an error=].
1. If |offsetLine| is equal to |previousOffset|[`"line"`] and |offsetColumn| is less than |previousOffset|[`"column"`], [=optionally report an error=].

Note: This part of the decoding algorithm checks that index source map sections do not overlap. While it is
expected that generators should not produce index source maps with overlapping sections, source map
consumers may choose instead to only check the simpler condition that the sections are ordered and therefore
that offsets appear in increasing order.
takikawa marked this conversation as resolved.
Show resolved Hide resolved

1. If |section|[`"map"`] does not [=map/exist=], then report an error.
1. Let |decodedSection| be the result of [=decoding a source map=] given |section|[`"map"`] and |baseURL|.
1. If the previous step reported an error, [=optionally report an error=] and continue with the next iteration.
1. Set |sourceMap|'s [=decoded source map/sources=] to the [=set/union=] of |sourceMap|'s [=decoded source map/sources=] and |decodedSection|'s [=decoded source map/sources=].
1. Let |offsetMappings| be a new [=list=].
1. [=For each=] |mapping| of |decodedSection|'s [=decoded source map/mappings=]:
1. Let |offsetMapping| be a new [=decoded mapping=].
1. Set |offsetMapping|'s [=decoded mapping/generatedLine=] to |mapping|'s [=decoded mapping/generatedLine=] + |offsetLine|.
1. If |offsetMapping|'s [=decoded mapping/generatedLine=] is equal to 1, set |offsetMapping|'s [=decoded mapping/generatedColumn=] to |mapping|'s [=decoded mapping/generatedColumn=] + |offsetColumn|.
takikawa marked this conversation as resolved.
Show resolved Hide resolved
1. Otherwise, set |offsetMapping|'s [=decoded mapping/generatedColumn=] to |mapping|'s [=decoded mapping/generatedColumn=].
1. Set |offsetMapping|'s [=decoded mapping/originalSource=] to |mapping|'s [=decoded mapping/originalSource=].
1. Set |offsetMapping|'s [=decoded mapping/originalLine=] to |mapping|'s [=decoded mapping/originalLine=].
1. Set |offsetMapping|'s [=decoded mapping/originalColumn=] to |mapping|'s [=decoded mapping/originalColumn=].
1. Set |offsetMapping|'s [=decoded mapping/name=] to |mapping|'s [=decoded mapping/name=].
1. [=list/Append=] |offsetMapping| to |offsetMappings|.
1. Set |sourceMap|'s [=decoded source map/mappings=] to the [=set/union=] of |sourceMap|'s [=decoded source map/mappings=] and |offsetMappings|.
takikawa marked this conversation as resolved.
Show resolved Hide resolved
1. Set |previousOffset| to |section|[`"offset"`].
1. Let |sortedMappings| be the result of [=list/sorting=] |offsetMappings| in ascending order, with an item |a| being less than |b| if |a| is [=generated position less than=] |b|.
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
1. Set |previousLastMapping| to the last item of |sortedMappings| if |sortedMappings| [=is not empty=] and to null otherwise.

Note: See the above note about section overlap and order. These last two steps of storing the last mapping of each
section are only necessary to enforce the conditions concerning section overlap.

1. Return |sourceMap|.

A [=decoded mapping=] |a| is <dfn>generated position less than</dfn> a [=decoded mapping=] b if the following steps return true:

1. If |a|'s [=decoded mapping/generatedLine=] is less than |b|'s [=decoded mapping/generatedLine=], return true.
1. If |a|'s [=decoded mapping/generatedLine=] is equal to |b|'s [=decoded mapping/generatedLine=] and |a|'s [=decoded mapping/generatedColumn=] is less than |b|'s [=decoded mapping/generatedColumn=], return true.
1. Otherwise, return false.

Retrieving Source Maps {#linking-and-fetching}
========================================================

Expand Down
Loading