草案 ECMA-426 / 2025年11月13日
源映射格式规范
简介
本 Ecma 标准定义了源映射格式,用于将转译后的源代码映射回原始源代码。
源映射格式有以下目标:
支持源级调试,允许双向映射
支持服务端堆栈跟踪去混淆
原始源 映射格式(v1)由 Joseph Schorr 创建,用于 Closure Inspector 以实现优化的 JavaScript
代码的源级调试(但格式本身与语言无关)。然而,随着使用源映射的项目规模扩大,格式的冗长性逐渐成为问题。v2 格式(Source Map Revision 2
Proposal )通过牺牲一定的简单性和灵活性以减小源映射总体大小而设计。即便经过 v2 版本的改动,源映射文件大小依然限制了它的实用性。v3 格式基于
Pavel Podivilov(Google)提出的建议。
源映射格式不再有版本号,现在固定为"3"。
2023-2024年间,源映射格式被开发为更精确的 Ecma 标准,许多人做出了重要贡献。源映射格式的后续迭代预计将来自 TC39-TG4。
Asumu Takikawa, Nicolò Ribaudo, Jon Kuperman
ECMA-426,第一版,项目编辑
1 范围
本标准定义了源映射格式,被多种开发者工具用于改善编译到 JavaScript、WebAssembly 和 CSS 的代码的调试体验。
2 一致性
符合规范的源映射文档是符合本规范详细结构的 JSON 文档。
符合规范的源映射生成器应生成符合源映射文档规范的文档,并能够被本规范中的算法解码而不报告任何错误(即使某些错误被规范指定为可选)。
符合规范的源映射使用者应实现规范中的算法,用于检索(如适用)和解码源映射文档。符合规范的使用者可以在规范允许算法可选择性报告错误 时,忽略错误或报告错误而不终止。
3 参考资料
下列文献中有些内容或全部内容构成本文件的要求。对于有日期的引用,仅适用于所引用的版本。对于无日期的引用,适用被引用文献的最新版本(包括所有修订)。
3.1 规范性引用
ECMA-262,ECMAScript® 语言规范 。
https://tc39.es/ecma262/
ECMA-404,JSON 数据交换格式 。
https://www.ecma-international.org/publications-and-standards/standards/ecma-404/
3.2 补充性引用
IETF RFC 4648,Base16、Base32 和 Base64 数据编码 。
https://datatracker.ietf.org/doc/html/rfc4648
WebAssembly 核心规范 。
https://www.w3.org/TR/wasm-core-2/
WHATWG 编码 。
https://encoding.spec.whatwg.org/
WHATWG 抓取(Fetch) 。
https://fetch.spec.whatwg.org/
WHATWG 基础设施(Infra) 。
https://infra.spec.whatwg.org/
WHATWG URL 。
https://url.spec.whatwg.org/
4 表示法约定
本规范遵循 ECMA-262(表示法约定) 中定义的表示法约定,并在本节中进行了扩展。
4.1 算法约定
4.1.1 隐式补全
本规范中声明的所有抽象操作 都默认返回一个正常补全,类型为 算法声明的返回类型,或一个抛出补全 。例如,一个声明如下的抽象操作:
4.1.1.1 GetTheAnswer ( input )
抽象操作 GetTheAnswer 接受参数 input (一个 整数 ),返回一个整数 。
等价于:
4.1.1.2 GetTheAnswer2 ( input )
抽象操作 GetTheAnswer2 接受参数 input (一个 整数 ),返回正常补全,其值为 整数 ,或一个抛出补全 。
所有返回补全 记录的抽象操作 调用,默认使用ReturnIfAbrupt 宏包裹,除非已经被Completion 显式包裹。例如:
令 result 为 GetTheAnswer (value )。
令 second 为 Completion (GetTheAnswer (value ))。
等价于:
令 result 为 ReturnIfAbrupt (GetTheAnswer (value ))。
令 second 为 Completion (GetTheAnswer (value ))。
4.1.2 可选错误
当某算法可选择性报告错误 时,实现可以选择以下行为之一:
针对不同的可选错误,实现可选择不同的行为。
4.2 语法表示
本规范采用 ECMA-262(语法表示) 中定义的语法表示惯例,并有以下注意事项:
本规范定义的语法终结符为单个码位。这类似于 ECMA-262 的词法语法 ,而不是 ECMA-262 的句法语法 。
本规范不使用语法参数 和前瞻限制 ,以简化语法定义。
5 术语与定义
在本文件范围内,下列术语与定义适用。
生成代码
通过编译器或转译器生成的代码。
原始源
未经过编译器或转译器处理的源代码。
源映射URL
URL ,用于从生成代码 引用源映射的位置。
列
在生成代码 的一行内,以零为起始的偏移量。对于
JavaScript 和 CSS 源映射按 UTF-16 码元计算,对于 WebAssembly 源映射的二进制内容(被表示为单行)则按字节索引计算。
注
这意味着 "A"(拉丁大写字母A)计为1个码元,而
"🔥"(火)计为2个码元。其他内容类型的源映射可能存在差异。
6 base64 VLQ
base64 VLQ 是一种base64 编码的变长数量 ,其中最高位(第6位)为延续位,“数字”以低位在前的顺序编码进字符串,且首个数字的最低位作为符号位。
注1
base64 VLQ
编码所能表示的值仅限于32位,除非出现更大值的应用场景。这意味着超过32位的值是无效的,实现可以拒绝它们。符号位计入限制,但延续位不计入。
注2
字符串 "iB" 表示一个有两个数字的 base64 VLQ。第一个数字 "i" 编码为比特模式
0b100010,其中延续位为 1(VLQ 继续)、符号位为 0(非负)、值位为
0b0001。第二个数字 B 编码为 0b000001,其中延续位为 0,无符号位,值位为
0b00001。该 VLQ 字符串解码的值为数字 17。
注3
字符串 "V" 表示一个有一个数字的 base64 VLQ。此数字 "V" 编码为比特模式 0b010101,其中延续位为
0(不延续)、符号位为 1(负)、值位为 0b1010。该 VLQ 字符串解码的值为数字 -10。
base64 VLQ 遵循下述词法语法:
Vlq ::
VlqDigitList
VlqDigitList ::
TerminalDigit
ContinuationDigit
VlqDigitList
TerminalDigit ::
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
a
b
c
d
e
f
ContinuationDigit ::
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
0
1
2
3
4
5
6
7
8
9
+
/
6.1 VLQSignedValue
语法指引操作
VLQSignedValue 无参数,返回一个整数 。其在以下产生式上分段定义:
Vlq :: VlqDigitList
令 unsigned 为 VLQUnsignedValue of VlqDigitList 。
如果 unsigned 取模 2 = 1,令 sign
为 -1。
否则,令 sign 为 1。
令 value 为 向下取整 (unsigned /
2)。
如果 value 是 0 且 sign 是 -1,返回 -231 。
如果 value ≥ 231 ,抛出错误。
返回 sign × value 。
注
步骤
6 中的检查是必要的,因为
unsigned 是
VLQUnsignedValue of
VlqDigitList ,而不是
Vlq 。
6.2 VLQUnsignedValue
语法指引操作
VLQUnsignedValue 无参数,返回一个非负整数 。其在以下产生式上分段定义:
Vlq :: VlqDigitList
令 value 为 VLQUnsignedValue of VlqDigitList 。
如果 value ≥ 232 ,抛出错误。
返回 value 。
VlqDigitList ::
ContinuationDigit
VlqDigitList
令 left 为 VLQUnsignedValue of ContinuationDigit 。
令 right 为 VLQUnsignedValue of VlqDigitList 。
返回 left + right × 25 。
TerminalDigit ::
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
a
b
c
d
e
f
令 digit 为本产生式匹配到的字符。
令 value 为对应 digit 的整数 ,根据 IETF RFC 4648
所定义的base64 编码。
断言 :value < 32。
返回 value 。
ContinuationDigit ::
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
0
1
2
3
4
5
6
7
8
9
+
/
令 digit 为本产生式匹配到的字符。
令 value 为对应 digit 的整数 ,根据 IETF RFC 4648
所定义的base64 编码。
断言 :32 ≤
value < 64。
返回 value - 32。
7 JSON值工具
虽然本规范的算法基于ECMA-262内部实现,但其目的是方便非JavaScript平台也能实现。本节包含用于处理JSON值 的工具,将ECMA-262的细节从本文件的其他部分中抽象出来。
JSON值 可以是JSON对象 、JSON数组 、字符串 、数字 、布尔值 ,或null 。
JSON对象 是一个对象 ,其中每个属性:
JSON数组 是一个JSON对象 ,满足:
它有一个键为"length" 的属性,值是数字 ,
其它属性的键都是整数索引 。
7.1 ParseJSON ( string )
抽象操作ParseJSON接受参数string (字符串),返回一个JSON值 。调用时按如下步骤执行:
令result 为Call (%JSON.parse% ,
null , « string »)。
断言 :result 是JSON值 。
返回result 。
编者注
该抽象操作正在ECMA-262规范自身中公开,参见
tc39/ecma262#3540 。
7.2 JSONObjectGet ( object , key )
抽象操作JSONObjectGet接受参数object (JSON对象 )和key (字符串),并返回JSON值 或missing 。返回该key 在object 中对应的值。执行时步骤如下:
若object 没有名为key 的自有属性,则返回missing 。
令prop 为object 中名为key 的自有属性。
返回prop 的[[Value]] 属性。
7.3 JSONArrayIterate ( array )
抽象操作JSONArrayIterate接受参数array (JSON数组 ),返回一个列表 ,元素为JSON值 。返回array 的所有元素,便于算法 "For each"
遍历。执行步骤如下:
令length 为JSONObjectGet (array ,
"length" )。
断言 :length 是非负整数 。
令list 为新的空列表 。
令i 为0。
重复,条件为i < ℝ (length ),
令value 为JSONObjectGet (array ,
ToString (𝔽 (i ))))。
断言 :value 不为missing 。
将value 加入list 。
i 加1。
返回list 。
7.4 StringSplit ( string , separators )
抽象操作StringSplit接受参数string (字符串)和separators (字符串列表),返回一个字符串列表。将string 按separators 中任意分隔符分割为子串。如果有多个分隔符匹配,则以separators 中较前的优先。执行步骤如下:
令parts 为新的空列表。
令strLen 为string 长度。
令lastStart 为0。
令i 为0。
重复,当i < strLen ,
令matched 为false 。
对separators 中的每个字符串sep ,执行:
令sepLen 为sep 长度。
令candidate 为string 从i 到min (i
+ sepLen , strLen )的子串。
如果candidate 等于sep 且matched 为false ,则:
令chunk 为string 从lastStart 到i 的子串。
将chunk 加入parts 。
lastStart 设为i + sepLen 。
i 设为i + sepLen 。
matched 设为true 。
如果matched 为false ,则i 加1。
令chunk 为string 从lastStart 到strLen 的子串。
将chunk 加入parts 。
返回parts 。
8 位置类型
8.1 位置记录
位置记录 是一个由非负行号和非负列号 组成的元组:
表1:位置记录 字段
字段名
值类型
[[Line]]
非负整数
[[Column]]
非负整数
8.2 原始位置记录
原始位置记录 是一个由解码源记录 、非负行号和非负列号 组成的元组。它类似于位置记录 ,但用于描述具体原始源文件 中的源位置。
表2:原始位置记录 字段
字段名
值类型
[[Source]]
解码源记录
[[Line]]
非负整数
[[Column]]
非负整数
8.3 ComparePositions ( first , second )
抽象操作ComparePositions接受参数first (一个位置记录 或原始位置记录 )和second (一个位置记录 或原始位置记录 ),返回lesser 、equal 或greater 。取值依赖于first 是发生在second 之前、等于或之后。原始位置记录 中的[[Source]] 字段将被忽略。调用时执行如下步骤:
如果first .[[Line]] < second .[[Line]] ,返回lesser 。
如果first .[[Line]] > second .[[Line]] ,返回greater 。
断言 :first .[[Line]] 等于second .[[Line]] 。
如果first .[[Column]] < second .[[Column]] ,返回lesser 。
如果first .[[Column]] > second .[[Column]] ,返回greater 。
返回equal 。
9 源映射格式
源映射是一个JSON文档,包含一个顶层JSON对象 ,其结构如下:
{
"version" : 3 ,
"file" : "out.js" ,
"sourceRoot" : "" ,
"sources" : [ "foo.js" , "bar.js" ] ,
"sourcesContent" : [ null , null ] ,
"names" : [ "src" , "maps" , "are" , "fun" ] ,
"mappings" : "A,AAAB;;ABCDE" ,
"ignoreList" : [ 0 ]
}
version字段 必须始终为数字3,类型为整数 。如果该字段为其他值,源映射可能会被拒绝。
file字段 是此源映射所关联的生成代码 的可选名称。未指明可以是URL 、相对路径名或仅仅是文件名。源映射生成器可根据实际使用场景自由选择。
sourceRoot字段 是可选的源根字符串,用于在服务器上重新定位源文件或去除sources条目中的重复值。该值会被添加到sources字段 的各个条目前。
sources字段 是被mappings字段 引用的原始源代码列表。每个条目要么是字符串(可能为相对的URL ),要么为null (如果源名称未知)。
sourcesContent字段 是源内容(即原始源代码 )字符串的可选列表,在源码无法托管时使用。内容顺序与sources字段 一致。如果某些原始源代码 需通过名称获得,则条目可以为null 。
names字段 是可供mappings字段 使用的符号名称的可选列表。
mappings字段 是包含编码后映射数据的字符串(参见章节9.2 )。
ignoreList字段 是被视为第三方代码(如框架代码或打包器生成代码 )的文件索引的可选列表。这样开发工具就能避开开发者不想查看或调试的代码,无需事先配置。它参照sources字段 ,列出所有已知第三方源码在源映射中的索引。部分浏览器也可能在没有ignoreList字段时使用已废弃的x_google_ignoreList字段。
9.1 源映射解码
解码后源映射记录 包含如下字段:
表3:解码后源映射记录 字段
字段名称
值类型
[[File]]
字符串或null
[[Sources]]
List ,其中每项为解码后源码记录
[[Mappings]]
List ,其中每项为解码后映射记录
解码后源码记录 包含如下字段:
表4:解码后源码记录 字段
字段名称
值类型
[[URL]]
URL 或
null
[[Content]]
字符串或null
[[Ignored]]
布尔值
9.1.1 ParseSourceMap ( string , baseURL )
抽象操作 ParseSourceMap 接收参数 string (字符串)和 baseURL (URL ),返回一个解码后源映射记录 。执行下列步骤:
令 json 为 ParseJSON (string )。
如果 json 不是 JSON
对象 ,则抛出错误。
如果 JSONObjectGet (json ,
"sections" ) 不是 missing ,则
返回 DecodeIndexSourceMap (json ,
baseURL )。
返回 DecodeSourceMap (json , baseURL )。
9.1.2 DecodeSourceMap ( json , baseURL )
抽象操作 DecodeSourceMap 接收参数 json (JSON
对象 )和 baseURL (URL ),返回解码后源映射记录 。执行下列步骤:
如果 JSONObjectGet (json ,
"version" ) 不是 3 𝔽 ,可选地报告错误 。
令 mappingsField 为 JSONObjectGet (json ,
"mappings" )。
如果 mappingsField 不是字符串 ,则抛出错误。
如果 JSONObjectGet (json ,
"sources" ) 不是 JSON
数组 ,则抛出错误。
令 fileField 为 GetOptionalString (json ,
"file" )。
令 sourceRootField 为 GetOptionalString (json ,
"sourceRoot" )。
令 sourcesField 为 GetOptionalListOfOptionalStrings (json ,
"sources" )。
令 sourcesContentField 为 GetOptionalListOfOptionalStrings (json ,
"sourcesContent" )。
令 ignoreListField 为 GetOptionalListOfArrayIndexes (json ,
"ignoreList" )。
令 sources 为 DecodeSourceMapSources (baseURL ,
sourceRootField , sourcesField , sourcesContentField ,
ignoreListField )。
令 namesField 为 GetOptionalListOfStrings (json ,
"names" )。
令 mappings 为 DecodeMappings (mappingsField ,
namesField , sources )。
按升序排序mappings ,若 解码后映射记录 a 比 解码后映射记录 b 小,则
ComparePositions (a .[[GeneratedPosition]] , b .[[GeneratedPosition]] ) 为 lesser 。
返回 解码后源映射记录 { [[File]] : fileField , [[Sources]] : sources , [[Mappings]] : mappings }。
9.1.2.1 GetOptionalString ( object , key )
抽象操作 GetOptionalString 接收参数 object (JSON 对象 )和 key (字符串),返回字符串或
null 。执行下列步骤:
令 value 为 JSONObjectGet (object , key )。
如果 value 为字符串 ,返回
value 。
如果 value 不是 missing ,可选地报告错误 。
返回 null 。
9.1.2.2 GetOptionalListOfStrings ( object ,
key )
抽象操作 GetOptionalListOfStrings 接收参数 object (JSON 对象 )和 key (字符串),返回字符串的List 。执行下列步骤:
令 list 为新的空List 。
令 values 为 JSONObjectGet (object , key )。
如果 values 是 missing ,返回 list 。
如果 values 不是 JSON
数组 ,则
可选地报告错误 。
返回 list 。
对于 JSONArrayIterate (values ) 的每个元素
item ,执行:
如果 item 为字符串 ,则
添加 item 到 list 。
否则,
可选地报告错误 。
添加 "" 到 list 。
返回 list 。
9.1.2.3 GetOptionalListOfOptionalStrings ( object ,
key )
抽象操作 GetOptionalListOfOptionalStrings 接收参数 object (JSON 对象 )和 key (字符串),返回(字符串或
null )的List 。执行下列步骤:
令 list 为新的空List 。
令 values 为 JSONObjectGet (object , key )。
如果 values 是 missing ,返回 list 。
如果 values 不是 JSON
数组 ,则
可选地报告错误 。
返回 list 。
对于 JSONArrayIterate (values ) 的每个元素
item ,执行:
如果 item 为字符串 ,则
添加 item 到 list 。
否则,
如果 item ≠ null ,可选地报告错误 。
添加 null 到 list 。
返回 list 。
9.1.2.4 GetOptionalListOfArrayIndexes ( object ,
key )
抽象操作 GetOptionalListOfArrayIndexes 接收参数 object (对象)和
key (字符串),返回非负整数 的List 。执行下列步骤:
令 list 为新的空List 。
令 values 为 JSONObjectGet (object , key )。
如果 values 是 missing ,返回 list 。
如果 values 不是 JSON
数组 ,则
可选地报告错误 。
返回 list 。
对于 JSONArrayIterate (values ) 的每个元素
item ,执行:
如果 item 是 整数 类型,item ≠
+0 𝔽 ,且 item ≥
+0 𝔽 ,则
添加 ℝ (item ) 到
list 。
否则,
如果 item ≠ null ,可选地报告错误 。
添加 null 到 list 。
返回 list 。
9.2 映射结构
mappings字段 的数据分解如下:
每组代表生成文件中的一行,由分号 (;) 分隔
每个片段由逗号 (,) 分隔
每个片段由1、4或5个可变长度字段组成。
每个片段中的字段为:
该行在生成代码 中的零基起始列 。如果这是首个片段的第一个字段,或在新生成行(;)后首个片段,则该字段表示完整的base64 VLQ 。否则,此字段为相对于前一次出现的base64
VLQ 。注意这个字段的前值会在每行生成代码后重置,这与下方其他字段不同。
如果存在,表示sources列表中的零基索引。此字段为相对于前一次出现的base64
VLQ ,除非是首次出现,则表示完整值。
如果存在,表示原始源码 中的零基起始行号。此字段为相对于前一次出现的base64 VLQ ,除非为首次出现则为完整值。有source字段时必须出现。
如果存在,表示原始源码 中该行的零基起始列 。此字段为相对于前一次出现的base64 VLQ ,首次出现则为完整值。有source字段时必须出现。
如果存在,表示与该片段相关的names列表中的零基索引。此字段为相对于前一次出现的base64
VLQ ,首次出现则为完整值。
注意 1
此编码目的是减小源映射的体积。VLQ编码相比Source Map Revision 2 Proposal 在Google Calendar测试中缩小约50%。
注意 2
仅有一个字段的片段用于表示没有对应原始源码 代码的生成代码 (如编译器自动生成),四字段片段表示有对应源码但无对应名称的片段,五字段片段表示既有源码又有名称的映射。
注意 3
曾考虑使用文件 偏移,但最终采用行/列 数据,以避免因平台行结束符不同导致和原文件不一致的问题。
解码后映射记录 具有以下字段:
表5:解码后映射记录 字段
字段名称
值类型
[[GeneratedPosition]]
位置记录
[[OriginalPosition]]
原始位置记录 或null
[[Name]]
字符串或null
9.2.1 映射语法
映射字符串必须遵循如下语法:
MappingsField :
LineList
LineList :
Line
Line
;
LineList
Line :
MappingListopt
MappingList :
Mapping
Mapping
,
MappingList
Mapping :
GeneratedColumn
GeneratedColumn
OriginalSource
OriginalLine
OriginalColumn
Nameopt
GeneratedColumn :
Vlq
OriginalSource :
Vlq
OriginalLine :
Vlq
OriginalColumn :
Vlq
Name :
Vlq
解码映射状态记录 包含以下字段:
表6:解码映射状态记录 字段
字段名称
值类型
[[GeneratedLine]]
非负整数
[[GeneratedColumn]]
非负整数
[[SourceIndex]]
非负整数
[[OriginalLine]]
非负整数
[[OriginalColumn]]
非负整数
[[NameIndex]]
非负整数
9.2.1.1 DecodeMappingsField
语法驱动操作 DecodeMappingsField,接收参数state (解码映射状态记录 )、mappings (List ,每项为解码后映射记录 )、names (字符串List )、sources (每项为解码后源码记录 的List )。定义如下:
LineList :
Line
;
LineList
对Line 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
设置state .[[GeneratedLine]] 为state .[[GeneratedLine]] +1。
设置state .[[GeneratedColumn]] 为0。
对LineList 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
Line : [empty]
返回。
MappingList :
Mapping
,
MappingList
对Mapping 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
对MappingList 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
Mapping :
GeneratedColumn
对GeneratedColumn 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
如果state .[[GeneratedColumn]] <0,则
可选地报告错误 。
返回。
令position 为新的位置记录 { [[Line]] :
state .[[GeneratedLine]] , [[Column]] : state .[[GeneratedColumn]] }。
令decodedMapping 为新的DecodedMappingRecord { [[GeneratedPosition]] : position , [[OriginalPosition]] : null , [[Name]] : null }。
将decodedMapping 加入mappings 。
Mapping :
GeneratedColumn
OriginalSource
OriginalLine
OriginalColumn
Nameopt
对GeneratedColumn 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
如果state .[[GeneratedColumn]] <0,则
可选地报告错误 。
返回。
令generatedPosition 为新的位置记录 { [[Line]] :
state .[[GeneratedLine]] , [[Column]] : state .[[GeneratedColumn]] }。
对OriginalSource 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
对OriginalLine 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
对OriginalColumn 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
如果state .[[SourceIndex]] <0或state .[[SourceIndex]] ≥sources 元素数,或state .[[OriginalLine]] <0,或state .[[OriginalColumn]] <0,则
可选地报告错误 。
令originalPosition 为null 。
否则,
令originalPosition 为新的原始位置记录 { [[Source]] :
sources [state .[[SourceIndex]] ],[[Line]] :
state .[[OriginalLine]] ,[[Column]] : state .[[OriginalColumn]] }。
令name 为null 。
如果Name 存在,则
对Name 执行DecodeMappingsField ,参数为state 、mappings 、names 和sources 。
如果state .[[NameIndex]] <0或state .[[NameIndex]] ≥names 元素数,可选地报告错误 。
否则,设置name 为names [state .[[NameIndex]] ]。
令decodedMapping 为新的DecodedMappingRecord { [[GeneratedPosition]] : generatedPosition , [[OriginalPosition]] : originalPosition , [[Name]] : name }。
将decodedMapping 加入mappings 。
GeneratedColumn :
Vlq
令relativeColumn 为VLQSignedValue 的Vlq 结果。
设置state .[[GeneratedColumn]] 为state .[[GeneratedColumn]] +relativeColumn 。
OriginalSource :
Vlq
令relativeSourceIndex 为VLQSignedValue 的Vlq 结果。
设置state .[[SourceIndex]] 为state .[[SourceIndex]] +relativeSourceIndex 。
OriginalLine :
Vlq
令relativeLine 为VLQSignedValue 的Vlq 结果。
设置state .[[OriginalLine]] 为state .[[OriginalLine]] +relativeLine 。
OriginalColumn :
Vlq
令relativeColumn 为VLQSignedValue 的Vlq 结果。
设置state .[[OriginalColumn]] 为state .[[OriginalColumn]] +relativeColumn 。
Name :
Vlq
令relativeName 为VLQSignedValue 的Vlq 结果。
设置state .[[NameIndex]] 为state .[[NameIndex]] +relativeName 。
9.2.2 DecodeMappings ( rawMappings , names ,
sources )
抽象操作 DecodeMappings 接收参数 rawMappings (字符串),names (字符串List ),sources (解码后源码记录 的List ),并返回解码后映射记录 的List 。调用时执行以下步骤:
令 mappings 为新的空List 。
令 mappingsNode 为解析 rawMappings 且以 MappingsField 为目标符号 时的根解析节点 。
如果解析失败,则
可选地报告错误 。
返回 mappings 。
令 state 为所有字段均设为0的新解码映射状态记录 。
对 mappingsNode 执行 DecodeMappingsField ,参数为
state 、mappings 、names 和 sources 。
返回 mappings 。
9.2.3 生成 JavaScript 代码的映射
生成代码 的位置可能具有
映射 条目,这些位置根据 ECMAScript
词法语法 定义为 输入元素 。映射条目应指向以下任一:
由 源文本匹配的
IdentifierName 、PrivateIdentifier 、Punctuator 、DivPunctuator 、RightBracePunctuator 、NumericLiteral
和 RegularExpressionLiteral 的第一个码点。
由 源文本匹配的
Comment 、HashbangComment 、StringLiteral 、Template 、TemplateSubstitutionTail 、WhiteSpace
和 LineTerminator 的任意码点。
9.2.4 生成 JavaScript 代码的名称
若 JavaScript 代码符号满足下列条件,源映射生成器应为该符号生成带 [[Name]] 字段的 映射 条目:
原始源码 语言结构在语义上映射到生成的 JavaScript 代码。
原始源码 语言结构有名称。
此时,该 映射 条目的 [[Name]] 应为 原始源码 语言结构的名称。 [[Name]] 非 null 的 映射 被称为 命名映射 。
注意 1
例如混淆器会重命名函数和变量,或从立即调用函数表达式中移除函数名。
下列枚举列出了 ECMAScript
句法语法 中的产生式,以及源映射生成器应 为其生成命名映射的对应 token 或非终结符(即产生式右侧项)。创建这些 token 的
映射 条目应遵循 9.2.3 。
该枚举应理解为“最低标准”。一般情况下,源映射生成器可自由生成其他命名映射。
注意 2
枚举也包含生成器“可”生成命名映射的 token,而不仅仅是“应”生成的那些。这反映了现有工具实际生成或期望命名映射。重复的命名映射开销很低:names
的索引是相对编码的,因此连续映射同一名称时编码为 0(A)。
BindingIdentifier ,用于
LexicalDeclaration 、VariableStatement 和
FormalParameterList 。
BindingIdentifier ,用于
FunctionDeclaration 、FunctionExpression 、AsyncFunctionDeclaration 、AsyncFunctionExpression 、GeneratorDeclaration 、GeneratorExpression 、AsyncGeneratorDeclaration
和 AsyncGeneratorExpression (如果存在),否则为紧接
FormalParameters 的左括号 (。
无论 BindingIdentifier 是否存在,源映射生成器可选择在左括号为位置生成命名映射。
对于 ArrowFunction 或 AsyncArrowFunction :
当 ArrowFunction 由单一 BindingIdentifier 参数生成,或
AsyncArrowFunction 由
AsyncArrowBindingIdentifier 生成时,=> token。
注意 3
这描述了(异步)箭头函数有单一参数且该参数未被圆括号包裹的情况。
当 ArrowFunction 或 AsyncArrowFunction 由
ArrowFormalParameters 生成时的左括号 (。
源映射生成器亦可选择在 => token 处生成命名映射,以与前述情况一致。
ClassElementName ,用于
MethodDefinition 。包含生成器、异步方法、异步生成器和访问器。若
ClassElementName 为 "constructor" ,则 [[Name]] 应为原始类名(如果适用)。
源映射生成器亦可选择在左括号 ( 处生成命名映射。
源映射生成器可为包含在 Expression 的 IdentifierReference 生成命名映射。
9.3 解析源码
在拼接 sourceRoot 后,如果 sources 不是绝对 URL,则应相对于 source map 解析 sources(就像在 HTML 文档中解析 script 的
src 属性)。
9.3.1 DecodeSourceMapSources ( baseURL ,
sourceRoot , sources , sourcesContent , ignoreList )
抽象操作 DecodeSourceMapSources 接收参数 baseURL (URL )、sourceRoot (字符串或
null )、sources (字符串或 null 的 List )、sourcesContent (字符串或
null 的 List )、ignoreList (非负
整数 的 List ),返回 解码后源码记录 。调用时执行以下步骤:
令 decodedSources 为新的空 List 。
令 sourcesContentCount 为 sourcesContent 的元素个数。
令 sourceUrlPrefix 为 "" 。
如果 sourceRoot ≠ null ,则
如果 sourceRoot 以码点 U+002F (SOLIDUS) 结尾,则
设置 sourceUrlPrefix 为 sourceRoot 。
否则,
设置 sourceUrlPrefix 为 sourceRoot 和
"/" 的字符串拼接 。
令 index 为 0。
当 index < sources 的长度时重复执行:
令 source 为 sources [index ]。
令 decodedSource 为 解码后源码记录 { [[URL]] :null ,[[Content]] :null ,[[Ignored]] :false }。
如果 source ≠ null ,则
将 source 设置为 sourceUrlPrefix 和 source
的字符串拼接 。
令 sourceURL 为 URL 解析 source (以
baseURL 作为基础)。
如果 sourceURL 为 failure ,可选地报告错误 。
否则,将 decodedSource .[[URL]] 设置为
sourceURL 。
如果 ignoreList 包含 index ,则将 decodedSource .[[Ignored]] 设置为 true 。
如果 sourcesContentCount > index ,则将
decodedSource .[[Content]] 设置为
sourcesContent [index ]。
将 decodedSource 添加到 decodedSources 。
返回 decodedSources 。
注意
支持显示源码内容但不支持显示具有同一URL 但内容不同的多个源码的实现,会从对应 URL 的多种内容中任意选取其一。
9.4 扩展
源映射的消费方应忽略任何无法识别的额外属性,而不是因其而拒绝该源映射,这样该格式可以在不破坏现有用户的情况下添加新特性。
10 索引源映射
为支持拼接生成代码 和其他常见后处理,支持源映射的另一种表示方式:
{
"version" : 3 ,
"file" : "app.js" ,
"sections" : [
{
"offset" : { "line" : 0 , "column" : 0 } ,
"map" : {
"version" : 3 ,
"file" : "section.js" ,
"sources" : [ "foo.js" , "bar.js" ] ,
"names" : [ "src" , "maps" , "are" , "fun" ] ,
"mappings" : "AAAA,E;;ABCDE"
}
} ,
{
"offset" : { "line" : 100 , "column" : 10 } ,
"map" : {
"version" : 3 ,
"file" : "another_section.js" ,
"sources" : [ "more.js" ] ,
"names" : [ "more" , "is" , "better" ] ,
"mappings" : "AAAA,E;AACA,C;ABCDE"
}
}
]
}
索引映射遵循标准映射的结构。和常规的源映射一样,文件格式为 JSON,顶层为对象。它共享常规源映射的 version 以及file字段 ,但增加了新的sections字段 。
sections字段 是一个对象数组,每个对象包含如下字段:
offset字段 是一个对象,拥有两个字段
line 和 column,分别表示该引用源映射所代表的生成代码 的偏移量。
map字段 是一个嵌入的完整源映射对象。嵌入的映射不会继承索引映射中的任何值。
sections 应按起始位置排序,并且所表示的区块不能重叠。
10.1 DecodeIndexSourceMap ( json , baseURL )
抽象操作 DecodeIndexSourceMap 接收参数 json (对象)和 baseURL (URL ),返回解码后源映射记录 。调用时执行以下步骤:
令 sectionsField 为 JSONObjectGet (json , "sections" )。
断言 :sectionsField 不是
missing 。
如果 sectionsField 不是 JSON数组 ,抛出错误。
如果 JSONObjectGet (json ,
"version" ) 不是 3 𝔽 ,可选地报告错误 。
令 fileField 为 GetOptionalString (json , "file" )。
令 sourceMap 为 解码后源映射记录 { [[File]] :
fileField , [[Sources]] : « », [[Mappings]] : « » }。
令 previousOffsetPosition 为 null 。
令 previousLastMapping 为 null 。
对于 JSON值 section 在
JSONArrayIterate (sectionsField ) 中,执行:
如果 section 不是 JSON对象 ,则
可选地报告错误 。
否则,
令 offset 为 JSONObjectGet (section ,
"offset" )。
如果 offset 不是 JSON对象 ,抛出错误。
令 offsetLine 为 JSONObjectGet (offset ,
"line" )。
令 offsetColumn 为 JSONObjectGet (offset ,
"column" )。
如果 offsetLine 不是 整数 ,则
可选地报告错误 。
设置 offsetLine 为 +0 𝔽 。
如果 offsetColumn 不是 整数 ,则
可选地报告错误 。
设置 offsetColumn 为 +0 𝔽 。
令 offsetPosition 为新的 位置记录 { [[Line]] :
offsetLine , [[Column]] :
offsetColumn }。
如果 previousOffsetPosition ≠ null ,则
如果 ComparePositions (offsetPosition ,
previousOffsetPosition ) 为
lesser ,可选地报告错误 。
如果 previousLastMapping ≠ null ,则
如果 ComparePositions (offsetPosition ,
previousLastMapping .[[GeneratedPosition]] ) 为
lesser ,可选地报告错误 。
注意:该部分解码算法确保 index 源映射的sections字段 的条目按顺序排列且不重叠。虽然应保证生成器不会生成重叠
section 的 index 源映射,但源映射消费方可能只检查较简单的 offset 顺序条件。
令 mapField 为 JSONObjectGet (section ,
"map" )。
如果 mapField 不是 JSON对象 ,抛出错误。
令 decodedSectionCompletion 为 Completion (DecodeSourceMap (json ,
baseURL ))。
如果 decodedSectionCompletion 是 抛出完成 ,则
可选地报告错误 。
否则,
令 decodedSection 为
decodedSectionCompletion .[[Value]] 。
对于 解码后源码记录 additionalSource 在
decodedSection .[[Sources]] ,执行:
如果 sourceMap .[[Sources]]
不包含 additionalSource ,则
将 additionalSource 加入
sourceMap .[[Sources]] 。
令 offsetMappings 为新的空 List 。
对于 解码后映射记录 mapping 在
decodedSection .[[Mappings]] ,执行:
如果 mapping .[[GeneratedPosition]] .[[Line]] = 0,则
设置 mapping .[[GeneratedPosition]] .[[Column]] 为
mapping .[[GeneratedPosition]] .[[Column]] +
offsetColumn 。
设置 mapping .[[GeneratedPosition]] .[[Line]] 为 mapping .[[GeneratedPosition]] .[[Line]] + offsetLine 。
将 mapping 加入 offsetMappings 。
设置 sourceMap .[[Mappings]] 为
列表拼接
sourceMap .[[Mappings]] 与
offsetMappings 。
设置 previousOffsetPosition 为 offsetPosition 。
如果 offsetMappings 不为空,设置 previousLastMapping 为
offsetMappings 的最后一个元素。
返回 sourceMap 。
注意
实现可以选择不将所有 section 的映射拼接在一起,例如可以分别存储每个 section 并使用二分查找来定位。
11 获取源映射
11.1 关联生成代码与源映射
虽然源映射格式旨在兼容多语言和多平台,但对于典型的 web 服务器托管 JavaScript 的情况,定义如何引用源映射是有用的。
有两种方式可以将源映射链接到输出文件。第一种需要服务器支持,添加 HTTP 头部;第二种则是在源码中添加注释。
源映射通过 URL 进行链接,参见WHATWG
URL 的定义;特别是,URI 规范外的字符应进行百分号编码,并且 URL 可以为 data URI。结合 sourcesContent 使用 data URI
可以实现完全自包含的源映射。
HTTP sourcemap 头部优先生效,如果同时存在注释和头部,优先使用头部中的 URL 解析源映射文件。
无论采用哪种方法获取 源映射URL ,解析流程一致,过程如下:
当 源映射URL
不是绝对路径时,其路径相对于 生成代码 的 源起始位置 (source
origin)。源起始位置 的确定方式如下:
11.1.2 通过源码内注释关联
生成代码 应该包含一个名为
sourceMappingURL 的注释(或者依语言/格式而定的等价结构),其中包含源映射的 URL 。本规范定义了 JavaScript、CSS 和 WebAssembly
中注释的格式,其他语言可参照类似约定。
一种语言可有多种方式检测到 sourceMappingURL 注释,以便不同实现选择较简单方式。生成代码 若经过所有方法提取的结果一致,则称为无歧义关联到源映射 。
如果工具处理的一或多个源文件 无歧义关联到源映射 ,并输出文件也关联源映射,则输出也必须是无歧义的关联。
注意 1
如下 JavaScript 代码关联了源映射,但方式不是无歧义:
let a = `
//# sourceMappingURL=foo.js.map
//`
若通过解析提取源映射URL 会得到 null ,但不解析直接提取则会得到
foo.js.map。
注意 2
多种方式提取源映射URL ,可能得到不同结果,会带来安全和隐私风险。建议实现始终同时采用两种算法,而不假定其结果一致。
标准的未来版本会修正此问题,可能包括:只要注释(或类注释)包含字符 U+0060 (`)、U+0022 (")、U+0027 ('),或序列 U+002A U+002F (*/)
就直接提前返回。
令 module 为 module_decode (bytes )。
如果 module 是 WebAssembly error ,返回 null 。
遍历 module 的 custom section customSection ,执行:
令 name 为 customSection 的 name。
如果 CodePointsToString (name ) 为
"sourceMappingURL" ,则
令 value 为 customSection 的 bytes。
返回 CodePointsToString (value )。
返回 null 。
由于 WebAssembly 不是文本格式且不支持注释,只支持唯一的无歧义提取方法。该 URL 以 WebAssembly 名称 编码,并放置于 custom section 内容中。工具生成
WebAssembly 代码时,不允许生成两个或更多 sourceMappingURL 名称的 custom section 。
11.2 获取源映射文件
11.2.1 FetchSourceMap ( url )
抽象操作 FetchSourceMap 接收参数 url (URL ),返回一个 Promise。调用时执行如下步骤:
令 promiseCapability 为 NewPromiseCapability (%Promise% )。
令 request 为新的 request ,其 request URL 为
url 。
令 processResponseConsumeBody 为新的 Abstract Closure ,参数 (response ,
bodyBytes ),捕获 promiseCapability 和 url ,步骤如下:
如果 bodyBytes 是 null 或
failure ,则
执行 Call (promiseCapability .[[Reject]] , undefined , « 新
TypeError »)。
返回。
如果 url 的 scheme 是 HTTP(S)
且字节序列 )]}' 为 byte-sequence-prefix ,则
重复,当 bodyBytes 长度 ≠ 0 且 bodyBytes [0] 非 HTTP
newline byte :
移除 bodyBytes 的第 0 个元素。
令 bodyString 为 Completion (UTF-8
decode (bodyBytes ))。
IfAbruptRejectPromise (bodyString ,
promiseCapability )。
令 jsonValue 为 Completion (ParseJSON (bodyString ))。
IfAbruptRejectPromise (jsonValue ,
promiseCapability )。
执行 Call (promiseCapability .[[Resolve]] , undefined , «
jsonValue »)。
执行 fetch
request ,并设置 processResponseConsumeBody 为
processResponseConsumeBody 。
返回 promiseCapability .[[Promise]] 。
注意
历史原因下,通过 HTTP(S) 传送源映射时,服务器可能在文件前加上行首为 )]}' 的内容。
)] } 'garbage here
{ "version" : 3 , ...}
会被解释为
{ "version" : 3 , ...}
12 源映射记录的操作
解码源映射后,源映射的使用者可以通过得到的 解码后的源映射记录
查询调试或其他场景所需的位置信息。本节描述了源映射使用者通常支持的操作行为。
GetOriginalPositions 操作可用于查询在 生成代码 中某个位置所对应 原始源码
中的所有位置。例如,调试器可以通过该操作根据用户点击从 生成代码 跳转到 原始源码 。
12.1 GetOriginalPositions ( sourceMapRecord ,
generatedPosition )
抽象操作 GetOriginalPositions 接收参数 sourceMapRecord (解码后源映射记录 )和 generatedPosition (位置记录 ),返回 原始位置记录 的 List 。调用时执行以下步骤:
令 mappings 为 sourceMapRecord .[[Mappings]] 。
令 last 为 null 。
令 originalPositions 为新的空 List 。
按逆序遍历 mappings 中每个 mapping ,执行:
如果 last 为 null ,则
如果 ComparePositions (mapping .[[GeneratedPosition]] , generatedPosition )
的结果为 lesser 或 equal ,则
将 last 设为 mapping 。
如果 last 不为 null ,则
遍历 mappings 中每个 mapping ,执行:
如果 ComparePositions (last .[[GeneratedPosition]] , mapping .[[GeneratedPosition]] ) 的结果为
equal ,则
将 mapping .[[OriginalPosition]]
加入 originalPositions 。
返回 originalPositions 。
附录A (资料性) 约定
在使用或生成源映射时,应遵循以下约定。
A.1 源映射命名
通常,源映射会和生成文件名称相同,只是文件扩展名为 .map。例如 page.js 的源映射会命名为
page.js.map。
A.2 关联eval代码与有名生成代码
在 eval 代码使用源映射时有一种现有约定,格式如下:
详细描述见 Give your eval a name with //@
sourceURL 。
附录B (资料性) 说明
B.1 语言无关的堆栈映射
无需源码语言知识的堆栈追踪映射,未在本文档中涉及 。
B.2 多级映射
目前常见工具会通过一些DSL(模板)或编译流程(TypeScript → JavaScript → 压缩
JavaScript)生成源文件,导致最终源映射在此过程中经过多次转换。此问题有两种解决方式。一种是简单但有信息损失的方法,即为调试目的忽略中间步骤,将中间产物视为“原始源码”,或者只传递源映射位置信息,隐藏上述过程。更完整的方法是支持多级映射:如果原始源码也有源映射引用,允许用户继续解析下一级源映射。
但尚不清楚除 JavaScript 外,其他语言的“源映射引用”应如何表达,特别是在不支持 JavaScript 风格单行注释的语言中。
附录C (资料性) 外部规范中定义的术语
本节列出了本文档中所用、由 ECMA-262 以外的外部规范定义的所有术语和算法。
WebAssembly 核心规范 <https://www.w3.org/TR/wasm-core-2/ >
custom section ,
module_decode ,
WebAssembly error ,
WebAssembly names
WHATWG Encoding <https://encoding.spec.whatwg.org/ >
UTF-8 decode
WHATWG Fetch <https://fetch.spec.whatwg.org/ >
fetch ,
HTTP newline byte ,
processResponseConsumeBody ,
request ,
request URL
WHATWG Infra <https://infra.spec.whatwg.org/ >
byte sequence ,
byte-sequence-prefix ,
byte-sequence-length ,
WHATWG URL <https://url.spec.whatwg.org/ >
HTTP(S) scheme ,
scheme ,
URL ,
URL parsing
附录D (资料性) 参考文献
IETF RFC 4648, Base16, Base32 和 Base64 数据编码 ,见 <https://datatracker.ietf.org/doc/html/rfc4648 >
ECMA-262, ECMAScript® 语言规范 ,见 <https://tc39.es/ecma262/ >
ECMA-404, JSON 数据交换格式 ,见 <https://www.ecma-international.org/publications-and-standards/standards/ecma-404/ >
WebAssembly 核心规范 ,见 <https://www.w3.org/TR/wasm-core-2/ >
WHATWG Encoding ,见 <https://encoding.spec.whatwg.org/ >
WHATWG Fetch ,见 <https://fetch.spec.whatwg.org/ >
WHATWG Infra ,见 <https://infra.spec.whatwg.org/ >
WHATWG URL ,见 <https://url.spec.whatwg.org/ >
给 eval 起名字 //@ sourceURL ,Firebug (2009),见
<http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/ >
Source Map Revision 2 Proposal ,John Lenz (2010),见
<https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/ >
Variable-length
quantity ,维基百科,见 <https://en.wikipedia.org/wiki/Variable-length_quantity >
附录E (资料性) 后记
本规范在 GitHub 上采用一种名为 Ecmarkup 的纯文本源格式编写。Ecmarkup 是一种基于 HTML 和 Markdown
的方言,提供了用于以纯文本编写 ECMAScript 规范并将其处理为符合本文件编辑规范的完整 HTML 渲染的框架和工具集。Ecmarkup 集成了诸多其他格式与技术,包括用于定义语法的 Grammarkdown 和用于编写算法步骤的 Ecmarkdown 。此规范的 PDF 版本通过打印 HTML 渲染结果到 PDF 完成。
本规范第一版是使用 Bikeshed 编写的,这是一种基于 HTML 和 Markdown 的不同纯文本源格式。
标准化前的版本采用 Google Docs 编写。
Copyright & Software License
Ecma International
Rue du Rhone 114
CH-1204 Geneva
Tel: +41 22 849 6000
Fax: +41 22 849 6001
Web: https://ecma-international.org/
Copyright Notice
© 2025 Ecma International
This draft document may be copied and furnished to others, and derivative works that comment on or
otherwise explain it or assist in its implementation may be prepared, copied, published, and
distributed, in whole or in part, without restriction of any kind, provided that the above copyright
notice and this section are included on all such copies and derivative works. However, this document
itself may not be modified in any way, including by removing the copyright notice or references to
Ecma International, except as needed for the purpose of developing any document or deliverable
produced by Ecma International.
This disclaimer is valid only prior to final version of this document. After approval all rights on
the standard are reserved by Ecma International.
The limited permissions are granted through the standardization phase and will not be revoked by Ecma
International or its successors or assigns during this time.
This document and the information contained herein is provided on an "AS IS" basis and ECMA
INTERNATIONAL DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY
WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY
IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Software License
All Software contained in this document ("Software") is protected by copyright and is being made
available under the "BSD License", included below. This Software may be subject to third party rights
(rights from parties other than Ecma International), including patent rights, and no licenses under such
third party rights are granted under this license even if the third party concerned is a member of Ecma
International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm
FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA
INTERNATIONAL STANDARDS.
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the
distribution.
Neither the name of the authors nor Ecma International may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.