Confirmed users
446
edits
(Add more implementation details info) |
No edit summary |
||
| Line 105: | Line 105: | ||
=== Implementation === | === Implementation === | ||
ECMA-402 currently exposes <code>Intl.Collator</code>, <code>Intl.DateTimeFormatter</code>, and <code>Intl.NumberFormatter</code> objects. The spec also permits initializing an existing object as one of these, for a wrinkle. The fundamental ICU data structures providing the relevant functionality are <code>UCollator*</code>, <code>UNumberFormat*</code>, and <code>UDateFormat*</code>, opaque pointers all. Instances are created using <code>u{col,num,date}_open</code>, passing in appropriate arguments. For objects ''created'' by the constructor, the pointer is stored in a reserved slot as a private value. For objects merely ''initialized'' by the constructor, the ICU data structures must be (inefficiently!) created anew every time. | ECMA-402 currently exposes <code>Intl.Collator</code>, <code>Intl.DateTimeFormatter</code>, and <code>Intl.NumberFormatter</code> objects. The spec also permits initializing an existing object as one of these, for a small wrinkle. The fundamental ICU data structures providing the relevant functionality are <code>UCollator*</code>, <code>UNumberFormat*</code>, and <code>UDateFormat*</code>, opaque pointers all. Instances are created using <code>u{col,num,date}_open</code>, passing in appropriate arguments. For objects ''created'' by the constructor, the pointer is stored in a reserved slot as a private value. For objects merely ''initialized'' by the constructor, the ICU data structures must be (inefficiently!) created anew every time. (This difference should not be observable, except through performance-timing, because the only structures consulted to create the ICU structure are internal ones , operations on which aren't observable.) | ||
Every object initialized as an Intl object has an associated set of internal properties. In ECMA-402 these properties are represented using ES5's traditional double-bracket notation: <code><nowiki>[[calendar]]</nowiki></code>, <code><nowiki>[[initializedIntlObject]]</nowiki></code>, and so on. The "ideal" means of implementing these properties would probably be ES6 private names, but they're not stable or well-understood enough to be specified yet (let alone implemented). In the meantime we associate ECMA-402 internal properties with objects using a weak map. Any object initialized as an <code>Intl</code> object has an internal <code><nowiki>[[initializedIntlObject]]</nowiki></code> property. This is implemented by placing all such objects as keys in a weak map (<code>internalsMap</code> in <code>builtin/Intl.js</code>). The corresponding value is an ''internals object''. The internals object is an | Every object initialized as an Intl object has an associated set of internal properties. In ECMA-402 these properties are represented using ES5's traditional double-bracket notation: <code><nowiki>[[calendar]]</nowiki></code>, <code><nowiki>[[initializedIntlObject]]</nowiki></code>, and so on. The "ideal" means of implementing these properties would probably be ES6 private names, but they're not stable or well-understood enough to be specified yet (let alone implemented). In the meantime we associate ECMA-402 internal properties with objects using a weak map. Any object initialized as an <code>Intl</code> object has an internal <code><nowiki>[[initializedIntlObject]]</nowiki></code> property. This is implemented by placing all such objects as keys in a weak map (<code>internalsMap</code> in <code>builtin/Intl.js</code>). The corresponding value is an ''internals object''. | ||
Checking whether an object has been initialized as an <code>Intl</code> object is encapsulated by the <code>isInitializedIntlObject</code> method in {{source|js/src/builtin/Intl.js}}. The <code>getInternals</code> function in the same file is used to encapsulate weak map access to an internals object. These methods ensure the weak map mechanism is only an implementation detail encoded in a very few places. | |||
Internals objects are objects with null <code><nowiki>[[Prototype]]</nowiki></code>, with properties corresponding to the other internal properties on the object, named naturally -- "calendar", "initializedDateTimeFormat", and so on (no brackets). Accessing any internal property is simply a matter of doing <code>internals.calendar</code>: this is safe because, with the <code><nowiki>[[Prototype]]</nowiki></code> nulled out, property accesses can't touch any script-visible state. Internal properties are added and set during the initialization process. They are lazily consulted to construct an ICU structure when collation/formatting/etc. actually occurs in the <code>js::intl_CompareStrings</code>, <code>js::intl_FormatNumber</code>, and <code>js::intl_FormatDateTime</code> functions. (Although not ''directly'' there, but rather in sub-methods called when the ICU structure isn't cached, or when the object was initialized as an <code>Intl</code> object but wasn't actually one -- see again the "inefficiently" bit above.) | |||
=== Known issues === | === Known issues === | ||