Draft ECMA-426 / September 28, 2026 源映射格式规范
引言
本 Ecma 标准定义了源映射格式,用于将转译后的源代码映射回原始源码 。
源映射格式具有以下目标:
支持源码级调试,允许双向映射
支持服务器端堆栈跟踪反混淆
最初的源 映射格式 (v1) 由 Joseph Schorr 创建,供 Closure Inspector 使用,以便对经过优化的 JavaScript 代码进行源码级调试(尽管该格式本身与语言无关)。然而,随着使用源映射的项目规模扩大,该格式的冗长性开始成为问题。v2 格式(源映射第 2 版提案 )通过牺牲部分简单性和灵活性来减小源映射的总体大小。即使 v2 版本的格式作出了这些更改,源映射文件的大小仍然限制了其实用性。v3 格式基于 Pavel Podivilov (Google) 提出的建议。
源映射格式不再使用版本号,而是将其硬编码为始终是“3”。
在 2023-2024 年,源映射格式发展为更精确的 Ecma 标准,许多人为此作出了重大贡献。源映射格式的进一步迭代预计将由 TC39-TG4 推进。
Asumu Takikawa, Nicolò Ribaudo, Jon Kuperman
ECMA-426, 第 1版 , 项目编辑
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 获取 。
https://fetch.spec.whatwg.org/
WHATWG 基础设施 。
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 )
The abstract operation GetTheAnswer takes argument input (an integer ) and returns an integer .
等价于:
4.1.1.2 GetTheAnswer2 ( input )
The abstract operation GetTheAnswer2 takes argument input (an integer ) and returns either a normal completion containing an integer or a throw completion .
对返回完成记录的抽象操作的所有调用,都隐式地假定为由 ECMA-262 的 ? 完成记录解包简写 包裹,除非它们由显式的 Completion 调用包裹。例如:
令 result 为 GetTheAnswer (value ). 令 second 为 Completion (GetTheAnswer (value )).
等价于:
令 result 为 ? 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 源映射,以二进制内容(表示为单行)中的字节索引计算。
Note 这意味着“A”(LATIN CAPITAL LETTER A) 计为 1 个码元,而“🔥”(FIRE) 计为 2 个码元。其他内容类型的源映射可能与此不同。
6 base64 VLQ
base64 VLQ 是采用 base64 编码的变长数量 ,其中最高有效位(第 6 位)用作延续位,“数位”按最低有效位优先的顺序编码到字符串中,第一个数位的最低有效位用作符号位。
Note 1 在提出需要更大数值的用例之前,base64 VLQ 编码能够表示的值仅限于 32 位数量。这意味着超过 32 位的值无效,实现可以拒绝它们。符号位计入此限制,而延续位不计入。
Note 2
字符串 "iB" 表示具有两个数位的 base64 VLQ。第一个数位 "i" 编码位模式 0b100010,其中延续位为 1(VLQ 继续),符号位为 0(非负),值位为 0b0001。第二个数位 B 编码位模式 0b000001,其中延续位为 0,没有符号位,值位为 0b00001。此 VLQ 字符串解码后的数值为 17。
Note 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
The syntax-directed operation VLQSignedValue takes no arguments and returns an integer . It is defined piecewise over the following productions:
Vlq :: VlqDigitList
令 unsigned 为 VlqDigitList 的 VLQUnsignedValue . 如果 unsigned modulo 2 = 1,则令 sign 为 -1. 否则,令 sign 为 1. 令 value 为 floor (unsigned / 2). 如果 value 为 0 且 sign 为 -1,则返回 -231 . 如果 value ≥ 231 ,则抛出错误. 返回 sign × value .
Note
6.2 VLQUnsignedValue
The syntax-directed operation VLQUnsignedValue takes no arguments and returns an non-negative integer . It is defined piecewise over the following productions:
Vlq :: VlqDigitList
令 value 为 VlqDigitList 的 VLQUnsignedValue . 如果 value ≥ 232 ,则抛出错误. 返回 value .
VlqDigitList ::
ContinuationDigit
VlqDigitList
令 left 为 ContinuationDigit 的 VLQUnsignedValue . 令 right 为 VlqDigitList 的 VLQUnsignedValue . 返回 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 为依据 IETF RFC 4648 定义的 base64 编码与 digit 对应的整数. 断言: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 为依据 IETF RFC 4648 定义的 base64 编码与 digit 对应的整数. 断言:32 ≤ value < 64. 返回 value - 32.
7 JSON 值工具
虽然本规范的算法基于 ECMA-262 的内部机制定义,但其目标是让非 JavaScript 平台也能轻松实现。本节包含用于处理 JSON 值的工具,将 ECMA-262 的细节与文档的其余部分隔离开来。
JSON value 可以是 JSON object 、JSON array 、String 、Number 、Boolean 或 null 。
JSON object 是一个 Object,其每个属性都满足:
JSON array 是满足以下条件的 JSON object :
它具有一个键为 "length" 且值为 Number 的属性,
它的所有其他属性的键都是整数索引。
7.1 ParseJSON ( string )
The abstract operation ParseJSON takes argument string (a String) and returns a JSON value . It performs the following steps when called:
令 result 为 Call (%JSON.parse% , null , « string »). 断言:result 是 JSON value . 返回 result .
Editor's Note
ECMA-262 本身正在着手公开此抽象操作,参见
tc39/ecma262#3540 。
7.2 JSONObjectGet ( object , key )
The abstract operation JSONObjectGet takes arguments object (a JSON object ) and key (a String) and returns a JSON value or missing . 它返回 object 中与指定 key 关联的值。 It performs the following steps when called:
如果 object 没有键为 key 的自有属性,则返回 missing . 令 prop 为 object 中键为 key 的自有属性. 返回 prop 的 [[Value]] 特性.
7.3 JSONArrayIterate ( array )
The abstract operation JSONArrayIterate takes argument array (a JSON array ) and returns a List of JSON values . 它返回包含 array 所有元素的 List ,以便算法使用“对于每个”对其进行迭代。 It performs the following steps when called:
令 length 为 JSONObjectGet (array , "length" ). 断言:length 是非负且为整数的 Number. 令 list 为新的空 List . 令 i 为 0. 当 i < ℝ (length ) 时,重复执行:令 value 为 JSONObjectGet (array , ToString (𝔽 (i ))). 断言:value 不是 missing . 将 value 追加到 list . 将 i 设为 i + 1. 返回 list .
7.4 StringSplit ( string , separators )
The abstract operation StringSplit takes arguments string (a String) and separators (a List of non-empty Strings) and returns a List of Strings. 它将字符串拆分为由 separators 中任意元素分隔的子字符串。如果多个分隔符匹配,则在 separators 中较早出现的分隔符具有更高优先级。 It performs the following steps when called:
令 parts 为新的空 List . 令 strLen 为 string 的长度. 令 lastStart 为 0. 令 i 为 0. 当 i < strLen 时,重复执行:令 matched 为 false . 对于 separators 中的每个 String 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 设为 i + 1. 令 chunk 为 string 中从 lastStart 到 strLen 的子字符串. 将 chunk 追加到 parts . 返回 parts .
8 位置类型
8.1 Position Record
Position Record 是由非负行号和非负列 号组成的元组:
Table 1: Position Record 字段
字段名
值类型
[[Line]]
非负且为整数的 Number
[[Column]]
非负且为整数的 Number
8.2 Original Position Record
Original Position Record 是由 Decoded Source Record 、非负行号和非负列 号组成的元组。它类似于 Position Record ,但描述的是具体原始源码 文件中的源码位置。
Table 2: Original Position Record 字段
8.3 ComparePositions ( first , second )
The abstract operation ComparePositions takes arguments first (a Position Record or a Original Position Record ) and second (a Position Record or a Original Position Record ) and returns lesser , equal or greater . 它根据 first 位于 second 之前、与之相等或位于其后,分别返回 lesser 、equal 或 greater 。Original Position Records 的 [[Source]] 字段会被忽略。 It performs the following steps when called:
如果 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 object :
{
"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 (可能为相对 URL )的字符串,要么在源码名称未知时为 null 。
sourcesContent 字段 是一个可选的源码内容(即原始源码 )字符串列 表,在无法托管源码时使用。内容的列 出顺序与 sources 字段 中的顺序相同。如果某些原始源码 应按名称检索,则条目可以为 null 。
names 字段 是一个可选的符号名称列 表,可供 mappings 字段 使用。
mappings 字段 是包含已编码映射数据的字符串(参见第 9.2 节)。
ignoreList 字段 是一个可选的文件索引列 表,这些文件应被视为第三方代码,例如框架代码或打包工具生成的代码 。这使开发者工具能够避开开发者可能不希望查看或逐步执行的代码,而无需开发者预先进行配置。它引用 sources 字段 ,并列 出源映射中所有已知第三方源码的索引。如果不存在 ignoreList,某些浏览器也可能使用已弃用的 x_google_ignoreList 字段。
9.1 解码源映射
Decoded Source Map Record 具有以下字段:
Table 3: Decoded Source Map Records 的字段
Decoded Source Record 具有以下字段:
Table 4: Decoded Source Records 的字段
字段名
值类型
[[URL]]
URL 或 null
[[Content]]
String 或 null
[[Ignored]]
一个 Boolean
9.1.1 ParseSourceMap ( string , baseURL )
The abstract operation ParseSourceMap takes arguments string (a String) and baseURL (an URL ) and returns a Decoded Source Map Record . It performs the following steps when called:
令 json 为 ParseJSON (string ). 如果 json 不是 JSON object ,则抛出错误. 如果 JSONObjectGet (json , "sections" ) 不是 missing ,则:返回 DecodeIndexSourceMap (json , baseURL ). 返回 DecodeSourceMap (json , baseURL ).
9.1.2 DecodeSourceMap ( json , baseURL )
The abstract operation DecodeSourceMap takes arguments json (a JSON object ) and baseURL (an URL ) and returns a Decoded Source Map Record . It performs the following steps when called:
如果 JSONObjectGet (json , "version" ) 不是 3 𝔽 ,则选择性地报告错误 . 令 mappingsField 为 JSONObjectGet (json , "mappings" ). 如果 mappingsField 不是 String,则抛出错误. 如果 JSONObjectGet (json , "sources" ) 不是 JSON array ,则抛出错误. 令 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 按升序排序,其中,如果 ComparePositions (a .[[GeneratedPosition]] , b .[[GeneratedPosition]] ) 为 lesser ,则 Decoded Mapping Record a 小于 Decoded Mapping Record b . 返回 Decoded Source Map Record { [[File]] : fileField , [[Sources]] : sources , [[Mappings]] : mappings }.
9.1.2.1 GetOptionalString ( object , key )
The abstract operation GetOptionalString takes arguments object (a JSON object ) and key (a String) and returns a String or null . It performs the following steps when called:
令 value 为 JSONObjectGet (object , key ). 如果 value 是 String,则返回 value . 如果 value 不是 missing ,则选择性地报告错误 . 返回 null .
9.1.2.2 GetOptionalListOfStrings ( object , key )
The abstract operation GetOptionalListOfStrings takes arguments object (a JSON object ) and key (a String) and returns a List of Strings. It performs the following steps when called:
令 list 为新的空 List . 令 values 为 JSONObjectGet (object , key ). 如果 values 为 missing ,则返回 list . 如果 values 不是 JSON array ,则:选择性地报告错误 .返回 list . 对于 JSONArrayIterate (values ) 中的每个元素 item ,执行:如果 item 是 String,则:将 item 追加到 list . 否则:选择性地报告错误 .将空 String 追加到 list . 返回 list .
9.1.2.3 GetOptionalListOfOptionalStrings ( object , key )
The abstract operation GetOptionalListOfOptionalStrings takes arguments object (a JSON object ) and key (a String) and returns a List of either Strings or null . It performs the following steps when called:
令 list 为新的空 List . 令 values 为 JSONObjectGet (object , key ). 如果 values 为 missing ,则返回 list . 如果 values 不是 JSON array ,则:选择性地报告错误 .返回 list . 对于 JSONArrayIterate (values ) 中的每个元素 item ,执行:如果 item 是 String,则:将 item 追加到 list . 否则:如果 item ≠ null ,则选择性地报告错误 . 将 null 追加到 list . 返回 list .
9.1.2.4 GetOptionalListOfArrayIndexes ( object , key )
The abstract operation GetOptionalListOfArrayIndexes takes arguments object (an Object) and key (a String) and returns a List of non-negative integers . It performs the following steps when called:
令 list 为新的空 List . 令 values 为 JSONObjectGet (object , key ). 如果 values 为 missing ,则返回 list . 如果 values 不是 JSON array ,则:选择性地报告错误 .返回 list . 对于 JSONArrayIterate (values ) 中的每个元素 item ,执行:如果 item 是整数 Number,且 item ≥ +0 𝔽 ,则:将 ℝ (item ) 追加到 list . 否则:选择性地报告错误 . 返回 list .
9.2 映射结构
mappings 字段 的数据按以下方式划分:
表示生成文件中一行的各组之间以分号 (;) 分隔
各段之间以逗号 (,) 分隔
每段由 1、4 或 5 个变长字段组成。
每段中的字段如下:
该段所表示的生成代码 行中从零开始计数的起始列 。如果这是第一段的第一个字段,或者是新生成行 (;) 之后第一段的第一个字段,则此字段保存完整的 base64 VLQ 。否则,此字段包含相对于该字段上一次出现时的值的 base64 VLQ 。请注意,这与下面的后续字段不同,因为每个生成行之后都会重置先前值。
如果存在,则为 sources 列 表中从零开始计数的索引。此字段包含相对于该字段上一次出现时的值的 base64 VLQ ,除非这是该字段首次出现,在这种情况下表示的是完整值。
如果存在,则为原始源码 中从零开始计数的起始行。此字段包含相对于该字段上一次出现时的值的 base64 VLQ ,除非这是该字段首次出现,在这种情况下表示的是完整值。如果存在源码字段,则此字段必须存在。
如果存在,则为原始源码 行中从零开始计数的起始列 。此字段包含相对于该字段上一次出现时的值的 base64 VLQ ,除非这是该字段首次出现,在这种情况下表示的是完整值。如果存在源码字段,则此字段必须存在。
如果存在,则为 names 列 表中与此段关联的、从零开始计数的索引。此字段包含相对于该字段上一次出现时的值的 base64 VLQ ,除非这是该字段首次出现,在这种情况下表示的是完整值。
Note 1 此编码的目的是减小源映射的大小。在使用 Google 日历进行的测试中,与
源映射第 2 版提案 相比,VLQ 编码使源映射的大小减小了 50%。
Note 2 只有一个字段的段旨在表示因不存在对应原始源代码而未映射的
生成代码 ,例如编译器生成的代码。具有四个字段的段表示不存在对应名称的已映射代码。具有五个字段的段表示同时具有映射名称的已映射代码。
Note 3 曾考虑使用文件 偏移量,但最终放弃,转而使用行/列 数据,以避免因平台特定的行结束符而与原始内容错位。
Decoded Mapping Record 具有以下字段:
Table 5: Decoded Mapping Records 的字段
9.2.1 映射文法
mappings String 必须遵循以下文法:
MappingsField :
LineList
LineList :
Line
Line
;
LineList
Line :
MappingList opt
MappingList :
Mapping
Mapping
,
MappingList
Mapping :
GeneratedColumn
GeneratedColumn
OriginalSource
OriginalLine
OriginalColumn
Name opt
GeneratedColumn :
Vlq
OriginalSource :
Vlq
OriginalLine :
Vlq
OriginalColumn :
Vlq
Name :
Vlq
Decode Mapping State Record 具有以下字段:
Table 6: Decode Mapping State Records 的字段
字段名
值类型
[[GeneratedLine]]
非负整数
[[GeneratedColumn]]
非负整数
[[SourceIndex]]
非负整数
[[OriginalLine]]
非负整数
[[OriginalColumn]]
非负整数
[[NameIndex]]
非负整数
9.2.1.1 DecodeMappingsField
The syntax-directed operation DecodeMappingsField takes arguments state (a Decode Mapping State Record ), mappings (a List of Decoded Mapping Records ), names (a List of Strings), and sources (a List of Decoded Source Records ). It is defined piecewise over the following productions:
LineList :
Line
;
LineList
以 state 、mappings 、names 和 sources 为参数,执行 Line 的 DecodeMappingsField . 将 state .[[GeneratedLine]] 设为 state .[[GeneratedLine]] + 1. 将 state .[[GeneratedColumn]] 设为 0. 以 state 、mappings 、names 和 sources 为参数,执行 LineList 的 DecodeMappingsField .
Line : [empty]
返回.
MappingList :
Mapping
,
MappingList
以 state 、mappings 、names 和 sources 为参数,执行 Mapping 的 DecodeMappingsField . 以 state 、mappings 、names 和 sources 为参数,执行 MappingList 的 DecodeMappingsField .
Mapping :
GeneratedColumn
以 state 、mappings 、names 和 sources 为参数,执行 GeneratedColumn 的 DecodeMappingsField . 如果 state .[[GeneratedColumn]] < 0,则:选择性地报告错误 .返回. 令 position 为新的 Position Record { [[Line]] : state .[[GeneratedLine]] , [[Column]] : state .[[GeneratedColumn]] }. 令 decodedMapping 为新的 DecodedMappingRecord { [[GeneratedPosition]] : position , [[OriginalPosition]] : null , [[Name]] : null }. 将 decodedMapping 追加到 mappings .
Mapping :
GeneratedColumn
OriginalSource
OriginalLine
OriginalColumn
Name opt
以 state 、mappings 、names 和 sources 为参数,执行 GeneratedColumn 的 DecodeMappingsField . 如果 state .[[GeneratedColumn]] < 0,则:选择性地报告错误 .返回. 令 generatedPosition 为新的 Position Record { [[Line]] : state .[[GeneratedLine]] , [[Column]] : state .[[GeneratedColumn]] }. 以 state 、mappings 、names 和 sources 为参数,执行 OriginalSource 的 DecodeMappingsField . 以 state 、mappings 、names 和 sources 为参数,执行 OriginalLine 的 DecodeMappingsField . 以 state 、mappings 、names 和 sources 为参数,执行 OriginalColumn 的 DecodeMappingsField . 如果 state .[[SourceIndex]] < 0,或 state .[[SourceIndex]] ≥ sources 的元素数量,或 state .[[OriginalLine]] < 0,或 state .[[OriginalColumn]] < 0,则:选择性地报告错误 .令 originalPosition 为 null . 否则:令 originalPosition 为新的 Original Position Record { [[Source]] : sources [state .[[SourceIndex]] ], [[Line]] : state .[[OriginalLine]] , [[Column]] : state .[[OriginalColumn]] }. 令 name 为 null . 如果存在 Name ,则:以 state 、mappings 、names 和 sources 为参数,执行 Name 的 DecodeMappingsField . 如果 state .[[NameIndex]] < 0 或 state .[[NameIndex]] ≥ names 的元素数量,则选择性地报告错误 . 否则,将 name 设为 names [state .[[NameIndex]] ]. 令 decodedMapping 为新的 DecodedMappingRecord { [[GeneratedPosition]] : generatedPosition , [[OriginalPosition]] : originalPosition , [[Name]] : name }. 将 decodedMapping 追加到 mappings .
GeneratedColumn :
Vlq
令 relativeColumn 为 Vlq 的 VLQSignedValue . 将 state .[[GeneratedColumn]] 设为 state .[[GeneratedColumn]] + relativeColumn .
OriginalSource :
Vlq
令 relativeSourceIndex 为 Vlq 的 VLQSignedValue . 将 state .[[SourceIndex]] 设为 state .[[SourceIndex]] + relativeSourceIndex .
OriginalLine :
Vlq
令 relativeLine 为 Vlq 的 VLQSignedValue . 将 state .[[OriginalLine]] 设为 state .[[OriginalLine]] + relativeLine .
OriginalColumn :
Vlq
令 relativeColumn 为 Vlq 的 VLQSignedValue . 将 state .[[OriginalColumn]] 设为 state .[[OriginalColumn]] + relativeColumn .
Name :
Vlq
令 relativeName 为 Vlq 的 VLQSignedValue . 将 state .[[NameIndex]] 设为 state .[[NameIndex]] + relativeName .
9.2.2 DecodeMappings ( rawMappings , names , sources )
The abstract operation DecodeMappings takes arguments rawMappings (a String), names (a List of Strings), and sources (a List of Decoded Source Records ) and returns a List of Decoded Mapping Record . It performs the following steps when called:
令 mappings 为新的空 List . 令 mappingsNode 为以 MappingsField 作为目标符号解析 rawMappings 时得到的根 Parse Node . 如果解析失败,则:选择性地报告错误 .返回 mappings . 令 state 为所有字段均设为 0 的新 Decode Mapping State Record . 以 state 、mappings 、names 和 sources 为参数,执行 mappingsNode 的 DecodeMappingsField . 返回 mappings .
9.2.3 生成的 JavaScript 代码的映射
可以具有映射 条目的生成代码 位置,依据 ECMAScript 词法文法 中的输入元素 定义。映射条目必须指向以下两者之一:
9.2.4 生成的 JavaScript 代码的名称
如果满足以下条件,源映射生成器应为 JavaScript 词法单元创建具有 [[Name]] 字段的映射 条目:
原始源码 语言的构造在语义上对应于生成的 JavaScript 代码。
原始源码 语言的构造具有名称。
此时,映射 条目的 [[Name]] 应为原始源码 语言构造的名称。[[Name]] 非空的映射 称为命名映射 。
Note 1
压缩工具重命名函数和变量,或移除立即调用的函数表达式中的函数名称。
以下枚举列 出了 ECMAScript 句法文法 的产生式,以及源映射生成器应为其生成命名映射的相应词法单元或非终结符(位于产生式右侧)。为这些词法单元创建的映射 条目必须遵循第 9.2.3 节。
此枚举应理解为“最低要求”。一般而言,源映射生成器可以自由生成任何额外的命名映射。
Note 2
除了生成器“应”生成命名映射的词法单元外,此枚举还
列 出了生成器“可以”生成命名映射的词法单元。这反映了现有工具生成或期望命名映射的实际情况。重复命名映射的成本相对较低:names 中的索引采用相对编码,因此后续指向同一名称的映射会编码为 0 (
A)。
9.3 解析源码地址
如果在前面添加 sourceRoot 后,sources 仍不是绝对 URL ,则相对于源映射解析 sources(类似于解析 HTML 文档中脚本的 src 属性)。
9.3.1 DecodeSourceMapSources ( baseURL , sourceRoot , sources , sourcesContent , ignoreList )
The abstract operation DecodeSourceMapSources takes arguments baseURL (an URL ), sourceRoot (a String or null ), sources (a List of either Strings or null ), sourcesContent (a List of either Strings or null ), and ignoreList (a List of non-negative integers ) and returns a List of Decoded Source Record . It performs the following steps when called:
令 decodedSources 为新的空 List . 令 sourcesContentCount 为 sourcesContent 中的元素数量. 令 sourceUrlPrefix 为空 String. 如果 sourceRoot ≠ null ,则:如果 sourceRoot 以码点 U+002F (SOLIDUS) 结尾,则:将 sourceUrlPrefix 设为 sourceRoot . 否则:将 sourceUrlPrefix 设为 sourceRoot 与 "/" 的字符串连接结果. 令 index 为 0. 当 index < sources 的长度时,重复执行:令 source 为 sources [index ]. 令 decodedSource 为 Decoded Source Record { [[URL]] : null , [[Content]] : null , [[Ignored]] : false }. 如果 source ≠ null ,则:将 source 设为 sourceUrlPrefix 与 source 的字符串连接结果. 令 sourceURL 为使用 baseURL 对 source 进行 URL 解析 的结果. 如果 sourceURL 为 failure ,则选择性地报告错误 . 否则,将 decodedSource .[[URL]] 设为 sourceURL . 如果 ignoreList 包含 index ,则将 decodedSource .[[Ignored]] 设为 true . 如果 sourcesContentCount > index ,则将 decodedSource .[[Content]] 设为 sourcesContent [index ]. 将 decodedSource 追加到 decodedSources . 将 index 设为 index + 1. 返回 decodedSources .
Note 如果实现支持显示源码内容,但不支持显示
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 字段 是嵌入的完整源映射对象。嵌入映射不会从包含它的索引映射中继承任何值。
各分区必须按起始位置排序,且所表示的分区不得重叠。
10.1 DecodeIndexSourceMap ( json , baseURL )
The abstract operation DecodeIndexSourceMap takes arguments json (an Object) and baseURL (an URL ) and returns a Decoded Source Map Record . It performs the following steps when called:
令 sectionsField 为 JSONObjectGet (json , "sections" ). 断言:sectionsField 不是 missing . 如果 sectionsField 不是 JSON array ,则抛出错误. 如果 JSONObjectGet (json , "version" ) 不是 3 𝔽 ,则选择性地报告错误 . 令 fileField 为 GetOptionalString (json , "file" ). 令 sourceMap 为 Decoded Source Map Record { [[File]] : fileField , [[Sources]] : « », [[Mappings]] : « » }. 令 previousOffsetPosition 为 null . 令 previousLastMapping 为 null . 对于 JSONArrayIterate (sectionsField ) 中的每个 JSON value section ,执行:如果 section 不是 JSON object ,则:选择性地报告错误 . 否则:令 offset 为 JSONObjectGet (section , "offset" ). 如果 offset 不是 JSON object ,则抛出错误. 令 offsetLine 为 JSONObjectGet (offset , "line" ). 令 offsetColumn 为 JSONObjectGet (offset , "column" ). 如果 offsetLine 不是整数 Number,则:选择性地报告错误 .将 offsetLine 设为 +0 𝔽 . 如果 offsetColumn 不是整数 Number,则:选择性地报告错误 .将 offsetColumn 设为 +0 𝔽 . 令 offsetPosition 为新的 Position Record { [[Line]] : offsetLine , [[Column]] : offsetColumn }. 如果 previousOffsetPosition ≠ null ,则:如果 ComparePositions (offsetPosition , previousOffsetPosition ) 为 lesser ,则选择性地报告错误 . 如果 previousLastMapping ≠ null ,则:如果 ComparePositions (offsetPosition , previousLastMapping .[[GeneratedPosition]] ) 为 lesser ,则选择性地报告错误 . 注:解码算法的这一部分检查索引源映射的 sections 字段 中的条目是否有序且不重叠。尽管生成器应避免生成包含重叠分区的索引源映射,但源映射使用方可以例如仅检查分区偏移量是否有序这一更简单的条件. 令 mapField 为 JSONObjectGet (section , "map" ). 如果 mapField 不是 JSON object ,则抛出错误. 令 decodedSectionCompletion 为 Completion (DecodeSourceMap (json , baseURL )). 如果 decodedSectionCompletion 是抛出完成,则:选择性地报告错误 . 否则:令 decodedSection 为 decodedSectionCompletion .[[Value]] . 对于 decodedSection .[[Sources]] 中的每个 Decoded Source Record additionalSource ,执行:如果 sourceMap .[[Sources]] 不包含 additionalSource ,则:将 additionalSource 追加到 sourceMap .[[Sources]] . 令 offsetMappings 为新的空 List . 对于 decodedSection .[[Mappings]] 中的每个 Decoded Mapping Record mapping ,执行:如果 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 .
Note
实现可以选择在不将映射追加在一起的情况下表示索引源映射的分区,例如分别存储每个分区并进行二分查找。
11 检索源映射
11.1 将生成代码链接到源映射
虽然源映射格式旨在与语言和平台无关,但对于 Web 服务器托管 JavaScript 这一预期用例,定义如何引用源映射仍然很有用。
可以通过两种方式将源映射链接到输出。第一种需要服务器支持,以便添加 HTTP 标头;第二种需要在源码中添加注解。
源映射通过 WHATWG URL 定义的 URL 进行链接;特别是,不属于 URI 中允许出现的字符集合的字符必须进行百分号编码,并且该 URL 可以是 data URI。将 data URI 与 sourcesContent 一起使用,可以实现完全自包含的源映射。
HTTP sourcemap 标头的优先级高于源码注解,如果两者都存在,则应使用标头中的 URL 来解析源映射文件的地址。
无论使用哪种方法检索源映射 URL ,都使用相同的过程来解析它,具体如下。
当源映射 URL 不是绝对 URL 时,它相对于生成代码 的源码来源 。源码来源 由以下情况之一确定:
11.1.2 通过行内注解链接
生成代码 应包含一个名为 sourceMappingURL 且包含源映射 URL 的注释,或依据其语言或格式采用等效构造。本规范定义了 JavaScript、CSS 和 WebAssembly 中此注释应采用的形式。其他语言应遵循类似约定。
对于给定语言,可以有多种检测 sourceMappingURL 注释的方法,以便不同实现选择对自身而言复杂度较低的方法。如果所有提取方法的结果都相同,则生成代码 无歧义地链接到源映射 。
如果工具使用一个或多个无歧义地链接到源映射 的源码文件,并生成链接到源映射的输出文件,则它必须无歧义地 进行此链接。
Note
以下 JavaScript 代码链接到源映射,但此链接并非无歧义的 :
let a = `
//# sourceMappingURL=foo.js.map
// `
通过解析 从中提取源映射 URL 会得到 foo.js.map,而不经解析 则会得到 null 。
令 module 为 module_decode (bytes ). 如果 module 是 WebAssembly 错误 ,则返回 null . 对于 module 中的每个自定义节 customSection ,执行:令 name 为 customSection 的 name. 如果 CodePointsToString (name ) 为 "sourceMappingURL" ,则:令 value 为 customSection 的 bytes. 返回 CodePointsToString (value ). 返回 null .
由于 WebAssembly 不是文本格式且不支持注释,因此它支持一种无歧义的提取方法。URL 编码为 WebAssembly 名称 ,并作为自定义节 的内容放置。生成 WebAssembly 代码的工具生成两个或更多名为 sourceMappingURL 的自定义节 是无效的。
11.2 获取源映射
11.2.1 FetchSourceMap ( url )
The abstract operation FetchSourceMap takes argument url (an URL ) and returns a Promise. It performs the following steps when called:
令 promiseCapability 为 NewPromiseCapability (%Promise% ). 令 request 为请求 URL 为 url 的新请求 . 令 processResponseConsumeBody 为新的 Abstract Closure ,其参数为 (response , bodyBytes ),捕获 promiseCapability 和 url ,并在被调用时执行以下步骤:如果 bodyBytes 为 null 或 failure ,则:执行 Call (promiseCapability .[[Reject]] , undefined , « 一个新的 TypeError »). 返回. 如果 url 的方案 是 HTTP(S) 方案 ,且字节序列 `)]}'` 是 bodyBytes 的字节序列前缀 ,则:当 bodyBytes 的字节序列长度 ≠ 0 且 bodyBytes [0] 不是 HTTP 换行字节 时,重复执行:从 bodyBytes 中移除第 0 个元素. 令 bodyString 为 Completion (对 bodyBytes 进行 UTF-8 解码 ). IfAbruptRejectPromise (bodyString , promiseCapability ).令 jsonValue 为 Completion (ParseJSON (bodyString )). IfAbruptRejectPromise (jsonValue , promiseCapability ).执行 Call (promiseCapability .[[Resolve]] , undefined , « jsonValue »). 获取 request ,并将 processResponseConsumeBody 设为 processResponseConsumeBody .返回 promiseCapability .[[Promise]] .
Note
由于历史原因,通过 HTTP(S) 传送源映射时,服务器可以在源映射前面添加以字符串 )]}' 开头的一行。
)] } 'garbage here
{ "version" : 3 , ...}
被解释为
{ "version" : 3 , ...}
12 源映射记录上的操作
解码源映射后,源映射使用方可以使用得到的 Decoded Source Map Records 来查找位置信息,用于调试或其他用例。本节描述源映射使用方可能支持的典型操作的行为。
GetOriginalPositions 操作可用于查询原始源码 中与生成代码 的某个位置对应的位置。例如,调试器可以利用它根据用户的鼠标点击,从生成代码 导航到原始源码 。
12.1 GetOriginalPositions ( sourceMapRecord , generatedPosition )
The abstract operation GetOriginalPositions takes arguments sourceMapRecord (a Decoded Source Map Record ) and generatedPosition (a Position Record ) and returns a List of Original Position Records . It performs the following steps when called:
令 mappings 为 sourceMapRecord .[[Mappings]] . 令 last 为 null . 令 originalPositions 为新的空 List . 按 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 .
Annex A (informative) 约定
使用或生成源映射时应遵循以下约定。
A.1 源映射命名
通常,源映射的名称与生成文件相同,但添加 .map 扩展名。例如,对于 page.js,会生成名为 page.js.map 的源映射。
A.2 将经 eval 求值的代码链接到命名的生成代码
将源映射用于经 eval 求值的代码时,应支持一项现有约定,其形式如下:
使用 //@ sourceURL 为你的 eval 命名 一文对此作了说明。
Annex B (informative) 注释
B.1 与语言无关的堆栈映射
本文档不涵盖 在不了解源码语言的情况下进行堆栈跟踪映射。
B.2 多级映射
使用工具从某种 DSL(模板)生成源码,或进行 TypeScript → JavaScript → 压缩后的 JavaScript 编译的情况越来越常见,这会导致在创建最终源映射之前进行多次转换。可以通过两种方式之一处理此问题。简单但有损的方式是,为了调试而忽略过程中的中间步骤:要么忽略转换中的源码位置信息(将中间转换结果视为“原始源码 ”),要么保留并传递源码位置信息(隐藏中间转换结果)。更完整的方式是支持多级映射:如果原始源码 也具有源映射引用,则让用户选择是否也使用它。
然而,在 JavaScript 之外,“源映射引用”应采用何种形式尚不明确。更具体地说,在不支持 JavaScript 风格单行注释的语言中,源映射引用应采用何种形式尚不明确。
Annex C (informative) 其他规范中定义的术语
本节列 出了本文档使用的、由 ECMA-262 以外的外部规范定义的所有术语和算法。
WebAssembly 核心规范 <https://www.w3.org/TR/wasm-core-2/ >
自定义节 ,
module_decode ,
WebAssembly 错误 ,
WebAssembly 名称
WHATWG 编码 <https://encoding.spec.whatwg.org/ >
UTF-8 解码
WHATWG 获取 <https://fetch.spec.whatwg.org/ >
获取 ,
HTTP 换行字节 ,
processResponseConsumeBody ,
请求 ,
请求 URL
WHATWG 基础设施 <https://infra.spec.whatwg.org/ >
字节序列 ,
字节序列前缀 ,
字节序列长度 ,
WHATWG URL <https://url.spec.whatwg.org/ >
HTTP(S) 方案 ,
方案 ,
URL ,
URL 解析
Annex D (informative) 参考书目
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 编码 , 可访问 <https://encoding.spec.whatwg.org/ >
WHATWG 获取 , 可访问 <https://fetch.spec.whatwg.org/ >
WHATWG 基础设施 , 可访问 <https://infra.spec.whatwg.org/ >
WHATWG URL , 可访问 <https://url.spec.whatwg.org/ >
使用 //@ sourceURL 为你的 eval 命名 , Firebug (2009), 可访问 <http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/ >
源映射第 2 版提案 , John Lenz (2010), 可访问 <https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/ >
变长数量 , 维基百科, 可访问 <https://en.wikipedia.org/wiki/Variable-length_quantity >
Annex E (informative) 编制说明
本规范在 GitHub 上使用名为 Ecmarkup 的纯文本源码格式编写。Ecmarkup 是 HTML 和 Markdown 的一种方言,提供框架和工具集,用于以纯文本编写 ECMAScript 规范,并将规范处理为遵循本文档编辑约定、功能完整的 HTML 呈现版本。Ecmarkup 基于并集成了多种其他格式和技术,包括用于定义语法的 Grammarkdown 和用于编写算法步骤的 Ecmarkdown 。本规范的 PDF 呈现版本通过将 HTML 呈现版本打印为 PDF 来生成。
本规范第一版使用 Bikeshed 编写,它是另一种基于 HTML 和 Markdown 的纯文本源码格式。
本文档在成为标准之前的版本使用 Google 文档编写。
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
ALTERNATIVE COPYRIGHT NOTICE AND COPYRIGHT LICENSE
© 2026 Ecma International
By obtaining and/or copying this work, you (the licensee) agree that you
have read, understood, and will comply with the following terms and
conditions.
Permission under Ecma’s copyright to copy, modify, prepare derivative works
of, and distribute this work, with or without modification, for any purpose
and without fee or royalty is hereby granted, provided that you include the
following on ALL copies of the work or portions thereof, including
modifications:
(i) The full text of this COPYRIGHT NOTICE AND COPYRIGHT LICENSE in a location
viewable to users of the redistributed or derivative work.
(ii) Any pre-existing intellectual property disclaimers, notices, or terms and
conditions. If none exist, the Ecma alternative copyright notice should be
included.
(iii) Notice of any changes or modifications, through a copyright statement on
the document such as “This document includes material copied from or
derived from 源映射格式规范 https://tc39.es/ecma426/.
Copyright © Ecma International.”
Disclaimers
THIS WORK IS PROVIDED “AS IS,” AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS
OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES
OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
THE DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
TRADEMARKS OR OTHER RIGHTS.
COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE DOCUMENT.
The name and trademarks of copyright holders may NOT be used in advertising
or publicity pertaining to the work without specific, written prior
permission. Title to copyright in this work will at all times remain with
copyright holders.
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.