User:Waldo/Internationalization API: Difference between revisions

Jump to navigation Jump to search
Update for lazy Intl object initialization work
(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 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.
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 ===
Confirmed users
446

edits

Navigation menu