Confirmed users
446
edits
(Discuss the Intl test-skipping mechanism) |
(Update for lazy Intl object initialization work) |
||
| Line 117: | Line 117: | ||
=== Implementation === | === Implementation === | ||
ECMA-402 currently exposes <code>Intl.Collator</code>, <code>Intl.DateTimeFormat</code>, and <code>Intl.NumberFormat</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.) | ECMA-402 currently exposes <code>Intl.Collator</code>, <code>Intl.DateTimeFormat</code>, and <code>Intl.NumberFormat</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''. | 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 | 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>getIntlObjectInternals</code> and (less preferred) <code>getInternals</code> function in the same file are 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.) | Internals objects are objects with null <code><nowiki>[[Prototype]]</nowiki></code> and the properties <code>type</code>, <code>lazyData</code>, and <code>internalProps</code>. This structure permits internals objects to be ''lazily'' initialized. Initially, <code>type</code> is <code>"partial"</code>; lazy initialization changes this to <code>"Collator"</code>, <code>"DateTimeFormat"</code>, or <code>"NumberFormat"</code> and sets <code>lazyData</code> to the information necessary to compute full initialization info; finally, first use fully initializes, converting <code>lazyData</code> into an <code>internalProps</code> object containing the actual ECMA-402-defined internal properties. (For more details on this scheme, see <code>initializeIntlObject</code> and adjacent functions, as well as the class-specific initialization methods, in {{source|js/src/builtin/Intl.js}}.) | ||
, 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.) | |||
=== Care and feeding of the Internationalization API === | === Care and feeding of the Internationalization API === | ||