Index: src/js/i18n.js |
diff --git a/src/js/i18n.js b/src/js/i18n.js |
index cbb664b526973beb6bb7df7d1fc256b31a4e511d..78e2fb5492449978c469203b5370aba2d8794bbb 100644 |
--- a/src/js/i18n.js |
+++ b/src/js/i18n.js |
@@ -29,6 +29,7 @@ var GlobalDate = global.Date; |
var GlobalNumber = global.Number; |
var GlobalRegExp = global.RegExp; |
var GlobalString = global.String; |
+var GlobalSymbol = global.Symbol; |
var MakeError; |
var MakeRangeError; |
var MakeTypeError; |
@@ -84,31 +85,31 @@ function InstallConstructor(object, name, func) { |
%ToFastProperties(object); |
} |
+var fallbackSymbol = GlobalSymbol("intl fallback"); |
+ |
/** |
* Adds bound method to the prototype of the given object. |
*/ |
-function AddBoundMethod(obj, methodName, implementation, length) { |
+function AddBoundMethod(obj, methodName, implementation, length, typename, compat) { |
%CheckIsBootstrapping(); |
var internalName = %CreatePrivateSymbol(methodName); |
var getter = function() { |
- if (!%IsInitializedIntlObject(this)) { |
- throw MakeTypeError(kMethodCalledOnWrongObject, methodName); |
- } |
- if (IS_UNDEFINED(this[internalName])) { |
+ var receiver = Unwrap(this, typename, obj, methodName, compat); |
+ if (IS_UNDEFINED(receiver[internalName])) { |
var boundMethod; |
if (IS_UNDEFINED(length) || length === 2) { |
- boundMethod = (x, y) => implementation(this, x, y); |
+ boundMethod = (x, y) => implementation(receiver, x, y); |
} else if (length === 1) { |
- boundMethod = x => implementation(this, x); |
+ boundMethod = x => implementation(receiver, x); |
} else { |
boundMethod = (...args) => { |
// DateTimeFormat.format needs to be 0 arg method, but can stil |
// receive optional dateValue param. If one was provided, pass it |
// along. |
if (args.length > 0) { |
- return implementation(this, args[0]); |
+ return implementation(receiver, args[0]); |
} else { |
- return implementation(this); |
+ return implementation(receiver); |
} |
} |
} |
@@ -118,14 +119,51 @@ function AddBoundMethod(obj, methodName, implementation, length) { |
%FunctionSetName(boundMethod, '__bound' + methodName + '__'); |
%FunctionRemovePrototype(boundMethod); |
%SetNativeFlag(boundMethod); |
- this[internalName] = boundMethod; |
+ receiver[internalName] = boundMethod; |
} |
- return this[internalName]; |
+ return receiver[internalName]; |
}; |
InstallGetter(obj.prototype, methodName, getter, DONT_ENUM); |
} |
+function IntlConstruct(receiver, constructor, initializer, newTarget, args, |
+ compat) { |
+ var locales = args[0]; |
+ var options = args[1]; |
+ |
+ if (IS_UNDEFINED(newTarget)) { |
+ if (compat && receiver instanceof constructor) { |
+ let success = ObjectDefineProperty(receiver, fallbackSymbol, |
+ { value: new constructor(locales, options) }); |
+ if (!success) { |
+ throw MakeTypeError(kReinitializeIntl, constructor); |
+ } |
+ return receiver; |
+ } |
+ |
+ return new constructor(locales, options); |
+ } |
+ |
+ return initializer(receiver, locales, options); |
+} |
+ |
+ |
+ |
+function Unwrap(receiver, typename, constructor, method, compat) { |
+ if (!%IsInitializedIntlObjectOfType(receiver, typename)) { |
+ if (compat && receiver instanceof constructor) { |
+ let fallback = receiver[fallbackSymbol]; |
+ if (%IsInitializedIntlObjectOfType(fallback, typename)) { |
+ return fallback; |
+ } |
+ } |
+ throw MakeTypeError(kIncompatibleMethodReceiver, method, receiver); |
+ } |
+ return receiver; |
+} |
+ |
+ |
// ------------------------------------------------------------------- |
var Intl = {}; |
@@ -1002,18 +1040,11 @@ function initializeCollator(collator, locales, options) { |
* |
* @constructor |
*/ |
-InstallConstructor(Intl, 'Collator', function() { |
- var locales = arguments[0]; |
- var options = arguments[1]; |
- |
- if (!this || this === Intl) { |
- // Constructor is called as a function. |
- return new Intl.Collator(locales, options); |
- } |
- |
- return initializeCollator(TO_OBJECT(this), locales, options); |
- } |
-); |
+function Collator() { |
+ return IntlConstruct(this, Collator, initializeCollator, new.target, |
+ arguments); |
+} |
+InstallConstructor(Intl, 'Collator', Collator); |
/** |
@@ -1024,11 +1055,7 @@ InstallFunction(Intl.Collator.prototype, 'resolvedOptions', function() { |
throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
} |
- if (!%IsInitializedIntlObjectOfType(this, 'collator')) { |
- throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "Collator"); |
- } |
- |
- var coll = this; |
+ var coll = Unwrap(this, 'collator', Collator, 'resolvedOptions', true); |
var locale = getOptimalLanguageTag(coll[resolvedSymbol].requestedLocale, |
coll[resolvedSymbol].locale); |
@@ -1077,7 +1104,7 @@ function compare(collator, x, y) { |
}; |
-AddBoundMethod(Intl.Collator, 'compare', compare, 2); |
+AddBoundMethod(Intl.Collator, 'compare', compare, 2, 'collator', Collator); |
/** |
* Verifies that the input is a well-formed ISO 4217 currency code. |
@@ -1246,18 +1273,11 @@ function initializeNumberFormat(numberFormat, locales, options) { |
* |
* @constructor |
*/ |
-InstallConstructor(Intl, 'NumberFormat', function() { |
- var locales = arguments[0]; |
- var options = arguments[1]; |
- |
- if (!this || this === Intl) { |
- // Constructor is called as a function. |
- return new Intl.NumberFormat(locales, options); |
- } |
- |
- return initializeNumberFormat(TO_OBJECT(this), locales, options); |
- } |
-); |
+function NumberFormat() { |
+ return IntlConstruct(this, NumberFormat, initializeNumberFormat, new.target, |
+ arguments, true); |
+} |
+InstallConstructor(Intl, 'NumberFormat', NumberFormat); |
/** |
@@ -1268,11 +1288,8 @@ InstallFunction(Intl.NumberFormat.prototype, 'resolvedOptions', function() { |
throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
} |
- if (!%IsInitializedIntlObjectOfType(this, 'numberformat')) { |
- throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "NumberFormat"); |
- } |
- |
- var format = this; |
+ var format = Unwrap(this, 'numberformat', NumberFormat, |
+ 'resolvedOptions', true); |
var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale, |
format[resolvedSymbol].locale); |
@@ -1346,8 +1363,8 @@ function parseNumber(formatter, value) { |
} |
-AddBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1); |
-AddBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1); |
+AddBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1, 'numberformat', true); |
+AddBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1, 'numberformat'); |
/** |
* Returns a string that matches LDML representation of the options object. |
@@ -1640,18 +1657,11 @@ function initializeDateTimeFormat(dateFormat, locales, options) { |
* |
* @constructor |
*/ |
-InstallConstructor(Intl, 'DateTimeFormat', function() { |
- var locales = arguments[0]; |
- var options = arguments[1]; |
- |
- if (!this || this === Intl) { |
- // Constructor is called as a function. |
- return new Intl.DateTimeFormat(locales, options); |
- } |
- |
- return initializeDateTimeFormat(TO_OBJECT(this), locales, options); |
- } |
-); |
+function DateTimeFormat() { |
+ return IntlConstruct(this, DateTimeFormat, initializeDateTimeFormat, |
+ new.target, arguments, true); |
+} |
+InstallConstructor(Intl, 'DateTimeFormat', DateTimeFormat); |
/** |
@@ -1662,9 +1672,8 @@ InstallFunction(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() { |
throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
} |
- if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) { |
- throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat"); |
- } |
+ var format = Unwrap(this, 'dateformat', DateTimeFormat, |
+ 'resolvedOptions', true); |
/** |
* Maps ICU calendar names into LDML type. |
@@ -1769,8 +1778,8 @@ function parseDate(formatter, value) { |
// 0 because date is optional argument. |
-AddBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0); |
-AddBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1); |
+AddBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0, 'dateformat', true); |
+AddBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1, 'dateformat'); |
/** |
@@ -1857,18 +1866,11 @@ function initializeBreakIterator(iterator, locales, options) { |
* |
* @constructor |
*/ |
-InstallConstructor(Intl, 'v8BreakIterator', function() { |
- var locales = arguments[0]; |
- var options = arguments[1]; |
- |
- if (!this || this === Intl) { |
- // Constructor is called as a function. |
- return new Intl.v8BreakIterator(locales, options); |
- } |
- |
- return initializeBreakIterator(TO_OBJECT(this), locales, options); |
- } |
-); |
+function v8BreakIterator() { |
+ return IntlConstruct(this, v8BreakIterator, initializeBreakIterator, |
+ new.target, arguments); |
+} |
+InstallConstructor(Intl, 'v8BreakIterator', v8BreakIterator); |
/** |
@@ -1880,9 +1882,7 @@ InstallFunction(Intl.v8BreakIterator.prototype, 'resolvedOptions', |
throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
} |
- if (!%IsInitializedIntlObjectOfType(this, 'breakiterator')) { |
- throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "v8BreakIterator"); |
- } |
+ var format = Unwrap(this, 'breakiterator', v8BreakIterator, 'resolvedOptions'); |
var segmenter = this; |
var locale = |
@@ -1956,11 +1956,11 @@ function breakType(iterator) { |
} |
-AddBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1); |
-AddBoundMethod(Intl.v8BreakIterator, 'first', first, 0); |
-AddBoundMethod(Intl.v8BreakIterator, 'next', next, 0); |
-AddBoundMethod(Intl.v8BreakIterator, 'current', current, 0); |
-AddBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0); |
+AddBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1, 'breakiterator'); |
+AddBoundMethod(Intl.v8BreakIterator, 'first', first, 0, 'breakiterator'); |
+AddBoundMethod(Intl.v8BreakIterator, 'next', next, 0, 'breakiterator'); |
+AddBoundMethod(Intl.v8BreakIterator, 'current', current, 0, 'breakiterator'); |
+AddBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0, 'breakiterator'); |
// Save references to Intl objects and methods we use, for added security. |
var savedObjects = { |