├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── index.html
├── package-lock.json
├── package.json
└── spec.emu
/.gitattributes:
--------------------------------------------------------------------------------
1 | index.html -diff merge=ours
2 | spec.js -diff merge=ours
3 | spec.css -diff merge=ours
4 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Deploy spec
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v2
11 | - uses: actions/setup-node@v1
12 | with:
13 | node-version: '12.x'
14 | - run: npm install
15 | - run: npm run build
16 | - name: commit changes
17 | uses: elstudio/actions-js-build/commit@v3
18 | with:
19 | commitMessage: "fixup: [spec] `npm run build`"
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # lockfile not ignored: https://classic.yarnpkg.com/blog/2016/11/24/lockfiles-for-all/
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 ECMA TC39 and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # String dedent
2 |
3 | Champions: [@jridgewell](https://github.com/jridgewell), [@hemanth](https://github.com/hemanth)
4 |
5 | Author: [@mmkal](https://github.com/mmkal)
6 |
7 | Status: [Stage 2](https://tc39.es/process-document/)
8 |
9 | ## Problem
10 |
11 | When trying to embed formatted text (for instance, Markdown contents, or the
12 | source text of a JS program) in JS code, developers are forced to make awkward
13 | concessions for readability of the code or output. For instance, to make the
14 | embedded text look consistent with the surrounding code, we'd write:
15 |
16 | ```javascript
17 | class MyClass {
18 | print() {
19 | console.log(`
20 | create table student(
21 | id int primary key,
22 | name text
23 | )
24 | `);
25 | }
26 | }
27 | ```
28 |
29 | This outputs (using `^` to mark the beginning of a line and `·` to mark a leading space):
30 |
31 | ```sql
32 | ^
33 | ^······create table student(
34 | ^········id int primary key,
35 | ^········name text
36 | ^······)
37 | ^····
38 | ```
39 |
40 | In order to for the output to look sensible, our code becomes illegible:
41 |
42 | ```javascript
43 | class MyClass {
44 | print() {
45 | console.log(`create table student(
46 | id int primary key,
47 | name text
48 | )`);
49 | }
50 | }
51 | ```
52 |
53 | This outputs a sensible:
54 |
55 | ```sql
56 | create table student(
57 | id int primary key,
58 | name text
59 | )
60 | ```
61 |
62 | ### With a library
63 |
64 | It's possible to write sensible code and have sensible output with the help of
65 | [libraries](https://www.npmjs.com/search?ranking=popularity&q=dedent).
66 |
67 | ```javascript
68 | import dedent from 'dedent'
69 |
70 | class MyClass {
71 | print() {
72 | console.log(dedent`
73 | create table student(
74 | id int primary key,
75 | name text
76 | )
77 | `);
78 | }
79 | }
80 | ```
81 |
82 | This outputs the sensible:
83 |
84 | ```sql
85 | create table student(
86 | id int primary key,
87 | name text
88 | )
89 | ```
90 |
91 | However, these libraries incur a runtime cost, and are subtly inconsistent with
92 | the way they perform "dedenting". The most popular package is stagnant without
93 | bug fixes and has problematic interpreting of the Template Object's `.raw`
94 | array, and none are able to pass the dedented text to tag template functions.
95 |
96 | ```javascript
97 | pythonInterpreter`
98 | print('Hello Python World')
99 | `; // IndentationError: unexpected indent
100 |
101 | const dedented = dedent`
102 | print('Hello Python World')
103 | `;
104 |
105 | pythonInterpreter`${dedented}`; // <- this doesn't work right.
106 | ```
107 |
108 | Additionally, even if a userland library were to support passing to tagged
109 | templates, the array would not be a true Template Object in proposals like
110 | [`Array.isTemplateObject`](https://github.com/tc39/proposal-array-is-template-object).
111 | This harms the ability of tagged templates functions to differentiate dedented
112 | templates that exist in the actual program source text (and ascribe a higher
113 | trust level to) vs a dynamically generated string (which may contain a user
114 | generated exploit string).
115 |
116 | ## Proposed solution
117 |
118 | Implement a `String.dedent` tag template function, for a tagged template
119 | literal behaving almost the same as a regular single backticked template
120 | literal, with a few key differences:
121 |
122 | - The opening line (everything immediately right of the opening `` ` ``) must
123 | contain only a literal newline char.
124 | - The opening line's literal newline is removed.
125 | - The closing line (everything immediately to the left of the closing `` ` ``)
126 | may contain whitespace, but the whitespace is removed.
127 | - The closing line's preceding literal newline char is removed.
128 | - Lines which only contain whitespace are emptied.
129 | - The "common indentation" of all non-empty content lines (lines that are not the
130 | opening or closing) are calculated.
131 | - That common indentation is removed from the start of every line.
132 |
133 | Play around with a [REPL](https://output.jsbin.com/wiwovot/quiet) implementation.
134 |
135 | The examples above would be solved like this:
136 |
137 | ```javascript
138 | class MyClass {
139 | print() {
140 | console.log(String.dedent`
141 | create table student(
142 | id int primary key,
143 | name text
144 | )
145 | `);
146 | }
147 | }
148 | ```
149 |
150 | This outputs the sensible:
151 |
152 | ```sql
153 | create table student(
154 | id int primary key,
155 | name text
156 | )
157 | ```
158 |
159 | Expressions can be directly supported, as well as composition with
160 | another tagged template function:
161 |
162 | ```javascript
163 | const message = 'Hello Python World';
164 | String.dedent(pythonInterpreter)`
165 | print('${message}')
166 | `;
167 | ```
168 |
169 | ## In other languages
170 |
171 | - *CoffeeScript* - [block strings](https://coffeescript.org/#strings) using `'''` and `"""` triple-quotes.
172 | - *Java* - [text blocks](https://openjdk.java.net/jeps/378) using triple-quotes.
173 | - *Kotlin* - [raw strings](https://kotlinlang.org/docs/strings.html#raw-strings) using triple-quotes and `.trimIndent()`.
174 | - *Scala* - [multiline strings](https://docs.scala-lang.org/overviews/scala-book/two-notes-about-strings.html)
175 | using triple-quotes and `.stripMargin`.
176 | - *Python* - [multiline strings](https://docs.python.org/3/library/textwrap.html) using triple-quotes
177 | to avoid escaping and `textwrap.dedent`.
178 | - *Jsonnet* - [text blocks](https://jsonnet.org/learning/tutorial.html) with `|||` as a delimiter.
179 | - *Bash* - [`<<-` Heredocs](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04).
180 | - *Ruby* - [`<<~` Heredocs](https://www.rubyguides.com/2018/11/ruby-heredoc/).
181 | - *Swift* - [multiline string literals](https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID286)
182 | using triple-quotes - strips margin based on whitespace before closing
183 | delimiter.
184 | - _PHP_ - `<<<` [heredoc/nowdoc](https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes#closing_marker_indentation)
185 | The indentation of the closing marker dictates the amount of whitespace to
186 | strip from each line.
187 |
188 | ## Q&A
189 |
190 | ### Why not use a library?
191 |
192 | To summarise the [problem](#problem) section above:
193 | - avoid a dependency for the desired behaviour of the vast majority of
194 | multiline strings (dedent has millions of downloads per week).
195 | - avoiding inconsistencies between the multiple current implementations.
196 | - improved performance.
197 | - better discoverability - the feature can be documented publicly, and used in
198 | code samples which wouldn't otherwise rely on a package like dedent.
199 | - give code generators a way to output readable code with correct indentation
200 | properties (e.g. jest inline snapshots).
201 | - support "dedenting" tagged template literal functions with customized
202 | expression parameter behaviour (e.g. [slonik](https://npmjs.com/package/slonik)).
203 | - allow formatters/linters to safely enforce code style without needing to be
204 | coupled to the runtime behaviour of multiple libraries in combination.
205 |
206 |
207 | ## Additional Links
208 |
209 | - [Original TC39 thread](https://es.discourse.group/t/triple-backtick-template-literal-with-indentation-support/337)
210 | - [2020-09 Committee Meeting](https://github.com/tc39/notes/blob/HEAD/meetings/2020-09/sept-23.md#stringdedent-for-stage-1)
211 | - [2022-03 Committee Meeting](https://github.com/tc39/notes/blob/HEAD/meetings/2022-03/mar-30.md#stringdedent-status-update)
212 | - [2022-06 Committee Meeting](https://github.com/tc39/notes/blob/main/meetings/2022-06/jun-07.md#stringdedent)
213 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
2532 | This is the spec text of the String Dedent proposal in ECMAScript.
2533 | String dedenting removes leading indentation from each line of a multiline string.
2534 |
The String.dedent function may be called with either a template array and a variable number of substitutions, or it may be called with a function which itself takes a template array and a variable number of substitutions.
2546 |
When called with a template and a variable number of substitutions, a string is returned which has common leading indentation removed from all lines with non-whitespace.
2547 |
When called with a function, a closure is returned which wraps the function. When the closure is called with a template and variable number of substitutions, the common leading indentation of the template is removed and the result of calling the function with this new dedented template and substitutions is returned.
2548 |
Common leading indentation is defined as the longest sequence of whitespace characters that match exactly on every line that contains non-whitespace. Empty lines and lines which contain only whitespace do not affect common leading indentation.
2549 |
When removing the common leading indentation, lines which contain only whitespace will have all whitespace removed.
2550 |
2551 |
If templateOrFn is not an object, then
NOTE: This check is to allow future extensions with different input types.
The dedent function is intended for use as a tag function of a Tagged Template (13.3.11). When called as such, the first argument will be a well formed template object and the rest parameter will contain the substitution values.
2554 |
2555 |
2556 |
2557 |
2558 |
2.2 DedentTemplateStringsArray ( template )
2559 |
The abstract operation DedentTemplateStringsArray takes argument template (an Object) and returns either a normal completion containing an Array or a throw completion. It performs the following steps when called:
The abstract operation CookStrings takes argument raw (a List of Strings) and returns a List of either String or undefined. It performs the following steps when called:
NOTE: Even though String.dedent can be manually invoked with a value, the CookStringsCharactersWithBackslash nonterminal matches every string, so the above parse is guaranteed to succeed.
NOTE: val can actually be undefined if the raw string contains an invalid escape sequence.
Append val to cooked.
Return cooked.
2573 |
2574 |
2575 |
2.4.1 The CookStrings Grammar
2576 |
The following grammar is used by CookStrings. It is identical to TemplateCharacters except that it allows "${"" and "`" to appear without being preceded by a backslash, allows a backslash as the final character, and accepts the empty sequence. Every sequence of code points is matched by this grammar.
The abstract operation SplitTemplateIntoBlockLines takes arguments template (an ECMAScript language value) and len (a non-negative integer) and returns either a normal completion containing a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean), or a throw completion. It performs the following steps when called:
Let substr be the substring of str from start to i.
Let newline be the substring of str from i to i + n.
Append the Record { [[String]]: substr, [[Newline]]: newline, [[LineEndsWithSubstitution]]: false } to lines.
Set start to i + n.
Set i to i + n.
Let tail be the substring of str from start to strLen.
If index + 1 < len, let lineEndsWithSubstitution be true; else let lineEndsWithSubstitution be false.
Append the Record { [[String]]: tail, [[Newline]]: "", [[LineEndsWithSubstitution]]: lineEndsWithSubstitution } to lines.
Append lines to blocks.
Else,
Throw a TypeError exception.
Set index to index + 1.
Return blocks.
2662 |
2663 |
2664 |
2665 |
2.6 EmptyWhiteSpaceLines ( blocks )
2666 |
The abstract operation EmptyWhiteSpaceLines takes argument blocks (a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean)). It performs the following steps when called:
2667 |
For each element lines of blocks, do
Let lineCount be the length of lines.
NOTE: We start i at 1 because the first line of every block is either (a) the opening line which must be empty or (b) the continuation of a line directly after a template substitution. Neither can be the start of a content line.
Let i be 1.
Repeat, while i < lineCount,
Let line be lines[i].
If line.[[LineEndsWithSubstitution]] is false and IsAllWhiteSpace(line.[[String]]) is true, then
NOTE: Lines which contain only whitespace are emptied in the output. Their trailing newline is not removed, so the line is maintained.
Set line.[[String]] to "".
Set i to i + 1.
2668 |
2669 |
2670 |
2671 |
2.7 RemoveOpeningAndClosingLines ( blocks, len )
2672 |
The abstract operation RemoveOpeningAndClosingLines takes arguments blocks (a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean)) and len (a non-negative integer). It performs the following steps when called:
2673 |
Assert: blocks contains at least 1 element, because we know the length of the template strings array is at least 1.
NOTE: The opening line is required to contain a trailing newline, and checking that there are at least 2 elements in lines ensures it. If it does not, either the opening line and the closing line are the same line, or the opening line contains a substitution.
Throw a TypeError exception.
Let openingLine be firstBlock[0].
If openingLine.[[String]] is not empty, then
NOTE: The opening line must not contain code units besides the trailing newline.
Throw a TypeError exception.
NOTE: Setting openingLine.[[Newline]] removes the opening line from the output.
NOTE: The closing line is required to be preceded by a newline, and checking that there are at least 2 elements in lines ensures it. If it does not, either the opening line and the closing line are the same line, or the closing line contains a substitution.
Throw a TypeError exception.
Let closingLine be lastBlock[lineCount - 1].
If closingLine.[[String]] is not the empty String, then
NOTE: The closing line may only contain whitespace. We've already performed EmptyWhiteSpaceLines, so if the line is not empty now, it contained some non-whitespace character.
Throw a TypeError exception.
Let preceding be lastBlock[lineCount - 2].
NOTE: Setting closingLine.[[String]] and preceding.[[Newline]] removes the closing line from the output.
Set closingLine.[[String]] to "".
Set preceding.[[Newline]] to "".
2674 |
2675 |
2676 |
2677 |
2.8 DetermineCommonLeadingIndentation ( blocks )
2678 |
The abstract operation DetermineCommonLeadingIndentation takes argument blocks (a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean)) and returns a String. It performs the following steps when called:
2679 |
Let common be empty.
For each element lines of blocks, do
Let lineCount be the length of lines.
NOTE: We start i at 1 because because the first line of every block is either (a) the opening line which must be empty or (b) the continuation of a line directly after a template substitution. Neither can be the start of a content line.
Let i be 1.
Repeat, while i < lineCount,
Let line be lines[i].
NOTE: Lines which contain substitutions are considered when finding the common indentation. Lines which contain only whitespace have already been emptied.
If line.[[LineEndsWithSubstitution]] is true or line.[[String]] is not the empty String, then
Assert: common is not empty, because SplitTemplateIntoBlockLines guarantees there is at least 1 line per block, and we know the length of the template strings array is at least 1.
Return common.
2680 |
2681 |
2682 |
2683 |
2.9 LeadingWhiteSpaceSubstring ( str )
2684 |
The abstract operation LeadingWhiteSpaceSubstring takes argument str (a String) and returns a String. It performs the following steps when called:
When determining whether a Unicode code point is in Unicode general category “Space_Separator” (“Zs”), code unit sequences are interpreted as UTF-16 encoded code point sequences as specified in 6.1.4.
2687 |
2688 |
2689 |
2690 |
2.10 IsAllWhiteSpace ( str )
2691 |
The abstract operation IsAllWhiteSpace takes argument str (a String) and returns a Boolean. It performs the following steps when called:
The abstract operation LongestMatchingLeadingSubstring takes arguments a (a String) and b (a String) and returns a String. It performs the following steps when called:
Before it is evaluated, all ECMAScript code must be associated with a realm. Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment, all of the ECMAScript code that is loaded within the scope of that global environment, and other associated state and resources.
2717 |
A realm is represented in this specification as a Realm Record with the fields specified in Table 1:
Template objects are canonicalized separately for each realm using its Realm Record's [[TemplateMap]]. Each [[Site]] value is a Parse Node that is a TemplateLiteral. The associated [[Array]] value is the corresponding template object that is passed to a tag function.
2773 | Note
Once a Parse Node becomes unreachable, the corresponding [[Array]] is also unreachable, and it would be unobservable if an implementation removed the pair from the [[TemplateMap]] list.
2774 |
2775 |
2776 |
2777 |
2778 | [[HostDefined]]
2779 |
2780 |
2781 | anything (default value is undefined)
2782 |
2783 |
2784 | Field reserved for use by hosts that need to associate additional information with a Realm Record.
2785 |
2786 |
2787 |
2788 |
2789 | [[DedentMap]]
2790 |
2791 |
2792 | a List of Record { [[Raw]]: Object, [[Dedented]]: Object }
2793 |
2794 |
2795 |
2796 | The [[DedentMap]] ensures template tag functions wrapped with String.dedent see consistent object identity for a given input template. The [[Raw]] key weakly holds the value of a template object's raw property. The associated [[Dedented]] value is the result of performing dedenting on that raw value.
2797 |
2798 |
2799 |
2800 |
2801 |
2802 |
2803 |
2804 |
3.1 CreateRealm ( )
2805 |
The abstract operation CreateRealm takes no arguments and returns a Realm Record. It performs the following steps when called:
If realm.[[DedentMap]] contains a Recordr such that r.[[Raw]] is obj, then
Set r.[[Raw]] to empty.
Set r.[[Dedented]] to empty.
2816 |
2817 | Note 1
2818 |
Together with the definition of liveness, this clause prescribes legal optimizations that an implementation may apply regarding WeakRefs.
2819 |
2820 |
It is possible to access an object without observing its identity. Optimizations such as dead variable elimination and scalar replacement on properties of non-escaping objects whose identity is not observed are allowed. These optimizations are thus allowed to observably empty WeakRefs that point to such objects.
2821 |
2822 |
On the other hand, if an object's identity is observable, and that object is in the [[WeakRefTarget]] internal slot of a WeakRef, optimizations such as rematerialization that observably empty the WeakRef are prohibited.
Implementations are not obligated to empty WeakRefs for maximal sets of non-live objects.
2828 |
If an implementation chooses a non-live set S in which to empty WeakRefs, it must empty WeakRefs for all objects in S simultaneously. In other words, an implementation must not empty a WeakRef pointing to an object obj without emptying out other WeakRefs that, if not emptied, could result in an execution that observes the Object value of obj.
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.
2838 |
2839 |
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
2840 |
2841 |
2842 |
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2843 |
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.
2844 |
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.
2845 |
2846 |
2847 |
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.
18 | This is the spec text of the String Dedent proposal in ECMAScript.
19 | String dedenting removes leading indentation from each line of a multiline string.
20 |
The `String.dedent` function may be called with either a template array and a variable number of substitutions, or it may be called with a function which itself takes a template array and a variable number of substitutions.
32 |
When called with a template and a variable number of substitutions, a string is returned which has common leading indentation removed from all lines with non-whitespace.
33 |
When called with a function, a closure is returned which wraps the function. When the closure is called with a template and variable number of substitutions, the common leading indentation of the template is removed and the result of calling the function with this new dedented template and substitutions is returned.
34 |
Common leading indentation is defined as the longest sequence of whitespace characters that match exactly on every line that contains non-whitespace. Empty lines and lines which contain only whitespace do not affect common leading indentation.
35 |
When removing the common leading indentation, lines which contain only whitespace will have all whitespace removed.
36 |
37 |
38 | 1. If _templateOrFn_ is not an object, then
39 | 1. NOTE: This check is to allow future extensions with different input types.
40 | 1. Throw a *TypeError* exception.
41 | 1. If IsCallable(_templateOrFn_) is *true*, then
42 | 1. Let _tag_ be _templateOrFn_.
43 | 1. Let _closure_ be a new Abstract Closure with parameters (_template_, ..._substitutions_) that captures _tag_ and performs the following steps when called:
44 | 1. If _template_ is not an object, then
45 | 1. Throw a *TypeError* exception.
46 | 1. Let _R_ be the *this* value.
47 | 1. Let _dedented_ be ? DedentTemplateStringsArray(_template_).
48 | 1. Let _args_ be the list-concatenation of « _dedented_ » and _substitutions_.
49 | 1. Return ? Call(_tag_, _R_, _args_).
50 | 1. Return CreateBuiltinFunction(_closure_, 1, *""*, « »).
51 | 1. Let _template_ be _templateOrFn_.
52 | 1. Let _dedented_ be ? DedentTemplateStringsArray(_template_).
53 | 1. Return ? CookTemplateStringsArray(_dedented_, _substitutions_, ~cooked~).
54 |
55 |
56 |
The `dedent` function is intended for use as a tag function of a Tagged Template (). When called as such, the first argument will be a well formed template object and the rest parameter will contain the substitution values.
57 |
58 |
59 |
60 |
61 |
62 | DedentTemplateStringsArray (
63 | _template_: an Object,
64 | ): either a normal completion containing an Array or a throw completion
65 |
66 |
67 |
68 |
69 | 1. Let _realm_ be the current Realm Record.
70 | 1. Let _dedentMap_ be _realm_.[[DedentMap]].
71 | 1. Let _rawInput_ be ? Get(_template_, *"raw"*).
72 | 1. For each element _e_ of the _dedentMap_, do
73 | 1. If _e_.[[Raw]] is not ~empty~ and SameValue(_e_.[[Raw]], _rawInput_) is *true*, return _e_.[[Dedented]].
74 | 1. Assert: _dedentMap_ does not currently contain an entry for _rawInput_.
75 | 1. Let _raw_ be ? DedentStringsArray(_rawInput_).
76 | 1. Let _cookedArr_ be CreateArrayFromList(CookStrings(_raw_)).
77 | 1. Let _rawArr_ be CreateArrayFromList(_raw_).
78 | 1. Perform ! DefinePropertyOrThrow(_cookedArr_, *"raw"*, PropertyDescriptor { [[Value]]: _rawArr_, [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *false* }).
79 | 1. Perform ! SetIntegrityLevel(_rawArr_, ~frozen~).
80 | 1. Perform ! SetIntegrityLevel(_cookedArr_, ~frozen~).
81 | 1. Append the Record { [[Raw]]: _rawInput_, [[Dedented]]: _cookedArr_ } to _dedentMap_.
82 | 1. Return _cookedArr_.
83 |
84 |
85 |
86 |
87 |
88 | DedentStringsArray (
89 | _template_: an ECMAScript language value,
90 | ): either a normal completion containing a List of Strings, or a throw completion
91 |
92 |
93 |
94 |
95 | 1. Let _t_ be ? ToObject(_template_).
96 | 1. Let _len_ be ? LengthOfArrayLike(_t_).
97 | 1. If _len_ = 0, then
98 | 1. NOTE: Well-formed template strings arrays always contain at least 1 string.
99 | 1. Throw a *TypeError* exception.
100 | 1. Let _blocks_ be ? SplitTemplateIntoBlockLines(_t_, _len_).
101 | 1. Perform ? EmptyWhiteSpaceLines(_blocks_).
102 | 1. Perform ? RemoveOpeningAndClosingLines(_blocks_, _len_).
103 | 1. Let _common_ be DetermineCommonLeadingIndentation(_blocks_).
104 | 1. Let _count_ be the length of _common_.
105 | 1. Let _dedented_ be a new empty List.
106 | 1. For each element _lines_ of _blocks_, do
107 | 1. Assert: _lines_ is not empty, because SplitTemplateIntoBlockLines guarantees there is at least 1 line per block.
108 | 1. Let _out_ be *""*.
109 | 1. Let _index_ be 0.
110 | 1. For each Record { [[String]], [[Newline]], [[LineEndsWithSubstitution]] } _line_ of _lines_, do
111 | 1. NOTE: The first line of each block does not need to be trimmed. It's either the opening line, or it's the continuation of a line after a substitution.
112 | 1. If _index_ = 0 or _line_.[[String]] is the empty String, let _c_ be 0; else let _c_ be _count_.
113 | 1. Let _strLen_ be the length of _line_.[[String]].
114 | 1. Let _trimmed_ be the substring of _line_.[[String]] from _c_ to _strLen_.
115 | 1. Set _out_ to the string-concatenation of _out_, _trimmed_, and _line_.[[Newline]].
116 | 1. Set _index_ to _index_ + 1.
117 | 1. Append _out_ to _dedented_.
118 | 1. Return _dedented_.
119 |
120 |
121 |
122 |
123 |
124 | CookStrings (
125 | _raw_: a List of Strings,
126 | ): a List of either String or *undefined*
127 |
128 |
129 |
130 |
131 | 1. Let _cooked_ be a new empty List.
132 | 1. For each element _str_ of _raw_, do
133 | 1. Let _parsed_ be ParseText(StringToCodePoints(_str_), |CookStringsCharactersWithBackslash|).
134 | 1. Assert: _parsed_ is a Parse Node.
135 | 1. NOTE: Even though String.dedent can be manually invoked with a value, the |CookStringsCharactersWithBackslash| nonterminal matches every string, so the above parse is guaranteed to succeed.
136 | 1. Let _val_ be the TV of _parsed_ as defined in .
137 | 1. NOTE: _val_ can actually be *undefined* if the raw string contains an invalid escape sequence.
138 | 1. Append _val_ to _cooked_.
139 | 1. Return _cooked_.
140 |
141 |
142 |
143 |
The CookStrings Grammar
144 |
The following grammar is used by CookStrings. It is identical to |TemplateCharacters| except that it allows *"${"*" and *"`"* to appear without being preceded by a backslash, allows a backslash as the final character, and accepts the empty sequence. Every sequence of code points is matched by this grammar.
161 | The following lines are added to the definition of TV.
162 |
163 |
164 | The TV of CookStringsCharactersWithBackslash :: [empty] is the empty String.
165 |
166 |
167 | The TV of CookStringsCharactersWithBackslash :: `\` is *undefined*.
168 |
169 |
170 | The TV of CookStringsCharactersWithBackslash :: CookStringsCharacters `\` is *undefined*.
171 |
172 |
173 | The TV of CookStringsCharacters :: CookStringsCharacter CookStringsCharacters is *undefined* if either the TV of |CookStringsCharacter| is *undefined* or the TV of |CookStringsCharacters| is *undefined*. Otherwise, it is the string-concatenation of the TV of |CookStringsCharacter| and the TV of |CookStringsCharacters|.
174 |
175 |
176 | The TV of CookStringsCharacter :: `$` [lookahead = `{`] is *"$"*.
177 |
178 |
179 | The TV of CookStringsCharacter :: ``` is *"`"*.
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | SplitTemplateIntoBlockLines (
189 | _template_: an ECMAScript language value,
190 | _len_: a non-negative integer,
191 | ): either a normal completion containing a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean), or a throw completion
192 |
193 |
194 |
195 |
196 | 1. Let _blocks_ be a new empty List.
197 | 1. Let _index_ be 0.
198 | 1. Repeat, while _index_ < _len_,
199 | 1. Let _str_ be ? Get(_template_, ! ToString(𝔽(_index_))).
200 | 1. If Type(_str_) is String, then
201 | 1. Let _lines_ be a new empty List.
202 | 1. Let _strLen_ be the length of _str_.
203 | 1. Let _start_ be 0.
204 | 1. Let _i_ be 0.
205 | 1. Repeat, while _i_ < _strLen_,
206 | 1. Let _c_ be the substring of _str_ from _i_ to _i_ + 1.
207 | 1. Let _n_ be 1.
208 | 1. If _c_ is *"\r"* and _i_ + 1 < _strLen_, then
209 | 1. Let _next_ be the substring of _str_ from _i_ + 1 to _i_ + 2.
210 | 1. If _next_ is *"\n"*, set _n_ to 2.
211 | 1. If _c_ is matched by |LineTerminator|, then
212 | 1. Let _substr_ be the substring of _str_ from _start_ to _i_.
213 | 1. Let _newline_ be the substring of _str_ from _i_ to _i_ + _n_.
214 | 1. Append the Record { [[String]]: _substr_, [[Newline]]: _newline_, [[LineEndsWithSubstitution]]: *false* } to _lines_.
215 | 1. Set _start_ to _i_ + _n_.
216 | 1. Set _i_ to _i_ + _n_.
217 | 1. Let _tail_ be the substring of _str_ from _start_ to _strLen_.
218 | 1. If _index_ + 1 < _len_, let _lineEndsWithSubstitution_ be *true*; else let _lineEndsWithSubstitution_ be *false*.
219 | 1. Append the Record { [[String]]: _tail_, [[Newline]]: *""*, [[LineEndsWithSubstitution]]: _lineEndsWithSubstitution_ } to _lines_.
220 | 1. Append _lines_ to _blocks_.
221 | 1. Else,
222 | 1. Throw a *TypeError* exception.
223 | 1. Set _index_ to _index_ + 1.
224 | 1. Return _blocks_.
225 |
226 |
227 |
228 |
229 |
230 | EmptyWhiteSpaceLines (
231 | _blocks_: a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean),
232 | )
233 |
234 |
235 |
236 |
237 | 1. For each element _lines_ of _blocks_, do
238 | 1. Let _lineCount_ be the length of _lines_.
239 | 1. NOTE: We start _i_ at 1 because the first line of every block is either (a) the opening line which must be empty or (b) the continuation of a line directly after a template substitution. Neither can be the start of a content line.
240 | 1. Let _i_ be 1.
241 | 1. Repeat, while _i_ < _lineCount_,
242 | 1. Let _line_ be _lines_[_i_].
243 | 1. If _line_.[[LineEndsWithSubstitution]] is *false* and IsAllWhiteSpace(_line_.[[String]]) is *true*, then
244 | 1. NOTE: Lines which contain only whitespace are emptied in the output. Their trailing newline is not removed, so the line is maintained.
245 | 1. Set _line_.[[String]] to *""*.
246 | 1. Set _i_ to _i_ + 1.
247 |
248 |
249 |
250 |
251 |
252 | RemoveOpeningAndClosingLines (
253 | _blocks_: a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean),
254 | _len_: a non-negative integer,
255 | )
256 |
257 |
258 |
259 |
260 | 1. Assert: _blocks_ contains at least 1 element, because we know the length of the template strings array is at least 1.
261 | 1. Let _firstBlock_ be _blocks_[0].
262 | 1. Assert: _firstBlock_ is not empty, because SplitTemplateIntoBlockLines guarantees there is at least 1 line per block.
263 | 1. Let _lineCount_ be the length of _firstBlock_.
264 | 1. If _lineCount_ = 1, then
265 | 1. NOTE: The opening line is required to contain a trailing newline, and checking that there are at least 2 elements in _lines_ ensures it. If it does not, either the opening line and the closing line are the same line, or the opening line contains a substitution.
266 | 1. Throw a *TypeError* exception.
267 | 1. Let _openingLine_ be _firstBlock_[0].
268 | 1. If _openingLine_.[[String]] is not empty, then
269 | 1. NOTE: The opening line must not contain code units besides the trailing newline.
270 | 1. Throw a *TypeError* exception.
271 | 1. NOTE: Setting _openingLine_.[[Newline]] removes the opening line from the output.
272 | 1. Set _openingLine_.[[Newline]] to *""*.
273 | 1. Let _lastBlock_ be _blocks_[_len_ - 1].
274 | 1. Assert: _lastBlock_ is not empty, because SplitTemplateIntoBlockLines guarantees there is at least 1 line per block.
275 | 1. Set _lineCount_ to the length of _lastBlock_.
276 | 1. If _lineCount_ = 1, then
277 | 1. NOTE: The closing line is required to be preceded by a newline, and checking that there are at least 2 elements in _lines_ ensures it. If it does not, either the opening line and the closing line are the same line, or the closing line contains a substitution.
278 | 1. Throw a *TypeError* exception.
279 | 1. Let _closingLine_ be _lastBlock_[_lineCount_ - 1].
280 | 1. If _closingLine_.[[String]] is not the empty String, then
281 | 1. NOTE: The closing line may only contain whitespace. We've already performed EmptyWhiteSpaceLines, so if the line is not empty now, it contained some non-whitespace character.
282 | 1. Throw a *TypeError* exception.
283 | 1. Let _preceding_ be _lastBlock_[_lineCount_ - 2].
284 | 1. NOTE: Setting _closingLine_.[[String]] and _preceding_.[[Newline]] removes the closing line from the output.
285 | 1. Set _closingLine_.[[String]] to *""*.
286 | 1. Set _preceding_.[[Newline]] to *""*.
287 |
288 |
289 |
290 |
291 |
292 | DetermineCommonLeadingIndentation (
293 | _blocks_: a List of Lists of Records with fields [[String]] (a String), [[Newline]] (a String), and [[LineEndsWithSubstitution]] (a Boolean),
294 | ): a String
295 |
296 |
297 |
298 |
299 | 1. Let _common_ be ~empty~.
300 | 1. For each element _lines_ of _blocks_, do
301 | 1. Let _lineCount_ be the length of _lines_.
302 | 1. NOTE: We start _i_ at 1 because because the first line of every block is either (a) the opening line which must be empty or (b) the continuation of a line directly after a template substitution. Neither can be the start of a content line.
303 | 1. Let _i_ be 1.
304 | 1. Repeat, while _i_ < _lineCount_,
305 | 1. Let _line_ be _lines_[_i_].
306 | 1. NOTE: Lines which contain substitutions are considered when finding the common indentation. Lines which contain only whitespace have already been emptied.
307 | 1. If _line_.[[LineEndsWithSubstitution]] is *true* or _line_.[[String]] is not the empty String, then
308 | 1. Let _leading_ be LeadingWhiteSpaceSubstring(_line_.[[String]]).
309 | 1. If _common_ is ~empty~, then
310 | 1. Set _common_ to _leading_.
311 | 1. Else,
312 | 1. Set _common_ to LongestMatchingLeadingSubstring(_common_, _leading_).
313 | 1. Set _i_ to _i_ + 1.
314 | 1. Assert: _common_ is not ~empty~, because SplitTemplateIntoBlockLines guarantees there is at least 1 line per block, and we know the length of the template strings array is at least 1.
315 | 1. Return _common_.
316 |
317 |
318 |
319 |
320 |
321 | LeadingWhiteSpaceSubstring (
322 | _str_: a String,
323 | ): a String
324 |
325 |
326 |
327 |
328 | 1. Let _len_ be the length of _str_.
329 | 1. Let _i_ be 0.
330 | 1. Repeat, while _i_ < _len_,
331 | 1. Let _c_ be the substring of _str_ from _i_ to _i_ + 1.
332 | 1. If _c_ does not match |WhiteSpace|, then
333 | 1. Return the substring of _str_ from 0 to _i_.
334 | 1. Set _i_ to _i_ + 1.
335 | 1. Return _str_.
336 |
337 |
When determining whether a Unicode code point is in Unicode general category “Space_Separator” (“Zs”), code unit sequences are interpreted as UTF-16 encoded code point sequences as specified in .
338 |
339 |
340 |
341 |
342 | IsAllWhiteSpace (
343 | _str_: a String,
344 | ): a Boolean
345 |
346 |
347 |
348 |
349 | 1. Let _trimmed_ be ! TrimString(_str_, ~start~).
350 | 1. If _trimmed_ is the empty String, return *true*.
351 | 1. Return *false*.
352 |
353 |
354 |
355 |
356 |
357 | LongestMatchingLeadingSubstring (
358 | _a_: a String,
359 | _b_: a String,
360 | ): a String
361 |
362 |
363 |
364 |
365 | 1. Let _aLen_ be the length of _a_.
366 | 1. Let _bLen_ be the length of _b_.
367 | 1. Let _len_ be min(_aLen_, _bLen_).
368 | 1. Let _i_ be 0.
369 | 1. Repeat, while _i_ < _len_,
370 | 1. Let _aChar_ be the substring of _a_ from _i_ to _i_ + 1.
371 | 1. Let _bChar_ be the substring of _b_ from _i_ to _i_ + 1.
372 | 1. If _aChar_ is not _bChar_, then
373 | 1. Return the substring of _a_ from 0 to _i_.
374 | 1. Set _i_ to _i_ + 1.
375 | 1. Return the substring of _a_ from 0 to _len_.
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 | CookTemplateStringsArray (
384 | _template_: an ECMAScript language value,
385 | _substitutions_: a List of ECMAScript language values,
386 | _prep_: ~cooked~ or ~raw~,
387 | ): either a normal completion containing a String or a throw completion
388 |
389 |
390 |
391 |
392 |
This abstract operation merges the behaviour of `String.raw` with the String.cooked proposal.
393 |
394 |
395 | 1. Let _substitutionCount_ be the number of elements in _substitutions_.
396 | 1. Let _cooked_ be ? ToObject(_template_).
397 | 1. If _prep_ is ~cooked~, let _literals_ be _cooked_; else let _literals_ be ? ToObject(? Get(_cooked_, *"raw"*)).
398 | 1. Let _literalCount_ be ? LengthOfArrayLike(_literals_).
399 | 1. If _literalCount_ ≤ 0, return the empty String.
400 | 1. Let _R_ be the empty String.
401 | 1. Let _nextIndex_ be 0.
402 | 1. Repeat,
403 | 1. Let _nextLiteralVal_ be ? Get(_literals_, ! ToString(𝔽(_nextIndex_))).
404 | 1. Let _nextLiteral_ be ? ToString(_nextLiteralVal_).
405 | 1. Set _R_ to the string-concatenation of _R_ and _nextLiteral_.
406 | 1. If _nextIndex_ + 1 = _literalCount_, return _R_.
407 | 1. If _nextIndex_ < _substitutionCount_, then
408 | 1. Let _nextSubVal_ be _substitutions_[_nextIndex_].
409 | 1. Let _nextSub_ be ? ToString(_nextSubVal_).
410 | 1. Set _R_ to the string-concatenation of _R_ and _nextSub_.
411 | 1. Set _nextIndex_ to _nextIndex_ + 1.
412 |
413 |
414 |
415 |
416 |
417 |
Realms
418 |
Before it is evaluated, all ECMAScript code must be associated with a realm. Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment, all of the ECMAScript code that is loaded within the scope of that global environment, and other associated state and resources.
419 |
A realm is represented in this specification as a Realm Record with the fields specified in :
420 |
421 |
422 |
423 |
424 | Field Name
425 |
426 |
427 | Value
428 |
429 |
430 | Meaning
431 |
432 |
433 |
434 |
435 | [[Intrinsics]]
436 |
437 |
438 | a Record whose field names are intrinsic keys and whose values are objects
439 |
440 |
441 | The intrinsic values used by code associated with this realm
442 |
443 |
444 |
445 |
446 | [[GlobalObject]]
447 |
448 |
449 | an Object or *undefined*
450 |
451 |
452 | The global object for this realm
453 |
454 |
455 |
456 |
457 | [[GlobalEnv]]
458 |
459 |
460 | a Global Environment Record
461 |
462 |
463 | The global environment for this realm
464 |
465 |
466 |
467 |
468 | [[TemplateMap]]
469 |
470 |
471 | a List of Record { [[Site]]: Parse Node, [[Array]]: Object }
472 |
473 |
474 |
Template objects are canonicalized separately for each realm using its Realm Record's [[TemplateMap]]. Each [[Site]] value is a Parse Node that is a |TemplateLiteral|. The associated [[Array]] value is the corresponding template object that is passed to a tag function.
475 | Once a Parse Node becomes unreachable, the corresponding [[Array]] is also unreachable, and it would be unobservable if an implementation removed the pair from the [[TemplateMap]] list.
476 |
477 |
478 |
479 |
480 | [[HostDefined]]
481 |
482 |
483 | anything (default value is *undefined*)
484 |
485 |
486 | Field reserved for use by hosts that need to associate additional information with a Realm Record.
487 |
488 |
489 |
490 |
491 | [[DedentMap]]
492 |
493 |
494 | a List of Record { [[Raw]]: Object, [[Dedented]]: Object }
495 |
496 |
497 |
498 | The [[DedentMap]] ensures template tag functions wrapped with `String.dedent` see consistent object identity for a given input template. The [[Raw]] key weakly holds the value of a template object's raw property. The associated [[Dedented]] value is the result of performing dedenting on that raw value.
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
CreateRealm ( ): a Realm Record
507 |
508 |
509 |
510 | 1. Let _realmRec_ be a new Realm Record.
511 | 1. Perform CreateIntrinsics(_realmRec_).
512 | 1. Set _realmRec_.[[GlobalObject]] to *undefined*.
513 | 1. Set _realmRec_.[[GlobalEnv]] to *undefined*.
514 | 1. Set _realmRec_.[[TemplateMap]] to a new empty List.
515 | 1. Set _realmRec_.[[DedentMap]] to a new empty List.
516 | 1. Return _realmRec_.
517 |
518 |
519 |
520 |
521 |
522 |
Execution
523 |
524 |
At any time, if a set of objects _S_ is not live, an ECMAScript implementation may perform the following steps atomically:
525 |
526 |
527 | 1. For each element _obj_ of _S_, do
528 | 1. For each WeakRef _ref_ such that _ref_.[[WeakRefTarget]] is _obj_, do
529 | 1. Set _ref_.[[WeakRefTarget]] to ~empty~.
530 | 1. For each FinalizationRegistry _fg_ such that _fg_.[[Cells]] contains a Record _cell_ such that _cell_.[[WeakRefTarget]] is _obj_, do
531 | 1. Set _cell_.[[WeakRefTarget]] to ~empty~.
532 | 1. Optionally, perform HostEnqueueFinalizationRegistryCleanupJob(_fg_).
533 | 1. For each WeakMap _map_ such that _map_.[[WeakMapData]] contains a Record _r_ such that _r_.[[Key]] is _obj_, do
534 | 1. Set _r_.[[Key]] to ~empty~.
535 | 1. Set _r_.[[Value]] to ~empty~.
536 | 1. For each WeakSet _set_ such that _set_.[[WeakSetData]] contains _obj_, do
537 | 1. Replace the element of _set_.[[WeakSetData]] whose value is _obj_ with an element whose value is ~empty~.
538 | 1. Let _realm_ be the current Realm Record.
539 | 1. If _realm_.[[DedentMap]] contains a Record _r_ such that _r_.[[Raw]] is _obj_, then
540 | 1. Set _r_.[[Raw]] to ~empty~.
541 | 1. Set _r_.[[Dedented]] to ~empty~.
542 |
543 |
544 |
545 |
Together with the definition of liveness, this clause prescribes legal optimizations that an implementation may apply regarding WeakRefs.
546 |
547 |
It is possible to access an object without observing its identity. Optimizations such as dead variable elimination and scalar replacement on properties of non-escaping objects whose identity is not observed are allowed. These optimizations are thus allowed to observably empty WeakRefs that point to such objects.
548 |
549 |
On the other hand, if an object's identity is observable, and that object is in the [[WeakRefTarget]] internal slot of a WeakRef, optimizations such as rematerialization that observably empty the WeakRef are prohibited.
550 |
551 |
Because calling HostEnqueueFinalizationRegistryCleanupJob is optional, registered objects in a FinalizationRegistry do not necessarily hold that FinalizationRegistry live. Implementations may omit FinalizationRegistry callbacks for any reason, e.g., if the FinalizationRegistry itself becomes dead, or if the application is shutting down.
552 |
553 |
554 |
Implementations are not obligated to empty WeakRefs for maximal sets of non-live objects.
555 |
If an implementation chooses a non-live set _S_ in which to empty WeakRefs, it must empty WeakRefs for all objects in _S_ simultaneously. In other words, an implementation must not empty a WeakRef pointing to an object _obj_ without emptying out other WeakRefs that, if not emptied, could result in an execution that observes the Object value of _obj_.