├── .npmrc ├── LICENSE ├── README.md ├── index.html ├── package.json └── spec.emu /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collection {coerceKey, coerceValue} 2 | 3 | This proposal seeks to add a `coerceKey` and `coerceValue` parameter to collection creation. 4 | 5 | [Rendered Spec](https://tc39.github.io/proposal-collection-normalization/) 6 | 7 | ## Motivation 8 | 9 | Various data structures are expected to have consistent constraints and or normalization applied to their keys and/or values when being used after creation. In order to facilitate normalization be guaranteed for such data types it needs to be applied prior to being inserted into the data structure. The current workflow is to manually ensure that at all locations which insert data into the structure properly normalize the data. 10 | 11 | This leads to various issues such as potentially fragile boilerplate problems such as; normalization not applied to all insertion locations or normalization not applied in consistent manners across insertion locations. 12 | 13 | This proposal does add a new capability of guarantees around the stored values in these data structures. Currently calling various built ins methods can avoid any mechanism to intercept and normalize data as it is inserted by calling methods on a structure like `Map.prototype.set.call(mapOfStringsToStrings, number)` which prevents guarantees about a `Map` having a consistent set of guarantees about the data it contains. 14 | 15 | Various examples of structures that normalize data as it is inserted exist in the wild: 16 | 17 | * [`URLSearchParams`](https://url.spec.whatwg.org/#urlsearchparams) 18 | 19 | Converts data to strings for both parameter name and value. 20 | 21 | * [`DomStringMap`](https://html.spec.whatwg.org/#domstringmap) 22 | 23 | Validates and converts data to strings for both header name and value. 24 | 25 | * [`Header`](https://fetch.spec.whatwg.org/#headers-class) 26 | 27 | Converts data to strings for both header name and value. 28 | 29 | * [`HTMLOptionsCollection[[setter]]`](https://html.spec.whatwg.org/#the-htmloptionscollection-interface) 30 | 31 | Clamps the key when appending to `length + 1` by using `append`. 32 | 33 | * [`Attributes.setNamedItem*`](https://dom.spec.whatwg.org/#concept-element-attributes-set) 34 | 35 | Performs validation on key. 36 | 37 | All of these do similar effects using differing ways of applying data type coercion and validation of data being inserted. 38 | 39 | In order to unify how to define these general operations on dealing with incoming data and to avoid boilerplate issues and fragile base class issues as mentioned in the FAQ a hook to 40 | intercept data would alleviate some issues. 41 | 42 | ## Use cases 43 | 44 | ### Specialized maps 45 | 46 | Given an application with User Objects it may be desirable to create collections based upon username and email for separate purposes. 47 | 48 | ```mjs 49 | new Map(undefined, { 50 | coerceKey({email}) { 51 | return email; 52 | }, 53 | coerceValue(state) { 54 | return state instanceof AccountState ? 55 | state : 56 | new AccountState(state); 57 | } 58 | }); 59 | ``` 60 | 61 | ```mjs 62 | new Set(undefined, { 63 | coerceValue({username}) { 64 | return username; 65 | } 66 | }); 67 | ``` 68 | 69 | ### Checked keys 70 | 71 | It is a common occurrence to want to check types when performing operations on collections. This can be done during keying. 72 | 73 | ```mjs 74 | new Map(undefined, { 75 | coerceKey(user) { 76 | if (user instanceof User !== true) { 77 | throw new TypeError('Expected User for key'); 78 | } 79 | return user; 80 | } 81 | }); 82 | ``` 83 | 84 | ### DOM Example: [Headers..set](https://fetch.spec.whatwg.org/#dom-headers-set) 85 | 86 | > 1. Normalize value. 87 | 88 | > 2. If name is not a name or value is not a value, then throw a TypeError. 89 | 90 | > 3. If this’s guard is "immutable", then throw a TypeError. 91 | 92 | > 4. Otherwise, if this’s guard is "request" and name is a forbidden header name, return. 93 | 94 | > 5. Otherwise, if this’s guard is "request-no-cors" and name/value is not a no-CORS-safelisted request-header, return. 95 | 96 | > 6. Otherwise, if this’s guard is "response" and name is a forbidden response-header name, return. 97 | 98 | > 7. Set name/value in this’s header list. 99 | 100 | > 8. If this’s guard is "request-no-cors", then remove privileged no-CORS request headers from this. 101 | 102 | All of these can be interacted with if a hook can intercept `name` and `value` as it comes in. The general workflow is to store a `name` and `value` in the backing `header list` storage. 103 | 104 | ### DOM Example: [SearchParams..has](https://url.spec.whatwg.org/#dom-urlsearchparams-has) 105 | 106 | > The has(name) method steps are to return true if there is a name-value pair whose name is name in this’s list, and false otherwise. 107 | 108 | Note, all normalization is done in WebIDL layer here to convert `name` to a USVString, there is no actual text stating that normalization is done for `name`. 109 | 110 | ## FAQ 111 | 112 | ### How do other languages handle this customized keying? 113 | 114 | A collection of references can be found via [this document](https://docs.google.com/document/d/1qxSLyiButKocM6ENufhvnNJcZh18nDAWcFT2HlTJahQ/edit#). 115 | 116 | Generally it falls into using container types. If you wanted to create a `Map` of `People` by `person.email`. You would implement a wrapper class `PersonByEmail` to use as your key, and others for keying of other aspects. Static typing and compiler/language enforced coercion can alleviate problems with misusing collections, but wrapping and unwrapping is manual in scenarios with dynamic typing that cannot be coerced automatically. 117 | 118 | This proposal would provide a hook to do that manual wrapping and unwrapping without requiring the user of a collection to remain vigilant about properly marshalling keys before providing them to the collection. 119 | 120 | ### When are the normalization steps applied? 121 | 122 | Normalization is applied when data is incoming to find the identity of the key location in `[[MapData]]` and when placing the value in `[[SetData]]` or `[[MapData]]`. e.g. 123 | 124 | ```mjs 125 | const map = new Map([], { 126 | coerceKey: String 127 | }); 128 | // stored using { [[Key]]: "1", [[Value]]: "one" } in map.[[MapData]] 129 | map.set(1, 'one'); 130 | // looks for corresponding { [[Key]]: "1" } in map.[[MapData]] 131 | map.has(1); // true 132 | // functions directly exposing the underlying entry list are unaffected 133 | [...map.entries()]; // [["1", "one"]] 134 | 135 | const set = new Set([], {coerceValue: JSON.stringify}); 136 | // stored using { [[Value]]: '{"path": "/foo"}' } in set.[[SetData]] 137 | set.add({path: '/foo'}); 138 | // looks for corresponding { [[Value]]: '{"path": "/foo"}' } in set.[[SetData]] 139 | set.has({path: '/foo')}; 140 | // functions directly exposing the underlying entry list are unaffected 141 | [...set]; // ['{"path": "/foo"}'] 142 | ``` 143 | 144 | Normalization is not done when iterating or returning internal data, it is only done on parameters. 145 | 146 | ### Why would someone want to use `coerceValue` with Map? 147 | 148 | A variety of use cases exist to normalize the values of map like structures in different APIs. Even if they do not directly use Map, we can se the utility of this pattern from existing DOM APIs. 149 | 150 | * `URLSearchParams` 151 | * `DomStringMap` 152 | * `Header` 153 | 154 | Node also has APIs that also normalize values such as `process.env`. 155 | 156 | Normalizing values can avoid certain situations as well such as putting invalid values into a Map by either validation errors, coercion, or other means. Existing map like structures such as `require.cache` can produce error if you put the incorrect values in them. A normalization step allows the map to properly handle situations when unexpected values are inserted. 157 | 158 | ### Why are Sets only given `coerceValue`? 159 | 160 | Sets are collections of values, and do not have a mapping operation from one value to another. 161 | 162 | #### Why not call it `coerceKey` for Sets? 163 | 164 | An audit of other languages was done with their documentation and APIs concerning Sets. The majority of terminology used was "elements"; however, the terms "keys" and "values" were also used. It was noted that whenever "keys" was used "values" was also used, but when "values" was used it did not always also use "keys". To match existing JS usage of terms "values" was chosen as the base for this name. 165 | 166 | ### Why not `value[Symbol.coerceKey]`? 167 | 168 | Having specialized identity conflicts with the idea of having multiple kinds of specialized maps per type of value. It also would cause conflicts when wanting to specialize keys that are based upon primitives. 169 | 170 | ### Why not encourage extending collections? 171 | 172 | 1. This would be succeptible to prototype crawling such as: 173 | 174 | ```mjs 175 | myCustomMap.__proto__.get.call(myCustomMap, key); 176 | ``` 177 | 178 | which would somewhat invalidate the idea of checking types of keys. 179 | 180 | 2. It prevents needing to synchronize all of the methods which is a fair amount of boilerplate and potential place for code going out of sync. It also means that your custom implementation will work even if new methods are added to collections in the JS standard library: 181 | 182 | ```mjs 183 | class MyMap extends Map { 184 | constructor([...entries]) { 185 | super(entries.map(...)); 186 | } 187 | delete(k) { ... } 188 | get(k) { ... } 189 | has(k) { ... } 190 | set(k, v) { ... } 191 | } 192 | ``` 193 | 194 | If we add something like `emplace()` this code now needs to be updated or it will have bugs if people expect it to work like a standard Map. 195 | 196 | This is roughly [the fragile base class problem](https://en.wikipedia.org/wiki/Fragile_base_class), where `Map` is the base class. 197 | 198 | 3. Even if this is a userland solution, it seems prudent to allow easier usage of maps. We should aim to alleviate developers without requiring that all new features have new kernel semantics. I spoke of this with respect to [expanding the standard library](https://docs.google.com/presentation/d/1QSwQYJz4c1VESEKTWPqrAPbDn_y9lTBBjaWRjej1c-w/view#slide=id.p). 199 | 200 | 4. Composition, while extending is nice it doesn't always allow for simple chaining and composition of features. If we introduce `RekeyableMap` as a concrete base class it may conflict with other base classes that may be introduced like if there was `InsertIfMissingMap`. Since both are base classes it would not allow both features to be combined easily. 201 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Collection data normalization

Stage 2 Draft / July 25, 2019

Collection data normalization

1809 | 1810 | 1811 |

1Data normalization operations

1812 | 1813 | 1814 |

1.1NormalizeCoerceKey( collection , data )

1815 |
  1. If collection does not have [[CoerceKey]] or collection.[[CoerceKey]] is undefined
    1. Return data.
  2. Else, return Call(collection.[[CoerceKey]], collection, « data »). 1816 |
1817 |
1818 | 1819 | 1820 |

1.2NormalizeCoerceValue( collection , data )

1821 |
  1. If collection does not have [[CoerceValue]] or collection.[[CoerceValue]] is undefined
    1. Return data.
  2. Else, return Call(collection.[[CoerceValue]], collection, « data »). 1822 |
1823 |
1824 |
1825 | 1826 |

Modified algorithms

1827 | 1828 | 1829 |

2Keyed Collections

1830 | 1831 | 1832 |

2.1Map Objects

1833 |

Map objects are collections of key/value pairs where both the keys and values may be arbitrary ECMAScript language values. A distinct key value may only occur in one key/value pair within the Map's collection. Distinct key values are discriminated using the SameValueZero comparison algorithm.

1834 |

Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Map objects specification is only intended to describe the required observable semantics of Map objects. It is not intended to be a viable implementation model.

1835 | 1836 | 1837 |

2.1.1The Map Constructor

1838 |

The Map constructor:

1839 |
    1840 |
  • is the intrinsic object %Map%.
  • 1841 |
  • is the initial value of the Map property of the global object.
  • 1842 |
  • creates and initializes a new Map object when called as a constructor.
  • 1843 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 1844 |
  • is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified Map behaviour must include a super call to the Map constructor to create and initialize the subclass instance with the internal state necessary to support the Map.prototype built-in methods.
  • 1845 |
1846 | 1847 | 1848 |

2.1.1.1Map ( [ iterable [, options ] ] )

1849 |

When the Map function is called with optional argument iterable, the following steps are taken:

1850 |
  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", « [[MapData]], [[CoerceKey]], [[CoerceValue]] »).
  3. Set map.[[MapData]] to a new empty List.
  4. If options is not present, or is either undefined or null
    1. Set map.[[CoerceKey]] to undefined
    2. Set map.[[CoerceValue]] to undefined 1851 |
  5. Else,
    1. Set map.[[CoerceKey]] to ? Get(options, "coerceKey").
    2. If map.[[CoerceKey]] is null set map.[[CoerceKey]] to undefined
    3. Set map.[[CoerceValue]] to ? Get(options, "coerceValue").
    4. If map.[[CoerceValue]] is null set map.[[CoerceValue]] to undefined 1852 |
  6. If iterable is not present, or is either undefined or null, return map.
  7. Let adder be ? Get(map, "set").
  8. Return ? AddEntriesFromIterable(map, iterable, adder). 1853 |
1854 | Note
1855 |

If the parameter iterable is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a Map key and whose second element is the value to associate with that key.

1856 |
1857 |
1858 |
1859 | 1860 | 1861 |

2.1.2Properties of the Map Prototype Object

1862 |

The Map prototype object:

1863 |
    1864 |
  • is the intrinsic object %MapPrototype%.
  • 1865 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 1866 |
  • is an ordinary object.
  • 1867 |
  • does not have a [[MapData]] internal slot.
  • 1868 |
  • does not have a [[CoerceKey]] internal slot.
  • 1869 |
  • does not have a [[CoerceValue]] internal slot.
  • 1870 |
1871 | 1872 | 1873 |

2.1.2.1Map.prototype.delete ( key )

1874 |

The following steps are taken:

1875 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let entries be the List that is M.[[MapData]].
  6. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], normalizedKeykey) is true, then
      1. Set p.[[Key]] to empty.
      2. Set p.[[Value]] to empty.
      3. Return true.
  7. Return false. 1876 |
1877 | Note
1878 |

The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

1879 |
1880 |
1881 | 1882 | 1883 |

2.1.2.2Map.prototype.get ( key )

1884 |

The following steps are taken:

1885 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let entries be the List that is M.[[MapData]].
  6. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], normalizedKeykey) is true, return p.[[Value]].
  7. Return undefined. 1886 |
1887 |
1888 | 1889 | 1890 |

2.1.2.3Map.prototype.has ( key )

1891 |

The following steps are taken:

1892 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let entries be the List that is M.[[MapData]].
  6. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], normalizedKeykey) is true, return true.
  7. Return false. 1893 |
1894 |
1895 | 1896 | 1897 |

2.1.2.4Map.prototype.set ( key, value )

1898 |

The following steps are taken:

1899 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let normalizedValue be ? NormalizeCoerceValue(M, value).
  6. Let entries be the List that is M.[[MapData]].
  7. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], normalizedKeykey) is true, then
      1. Set p.[[Value]] to normalizedValuevalue.
      2. Return M.
  8. If key is -0, set key to +0.
  9. Let p be the Record { [[Key]]: key, [[Value]]: normalizedValuevalue }.
  10. Append p as the last element of entries.
  11. Return M. 1900 |
1901 |
1902 | 1903 | 1904 |

2.1.2.5Properties of Map Instances

1905 |

Map instances are ordinary objects that inherit properties from the Map prototype. Map instances also have [[MapData]], [[CoerceKey]], and [[CoerceValue]] internal slots.

1906 |
1907 |
1908 | 1909 | 1910 |

2.1.3Set Objects

1911 |

Set objects are collections of ECMAScript language values. A distinct value may only occur once as an element of a Set's collection. Distinct values are discriminated using the SameValueZero comparison algorithm.

1912 |

Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Set objects specification is only intended to describe the required observable semantics of Set objects. It is not intended to be a viable implementation model.

1913 | 1914 | 1915 |

2.1.3.1The Set Constructor

1916 |

The Set constructor:

1917 |
    1918 |
  • is the intrinsic object %Set%.
  • 1919 |
  • is the initial value of the Set property of the global object.
  • 1920 |
  • creates and initializes a new Set object when called as a constructor.
  • 1921 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 1922 |
  • is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified Set behaviour must include a super call to the Set constructor to create and initialize the subclass instance with the internal state necessary to support the Set.prototype built-in methods.
  • 1923 |
1924 | 1925 | 1926 |

2.1.3.1.1Set ( [ iterable [, options ]] )

1927 |

When the Set function is called with optional argument iterable, the following steps are taken:

1928 |
  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, "%SetPrototype%", « [[SetData]] »).
  3. Set set.[[SetData]] to a new empty List. 1929 |
  4. If options is not present, or is either undefined or null
    1. Set set.[[CoerceValue]] to undefined 1930 |
  5. Else,
    1. Set set.[[CoerceValue]] to ? Get(options, "coerceValue").
    2. If set.[[CoerceValue]] is null set set.[[CoerceValue]] to undefined 1931 |
  6. If iterable is not present, set iterable to undefined.
  7. If iterable is either undefined or null, return set.
  8. Let adder be ? Get(set, "add").
  9. If IsCallable(adder) is false, throw a TypeError exception.
  10. Let iteratorRecord be ? GetIterator(iterable).
  11. Repeat,
    1. Let next be ? IteratorStep(iteratorRecord).
    2. If next is false, return set.
    3. Let nextValue be ? IteratorValue(next).
    4. Let status be Call(adder, set, « nextValue »).
    5. If status is an abrupt completion, return ? IteratorClose(iteratorRecord, status). 1932 |
1933 |
1934 |
1935 | 1936 | 1937 |

2.1.3.2Properties of the Set Prototype Object

1938 |

The Set prototype object:

1939 |
    1940 |
  • is the intrinsic object %SetPrototype%.
  • 1941 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 1942 |
  • is an ordinary object.
  • 1943 |
  • does not have a [[SetData]] internal slot.
  • 1944 |
  • does not have a [[CoerceValue]] internal slot.
  • 1945 |
1946 | 1947 | 1948 |

2.1.3.2.1Set.prototype.add ( value )

1949 |

The following steps are taken:

1950 |
  1. Let S be the this value.
  2. If Type(S) is not Object, throw a TypeError exception.
  3. If S does not have a [[SetData]] internal slot, throw a TypeError exception.
  4. Let normalizedValue be ? NormalizeCoerceValue(S, value).
  5. Let entries be the List that is S.[[SetData]].
  6. For each e that is an element of entries, do
    1. If e is not empty and SameValueZero(e, normalizedValuevalue) is true, then
      1. Return S.
  7. If value is -0, set value to +0.
  8. Append value as the last element of entries.
  9. Return S. 1951 |
1952 |
1953 | 1954 | 1955 |

2.1.3.2.2Set.prototype.delete ( value )

1956 |

The following steps are taken:

1957 |
  1. Let S be the this value.
  2. If Type(S) is not Object, throw a TypeError exception.
  3. If S does not have a [[SetData]] internal slot, throw a TypeError exception.
  4. Let normalizedValue be ? NormalizeCoerceValue(S, value).
  5. Let entries be the List that is S.[[SetData]].
  6. For each e that is an element of entries, do
    1. If e is not empty and SameValueZero(e, normalizedValuevalue) is true, then
      1. Replace the element of entries whose value is e with an element whose value is empty.
      2. Return true.
  7. Return false. 1958 |
1959 | Note
1960 |

The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

1961 |
1962 |
1963 | 1964 | 1965 |

2.1.3.2.3Set.prototype.has ( value )

1966 |

The following steps are taken:

1967 |
  1. Let S be the this value.
  2. If Type(S) is not Object, throw a TypeError exception.
  3. If S does not have a [[SetData]] internal slot, throw a TypeError exception.
  4. Let normalizedValue be ? NormalizeCoerceValue(S, value).
  5. Let entries be the List that is S.[[SetData]].
  6. For each e that is an element of entries, do
    1. If e is not empty and SameValueZero(e, normalizedValuevalue) is true, return true.
  7. Return false. 1968 |
1969 |
1970 |
1971 |
1972 | 1973 | 1974 |

2.1.4WeakMap Objects

1975 |

WeakMap objects are collections of key/value pairs where the keys are objects and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects it holds as keys. If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.

1976 |

An implementation may impose an arbitrarily determined latency between the time a key/value pair of a WeakMap becomes inaccessible and the time when the key/value pair is removed from the WeakMap. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to observe a key of a WeakMap that does not require the observer to present the observed key.

1977 |

WeakMap objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of key/value pairs in the collection. The data structure used in this WeakMap objects specification are only intended to describe the required observable semantics of WeakMap objects. It is not intended to be a viable implementation model.

1978 | Note
1979 |

WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object in a manner that does not “leak” memory resources if, in the absence of the WeakMap or WeakSet, the object otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object mapping of weak map instances to keys. Alternatively each weak map may internally store its key to value mappings but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSets:

1980 |

Barry Hayes. 1997. Ephemerons: a new finalization mechanism. In Proceedings of the 12th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '97), A. Michael Berman (Ed.). ACM, New York, NY, USA, 176-183, http://doi.acm.org/10.1145/263698.263733.

1981 |

Alexandra Barros, Roberto Ierusalimschy, Eliminating Cycles in Weak Tables. Journal of Universal Computer Science - J.UCS, vol. 14, no. 21, pp. 3481-3497, 2008, http://www.jucs.org/jucs_14_21/eliminating_cycles_in_weak

1982 |
1983 | 1984 | 1985 |

2.1.4.1The WeakMap Constructor

1986 |

The WeakMap constructor:

1987 |
    1988 |
  • is the intrinsic object %WeakMap%.
  • 1989 |
  • is the initial value of the WeakMap property of the global object.
  • 1990 |
  • creates and initializes a new WeakMap object when called as a constructor.
  • 1991 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 1992 |
  • is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified WeakMap behaviour must include a super call to the WeakMap constructor to create and initialize the subclass instance with the internal state necessary to support the WeakMap.prototype built-in methods.
  • 1993 |
1994 | 1995 | 1996 |

2.1.4.1.1WeakMap ( [ iterable [, options ]] )

1997 |

When the WeakMap function is called with optional argument iterable, the following steps are taken:

1998 |
  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakMapPrototype%", « [[WeakMapData]] »).
  3. Set map.[[WeakMapData]] to a new empty List.
  4. If options is not present, or is either undefined or null
    1. Set map.[[CoerceKey]] to undefined
    2. Set map.[[CoerceValue]] to undefined 1999 |
  5. Else,
    1. Set map.[[CoerceKey]] to ? Get(options, "coerceKey").
    2. If map.[[CoerceKey]] is null set map.[[CoerceKey]] to undefined
    3. Set map.[[CoerceValue]] to ? Get(options, "coerceValue").
    4. If map.[[CoerceValue]] is null set map.[[CoerceValue]] to undefined 2000 |
  6. If iterable is not present, or is either undefined or null, return map.
  7. Let adder be ? Get(map, "set").
  8. Return ? AddEntriesFromIterable(map, iterable, adder). 2001 |
2002 | Note
2003 |

If the parameter iterable is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a WeakMap key and whose second element is the value to associate with that key.

2004 |
2005 |
2006 |
2007 | 2008 | 2009 |

2.1.4.2Properties of the WeakMap Prototype Object

2010 |

The WeakMap prototype object:

2011 |
    2012 |
  • is the intrinsic object %WeakMapPrototype%.
  • 2013 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 2014 |
  • is an ordinary object.
  • 2015 |
  • does not have a [[WeakMapData]] internal slot.
  • 2016 |
  • does not have a [[CoerceKey]] internal slot.
  • 2017 |
  • does not have a [[CoerceValue]] internal slot.
  • 2018 |
2019 | 2020 | 2021 |

2.1.4.2.1WeakMap.prototype.delete ( key )

2022 |

The following steps are taken:

2023 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let entries be the List that is M.[[WeakMapData]].
  6. If Type(key) is not Object, return false.
  7. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, then
      1. Set p.[[Key]] to empty.
      2. Set p.[[Value]] to empty.
      3. Return true.
  8. Return false. 2024 |
2025 | Note
2026 |

The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

2027 |
2028 |
2029 | 2030 | 2031 |

2.1.4.2.2WeakMap.prototype.get ( key )

2032 |

The following steps are taken:

2033 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let entries be the List that is M.[[WeakMapData]].
  6. If Type(key) is not Object, return undefined.
  7. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, return p.[[Value]].
  8. Return undefined. 2034 |
2035 |
2036 | 2037 | 2038 |

2.1.4.2.3WeakMap.prototype.has ( key )

2039 |

The following steps are taken:

2040 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let entries be the List that is M.[[WeakMapData]].
  6. If Type(key) is not Object, return false.
  7. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, return true.
  8. Return false. 2041 |
2042 |
2043 | 2044 | 2045 |

2.1.4.2.4WeakMap.prototype.set ( key, value )

2046 |

The following steps are taken:

2047 |
  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
  4. Let normalizedKey be ? NormalizeCoerceKey(M, key).
  5. Let normalizedValue be ? NormalizeCoerceValue(M, value).
  6. Let entries be the List that is M.[[WeakMapData]].
  7. If Type(key) is not Object, throw a TypeError exception.
  8. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
    1. If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, then
      1. Set p.[[Value]] to value.
      2. Return M.
  9. Let p be the Record { [[Key]]: key, [[Value]]: normalizedValuevalue }.
  10. Append p as the last element of entries.
  11. Return M. 2048 |
2049 |
2050 |
2051 | 2052 | 2053 |

2.1.4.3WeakSet Objects

2054 |

WeakSet objects are collections of objects. A distinct object may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object, but no mechanism is provided for enumerating the objects it holds. If an object that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that object is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such objects and any associated resources.

2055 |

An implementation may impose an arbitrarily determined latency between the time an object contained in a WeakSet becomes inaccessible and the time when the object is removed from the WeakSet. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to determine if a WeakSet contains a particular object that does not require the observer to present the observed object.

2056 |

WeakSet objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structure used in this WeakSet objects specification is only intended to describe the required observable semantics of WeakSet objects. It is not intended to be a viable implementation model.

2057 | Note
2058 |

See the NOTE in 2.1.4.

2059 |
2060 | 2061 | 2062 |

2.1.4.3.1The WeakSet Constructor

2063 |

The WeakSet constructor:

2064 |
    2065 |
  • is the intrinsic object %WeakSet%.
  • 2066 |
  • is the initial value of the WeakSet property of the global object.
  • 2067 |
  • creates and initializes a new WeakSet object when called as a constructor.
  • 2068 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 2069 |
  • is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified WeakSet behaviour must include a super call to the WeakSet constructor to create and initialize the subclass instance with the internal state necessary to support the WeakSet.prototype built-in methods.
  • 2070 |
2071 | 2072 | 2073 |

2.1.4.3.1.1WeakSet ( [ iterable ] )

2074 |

When the WeakSet function is called with optional argument iterable, the following steps are taken:

2075 |
  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakSetPrototype%", « [[WeakSetData]] »).
  3. Set set.[[WeakSetData]] to a new empty List.
  4. If options is not present, or is either undefined or null
    1. Set set.[[CoerceValue]] to undefined 2076 |
  5. Else,
    1. Set set.[[CoerceValue]] to ? Get(options, "coerceValue").
    2. If set.[[CoerceValue]] is null set set.[[CoerceValue]] to undefined 2077 |
  6. If iterable is not present, set iterable to undefined.
  7. If iterable is either undefined or null, return set.
  8. Let adder be ? Get(set, "add").
  9. If IsCallable(adder) is false, throw a TypeError exception.
  10. Let iteratorRecord be ? GetIterator(iterable).
  11. Repeat,
    1. Let next be ? IteratorStep(iteratorRecord).
    2. If next is false, return set.
    3. Let nextValue be ? IteratorValue(next).
    4. Let status be Call(adder, set, « nextValue »).
    5. If status is an abrupt completion, return ? IteratorClose(iteratorRecord, status). 2078 |
2079 |
2080 |
2081 | 2082 | 2083 |

2.1.4.3.2Properties of the WeakSet Prototype Object

2084 |

The WeakSet prototype object:

2085 |
    2086 |
  • is the intrinsic object %WeakSetPrototype%.
  • 2087 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 2088 |
  • is an ordinary object.
  • 2089 |
  • does not have a [[WeakSetData]] internal slot.
  • 2090 |
  • does not have a [[CoerceValue]] internal slot.
  • 2091 |
2092 | 2093 | 2094 |

2.1.4.3.2.1WeakSet.prototype.add ( value )

2095 |

The following steps are taken:

2096 |
  1. Let S be the this value.
  2. If Type(S) is not Object, throw a TypeError exception.
  3. If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception.
  4. Let normalizedValue be ? NormalizeCoerceValue(S, value).
  5. If Type(value) is not Object, throw a TypeError exception.
  6. Let entries be the List that is S.[[WeakSetData]].
  7. For each e that is an element of entries, do
    1. If e is not empty and SameValue(e, normalizedValuevalue) is true, then
      1. Return S.
  8. Append value as the last element of entries.
  9. Return S. 2097 |
2098 |
2099 | 2100 | 2101 |

2.1.4.3.2.2WeakSet.prototype.delete ( value )

2102 |

The following steps are taken:

2103 |
  1. Let S be the this value.
  2. If Type(S) is not Object, throw a TypeError exception.
  3. If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception.
  4. Let normalizedValue be ? NormalizeCoerceValue(S, value).
  5. If Type(value) is not Object, return false.
  6. Let entries be the List that is S.[[WeakSetData]].
  7. For each e that is an element of entries, do
    1. If e is not empty and SameValue(e, normalizedValuevalue) is true, then
      1. Replace the element of entries whose value is e with an element whose value is empty.
      2. Return true.
  8. Return false. 2104 |
2105 | Note
2106 |

The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

2107 |
2108 |
2109 | 2110 | 2111 |

2.1.4.3.2.3WeakSet.prototype.has ( value )

2112 |

The following steps are taken:

2113 |
  1. Let S be the this value.
  2. If Type(S) is not Object, throw a TypeError exception.
  3. If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception.
  4. Let normalizedValue be ? NormalizeCoerceValue(S, value).
  5. Let entries be the List that is S.[[WeakSetData]].
  6. If Type(value) is not Object, return false.
  7. For each e that is an element of entries, do
    1. If e is not empty and SameValue(e, normalizedValuevalue) is true, return true.
  8. Return false. 2114 |
2115 |
2116 |
2117 |
2118 |
2119 |

ACopyright & Software License

2120 | 2121 |

Copyright Notice

2122 |

© 2019 Bradley Farias

2123 | 2124 |

Software License

2125 |

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.

2126 | 2127 |

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

2128 | 2129 |
    2130 |
  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. 2131 |
  3. 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.
  4. 2132 |
  5. 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.
  6. 2133 |
2134 | 2135 |

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.

2136 | 2137 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "ecmarkup spec.emu > index.html" 4 | }, 5 | "dependencies": { 6 | "ecmarkup": "3.16.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec.emu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
  7 | title: Collection data normalization
  8 | stage: 2
  9 | contributors: Bradley Farias
 10 | 
11 | 12 | 13 |

Data normalization operations

14 | 15 | 16 |

NormalizeCoerceKey( _collection_ , _data_ )

17 | 18 | 1. Let _target_ be _collection_.[[CoerceHandler]]. 19 | 1. Let _trap_ be ? GetMethod(_target_, `"coerceKey"`). 20 | 1. If _trap_ is undefined 21 | 1. Return _data_. 22 | 1. return Call(_trap_, _target_, « _data_, _collection_ »). 23 | 24 |
25 | 26 | 27 |

NormalizeCoerceValue( _collection_ , _data_ )

28 | 29 | 1. Let _target_ be _collection_.[[CoerceHandler]]. 30 | 1. Let _trap_ be ? GetMethod(_target_, `"coerceValue"`). 31 | 1. If _trap_ is undefined 32 | 1. Return _data_. 33 | 1. return Call(_trap_, _target_, « _data_, _collection_ »). 34 | 35 |
36 |
37 | 38 |

Modified algorithms

39 | 40 | 41 |

Keyed Collections

42 | 43 | 44 |

Map Objects

45 |

Map objects are collections of key/value pairs where both the keys and values may be arbitrary ECMAScript language values. A distinct key value may only occur in one key/value pair within the Map's collection. Distinct key values are discriminated using the SameValueZero comparison algorithm.

46 |

Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Map objects specification is only intended to describe the required observable semantics of Map objects. It is not intended to be a viable implementation model.

47 | 48 | 49 |

The Map Constructor

50 |

The Map constructor:

51 |
    52 |
  • is the intrinsic object %Map%.
  • 53 |
  • is the initial value of the `Map` property of the global object.
  • 54 |
  • creates and initializes a new Map object when called as a constructor.
  • 55 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 56 |
  • is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `Map` behaviour must include a `super` call to the `Map` constructor to create and initialize the subclass instance with the internal state necessary to support the `Map.prototype` built-in methods.
  • 57 |
58 | 59 | 60 |

Map ( [ _iterable_ [, _options_ ] ] )

61 |

When the `Map` function is called with optional argument _iterable_, the following steps are taken:

62 | 63 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 64 | 1. Let _map_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%MapPrototype%"`, « [[MapData]], [[CoerceHandler]] »). 65 | 1. Set _map_.[[MapData]] to a new empty List. 66 | 1. If Type(_options_) is Object, 67 | 1. Set _map_.[[CoerceHandler]] to _options_. 68 | 69 | 1. Else, 70 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*). 71 | 72 | 1. If _iterable_ is not present, or is either *undefined* or *null*, return _map_. 73 | 1. Let _adder_ be ? Get(_map_, `"set"`). 74 | 1. Return ? AddEntriesFromIterable(_map_, _iterable_, _adder_). 75 | 76 | 77 |

If the parameter _iterable_ is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a Map key and whose second element is the value to associate with that key.

78 |
79 |
80 |
81 | 82 | 83 |

Properties of the Map Prototype Object

84 |

The Map prototype object:

85 |
    86 |
  • is the intrinsic object %MapPrototype%.
  • 87 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 88 |
  • is an ordinary object.
  • 89 |
  • does not have a [[MapData]] internal slot.
  • 90 |
  • does not have a [[CoerceHandler]] internal slot.
  • 91 |
92 | 93 | 94 |

Map.prototype.delete ( _key_ )

95 |

The following steps are taken:

96 | 97 | 1. Let _M_ be the *this* value. 98 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 99 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception. 100 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 101 | 1. Let _entries_ be the List that is _M_.[[MapData]]. 102 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 103 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, then 104 | 1. Set _p_.[[Key]] to ~empty~. 105 | 1. Set _p_.[[Value]] to ~empty~. 106 | 1. Return *true*. 107 | 1. Return *false*. 108 | 109 | 110 |

The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

111 |
112 |
113 | 114 | 115 |

Map.prototype.get ( _key_ )

116 |

The following steps are taken:

117 | 118 | 1. Let _M_ be the *this* value. 119 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 120 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception. 121 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 122 | 1. Let _entries_ be the List that is _M_.[[MapData]]. 123 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 124 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, return _p_.[[Value]]. 125 | 1. Return *undefined*. 126 | 127 |
128 | 129 | 130 |

Map.prototype.has ( _key_ )

131 |

The following steps are taken:

132 | 133 | 1. Let _M_ be the *this* value. 134 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 135 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception. 136 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 137 | 1. Let _entries_ be the List that is _M_.[[MapData]]. 138 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 139 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, return *true*. 140 | 1. Return *false*. 141 | 142 |
143 | 144 | 145 |

Map.prototype.set ( _key_, _value_ )

146 |

The following steps are taken:

147 | 148 | 1. Let _M_ be the *this* value. 149 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 150 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception. 151 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 152 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_M_, _value_). 153 | 1. Let _entries_ be the List that is _M_.[[MapData]]. 154 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 155 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, then 156 | 1. Set _p_.[[Value]] to _normalizedValue__value_. 157 | 1. Return _M_. 158 | 1. If _key_ is *-0*, set _key_ to *+0*. 159 | 1. Let _p_ be the Record { [[Key]]: _key_, [[Value]]: _normalizedValue__value_ }. 160 | 1. Append _p_ as the last element of _entries_. 161 | 1. Return _M_. 162 | 163 |
164 | 165 | 166 |

Properties of Map Instances

167 |

Map instances are ordinary objects that inherit properties from the Map prototype. Map instances also have [[MapData]], and [[CoerceHandler]] internal slots.

168 |
169 |
170 | 171 | 172 |

Set Objects

173 |

Set objects are collections of ECMAScript language values. A distinct value may only occur once as an element of a Set's collection. Distinct values are discriminated using the SameValueZero comparison algorithm.

174 |

Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Set objects specification is only intended to describe the required observable semantics of Set objects. It is not intended to be a viable implementation model.

175 | 176 | 177 |

The Set Constructor

178 |

The Set constructor:

179 |
    180 |
  • is the intrinsic object %Set%.
  • 181 |
  • is the initial value of the `Set` property of the global object.
  • 182 |
  • creates and initializes a new Set object when called as a constructor.
  • 183 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 184 |
  • is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `Set` behaviour must include a `super` call to the `Set` constructor to create and initialize the subclass instance with the internal state necessary to support the `Set.prototype` built-in methods.
  • 185 |
186 | 187 | 188 |

Set ( [ _iterable_ [, _options_ ]] )

189 |

When the `Set` function is called with optional argument _iterable_, the following steps are taken:

190 | 191 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 192 | 1. Let _set_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%SetPrototype%"`, « [[SetData]] »). 193 | 1. Set _set_.[[SetData]] to a new empty List. 194 | 195 | 1. If Type(_options_) is Object, 196 | 1. Set _map_.[[CoerceHandler]] to _options_. 197 | 198 | 1. Else, 199 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*). 200 | 201 | 1. If _iterable_ is not present, set _iterable_ to *undefined*. 202 | 1. If _iterable_ is either *undefined* or *null*, return _set_. 203 | 1. Let _adder_ be ? Get(_set_, `"add"`). 204 | 1. If IsCallable(_adder_) is *false*, throw a *TypeError* exception. 205 | 1. Let _iteratorRecord_ be ? GetIterator(_iterable_). 206 | 1. Repeat, 207 | 1. Let _next_ be ? IteratorStep(_iteratorRecord_). 208 | 1. If _next_ is *false*, return _set_. 209 | 1. Let _nextValue_ be ? IteratorValue(_next_). 210 | 1. Let _status_ be Call(_adder_, _set_, « _nextValue_ »). 211 | 1. If _status_ is an abrupt completion, return ? IteratorClose(_iteratorRecord_, _status_). 212 | 213 |
214 |
215 | 216 | 217 |

Properties of the Set Prototype Object

218 |

The Set prototype object:

219 |
    220 |
  • is the intrinsic object %SetPrototype%.
  • 221 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 222 |
  • is an ordinary object.
  • 223 |
  • does not have a [[SetData]] internal slot.
  • 224 |
  • does not have a [[CoerceHandler]] internal slot.
  • 225 |
226 | 227 | 228 |

Set.prototype.add ( _value_ )

229 |

The following steps are taken:

230 | 231 | 1. Let _S_ be the *this* value. 232 | 1. If Type(_S_) is not Object, throw a *TypeError* exception. 233 | 1. If _S_ does not have a [[SetData]] internal slot, throw a *TypeError* exception. 234 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_). 235 | 1. Let _entries_ be the List that is _S_.[[SetData]]. 236 | 1. For each _e_ that is an element of _entries_, do 237 | 1. If _e_ is not ~empty~ and SameValueZero(_e_, _normalizedValue__value_) is *true*, then 238 | 1. Return _S_. 239 | 1. If _value_ is *-0*, set _value_ to *+0*. 240 | 1. Append _value_ as the last element of _entries_. 241 | 1. Return _S_. 242 | 243 |
244 | 245 | 246 |

Set.prototype.delete ( _value_ )

247 |

The following steps are taken:

248 | 249 | 1. Let _S_ be the *this* value. 250 | 1. If Type(_S_) is not Object, throw a *TypeError* exception. 251 | 1. If _S_ does not have a [[SetData]] internal slot, throw a *TypeError* exception. 252 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_). 253 | 1. Let _entries_ be the List that is _S_.[[SetData]]. 254 | 1. For each _e_ that is an element of _entries_, do 255 | 1. If _e_ is not ~empty~ and SameValueZero(_e_, _normalizedValue__value_) is *true*, then 256 | 1. Replace the element of _entries_ whose value is _e_ with an element whose value is ~empty~. 257 | 1. Return *true*. 258 | 1. Return *false*. 259 | 260 | 261 |

The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

262 |
263 |
264 | 265 | 266 |

Set.prototype.has ( _value_ )

267 |

The following steps are taken:

268 | 269 | 1. Let _S_ be the *this* value. 270 | 1. If Type(_S_) is not Object, throw a *TypeError* exception. 271 | 1. If _S_ does not have a [[SetData]] internal slot, throw a *TypeError* exception. 272 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_). 273 | 1. Let _entries_ be the List that is _S_.[[SetData]]. 274 | 1. For each _e_ that is an element of _entries_, do 275 | 1. If _e_ is not ~empty~ and SameValueZero(_e_, _normalizedValue__value_) is *true*, return *true*. 276 | 1. Return *false*. 277 | 278 |
279 |
280 |
281 | 282 | 283 |

WeakMap Objects

284 |

WeakMap objects are collections of key/value pairs where the keys are objects and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects it holds as keys. If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.

285 |

An implementation may impose an arbitrarily determined latency between the time a key/value pair of a WeakMap becomes inaccessible and the time when the key/value pair is removed from the WeakMap. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to observe a key of a WeakMap that does not require the observer to present the observed key.

286 |

WeakMap objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of key/value pairs in the collection. The data structure used in this WeakMap objects specification are only intended to describe the required observable semantics of WeakMap objects. It is not intended to be a viable implementation model.

287 | 288 |

WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object in a manner that does not “leak” memory resources if, in the absence of the WeakMap or WeakSet, the object otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object mapping of weak map instances to keys. Alternatively each weak map may internally store its key to value mappings but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSets:

289 |

Barry Hayes. 1997. Ephemerons: a new finalization mechanism. In Proceedings of the 12th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '97), A. Michael Berman (Ed.). ACM, New York, NY, USA, 176-183, http://doi.acm.org/10.1145/263698.263733.

290 |

Alexandra Barros, Roberto Ierusalimschy, Eliminating Cycles in Weak Tables. Journal of Universal Computer Science - J.UCS, vol. 14, no. 21, pp. 3481-3497, 2008, http://www.jucs.org/jucs_14_21/eliminating_cycles_in_weak

291 |
292 | 293 | 294 |

The WeakMap Constructor

295 |

The WeakMap constructor:

296 |
    297 |
  • is the intrinsic object %WeakMap%.
  • 298 |
  • is the initial value of the `WeakMap` property of the global object.
  • 299 |
  • creates and initializes a new WeakMap object when called as a constructor.
  • 300 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 301 |
  • is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `WeakMap` behaviour must include a `super` call to the `WeakMap` constructor to create and initialize the subclass instance with the internal state necessary to support the `WeakMap.prototype` built-in methods.
  • 302 |
303 | 304 | 305 |

WeakMap ( [ _iterable_ [, _options_ ]] )

306 |

When the `WeakMap` function is called with optional argument _iterable_, the following steps are taken:

307 | 308 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 309 | 1. Let _map_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%WeakMapPrototype%"`, « [[WeakMapData]] »). 310 | 1. Set _map_.[[WeakMapData]] to a new empty List. 311 | 1. If Type(_options_) is Object, 312 | 1. Set _map_.[[CoerceHandler]] to _options_. 313 | 314 | 1. Else, 315 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*). 316 | 317 | 1. If _iterable_ is not present, or is either *undefined* or *null*, return _map_. 318 | 1. Let _adder_ be ? Get(_map_, `"set"`). 319 | 1. Return ? AddEntriesFromIterable(_map_, _iterable_, _adder_). 320 | 321 | 322 |

If the parameter _iterable_ is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a WeakMap key and whose second element is the value to associate with that key.

323 |
324 |
325 |
326 | 327 | 328 |

Properties of the WeakMap Prototype Object

329 |

The WeakMap prototype object:

330 |
    331 |
  • is the intrinsic object %WeakMapPrototype%.
  • 332 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 333 |
  • is an ordinary object.
  • 334 |
  • does not have a [[WeakMapData]] internal slot.
  • 335 |
  • does not have a [[CoerceHandler]] internal slot.
  • 336 |
337 | 338 | 339 |

WeakMap.prototype.delete ( _key_ )

340 |

The following steps are taken:

341 | 342 | 1. Let _M_ be the *this* value. 343 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 344 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception. 345 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 346 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]]. 347 | 1. If Type(_key_) is not Object, return *false*. 348 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 349 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, then 350 | 1. Set _p_.[[Key]] to ~empty~. 351 | 1. Set _p_.[[Value]] to ~empty~. 352 | 1. Return *true*. 353 | 1. Return *false*. 354 | 355 | 356 |

The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

357 |
358 |
359 | 360 | 361 |

WeakMap.prototype.get ( _key_ )

362 |

The following steps are taken:

363 | 364 | 1. Let _M_ be the *this* value. 365 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 366 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception. 367 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 368 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]]. 369 | 1. If Type(_key_) is not Object, return *undefined*. 370 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 371 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, return _p_.[[Value]]. 372 | 1. Return *undefined*. 373 | 374 |
375 | 376 | 377 |

WeakMap.prototype.has ( _key_ )

378 |

The following steps are taken:

379 | 380 | 1. Let _M_ be the *this* value. 381 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 382 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception. 383 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 384 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]]. 385 | 1. If Type(_key_) is not Object, return *false*. 386 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 387 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, return *true*. 388 | 1. Return *false*. 389 | 390 |
391 | 392 | 393 |

WeakMap.prototype.set ( _key_, _value_ )

394 |

The following steps are taken:

395 | 396 | 1. Let _M_ be the *this* value. 397 | 1. If Type(_M_) is not Object, throw a *TypeError* exception. 398 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception. 399 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_). 400 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_M_, _value_). 401 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]]. 402 | 1. If Type(_key_) is not Object, throw a *TypeError* exception. 403 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do 404 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, then 405 | 1. Set _p_.[[Value]] to _value_. 406 | 1. Return _M_. 407 | 1. Let _p_ be the Record { [[Key]]: _key_, [[Value]]: _normalizedValue__value_ }. 408 | 1. Append _p_ as the last element of _entries_. 409 | 1. Return _M_. 410 | 411 |
412 |
413 | 414 | 415 |

WeakSet Objects

416 |

WeakSet objects are collections of objects. A distinct object may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object, but no mechanism is provided for enumerating the objects it holds. If an object that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that object is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such objects and any associated resources.

417 |

An implementation may impose an arbitrarily determined latency between the time an object contained in a WeakSet becomes inaccessible and the time when the object is removed from the WeakSet. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to determine if a WeakSet contains a particular object that does not require the observer to present the observed object.

418 |

WeakSet objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structure used in this WeakSet objects specification is only intended to describe the required observable semantics of WeakSet objects. It is not intended to be a viable implementation model.

419 | 420 |

See the NOTE in .

421 |
422 | 423 | 424 |

The WeakSet Constructor

425 |

The WeakSet constructor:

426 |
    427 |
  • is the intrinsic object %WeakSet%.
  • 428 |
  • is the initial value of the `WeakSet` property of the global object.
  • 429 |
  • creates and initializes a new WeakSet object when called as a constructor.
  • 430 |
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • 431 |
  • is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `WeakSet` behaviour must include a `super` call to the `WeakSet` constructor to create and initialize the subclass instance with the internal state necessary to support the `WeakSet.prototype` built-in methods.
  • 432 |
433 | 434 | 435 |

WeakSet ( [ _iterable_ ] )

436 |

When the `WeakSet` function is called with optional argument _iterable_, the following steps are taken:

437 | 438 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 439 | 1. Let _set_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%WeakSetPrototype%"`, « [[WeakSetData]] »). 440 | 1. Set _set_.[[WeakSetData]] to a new empty List. 441 | 1. If Type(_options_) is Object, 442 | 1. Set _map_.[[CoerceHandler]] to _options_. 443 | 444 | 1. Else, 445 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*). 446 | 447 | 1. If _iterable_ is not present, set _iterable_ to *undefined*. 448 | 1. If _iterable_ is either *undefined* or *null*, return _set_. 449 | 1. Let _adder_ be ? Get(_set_, `"add"`). 450 | 1. If IsCallable(_adder_) is *false*, throw a *TypeError* exception. 451 | 1. Let _iteratorRecord_ be ? GetIterator(_iterable_). 452 | 1. Repeat, 453 | 1. Let _next_ be ? IteratorStep(_iteratorRecord_). 454 | 1. If _next_ is *false*, return _set_. 455 | 1. Let _nextValue_ be ? IteratorValue(_next_). 456 | 1. Let _status_ be Call(_adder_, _set_, « _nextValue_ »). 457 | 1. If _status_ is an abrupt completion, return ? IteratorClose(_iteratorRecord_, _status_). 458 | 459 |
460 |
461 | 462 | 463 |

Properties of the WeakSet Prototype Object

464 |

The WeakSet prototype object:

465 |
    466 |
  • is the intrinsic object %WeakSetPrototype%.
  • 467 |
  • has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
  • 468 |
  • is an ordinary object.
  • 469 |
  • does not have a [[WeakSetData]] internal slot.
  • 470 |
  • does not have a [[CoerceHandler]] internal slot.
  • 471 |
472 | 473 | 474 |

WeakSet.prototype.add ( _value_ )

475 |

The following steps are taken:

476 | 477 | 1. Let _S_ be the *this* value. 478 | 1. If Type(_S_) is not Object, throw a *TypeError* exception. 479 | 1. If _S_ does not have a [[WeakSetData]] internal slot, throw a *TypeError* exception. 480 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_). 481 | 1. If Type(_value_) is not Object, throw a *TypeError* exception. 482 | 1. Let _entries_ be the List that is _S_.[[WeakSetData]]. 483 | 1. For each _e_ that is an element of _entries_, do 484 | 1. If _e_ is not ~empty~ and SameValue(_e_, _normalizedValue__value_) is *true*, then 485 | 1. Return _S_. 486 | 1. Append _value_ as the last element of _entries_. 487 | 1. Return _S_. 488 | 489 |
490 | 491 | 492 |

WeakSet.prototype.delete ( _value_ )

493 |

The following steps are taken:

494 | 495 | 1. Let _S_ be the *this* value. 496 | 1. If Type(_S_) is not Object, throw a *TypeError* exception. 497 | 1. If _S_ does not have a [[WeakSetData]] internal slot, throw a *TypeError* exception. 498 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_). 499 | 1. If Type(_value_) is not Object, return *false*. 500 | 1. Let _entries_ be the List that is _S_.[[WeakSetData]]. 501 | 1. For each _e_ that is an element of _entries_, do 502 | 1. If _e_ is not ~empty~ and SameValue(_e_, _normalizedValue__value_) is *true*, then 503 | 1. Replace the element of _entries_ whose value is _e_ with an element whose value is ~empty~. 504 | 1. Return *true*. 505 | 1. Return *false*. 506 | 507 | 508 |

The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.

509 |
510 |
511 | 512 | 513 |

WeakSet.prototype.has ( _value_ )

514 |

The following steps are taken:

515 | 516 | 1. Let _S_ be the *this* value. 517 | 1. If Type(_S_) is not Object, throw a *TypeError* exception. 518 | 1. If _S_ does not have a [[WeakSetData]] internal slot, throw a *TypeError* exception. 519 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_). 520 | 1. Let _entries_ be the List that is _S_.[[WeakSetData]]. 521 | 1. If Type(_value_) is not Object, return *false*. 522 | 1. For each _e_ that is an element of _entries_, do 523 | 1. If _e_ is not ~empty~ and SameValue(_e_, _normalizedValue__value_) is *true*, return *true*. 524 | 1. Return *false*. 525 | 526 |
527 |
528 |
529 | --------------------------------------------------------------------------------