Draft ECMA-426 / May 28, 2026

Source map format specification

关于本规范

https://tc39.es/ecma426/ 上的文档是最准确且最新的源映射规范。它包含最近发布的快照的内容,以及将被包含在下一个快照中的任何修改。

为本规范作贡献

本规范在 GitHub 上开发。可以通过多种方式为本规范的开发作贡献:

有关本文档如何创建的更多信息,请参见版权页说明

简介

本 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,第 1st 版,项目编辑

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 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/

4 记号约定

本规范遵循 ECMA-262(记号约定)中定义的相同记号约定,并带有本节中定义的扩展。

4.1 算法约定

4.1.1 隐式完成

本规范中声明的所有抽象操作都被隐式假定为要么返回一个包含算法声明返回类型的正常完成,要么返回一个抛出完成。例如,声明为如下形式的抽象操作

4.1.1.1 GetTheAnswer ( input )

The abstract operation GetTheAnswer takes argument input (整数) and returns 整数.

等价于:

4.1.1.2 GetTheAnswer2 ( input )

The abstract operation GetTheAnswer2 takes argument input (整数) and returns 包含整数的正常完成或抛出完成.

所有对返回完成记录的抽象操作的调用都被隐式假定为由 ECMA-262 的 ? 完成记录解包简写包裹,除非它们由显式的 Completion 调用包裹。例如:

  1. resultGetTheAnswer(value)。
  2. secondCompletion(GetTheAnswer(value))。

等价于:

  1. result 为 ? GetTheAnswer(value)。
  2. secondCompletion(GetTheAnswer(value))。

4.1.2 可选错误

每当算法要可选地报告错误时,实现可以选择以下行为之一:

  • 继续执行算法的其余部分。
  • 向用户报告错误(例如,在浏览器控制台中),并继续执行算法的其余部分。
  • 返回 ThrowCompletion

实现可以针对不同的可选错误选择不同的行为。

4.2 语法记号

本规范遵循 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 整数. It is defined piecewise over the following productions:

Vlq :: VlqDigitList
  1. unsignedVlqDigitListVLQUnsignedValue
  2. 如果 unsigned modulo 2 = 1,则令 sign 为 -1。
  3. 否则,令 sign 为 1。
  4. valuefloor(unsigned / 2)。
  5. 如果 value 是 0 且 sign 是 -1,则返回 -231
  6. 如果 value ≥ 231,则抛出错误。
  7. 返回 sign × value
Note
步骤 6 中的检查是必要的,因为 unsignedVlqDigitListVLQUnsignedValue,而不是 VlqVLQUnsignedValue

6.2 VLQUnsignedValue

The syntax-directed operation VLQUnsignedValue takes no arguments and returns 非负整数. It is defined piecewise over the following productions:

Vlq :: VlqDigitList
  1. valueVlqDigitListVLQUnsignedValue
  2. 如果 value ≥ 232,则抛出错误。
  3. 返回 value
VlqDigitList :: ContinuationDigit VlqDigitList
  1. leftContinuationDigitVLQUnsignedValue
  2. rightVlqDigitListVLQUnsignedValue
  3. 返回 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
  1. digit 为此产生式匹配的字符。
  2. value 为根据 IETF RFC 4648 定义的 base64 编码,与 digit 对应的整数。
  3. 断言:value < 32。
  4. 返回 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 + /
  1. digit 为此产生式匹配的字符。
  2. value 为根据 IETF RFC 4648 定义的 base64 编码,与 digit 对应的整数。
  3. 断言:32 ≤ value < 64。
  4. 返回 value - 32。

7 JSON 值实用工具

虽然本规范的算法是在 ECMA-262 内部机制之上定义的,但它旨在易于由非 JavaScript 平台实现。本节包含用于处理 JSON 值的实用工具,从文档其余部分中抽象掉 ECMA-262 细节。

JSON 值要么是 JSON 对象JSON 数组StringNumberBoolean,要么是 null

JSON 对象是满足其每个属性均符合以下条件的 Object:

JSON 数组是满足以下条件的 JSON 对象

7.1 ParseJSON ( string )

The abstract operation ParseJSON takes argument string (String) and returns JSON 值. It performs the following steps when called:

  1. resultCall(%JSON.parse%, null, « string »)。
  2. 断言:resultJSON 值
  3. 返回 result
Editor's Note
此抽象操作正在由 ECMA-262 本身暴露,见 tc39/ecma262#3540

7.2 JSONObjectGet ( object, key )

The abstract operation JSONObjectGet takes arguments object (JSON 对象) and key (String) and returns JSON 值missing. It performs the following steps when called:

  1. 如果 object 没有键为 key 的自有属性,则返回 missing
  2. propobject 的键为 key 的自有属性。
  3. 返回 prop[[Value]] 特性。

7.3 JSONArrayIterate ( array )

The abstract operation JSONArrayIterate takes argument array (JSON 数组) and returns JSON 值List. It performs the following steps when called:

  1. lengthJSONObjectGet(array, "length")。
  2. 断言:length 是非负整数 Number。
  3. list 为一个新的空 List
  4. i 为 0。
  5. 重复,当 i < (length) 时,
    1. valueJSONObjectGet(array, ToString(𝔽(i)))。
    2. 断言:value 不是 missing
    3. value 追加到 list
    4. i 设为 i + 1。
  6. 返回 list

7.4 StringSplit ( string, separators )

The abstract operation StringSplit takes arguments string (String) and separators (非空 Strings 的 List) and returns Strings 的 List. It performs the following steps when called:

  1. parts 为一个新的空 List
  2. strLenstring 的长度。
  3. lastStart 为 0。
  4. i 为 0。
  5. 重复,当 i < strLen 时,
    1. matchedfalse
    2. 对于 separators 的每个 String sep,执行
      1. sepLensep 的长度。
      2. candidatestringimin(i + sepLen, strLen) 的子字符串。
      3. 如果 candidate = sepmatchedfalse,则
        1. chunkstringlastStarti 的子字符串。
        2. chunk 追加到 parts
        3. lastStart 设为 i + sepLen
        4. i 设为 i + sepLen
        5. matched 设为 true
    3. 如果 matchedfalse,则将 i 设为 i + 1。
  6. chunkstringlastStartstrLen 的子字符串。
  7. chunk 追加到 parts
  8. 返回 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 字段
字段名 值类型
[[Source]] Decoded Source Record
[[Line]] 非负整数 Number
[[Column]] 非负整数 Number

8.3 ComparePositions ( first, second )

The abstract operation ComparePositions takes arguments first (Position RecordOriginal Position Record) and second (Position RecordOriginal Position Record) and returns lesserequalgreater. It performs the following steps when called:

  1. 如果 first.[[Line]] < second.[[Line]],则返回 lesser
  2. 如果 first.[[Line]] > second.[[Line]],则返回 greater
  3. 断言:first.[[Line]] 等于 second.[[Line]]
  4. 如果 first.[[Column]] < second.[[Column]],则返回 lesser
  5. 如果 first.[[Column]] > second.[[Column]],则返回 greater
  6. 返回 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]
}

9.1 解码源映射

Decoded Source Map Record 具有以下字段:

Table 3: Decoded Source Map Records 的字段
字段名 值类型
[[File]] String 或 null
[[Sources]] Decoded Source RecordsList
[[Mappings]] Decoded Mapping RecordsList

Decoded Source Record 具有以下字段:

Table 4: Decoded Source Records 的字段
字段名 值类型
[[URL]] URLnull
[[Content]] String 或 null
[[Ignored]] Boolean

9.1.1 ParseSourceMap ( string, baseURL )

The abstract operation ParseSourceMap takes arguments string (String) and baseURL (URL) and returns Decoded Source Map Record. It performs the following steps when called:

  1. jsonParseJSON(string)。
  2. 如果 json 不是 JSON 对象,则抛出错误。
  3. 如果 JSONObjectGet(json, "sections") 不是 missing,则
    1. 返回 DecodeIndexSourceMap(json, baseURL)。
  4. 返回 DecodeSourceMap(json, baseURL)。

9.1.2 DecodeSourceMap ( json, baseURL )

The abstract operation DecodeSourceMap takes arguments json (JSON 对象) and baseURL (URL) and returns Decoded Source Map Record. It performs the following steps when called:

  1. 如果 JSONObjectGet(json, "version") 不是 3𝔽,则可选地报告错误
  2. mappingsFieldJSONObjectGet(json, "mappings")。
  3. 如果 mappingsField 不是 String,则抛出错误。
  4. 如果 JSONObjectGet(json, "sources") 不是 JSON 数组,则抛出错误。
  5. fileFieldGetOptionalString(json, "file")。
  6. sourceRootFieldGetOptionalString(json, "sourceRoot")。
  7. sourcesFieldGetOptionalListOfOptionalStrings(json, "sources")。
  8. sourcesContentFieldGetOptionalListOfOptionalStrings(json, "sourcesContent")。
  9. ignoreListFieldGetOptionalListOfArrayIndexes(json, "ignoreList")。
  10. sourcesDecodeSourceMapSources(baseURL, sourceRootField, sourcesField, sourcesContentField, ignoreListField)。
  11. namesFieldGetOptionalListOfStrings(json, "names")。
  12. mappingsDecodeMappings(mappingsField, namesField, sources)。
  13. 按升序对 mappings 排序,其中如果 ComparePositions(a.[[GeneratedPosition]], b.[[GeneratedPosition]]) 是 lesser,则 Decoded Mapping Record a 小于 Decoded Mapping Record b
  14. 返回 Decoded Source Map Record { [[File]]: fileField, [[Sources]]: sources, [[Mappings]]: mappings }。

9.1.2.1 GetOptionalString ( object, key )

The abstract operation GetOptionalString takes arguments object (JSON 对象) and key (String) and returns String 或 null. It performs the following steps when called:

  1. valueJSONObjectGet(object, key)。
  2. 如果 value 是 String,则返回 value
  3. 如果 value 不是 missing,则可选地报告错误
  4. 返回 null

9.1.2.2 GetOptionalListOfStrings ( object, key )

The abstract operation GetOptionalListOfStrings takes arguments object (JSON 对象) and key (String) and returns Strings 的 List. It performs the following steps when called:

  1. list 为一个新的空 List
  2. valuesJSONObjectGet(object, key)。
  3. 如果 valuesmissing,则返回 list
  4. 如果 values 不是 JSON 数组,则
    1. 可选地报告错误
    2. 返回 list
  5. 对于 JSONArrayIterate(values) 的每个元素 item,执行
    1. 如果 item 是 String,则
      1. item 追加到 list
    2. 否则,
      1. 可选地报告错误
      2. "" 追加到 list
  6. 返回 list

9.1.2.3 GetOptionalListOfOptionalStrings ( object, key )

The abstract operation GetOptionalListOfOptionalStrings takes arguments object (JSON 对象) and key (String) and returns Strings 或 nullList. It performs the following steps when called:

  1. list 为一个新的空 List
  2. valuesJSONObjectGet(object, key)。
  3. 如果 valuesmissing,则返回 list
  4. 如果 values 不是 JSON 数组,则
    1. 可选地报告错误
    2. 返回 list
  5. 对于 JSONArrayIterate(values) 的每个元素 item,执行
    1. 如果 item 是 String,则
      1. item 追加到 list
    2. 否则,
      1. 如果 itemnull,则可选地报告错误
      2. null 追加到 list
  6. 返回 list

9.1.2.4 GetOptionalListOfArrayIndexes ( object, key )

The abstract operation GetOptionalListOfArrayIndexes takes arguments object (Object) and key (String) and returns 非负整数的 List. It performs the following steps when called:

  1. list 为一个新的空 List
  2. valuesJSONObjectGet(object, key)。
  3. 如果 valuesmissing,则返回 list
  4. 如果 values 不是 JSON 数组,则
    1. 可选地报告错误
    2. 返回 list
  5. 对于 JSONArrayIterate(values) 的每个元素 item,执行
    1. 如果 item 是整数 Number,且 item+0𝔽,则
      1. (item) 追加到 list
    2. 否则,
      1. 可选地报告错误
  6. 返回 list

9.2 Mappings 结构

mappings 字段数据按如下方式分解:

  • 表示生成文件中一行的每个组由分号(;)分隔
  • 每个段由逗号(,)分隔
  • 每个段由 1 个、4 个或 5 个可变长度字段组成。

每个段中的字段是:

  1. 该段所表示的生成代码行中的从零开始的起始。如果这是第一个段的第一个字段,或跟在新生成行(;)之后的第一个段,则此字段保存整个 base64 VLQ。否则,此字段包含一个相对于该字段上一次出现的 base64 VLQ请注意,这不同于下面的后续字段,因为前一个值会在每个生成行之后重置。
  2. 如果存在,则为 sources 表中的从零开始的索引。此字段包含一个相对于该字段上一次出现的 base64 VLQ,除非它是该字段的第一次出现,在这种情况下表示的是完整值。
  3. 如果存在,则为原始源中的从零开始的起始行。此字段包含一个相对于该字段上一次出现的 base64 VLQ,除非它是该字段的第一次出现,在这种情况下表示的是完整值。如果存在 source 字段,则应存在该字段。
  4. 如果存在,则为原始源中该行的从零开始的起始。此字段包含一个相对于该字段上一次出现的 base64 VLQ,除非它是该字段的第一次出现,在这种情况下表示的是完整值。如果存在 source 字段,则应存在该字段。
  5. 如果存在,则为与此段关联的 names 表中的从零开始的索引。此字段包含一个相对于该字段上一次出现的 base64 VLQ,除非它是该字段的第一次出现,在这种情况下表示的是完整值。
Note 1
此编码的目的是减小源映射大小。在使用 Google Calendar 进行的测试中,相对于 Source Map Revision 2 Proposal,VLQ 编码使源映射减少了 50%。
Note 2
具有一个字段的段旨在表示没有对应原始源代码的未映射生成代码,例如由编译器生成的代码。具有四个字段的段表示不存在对应名称的已映射代码。具有五个字段的段表示还具有映射名称的已映射代码。
Note 3
曾考虑使用 file 偏移,但最终拒绝,转而使用行/数据,以避免由于平台特定的行尾而与原始内容失去对齐。

Decoded Mapping Record 具有以下字段:

Table 5: Decoded Mapping Records 的字段
字段名 值类型
[[GeneratedPosition]] Position Record
[[OriginalPosition]] Original Position Recordnull
[[Name]] String 或 null

9.2.1 Mappings 语法

mappings String 必须遵循以下语法:

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

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 (Decode Mapping State Record), mappings (Decoded Mapping RecordsList), names (Strings 的 List), and sources (Decoded Source RecordsList). It is defined piecewise over the following productions:

LineList : Line ; LineList
  1. 使用参数 statemappingsnamessources,执行 LineDecodeMappingsField
  2. state.[[GeneratedLine]] 设为 state.[[GeneratedLine]] + 1。
  3. state.[[GeneratedColumn]] 设为 0。
  4. 使用参数 statemappingsnamessources,执行 LineListDecodeMappingsField
Line : [empty]
  1. 返回。
MappingList : Mapping , MappingList
  1. 使用参数 statemappingsnamessources,执行 MappingDecodeMappingsField
  2. 使用参数 statemappingsnamessources,执行 MappingListDecodeMappingsField
Mapping : GeneratedColumn
  1. 使用参数 statemappingsnamessources,执行 GeneratedColumnDecodeMappingsField
  2. 如果 state.[[GeneratedColumn]] < 0,则
    1. 可选地报告错误
    2. 返回。
  3. position 为新的 Position Record { [[Line]]: state.[[GeneratedLine]], [[Column]]: state.[[GeneratedColumn]] }。
  4. decodedMapping 为新的 DecodedMappingRecord { [[GeneratedPosition]]: position, [[OriginalPosition]]: null, [[Name]]: null }。
  5. decodedMapping 追加到 mappings
Mapping : GeneratedColumn OriginalSource OriginalLine OriginalColumn Nameopt
  1. 使用参数 statemappingsnamessources,执行 GeneratedColumnDecodeMappingsField
  2. 如果 state.[[GeneratedColumn]] < 0,则
    1. 可选地报告错误
    2. 返回。
  3. generatedPosition 为新的 Position Record { [[Line]]: state.[[GeneratedLine]], [[Column]]: state.[[GeneratedColumn]] }。
  4. 使用参数 statemappingsnamessources,执行 OriginalSourceDecodeMappingsField
  5. 使用参数 statemappingsnamessources,执行 OriginalLineDecodeMappingsField
  6. 使用参数 statemappingsnamessources,执行 OriginalColumnDecodeMappingsField
  7. 如果 state.[[SourceIndex]] < 0,或 state.[[SourceIndex]]sources 的元素数量,或 state.[[OriginalLine]] < 0,或 state.[[OriginalColumn]] < 0,则
    1. 可选地报告错误
    2. originalPositionnull
  8. 否则,
    1. originalPosition 为新的 Original Position Record { [[Source]]: sources[state.[[SourceIndex]]], [[Line]]: state.[[OriginalLine]], [[Column]]: state.[[OriginalColumn]] }。
  9. namenull
  10. 如果 Name 存在,则
    1. 使用参数 statemappingsnamessources,执行 NameDecodeMappingsField
    2. 如果 state.[[NameIndex]] < 0,或 state.[[NameIndex]]names 的元素数量,则可选地报告错误
    3. 否则,将 name 设为 names[state.[[NameIndex]]]。
  11. decodedMapping 为新的 DecodedMappingRecord { [[GeneratedPosition]]: generatedPosition, [[OriginalPosition]]: originalPosition, [[Name]]: name }。
  12. decodedMapping 追加到 mappings
GeneratedColumn : Vlq
  1. relativeColumnVlqVLQSignedValue
  2. state.[[GeneratedColumn]] 设为 state.[[GeneratedColumn]] + relativeColumn
OriginalSource : Vlq
  1. relativeSourceIndexVlqVLQSignedValue
  2. state.[[SourceIndex]] 设为 state.[[SourceIndex]] + relativeSourceIndex
OriginalLine : Vlq
  1. relativeLineVlqVLQSignedValue
  2. state.[[OriginalLine]] 设为 state.[[OriginalLine]] + relativeLine
OriginalColumn : Vlq
  1. relativeColumnVlqVLQSignedValue
  2. state.[[OriginalColumn]] 设为 state.[[OriginalColumn]] + relativeColumn
Name : Vlq
  1. relativeNameVlqVLQSignedValue
  2. state.[[NameIndex]] 设为 state.[[NameIndex]] + relativeName

9.2.2 DecodeMappings ( rawMappings, names, sources )

The abstract operation DecodeMappings takes arguments rawMappings (String), names (Strings 的 List), and sources (Decoded Source RecordsList) and returns Decoded Mapping RecordList. It performs the following steps when called:

  1. mappings 为一个新的空 List
  2. mappingsNode 为使用 MappingsField 作为目标符号解析 rawMappings 时得到的根 Parse Node
  3. 如果解析失败,则
    1. 可选地报告错误
    2. 返回 mappings
  4. state 为一个新的 Decode Mapping State Record,其所有字段均设为 0。
  5. 使用参数 statemappingsnamessources,执行 mappingsNodeDecodeMappingsField
  6. 返回 mappings

9.2.3 生成的 JavaScript 代码的映射

可以具有映射条目的生成代码位置,是按照 ECMAScript 词法语法中的输入元素来定义的。映射条目应指向:

9.2.4 生成的 JavaScript 代码的名称

如果满足以下条件,源映射生成器应该为 JavaScript token 创建一个具有 [[Name]] 字段的映射条目:

  • 原始源语言构造在语义上映射到生成的 JavaScript 代码。
  • 原始源语言构造具有名称。

那么该映射条目的 [[Name]] 应该是原始源语言构造的名称。具有非空 [[Name]]映射称为命名映射

Note 1
缩小器重命名函数和变量,或从立即调用函数表达式中移除函数名称。

以下枚举出了 ECMAScript 句法语法的产生式,以及源映射生成器应该为其发出命名映射的相应 token 或非终结符(产生式右侧)。为此类 token 创建的映射条目应遵循 9.2.3 节。

该枚举应该被理解为“最低要求”。一般而言,源映射生成器可以自由发出任何额外的命名映射。

Note 2
该枚举还出了生成器“可以”发出命名映射的 token,以及它们“应该”发出命名映射的 token。这些反映了现有工具发出或期望命名映射的现实情况。重复的命名映射成本相对较低:names 的索引是相对于彼此编码的,因此到同一名称的后续映射会编码为 0(A)。

9.3 解析源

如果在前置 sourceRoot 之后 sources 不是绝对 URL,则 sources 会相对于源映射进行解析(类似于解析 HTML 文档中的 script src 属性)。

9.3.1 DecodeSourceMapSources ( baseURL, sourceRoot, sources, sourcesContent, ignoreList )

The abstract operation DecodeSourceMapSources takes arguments baseURL (URL), sourceRoot (String 或 null), sources (Strings 或 nullList), sourcesContent (Strings 或 nullList), and ignoreList (非负整数的 List) and returns Decoded Source RecordList. It performs the following steps when called:

  1. decodedSources 为一个新的空 List
  2. sourcesContentCountsourcesContent 中的元素数量。
  3. sourceUrlPrefix""
  4. 如果 sourceRootnull,则
    1. 如果 sourceRoot 以码点 U+002F (SOLIDUS) 结尾,则
      1. sourceUrlPrefix 设为 sourceRoot
    2. 否则,
      1. sourceUrlPrefix 设为 sourceRoot"/" 的字符串拼接。
  5. index 为 0。
  6. 重复,当 index < sources 的长度时,
    1. sourcesources[index]。
    2. decodedSourceDecoded Source Record { [[URL]]: null, [[Content]]: null, [[Ignored]]: false }。
    3. 如果 sourcenull,则
      1. source 设为 sourceUrlPrefixsource 的字符串拼接。
      2. sourceURL 为使用 baseURLsource 进行 URL parsing 的结果。
      3. 如果 sourceURLfailure,则可选地报告错误
      4. 否则,将 decodedSource.[[URL]] 设为 sourceURL
    4. 如果 ignoreList 包含 index,则将 decodedSource.[[Ignored]] 设为 true
    5. 如果 sourcesContentCount > index,则将 decodedSource.[[Content]] 设为 sourcesContent[index]。
    6. decodedSource 追加到 decodedSources
    7. index 设为 index + 1。
  7. 返回 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 字段是一个对象数组,其对象具有以下字段:

sections 应按起始位置排序,并且所表示的 sections 不应重叠。

10.1 DecodeIndexSourceMap ( json, baseURL )

The abstract operation DecodeIndexSourceMap takes arguments json (Object) and baseURL (URL) and returns Decoded Source Map Record. It performs the following steps when called:

  1. sectionsFieldJSONObjectGet(json, "sections")。
  2. 断言:sectionsField 不是 missing
  3. 如果 sectionsField 不是 JSON 数组,则抛出错误。
  4. 如果 JSONObjectGet(json, "version") 不是 3𝔽,则可选地报告错误
  5. fileFieldGetOptionalString(json, "file")。
  6. sourceMapDecoded Source Map Record { [[File]]: fileField, [[Sources]]: « », [[Mappings]]: « » }。
  7. previousOffsetPositionnull
  8. previousLastMappingnull
  9. 对于 JSONArrayIterate(sectionsField) 的每个 JSON 值 section,执行
    1. 如果 section 不是 JSON 对象,则
      1. 可选地报告错误
    2. 否则,
      1. offsetJSONObjectGet(section, "offset")。
      2. 如果 offset 不是 JSON 对象,则抛出错误。
      3. offsetLineJSONObjectGet(offset, "line")。
      4. offsetColumnJSONObjectGet(offset, "column")。
      5. 如果 offsetLine 不是整数 Number,则
        1. 可选地报告错误
        2. offsetLine 设为 +0𝔽
      6. 如果 offsetColumn 不是整数 Number,则
        1. 可选地报告错误
        2. offsetColumn 设为 +0𝔽
      7. offsetPosition 为新的 Position Record { [[Line]]: offsetLine, [[Column]]: offsetColumn }。
      8. 如果 previousOffsetPositionnull,则
        1. 如果 ComparePositions(offsetPosition, previousOffsetPosition) 是 lesser,则可选地报告错误
      9. 如果 previousLastMappingnull,则
        1. 如果 ComparePositions(offsetPosition, previousLastMapping.[[GeneratedPosition]]) 是 lesser,则可选地报告错误
        2. 注:解码算法的这一部分检查索引源映射的 sections 字段条目是否有序且不重叠。虽然期望生成器不应生成具有重叠 sections 的索引源映射,但源映射消费者例如可以只检查更简单的条件,即 section 偏移是有序的。
      10. mapFieldJSONObjectGet(section, "map")。
      11. 如果 mapField 不是 JSON 对象,则抛出错误。
      12. decodedSectionCompletionCompletion(DecodeSourceMap(json, baseURL))。
      13. 如果 decodedSectionCompletion 是抛出完成,则
        1. 可选地报告错误
      14. 否则,
        1. decodedSectiondecodedSectionCompletion.[[Value]]
        2. 对于 decodedSection.[[Sources]] 的每个 Decoded Source Record additionalSource,执行
          1. 如果 sourceMap.[[Sources]] 不包含 additionalSource,则
            1. additionalSource 追加到 sourceMap.[[Sources]]
        3. offsetMappings 为一个新的空 List
        4. 对于 decodedSection.[[Mappings]] 的每个 Decoded Mapping Record mapping,执行
          1. 如果 mapping.[[GeneratedPosition]].[[Line]] = 0,则
            1. mapping.[[GeneratedPosition]].[[Column]] 设为 mapping.[[GeneratedPosition]].[[Column]] + offsetColumn
          2. mapping.[[GeneratedPosition]].[[Line]] 设为 mapping.[[GeneratedPosition]].[[Line]] + offsetLine
          3. mapping 追加到 offsetMappings
        5. sourceMap.[[Mappings]] 设为 sourceMap.[[Mappings]]offsetMappings表拼接。
        6. previousOffsetPosition 设为 offsetPosition
        7. 如果 offsetMappings 非空,则将 previousLastMapping 设为 offsetMappings 的最后一个元素。
    3. 返回 sourceMap
Note
实现可以选择表示索引源映射 sections 而不把 mappings 追加在一起,例如通过单独存储每个 section 并执行二分搜索。

11 获取源映射

11.1 将生成代码链接到源映射

虽然源映射格式旨在与语言和平台无关,但为 Web 服务器托管的 JavaScript 这一预期用例定义如何引用它们是有用的。

有两种可能的方法将源映射链接到输出。第一种需要服务器支持以添加 HTTP 标头,第二种需要源中的注释。

源映射通过 WHATWG URL 中定义的 URL 进行链接;特别是,不属于允许出现在 URI 中的字符集的字符应被百分比编码,并且它可以是 data URI。将 data URI 与 sourcesContent 结合使用允许完全自包含的源映射。

HTTP sourcemap 标头优先于源注释,如果两者都存在,则应该使用标头 URL 来解析源映射文件。

无论使用哪种方法获取源映射 URL,都会使用相同的过程来解析它,过程如下。

源映射 URL 不是绝对 URL 时,则它相对于生成代码源起点源起点由以下情况之一确定:

  • 如果生成源未关联到具有 src 属性的 script 元素,并且生成代码中存在 //# sourceURL 注释,则应该使用该注释来确定源起点

    Note
    以前,这是 //@ sourceURL,与 //@ sourceMappingURL 一样,接受两者是合理的,但首选 //#
  • 如果生成代码关联到 script 元素,并且 script 元素具有 src 属性,则 script 元素的 src 属性将是源起点
  • 如果生成代码关联到 script 元素,并且 script 元素没有 src 属性,则源起点将是页面的起点。
  • 如果生成代码作为字符串通过 eval() 函数或通过 new Function() 求值,则源起点将是页面的起点。

11.1.1 通过 HTTP 标头链接

如果文件通过 HTTP(S) 提供并带有 sourcemap 标头,则该标头的值是被链接源映射的 URL

sourcemap: <url>
Note
本文档以前的修订版推荐使用 x-sourcemap 作为标头名称。现在已弃用;现在预期使用 sourcemap

11.1.2 通过内联注释链接

生成代码应该包含一个名为 sourceMappingURL 的注释,或根据其语言或格式的等效构造,并且该注释包含源映射的 URL。本规范定义了该注释对于 JavaScript、CSS 和 WebAssembly 应该是什么样子。其他语言应该遵循类似约定。

对于给定语言,可能有多种检测 sourceMappingURL 注释的方法,以允许不同实现选择对它们来说较不复杂的方法。如果所有提取方法的结果相同,则生成代码无歧义地链接到源映射

如果工具消费一个或多个无歧义地链接到源映射的源文件,并产生一个链接到源映射的输出文件,则它应当无歧义地这样做。

Note

以下 JavaScript 代码链接到源映射,但并非无歧义地这样做:

let a = `
//# sourceMappingURL=foo.js.map
// `

通过解析从中提取源映射 URL 会得到 foo.js.map,而不通过解析则会得到 null

11.1.2.1 JavaScriptExtractSourceMapURL ( source )

The abstract operation JavaScriptExtractSourceMapURL takes argument source (String) and returns String 或 null.

通过解析提取源映射 URL

  1. tokens 为根据 ECMA-262 的词法语法解析 source 而得到的输入元素List
  2. tokens 中的每个非终结符 token,按反向顺序执行
    1. 如果 token 不是 SingleLineCommentWhiteSpaceLineTerminatorSequence,则返回 null
    2. commenttoken 的内容。
    3. sourceMapURLMatchSourceMapURL(comment)。
    4. 如果 sourceMapURL 是 String,则返回 sourceMapURL
  3. 返回 null

不通过解析提取源映射 URL

  1. linesStringSplit(source, « "\u000D\u000A", "\u000A", "\u000D", "\u2028", "\u2029" »)。
  2. 注:上面的字符串表匹配 LineTerminatorSequence 产生式。
  3. lines 中的每个 String lineStr,按反向 List 顺序执行
    1. lineStringToCodePoints(lineStr)。
    2. position 为 0。
    3. lineLengthline 的长度。
    4. 重复,当 position < lineLength 时,
      1. firstline[position]。
      2. 如果 first 是 U+002F (SOLIDUS) 且 position + 1 < lineLength,则
        1. position 设为 position + 1。
        2. secondline[position]。
        3. 如果 second 是 U+002F (SOLIDUS),则
          1. position 设为 position + 1。
          2. commentlineStrpositionlineLength 的子字符串。
          3. 如果 comment 包含码点 U+0022 (QUOTATION MARK)、U+0027 (APOSTROPHE) 或 U+0060 (GRAVE ACCENT) 之一,则
            1. 返回 null
          4. 如果 comment 包含码点 U+002A (ASTERISK) 后紧跟码点 U+002F (SOLIDUS),则
            1. 返回 null
          5. sourceMapURLMatchSourceMapURL(comment)。
          6. 如果 sourceMapURL 是 String,则返回 sourceMapURL
          7. position 设为 lineLength
        4. 否则,
          1. 返回 null
      3. 否则,如果 first 是 ECMAScript WhiteSpace,则
        1. position 设为 position + 1。
      4. 否则,
        1. 返回 null
  4. 返回 null
Note

如果 source 可以无错误解析,并且不通过解析从中提取源映射 URL 得到非 null 结果,则通过解析提取它将得到相同结果。

11.1.2.1.1 MatchSourceMapURL ( comment )

The abstract operation MatchSourceMapURL takes argument comment (String) and returns none 或 String. It performs the following steps when called:

  1. patternRegExpCreate("^[@#]\s*sourceMappingURL=(\S*?)\s*$", "")。
  2. matchRegExpExec(pattern, comment)。
  3. 如果 match 不是 null,则返回 Get(match, "1")。
  4. 返回 none
Note
此注释的前缀最初是 //@,然而这与 Internet Explorer 的条件编译冲突,因此改为 //#

源映射生成器应只发出 //#,而源映射消费者应接受 //@//# 两者。

11.1.2.2 CSSExtractSourceMapURL ( source )

The abstract operation CSSExtractSourceMapURL takes argument source (String) and returns String 或 null.

从 CSS 中提取源映射 URL与 JavaScript 类似,例外是 CSS 只支持 /* ... */ 风格的注释。

11.1.2.3 WebAssemblyExtractSourceMapURL ( bytes )

The abstract operation WebAssemblyExtractSourceMapURL takes argument bytes (Data Block) and returns String 或 null.

  1. modulemodule_decode(bytes)。
  2. 如果 moduleWebAssembly error,则返回 null
  3. module 的每个 custom section customSection,执行
    1. namecustomSectionname
    2. 如果 CodePointsToString(name) 是 "sourceMappingURL",则
      1. valuecustomSectionbytes
      2. 返回 CodePointsToString(value)。
  4. 返回 null

由于 WebAssembly 不是文本格式且不支持注释,因此它支持单一的无歧义提取方法。URL 被编码为 WebAssembly 名称,并放置为自定义节的内容。对于生成 WebAssembly 代码的工具来说,生成两个或更多具有 sourceMappingURL 名称的自定义节是无效的。

11.2 获取源映射

11.2.1 FetchSourceMap ( url )

The abstract operation FetchSourceMap takes argument url (URL) and returns Promise. It performs the following steps when called:

  1. promiseCapabilityNewPromiseCapability(%Promise%)。
  2. request 为一个新的 request,其 request URLurl
  3. processResponseConsumeBody 为一个新的 Abstract Closure,其参数为 (response, bodyBytes),捕获 promiseCapabilityurl,并在被调用时执行以下步骤:
    1. 如果 bodyBytesnullfailure,则
      1. 执行 Call(promiseCapability.[[Reject]], undefined, « 新的 TypeError »)。
      2. 返回。
    2. 如果 urlschemeHTTP(S) scheme,且字节序 `)]}'` 是 bodyBytesbyte-sequence-prefix,则
      1. 重复,当 bodyBytesbyte-sequence-length ≠ 0 且 bodyBytes[0] 不是 HTTP newline byte 时,
        1. bodyBytes 中移除第 0 个元素。
    3. bodyStringUTF-8 decode of bodyBytesCompletion
    4. IfAbruptRejectPromise(bodyString, promiseCapability)。
    5. jsonValueCompletion(ParseJSON(bodyString))。
    6. IfAbruptRejectPromise(jsonValue, promiseCapability)。
    7. 执行 Call(promiseCapability.[[Resolve]], undefined, « jsonValue »)。
  4. 使用 processResponseConsumeBody 设为 processResponseConsumeBody 来执行 fetch request
  5. 返回 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 (Decoded Source Map Record) and generatedPosition (Position Record) and returns Original Position RecordsList. It performs the following steps when called:

  1. mappingssourceMapRecord.[[Mappings]]
  2. lastnull
  3. originalPositions 为一个新的空 List
  4. mappings 的每个元素 mapping,按反向 List 顺序执行
    1. 如果 lastnull,则
      1. 如果执行 ComparePositions(mapping.[[GeneratedPosition]], generatedPosition) 的结果是 lesserequal,则
        1. last 设为 mapping
  5. 如果 last 不是 null,则
    1. mappings 的每个元素 mapping,执行
      1. 如果执行 ComparePositions(last.[[GeneratedPosition]], mapping.[[GeneratedPosition]]) 的结果是 equal,则
        1. mapping.[[OriginalPosition]] 追加到 originalPositions
  6. 返回 originalPositions

Annex A (informative) 约定

在处理源映射或生成源映射时,应遵循以下约定。

A.1 源映射命名

通常,源映射将具有与生成文件相同的名称,但带有 .map 扩展名。例如,对于 page.js,会生成名为 page.js.map 的源映射。

A.2 将 eval'd 代码链接到命名的生成代码

对于将源映射与 eval'd 代码一起使用,有一个应被支持的现有约定,其形式如下:

//# sourceURL=foo.js

它在 Give your eval a name with //@ sourceURL 中描述。

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/>
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

Annex D (informative) 参考文献

  1. IETF RFC 4648,Base16、Base32 和 Base64 数据编码,可访问 <https://datatracker.ietf.org/doc/html/rfc4648>
  2. ECMA-262,ECMAScript® 语言规范,可访问 <https://tc39.es/ecma262/>
  3. ECMA-404,JSON 数据交换格式,可访问 <https://www.ecma-international.org/publications-and-standards/standards/ecma-404/>
  4. WebAssembly 核心规范,可访问 <https://www.w3.org/TR/wasm-core-2/>
  5. WHATWG Encoding,可访问 <https://encoding.spec.whatwg.org/>
  6. WHATWG Fetch,可访问 <https://fetch.spec.whatwg.org/>
  7. WHATWG Infra,可访问 <https://infra.spec.whatwg.org/>
  8. WHATWG URL,可访问 <https://url.spec.whatwg.org/>
  9. Give your eval a name with //@ sourceURL,Firebug (2009),可访问 <http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/>
  10. Source Map Revision 2 Proposal,John Lenz (2010),可访问 <https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/>
  11. 可变长度数量,Wikipedia,可访问 <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 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/

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:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. 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.
  3. 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.