From 532ed0e43c710491cfe0917a5fd409d1216e3c87 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Wed, 22 Jan 2025 07:28:13 -0800 Subject: [PATCH] Add a decoding algorithm for index maps (#132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolò Ribaudo --- source-map.bs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/source-map.bs b/source-map.bs index 86a606f..9598e72 100644 --- a/source-map.bs +++ b/source-map.bs @@ -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
 urlPrefix:https://tc39.es/ecma262/#; type:dfn; spec:ECMA-262
@@ -91,6 +92,7 @@ urlPrefix:https://tc39.es/ecma262/#; type:dfn; spec:ECMA-262
     url:prod-VariableStatement; text:VariableStatement
     url:sec-parameter-lists; text:Parameter List
     url:sec-syntactic-grammar; text:Syntactic Grammar
+    url:integral-number; text:integral Number
 
 urlPrefix:https://webassembly.github.io/spec/core/; type:dfn; spec:wasm
     url:binary/modules.html#binary-customsec; text:custom section
@@ -363,7 +365,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 decode a source map given a [=string=]-keyed [=/map=] |jsonMap| and a [=/URL=]
 |baseURL|, run the following steps:
@@ -785,16 +788,75 @@ the file format is JSON with a top-level object.  It shares the [=json/version=]
 
 Section objects have the following fields:
 
-* offset is an object with two fields, `line` and `column`,
+* offset is an object with two fields, `line` and `column`,
     that represent the offset into generated code that the referenced source map
     represents.
 
-* map is an embedded complete source map object.
+* map is an embedded complete source map object.
     An embedded map does not inherit any values from the containing index map.
 
 The sections shall be sorted by starting position and the represented sections
 shall not overlap.
 
+## Decoding an index map ## {#decoding-index-map}
+
+To decode an index source map given a [=string=]-keyed [=/map=] |jsonMap| and a [=/URL=]
+|baseURL|, run the following steps:
+1. 

Assert: |jsonMap|[`"sections"`] [=map/exists=].

+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 an [=integral Number=], [=optionally report an error=] and set |offsetLine| to 0. + 1. If |offsetColumn| is not an [=integral Number=], [=optionally report an error=] and set |offsetColumn| to 0. + 1. If |previousOffset| is not null, + 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=]. + 1. If |previousLastMapping| is not null, + 1. If |offsetLine| is less than |previousLastMapping|'s [=decoded mapping/generatedLine=], [=optionally report an error=]. + 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=]. + + Note: This part of the decoding algorithm checks that index source map sections are ordered and do not overlap. + While it is expected that generators should not produce index source maps with overlapping sections, source map + consumers may, for example, only check the simpler condition that the section offsets are ordered. + 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 |mapping|'s [=decoded mapping/generatedLine=] is equal to 0, set |offsetMapping|'s [=decoded mapping/generatedColumn=] to |mapping|'s [=decoded mapping/generatedColumn=] + |offsetColumn|. + 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|. + + Note: Implementations may choose to represent index source map sections without appending the mappings together, + for example, by storing each section separately and conducting a binary search. + 1. [=list/Extend=] |sourceMap|'s [=decoded source map/mappings=] with |offsetMappings|. + 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|. + 1. Set |previousLastMapping| to the last item of |sortedMappings| if |sortedMappings| [=is not empty=]. +1. Return |sourceMap|. + +A [=decoded mapping=] |a| is generated position less than 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} ========================================================