diff options
Diffstat (limited to 'js/src/builtin')
-rw-r--r-- | js/src/builtin/Array.js | 50 | ||||
-rw-r--r-- | js/src/builtin/Classes.js | 2 | ||||
-rw-r--r-- | js/src/builtin/Intl.cpp | 102 | ||||
-rw-r--r-- | js/src/builtin/Intl.js | 626 | ||||
-rw-r--r-- | js/src/builtin/IntlTimeZoneData.h | 2 | ||||
-rw-r--r-- | js/src/builtin/Map.js | 27 | ||||
-rw-r--r-- | js/src/builtin/ModuleObject.cpp | 4 | ||||
-rw-r--r-- | js/src/builtin/Promise.cpp | 53 | ||||
-rw-r--r-- | js/src/builtin/ReflectParse.cpp | 6 | ||||
-rw-r--r-- | js/src/builtin/RegExp.cpp | 140 | ||||
-rw-r--r-- | js/src/builtin/RegExp.js | 134 | ||||
-rw-r--r-- | js/src/builtin/RegExpLocalReplaceOpt.h.js | 36 | ||||
-rw-r--r-- | js/src/builtin/Set.js | 29 | ||||
-rw-r--r-- | js/src/builtin/TestingFunctions.cpp | 2 | ||||
-rw-r--r-- | js/src/builtin/TypedArray.js | 79 | ||||
-rw-r--r-- | js/src/builtin/TypedObject.cpp | 2 | ||||
-rw-r--r-- | js/src/builtin/Utilities.js | 12 | ||||
-rw-r--r-- | js/src/builtin/WeakMap.js | 27 | ||||
-rw-r--r-- | js/src/builtin/WeakSet.js | 29 |
19 files changed, 859 insertions, 503 deletions
diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 54b47b72f..45f90a7b8 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -784,7 +784,7 @@ function ArrayKeys() { return CreateArrayIterator(this, ITEM_KIND_KEY); } -// ES6 draft rev31 (2015/01/15) 22.1.2.1 Array.from(source[, mapfn[, thisArg]]). +// ES 2017 draft 0f10dba4ad18de92d47d421f378233a2eae8f077 22.1.2.1 function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { // Step 1. var C = this; @@ -795,44 +795,40 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn)); var T = thisArg; - // Steps 4-5. + // Step 4. var usingIterator = GetMethod(items, std_iterator); - // Step 6. + // Step 5. if (usingIterator !== undefined) { - // Steps 6.a-c. + // Steps 5.a-c. var A = IsConstructor(C) ? new C() : []; - // Steps 6.d-e. - var iterator = GetIterator(items, usingIterator); - - // Step 6.f. + // Step 5.d. var k = 0; - // Step 6.g. - // These steps cannot be implemented using a for-of loop. - // See <https://bugs.ecmascript.org/show_bug.cgi?id=2883>. - while (true) { - // Steps 6.g.i-iii. - var next = callContentFunction(iterator.next, iterator); - if (!IsObject(next)) - ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE); - - // Step 6.g.iv. - if (next.done) { - A.length = k; - return A; - } - - // Steps 6.g.v-vi. - var nextValue = next.value; + // Step 5.c, 5.e. + var iteratorWrapper = { [std_iterator]() { return GetIterator(items, usingIterator); } }; + for (var nextValue of allowContentIter(iteratorWrapper)) { + // Step 5.e.i. + // Disabled for performance reason. We won't hit this case on + // normal array, since _DefineDataProperty will throw before it. + // We could hit this when |A| is a proxy and it ignores + // |_DefineDataProperty|, but it happens only after too long loop. + /* + if (k >= 0x1fffffffffffff) + ThrowTypeError(JSMSG_TOO_LONG_ARRAY); + */ - // Steps 6.g.vii-viii. + // Steps 5.e.vi-vii. var mappedValue = mapping ? callContentFunction(mapfn, thisArg, nextValue, k) : nextValue; - // Steps 6.g.ix-xi. + // Steps 5.e.ii (reordered), 5.e.viii. _DefineDataProperty(A, k++, mappedValue); } + + // Step 5.e.iv. + A.length = k; + return A; } // Step 7. diff --git a/js/src/builtin/Classes.js b/js/src/builtin/Classes.js index d0f20b5fd..24841d605 100644 --- a/js/src/builtin/Classes.js +++ b/js/src/builtin/Classes.js @@ -5,7 +5,7 @@ var DefaultDerivedClassConstructor = class extends null { constructor(...args) { - super(...allowContentSpread(args)); + super(...allowContentIter(args)); } }; MakeDefaultConstructor(DefaultDerivedClassConstructor); diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 990a4acdb..3a20c487b 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -765,42 +765,53 @@ static const JSFunctionSpec collator_methods[] = { }; /** - * Collator constructor. - * Spec: ECMAScript Internationalization API Specification, 10.1 + * 10.1.2 Intl.Collator([ locales [, options]]) + * + * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ static bool Collator(JSContext* cx, const CallArgs& args, bool construct) { RootedObject obj(cx); + // We're following ECMA-402 1st Edition when Collator is called because of + // backward compatibility issues. + // See https://github.com/tc39/ecma402/issues/57 if (!construct) { - // 10.1.2.1 step 3 + // ES Intl 1st ed., 10.1.2.1 step 3 JSObject* intl = cx->global()->getOrCreateIntlObject(cx); if (!intl) return false; RootedValue self(cx, args.thisv()); if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) { - // 10.1.2.1 step 4 + // ES Intl 1st ed., 10.1.2.1 step 4 obj = ToObject(cx, self); if (!obj) return false; - // 10.1.2.1 step 5 + // ES Intl 1st ed., 10.1.2.1 step 5 bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); } else { - // 10.1.2.1 step 3.a + // ES Intl 1st ed., 10.1.2.1 step 3.a construct = true; } } if (construct) { - // 10.1.3.1 paragraph 2 - RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx)); - if (!proto) + // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor). + RootedObject proto(cx); + if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; + + if (!proto) { + proto = cx->global()->getOrCreateCollatorPrototype(cx); + if (!proto) + return false; + } + obj = NewObjectWithGivenProto(cx, &CollatorClass, proto); if (!obj) return false; @@ -808,15 +819,13 @@ Collator(JSContext* cx, const CallArgs& args, bool construct) obj->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr)); } - // 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2 RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue()); RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue()); - // 10.1.2.1 step 6; 10.1.3.1 step 3 + // Step 6. if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options)) return false; - // 10.1.2.1 steps 3.a and 7 args.rval().setObject(*obj); return true; } @@ -833,6 +842,7 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(!args.isConstructing()); // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot // be used with "new", but it still has to be treated as a constructor. return Collator(cx, args, true); @@ -1257,42 +1267,53 @@ static const JSFunctionSpec numberFormat_methods[] = { }; /** - * NumberFormat constructor. - * Spec: ECMAScript Internationalization API Specification, 11.1 + * 11.2.1 Intl.NumberFormat([ locales [, options]]) + * + * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ static bool NumberFormat(JSContext* cx, const CallArgs& args, bool construct) { RootedObject obj(cx); + // We're following ECMA-402 1st Edition when NumberFormat is called + // because of backward compatibility issues. + // See https://github.com/tc39/ecma402/issues/57 if (!construct) { - // 11.1.2.1 step 3 + // ES Intl 1st ed., 11.1.2.1 step 3 JSObject* intl = cx->global()->getOrCreateIntlObject(cx); if (!intl) return false; RootedValue self(cx, args.thisv()); if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) { - // 11.1.2.1 step 4 + // ES Intl 1st ed., 11.1.2.1 step 4 obj = ToObject(cx, self); if (!obj) return false; - // 11.1.2.1 step 5 + // ES Intl 1st ed., 11.1.2.1 step 5 bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); } else { - // 11.1.2.1 step 3.a + // ES Intl 1st ed., 11.1.2.1 step 3.a construct = true; } } if (construct) { - // 11.1.3.1 paragraph 2 - RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx)); - if (!proto) + // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). + RootedObject proto(cx); + if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; + + if (!proto) { + proto = cx->global()->getOrCreateNumberFormatPrototype(cx); + if (!proto) + return false; + } + obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto); if (!obj) return false; @@ -1300,15 +1321,13 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct) obj->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr)); } - // 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2 RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue()); RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue()); - // 11.1.2.1 step 6; 11.1.3.1 step 3 + // Step 3. if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options)) return false; - // 11.1.2.1 steps 3.a and 7 args.rval().setObject(*obj); return true; } @@ -1325,6 +1344,7 @@ js::intl_NumberFormat(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(!args.isConstructing()); // intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it // cannot be used with "new", but it still has to be treated as a // constructor. @@ -1725,42 +1745,53 @@ static const JSFunctionSpec dateTimeFormat_methods[] = { }; /** - * DateTimeFormat constructor. - * Spec: ECMAScript Internationalization API Specification, 12.1 + * 12.2.1 Intl.DateTimeFormat([ locales [, options]]) + * + * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ static bool DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) { RootedObject obj(cx); + // We're following ECMA-402 1st Edition when DateTimeFormat is called + // because of backward compatibility issues. + // See https://github.com/tc39/ecma402/issues/57 if (!construct) { - // 12.1.2.1 step 3 + // ES Intl 1st ed., 12.1.2.1 step 3 JSObject* intl = cx->global()->getOrCreateIntlObject(cx); if (!intl) return false; RootedValue self(cx, args.thisv()); if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) { - // 12.1.2.1 step 4 + // ES Intl 1st ed., 12.1.2.1 step 4 obj = ToObject(cx, self); if (!obj) return false; - // 12.1.2.1 step 5 + // ES Intl 1st ed., 12.1.2.1 step 5 bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); } else { - // 12.1.2.1 step 3.a + // ES Intl 1st ed., 12.1.2.1 step 3.a construct = true; } } if (construct) { - // 12.1.3.1 paragraph 2 - RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx)); - if (!proto) + // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). + RootedObject proto(cx); + if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; + + if (!proto) { + proto = cx->global()->getOrCreateDateTimeFormatPrototype(cx); + if (!proto) + return false; + } + obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto); if (!obj) return false; @@ -1768,15 +1799,13 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) obj->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr)); } - // 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2 RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue()); RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue()); - // 12.1.2.1 step 6; 12.1.3.1 step 3 + // Step 3. if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options)) return false; - // 12.1.2.1 steps 3.a and 7 args.rval().setObject(*obj); return true; } @@ -1793,6 +1822,7 @@ js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(!args.isConstructing()); // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it // cannot be used with "new", but it still has to be treated as a // constructor. diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 493062c1c..37c87365b 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -9,7 +9,8 @@ JSMSG_INVALID_OPTION_VALUE: false, JSMSG_INVALID_DIGITS_VALUE: false, JSMSG_INTL_OBJECT_REINITED: false, JSMSG_INVALID_CURRENCY_CODE: false, JSMSG_UNDEFINED_CURRENCY: false, JSMSG_INVALID_TIME_ZONE: false, - JSMSG_DATE_NOT_FINITE: false, + JSMSG_DATE_NOT_FINITE: false, JSMSG_INVALID_KEYS_TYPE: false, + JSMSG_INVALID_KEY: false, intl_Collator_availableLocales: false, intl_availableCollations: false, intl_CompareStrings: false, @@ -20,6 +21,9 @@ intl_availableCalendars: false, intl_patternForSkeleton: false, intl_FormatDateTime: false, + intl_SelectPluralRule: false, + intl_GetPluralCategories: false, + intl_GetCalendarInfo: false, */ /* @@ -432,7 +436,7 @@ function CanonicalizeLanguageTag(locale) { subtags[i] = subtag; i++; } - var normal = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, 0, i), "-"); + var normal = ArrayJoinRange(subtags, "-", 0, i); // Extension sequences are sorted by their singleton characters. // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese" @@ -442,7 +446,7 @@ function CanonicalizeLanguageTag(locale) { i++; while (i < subtags.length && subtags[i].length > 1) i++; - var extension = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, extensionStart, i), "-"); + var extension = ArrayJoinRange(subtags, "-", extensionStart, i); callFunction(std_Array_push, extensions, extension); } callFunction(std_Array_sort, extensions); @@ -450,7 +454,7 @@ function CanonicalizeLanguageTag(locale) { // Private use sequences are left as is. "x-private" var privateUse = ""; if (i < subtags.length) - privateUse = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, i), "-"); + privateUse = ArrayJoinRange(subtags, "-", i); // Put everything back together. var canonical = normal; @@ -467,6 +471,24 @@ function CanonicalizeLanguageTag(locale) { return canonical; } +/** + * Joins the array elements in the given range with the supplied separator. + */ +function ArrayJoinRange(array, separator, from, to = array.length) { + assert(typeof separator === "string", "|separator| is a string value"); + assert(typeof from === "number", "|from| is a number value"); + assert(typeof to === "number", "|to| is a number value"); + assert(0 <= from && from <= to && to <= array.length, "|from| and |to| form a valid range"); + + if (from === to) + return ""; + + var result = array[from]; + for (var i = from + 1; i < to; i++) { + result += separator + array[i]; + } + return result; +} function localeContainsNoUnicodeExtensions(locale) { // No "-u-", no possible Unicode extension. @@ -838,6 +860,7 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) { return BestAvailableLocaleHelper(availableLocales, locale, false); } +var noRelevantExtensionKeys = []; /** * Compares a BCP 47 language priority list against the set of locales in @@ -1165,9 +1188,10 @@ function GetOption(options, property, type, values, fallback) { * Spec: ECMAScript Internationalization API Specification, 9.2.10. */ function GetNumberOption(options, property, minimum, maximum, fallback) { - assert(typeof minimum === "number", "GetNumberOption"); - assert(typeof maximum === "number", "GetNumberOption"); - assert(fallback === undefined || (fallback >= minimum && fallback <= maximum), "GetNumberOption"); + assert(typeof minimum === "number" && (minimum | 0) === minimum, "GetNumberOption"); + assert(typeof maximum === "number" && (maximum | 0) === maximum, "GetNumberOption"); + assert(typeof fallback === "number" && (fallback | 0) === fallback, "GetNumberOption"); + assert(minimum <= fallback && fallback <= maximum, "GetNumberOption"); // Step 1. var value = options[property]; @@ -1177,7 +1201,10 @@ function GetNumberOption(options, property, minimum, maximum, fallback) { value = ToNumber(value); if (Number_isNaN(value) || value < minimum || value > maximum) ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value); - return std_Math_floor(value); + + // Apply bitwise-or to convert -0 to +0 per ES2017, 5.2 and to ensure + // the result is an int32 value. + return std_Math_floor(value) | 0; } // Step 3. @@ -1251,7 +1278,9 @@ function initializeIntlObject(obj) { function setLazyData(internals, type, lazyData) { assert(internals.type === "partial", "can't set lazy data for anything but a newborn"); - assert(type === "Collator" || type === "DateTimeFormat" || type == "NumberFormat", "bad type"); + assert(type === "Collator" || type === "DateTimeFormat" || + type == "NumberFormat" || type === "PluralRules", + "bad type"); assert(IsObject(lazyData), "non-object lazy data"); // Set in reverse order so that the .type change is a barrier. @@ -1301,7 +1330,9 @@ function isInitializedIntlObject(obj) { if (IsObject(internals)) { assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type"); var type = internals.type; - assert(type === "partial" || type === "Collator" || type === "DateTimeFormat" || type === "NumberFormat", "unexpected type"); + assert(type === "partial" || type === "Collator" || + type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules", + "unexpected type"); assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData"); assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps"); } else { @@ -1358,6 +1389,8 @@ function getInternals(obj) internalProps = resolveCollatorInternals(lazyData) else if (type === "DateTimeFormat") internalProps = resolveDateTimeFormatInternals(lazyData) + else if (type === "PluralRules") + internalProps = resolvePluralRulesInternals(lazyData) else internalProps = resolveNumberFormatInternals(lazyData); setInternalProperties(internals, internalProps); @@ -1689,6 +1722,7 @@ function Intl_Collator_compare_get() { // Step 2. return internals.boundCompare; } +_SetCanonicalName(Intl_Collator_compare_get, "get compare"); /** @@ -1757,45 +1791,37 @@ function resolveNumberFormatInternals(lazyNumberFormatData) { // Step 6. var opt = lazyNumberFormatData.opt; - // Compute effective locale. - // Step 9. var NumberFormat = numberFormatInternalProperties; - // Step 10. + // Step 9. var localeData = NumberFormat.localeData; - // Step 11. + // Step 10. var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat), lazyNumberFormatData.requestedLocales, lazyNumberFormatData.opt, NumberFormat.relevantExtensionKeys, localeData); - // Steps 12-13. (Step 14 is not relevant to our implementation.) + // Steps 11-12. (Step 13 is not relevant to our implementation.) internalProps.locale = r.locale; internalProps.numberingSystem = r.nu; // Compute formatting options. - // Step 16. - var s = lazyNumberFormatData.style; - internalProps.style = s; + // Step 15. + var style = lazyNumberFormatData.style; + internalProps.style = style; - // Steps 20, 22. - if (s === "currency") { + // Steps 19, 21. + if (style === "currency") { internalProps.currency = lazyNumberFormatData.currency; internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay; } - // Step 24. internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits; - - // Steps 27. internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits; - - // Step 30. internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits; - // Step 33. if ("minimumSignificantDigits" in lazyNumberFormatData) { // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the // actual presence (versus undefined-ness) of these properties. @@ -1804,10 +1830,10 @@ function resolveNumberFormatInternals(lazyNumberFormatData) { internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits; } - // Step 35. + // Step 27. internalProps.useGrouping = lazyNumberFormatData.useGrouping; - // Step 42. + // Step 34. internalProps.boundFormat = undefined; // The caller is responsible for associating |internalProps| with the right @@ -1835,6 +1861,44 @@ function getNumberFormatInternals(obj, methodName) { return internalProps; } +/** + * Applies digit options used for number formatting onto the intl object. + * + * Spec: ECMAScript Internationalization API Specification, 11.1.1. + */ +function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault) { + // We skip Step 1 because we set the properties on a lazyData object. + + // Step 2-3. + assert(IsObject(options), "SetNumberFormatDigitOptions"); + assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions"); + assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions"); + assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions"); + + // Steps 4-6. + const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1); + const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault); + const mxfdActualDefault = std_Math_max(mnfd, mxfdDefault); + const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault); + + // Steps 7-8. + let mnsd = options.minimumSignificantDigits; + let mxsd = options.maximumSignificantDigits; + + // Steps 9-11. + lazyData.minimumIntegerDigits = mnid; + lazyData.minimumFractionDigits = mnfd; + lazyData.maximumFractionDigits = mxfd; + + // Step 12. + if (mnsd !== undefined || mxsd !== undefined) { + mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1); + mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21); + lazyData.minimumSignificantDigits = mnsd; + lazyData.maximumSignificantDigits = mxsd; + } +} + /** * Initializes an object as a NumberFormat. @@ -1884,7 +1948,7 @@ function InitializeNumberFormat(numberFormat, locales, options) { // } // // Note that lazy data is only installed as a final step of initialization, - // so every Collator lazy data object has *all* these properties, never a + // so every NumberFormat lazy data object has *all* these properties, never a // subset of them. var lazyNumberFormatData = std_Object_create(null); @@ -1914,67 +1978,46 @@ function InitializeNumberFormat(numberFormat, locales, options) { opt.localeMatcher = matcher; // Compute formatting options. - // Step 15. - var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal"); - lazyNumberFormatData.style = s; + // Step 14. + var style = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal"); + lazyNumberFormatData.style = style; - // Steps 17-20. + // Steps 16-19. var c = GetOption(options, "currency", "string", undefined, undefined); if (c !== undefined && !IsWellFormedCurrencyCode(c)) ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c); var cDigits; - if (s === "currency") { + if (style === "currency") { if (c === undefined) ThrowTypeError(JSMSG_UNDEFINED_CURRENCY); - // Steps 20.a-c. + // Steps 19.a-c. c = toASCIIUpperCase(c); lazyNumberFormatData.currency = c; cDigits = CurrencyDigits(c); } - // Step 21. + // Step 20. var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol"); - if (s === "currency") + if (style === "currency") lazyNumberFormatData.currencyDisplay = cd; - // Step 23. - var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1); - lazyNumberFormatData.minimumIntegerDigits = mnid; - - // Steps 25-26. - var mnfdDefault = (s === "currency") ? cDigits : 0; - var mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault); - lazyNumberFormatData.minimumFractionDigits = mnfd; - - // Steps 28-29. - var mxfdDefault; - if (s === "currency") - mxfdDefault = std_Math_max(mnfd, cDigits); - else if (s === "percent") - mxfdDefault = std_Math_max(mnfd, 0); - else - mxfdDefault = std_Math_max(mnfd, 3); - var mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault); - lazyNumberFormatData.maximumFractionDigits = mxfd; - - // Steps 31-32. - var mnsd = options.minimumSignificantDigits; - var mxsd = options.maximumSignificantDigits; - - // Step 33. - if (mnsd !== undefined || mxsd !== undefined) { - mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1); - mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21); - lazyNumberFormatData.minimumSignificantDigits = mnsd; - lazyNumberFormatData.maximumSignificantDigits = mxsd; + // Steps 22-25. + var mnfdDefault, mxfdDefault; + if (style === "currency") { + mnfdDefault = cDigits; + mxfdDefault = cDigits; + } else { + mnfdDefault = 0; + mxfdDefault = style === "percent" ? 0 : 3; } + SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault); - // Step 34. + // Steps 26. var g = GetOption(options, "useGrouping", "boolean", undefined, true); lazyNumberFormatData.useGrouping = g; - // Step 43. + // Steps 35-36. // // We've done everything that must be done now: mark the lazy data as fully // computed and install it. @@ -1983,43 +2026,6 @@ function InitializeNumberFormat(numberFormat, locales, options) { /** - * Mapping from currency codes to the number of decimal digits used for them. - * Default is 2 digits. - * - * Spec: ISO 4217 Currency and Funds Code List. - * http://www.currency-iso.org/en/home/tables/table-a1.html - */ -var currencyDigits = { - BHD: 3, - BIF: 0, - BYR: 0, - CLF: 4, - CLP: 0, - DJF: 0, - GNF: 0, - IQD: 3, - ISK: 0, - JOD: 3, - JPY: 0, - KMF: 0, - KRW: 0, - KWD: 3, - LYD: 3, - OMR: 3, - PYG: 0, - RWF: 0, - TND: 3, - UGX: 0, - UYI: 0, - VND: 0, - VUV: 0, - XAF: 0, - XOF: 0, - XPF: 0 -}; - - -/** * Returns the number of decimal digits to be used for the given currency. * * Spec: ECMAScript Internationalization API Specification, 11.1.1. @@ -2100,7 +2106,7 @@ function numberFormatFormatToBind(value) { // Step 1.a.ii-iii. var x = ToNumber(value); - return intl_FormatNumber(this, x); + return intl_FormatNumber(this, x, /* formatToParts = */ false); } @@ -2127,6 +2133,22 @@ function Intl_NumberFormat_format_get() { // Step 2. return internals.boundFormat; } +_SetCanonicalName(Intl_NumberFormat_format_get, "get format"); + + +function Intl_NumberFormat_formatToParts(value) { + // Step 1. + var nf = this; + + // Steps 2-3. + getNumberFormatInternals(nf, "formatToParts"); + + // Step 4. + var x = ToNumber(value); + + // Step 5. + return intl_FormatNumber(nf, x, /* formatToParts = */ true); +} /** @@ -2268,26 +2290,6 @@ function getDateTimeFormatInternals(obj, methodName) { return internalProps; } -/** - * Components of date and time formats and their values. - * - * Spec: ECMAScript Internationalization API Specification, 12.1.1. - */ -var dateTimeComponentValues = { - weekday: ["narrow", "short", "long"], - era: ["narrow", "short", "long"], - year: ["2-digit", "numeric"], - month: ["2-digit", "numeric", "narrow", "short", "long"], - day: ["2-digit", "numeric"], - hour: ["2-digit", "numeric"], - minute: ["2-digit", "numeric"], - second: ["2-digit", "numeric"], - timeZoneName: ["short", "long"] -}; - - -var dateTimeComponents = std_Object_getOwnPropertyNames(dateTimeComponentValues); - /** * Initializes an object as a DateTimeFormat. @@ -2377,12 +2379,19 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) { lazyDateTimeFormatData.formatOpt = formatOpt; // Step 19. - var i, prop; - for (i = 0; i < dateTimeComponents.length; i++) { - prop = dateTimeComponents[i]; - var value = GetOption(options, prop, "string", dateTimeComponentValues[prop], undefined); - formatOpt[prop] = value; - } + // 12.1, Table 4: Components of date and time formats. + formatOpt.weekday = GetOption(options, "weekday", "string", ["narrow", "short", "long"], + undefined); + formatOpt.era = GetOption(options, "era", "string", ["narrow", "short", "long"], undefined); + formatOpt.year = GetOption(options, "year", "string", ["2-digit", "numeric"], undefined); + formatOpt.month = GetOption(options, "month", "string", + ["2-digit", "numeric", "narrow", "short", "long"], undefined); + formatOpt.day = GetOption(options, "day", "string", ["2-digit", "numeric"], undefined); + formatOpt.hour = GetOption(options, "hour", "string", ["2-digit", "numeric"], undefined); + formatOpt.minute = GetOption(options, "minute", "string", ["2-digit", "numeric"], undefined); + formatOpt.second = GetOption(options, "second", "string", ["2-digit", "numeric"], undefined); + formatOpt.timeZoneName = GetOption(options, "timeZoneName", "string", ["short", "long"], + undefined); // Steps 20-21 provided by ICU - see comment after this function. @@ -2650,7 +2659,6 @@ function ToDateTimeOptions(options, required, defaults) { return options; } - /** * Compares the date and time components requested by options with the available * date and time formats in formats, and selects the best match according @@ -2836,6 +2844,7 @@ function Intl_DateTimeFormat_format_get() { // Step 2. return internals.boundFormat; } +_SetCanonicalName(Intl_DateTimeFormat_format_get, "get format"); function Intl_DateTimeFormat_formatToParts() { @@ -2971,6 +2980,232 @@ function resolveICUPattern(pattern, result) { } } +/********** Intl.PluralRules **********/ + +/** + * PluralRules internal properties. + * + * Spec: ECMAScript 402 API, PluralRules, 1.3.3. + */ +var pluralRulesInternalProperties = { + _availableLocales: null, + availableLocales: function() + { + var locales = this._availableLocales; + if (locales) + return locales; + + locales = intl_PluralRules_availableLocales(); + addSpecialMissingLanguageTags(locales); + return (this._availableLocales = locales); + } +}; + +/** + * Compute an internal properties object from |lazyPluralRulesData|. + */ +function resolvePluralRulesInternals(lazyPluralRulesData) { + assert(IsObject(lazyPluralRulesData), "lazy data not an object?"); + + var internalProps = std_Object_create(null); + + var requestedLocales = lazyPluralRulesData.requestedLocales; + + var PluralRules = pluralRulesInternalProperties; + + // Step 13. + const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules), + lazyPluralRulesData.requestedLocales, + lazyPluralRulesData.opt, + noRelevantExtensionKeys, undefined); + + // Step 14. + internalProps.locale = r.locale; + internalProps.type = lazyPluralRulesData.type; + + internalProps.pluralCategories = intl_GetPluralCategories( + internalProps.locale, + internalProps.type); + + internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits; + internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits; + internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits; + + if ("minimumSignificantDigits" in lazyPluralRulesData) { + assert("maximumSignificantDigits" in lazyPluralRulesData, "min/max sig digits mismatch"); + internalProps.minimumSignificantDigits = lazyPluralRulesData.minimumSignificantDigits; + internalProps.maximumSignificantDigits = lazyPluralRulesData.maximumSignificantDigits; + } + + return internalProps; +} + +/** + * Returns an object containing the PluralRules internal properties of |obj|, + * or throws a TypeError if |obj| isn't PluralRules-initialized. + */ +function getPluralRulesInternals(obj, methodName) { + var internals = getIntlObjectInternals(obj, "PluralRules", methodName); + assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals"); + + var internalProps = maybeInternalProperties(internals); + if (internalProps) + return internalProps; + + internalProps = resolvePluralRulesInternals(internals.lazyData); + setInternalProperties(internals, internalProps); + return internalProps; +} + +/** + * Initializes an object as a PluralRules. + * + * This method is complicated a moderate bit by its implementing initialization + * as a *lazy* concept. Everything that must happen now, does -- but we defer + * all the work we can until the object is actually used as a PluralRules. + * This later work occurs in |resolvePluralRulesInternals|; steps not noted + * here occur there. + * + * Spec: ECMAScript 402 API, PluralRules, 1.1.1. + */ +function InitializePluralRules(pluralRules, locales, options) { + assert(IsObject(pluralRules), "InitializePluralRules"); + + // Step 1. + if (isInitializedIntlObject(pluralRules)) + ThrowTypeError(JSMSG_INTL_OBJECT_REINITED); + + let internals = initializeIntlObject(pluralRules); + + // Lazy PluralRules data has the following structure: + // + // { + // requestedLocales: List of locales, + // type: "cardinal" / "ordinal", + // + // opt: // opt object computer in InitializePluralRules + // { + // localeMatcher: "lookup" / "best fit", + // } + // + // minimumIntegerDigits: integer ∈ [1, 21], + // minimumFractionDigits: integer ∈ [0, 20], + // maximumFractionDigits: integer ∈ [0, 20], + // + // // optional + // minimumSignificantDigits: integer ∈ [1, 21], + // maximumSignificantDigits: integer ∈ [1, 21], + // } + // + // Note that lazy data is only installed as a final step of initialization, + // so every PluralRules lazy data object has *all* these properties, never a + // subset of them. + const lazyPluralRulesData = std_Object_create(null); + + // Step 3. + let requestedLocales = CanonicalizeLocaleList(locales); + lazyPluralRulesData.requestedLocales = requestedLocales; + + // Steps 4-5. + if (options === undefined) + options = {}; + else + options = ToObject(options); + + // Step 6. + const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal"); + lazyPluralRulesData.type = type; + + // Step 8. + let opt = new Record(); + lazyPluralRulesData.opt = opt; + + // Steps 9-10. + let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit"); + opt.localeMatcher = matcher; + + // Steps 11-12. + SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3); + + setLazyData(internals, "PluralRules", lazyPluralRulesData) +} + +/** + * Returns the subset of the given locale list for which this locale list has a + * matching (possibly fallback) locale. Locales appear in the same order in the + * returned list as in the input list. + * + * Spec: ECMAScript 402 API, PluralRules, 1.3.2. + */ +function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) { + var options = arguments.length > 1 ? arguments[1] : undefined; + + // Step 1. + var availableLocales = callFunction(pluralRulesInternalProperties.availableLocales, + pluralRulesInternalProperties); + // Step 2. + let requestedLocales = CanonicalizeLocaleList(locales); + + // Step 3. + return SupportedLocales(availableLocales, requestedLocales, options); +} + +/** + * Returns a String value representing the plural category matching + * the number passed as value according to the + * effective locale and the formatting options of this PluralRules. + * + * Spec: ECMAScript 402 API, PluralRules, 1.4.3. + */ +function Intl_PluralRules_select(value) { + // Step 1. + let pluralRules = this; + // Step 2. + let internals = getPluralRulesInternals(pluralRules, "select"); + + // Steps 3-4. + let n = ToNumber(value); + + // Step 5. + return intl_SelectPluralRule(pluralRules, n); +} + +/** + * Returns the resolved options for a PluralRules object. + * + * Spec: ECMAScript 402 API, PluralRules, 1.4.4. + */ +function Intl_PluralRules_resolvedOptions() { + var internals = getPluralRulesInternals(this, "resolvedOptions"); + + var internalsPluralCategories = internals.pluralCategories; + var pluralCategories = []; + for (var i = 0; i < internalsPluralCategories.length; i++) + _DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]); + + var result = { + locale: internals.locale, + type: internals.type, + pluralCategories, + minimumIntegerDigits: internals.minimumIntegerDigits, + minimumFractionDigits: internals.minimumFractionDigits, + maximumFractionDigits: internals.maximumFractionDigits, + }; + + var optionalProperties = [ + "minimumSignificantDigits", + "maximumSignificantDigits" + ]; + + for (var i = 0; i < optionalProperties.length; i++) { + var p = optionalProperties[i]; + if (callFunction(std_Object_hasOwnProperty, internals, p)) + _DefineDataProperty(result, p, internals[p]); + } + return result; +} + + function Intl_getCanonicalLocales(locales) { let codes = CanonicalizeLocaleList(locales); let result = []; @@ -3006,3 +3241,126 @@ function Intl_getCalendarInfo(locales) { return result; } + +/** + * This function is a custom method designed after Intl API, but currently + * not part of the spec or spec proposal. + * We want to use it internally to retrieve translated values from CLDR in + * order to ensure they're aligned with what Intl API returns. + * + * This API may one day be a foundation for an ECMA402 API spec proposal. + * + * The function takes two arguments - locales which is a list of locale strings + * and options which is an object with two optional properties: + * + * keys: + * an Array of string values that are paths to individual terms + * + * style: + * a String with a value "long", "short" or "narrow" + * + * It returns an object with properties: + * + * locale: + * a negotiated locale string + * + * style: + * negotiated style + * + * values: + * A key-value pair list of requested keys and corresponding + * translated values + * + */ +function Intl_getDisplayNames(locales, options) { + // 1. Let requestLocales be ? CanonicalizeLocaleList(locales). + const requestedLocales = CanonicalizeLocaleList(locales); + + // 2. If options is undefined, then + if (options === undefined) + // a. Let options be ObjectCreate(%ObjectPrototype%). + options = {}; + // 3. Else, + else + // a. Let options be ? ToObject(options). + options = ToObject(options); + + const DateTimeFormat = dateTimeFormatInternalProperties; + + // 4. Let localeData be %DateTimeFormat%.[[localeData]]. + const localeData = DateTimeFormat.localeData; + + // 5. Let opt be a new Record. + const localeOpt = new Record(); + // 6. Set localeOpt.[[localeMatcher]] to "best fit". + localeOpt.localeMatcher = "best fit"; + + // 7. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]], requestedLocales, localeOpt, + // %DateTimeFormat%.[[relevantExtensionKeys]], localeData). + const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat), + requestedLocales, + localeOpt, + DateTimeFormat.relevantExtensionKeys, + localeData); + + // 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long"). + const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long"); + // 9. Let keys be ? Get(options, "keys"). + let keys = options.keys; + + // 10. If keys is undefined, + if (keys === undefined) { + // a. Let keys be ArrayCreate(0). + keys = []; + } else if (!IsObject(keys)) { + // 11. Else, + // a. If Type(keys) is not Object, throw a TypeError exception. + ThrowTypeError(JSMSG_INVALID_KEYS_TYPE); + } + + // 12. Let processedKeys be ArrayCreate(0). + // (This really should be a List, but we use an Array here in order that + // |intl_ComputeDisplayNames| may infallibly access the list's length via + // |ArrayObject::length|.) + let processedKeys = []; + // 13. Let len be ? ToLength(? Get(keys, "length")). + let len = ToLength(keys.length); + // 14. Let i be 0. + // 15. Repeat, while i < len + for (let i = 0; i < len; i++) { + // a. Let processedKey be ? ToString(? Get(keys, i)). + // b. Perform ? CreateDataPropertyOrThrow(processedKeys, i, processedKey). + callFunction(std_Array_push, processedKeys, ToString(keys[i])); + } + + // 16. Let names be ? ComputeDisplayNames(r.[[locale]], style, processedKeys). + const names = intl_ComputeDisplayNames(r.locale, style, processedKeys); + + // 17. Let values be ObjectCreate(%ObjectPrototype%). + const values = {}; + + // 18. Set i to 0. + // 19. Repeat, while i < len + for (let i = 0; i < len; i++) { + // a. Let key be ? Get(processedKeys, i). + const key = processedKeys[i]; + // b. Let name be ? Get(names, i). + const name = names[i]; + // c. Assert: Type(name) is string. + assert(typeof name === "string", "unexpected non-string value"); + // d. Assert: the length of name is greater than zero. + assert(name.length > 0, "empty string value"); + // e. Perform ? DefinePropertyOrThrow(values, key, name). + _DefineDataProperty(values, key, name); + } + + // 20. Let options be ObjectCreate(%ObjectPrototype%). + // 21. Perform ! DefinePropertyOrThrow(result, "locale", r.[[locale]]). + // 22. Perform ! DefinePropertyOrThrow(result, "style", style). + // 23. Perform ! DefinePropertyOrThrow(result, "values", values). + const result = { locale: r.locale, style, values }; + + // 24. Return result. + return result; +} + diff --git a/js/src/builtin/IntlTimeZoneData.h b/js/src/builtin/IntlTimeZoneData.h index bb7d22109..f92c583df 100644 --- a/js/src/builtin/IntlTimeZoneData.h +++ b/js/src/builtin/IntlTimeZoneData.h @@ -1,5 +1,5 @@ // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2017c +// tzdata version = 2018c #ifndef builtin_IntlTimeZoneData_h #define builtin_IntlTimeZoneData_h diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index 432364614..580629a13 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -14,31 +14,8 @@ function MapConstructorInit(iterable) { if (!IsCallable(adder)) ThrowTypeError(JSMSG_NOT_FUNCTION, typeof adder); - // Step 6.c. - var iterFn = iterable[std_iterator]; - if (!IsCallable(iterFn)) - ThrowTypeError(JSMSG_NOT_ITERABLE, DecompileArg(0, iterable)); - - var iter = callContentFunction(iterFn, iterable); - if (!IsObject(iter)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof iter); - - // Step 7 (not applicable). - - // Step 8. - while (true) { - // Step 8.a. - var next = callContentFunction(iter.next, iter); - if (!IsObject(next)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof next); - - // Step 8.b. - if (next.done) - return; - - // Step 8.c. - var nextItem = next.value; - + // Steps 6.c-8. + for (var nextItem of allowContentIter(iterable)) { // Step 8.d. if (!IsObject(nextItem)) ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Map"); diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 3bfc8f60b..710c7a76c 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -1159,7 +1159,7 @@ bool ModuleBuilder::processExport(frontend::ParseNode* pn) { MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_DEFAULT)); - MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY); + MOZ_ASSERT(pn->getArity() == (pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY)); bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT; ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid; @@ -1205,7 +1205,7 @@ ModuleBuilder::processExport(frontend::ParseNode* pn) case PNK_FUNCTION: { RootedFunction func(cx_, kid->pn_funbox->function()); if (!func->isArrow()) { - RootedAtom localName(cx_, func->name()); + RootedAtom localName(cx_, func->explicitName()); RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); MOZ_ASSERT_IF(isDefault, localName); if (!appendExportEntry(exportName, localName)) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 59c97e529..c781a336d 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -1369,7 +1369,8 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto / static MOZ_MUST_USE bool PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, HandleObject promiseObj, - HandleObject resolve, HandleObject reject); + HandleObject resolve, HandleObject reject, + bool* done); // ES2016, 25.4.4.1. static bool @@ -1410,12 +1411,14 @@ Promise_static_all(JSContext* cx, unsigned argc, Value* vp) // Step 6 (implicit). // Step 7. - bool result = PerformPromiseAll(cx, iter, C, resultPromise, resolve, reject); + bool done; + bool result = PerformPromiseAll(cx, iter, C, resultPromise, resolve, reject, &done); // Step 8. if (!result) { // Step 8.a. - // TODO: implement iterator closing. + if (!done) + iter.closeThrow(); // Step 8.b. return AbruptRejectPromise(cx, args, resultPromise, reject); @@ -1598,8 +1601,11 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res // ES2016, 25.4.4.1.1. static MOZ_MUST_USE bool PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, - HandleObject promiseObj, HandleObject resolve, HandleObject reject) + HandleObject promiseObj, HandleObject resolve, HandleObject reject, + bool* done) { + *done = false; + RootedObject unwrappedPromiseObj(cx); if (IsWrapper(promiseObj)) { unwrappedPromiseObj = CheckedUnwrap(promiseObj); @@ -1666,14 +1672,19 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); while (true) { - bool done; - // Steps a, b, c, e, f, g. - if (!iterator.next(&nextValue, &done)) + // Steps a-c, e-g. + if (!iterator.next(&nextValue, done)) { + // Steps b, f. + *done = true; + + // Steps c, g. return false; + } // Step d. - if (done) { + if (*done) { // Step d.i (implicit). + // Step d.ii. int32_t remainingCount = dataHolder->decreaseRemainingCount(); @@ -1822,7 +1833,8 @@ PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp) static MOZ_MUST_USE bool PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, HandleObject promiseObj, - HandleObject resolve, HandleObject reject); + HandleObject resolve, HandleObject reject, + bool* done); // ES2016, 25.4.4.3. static bool @@ -1863,12 +1875,14 @@ Promise_static_race(JSContext* cx, unsigned argc, Value* vp) // Step 6 (implicit). // Step 7. - bool result = PerformPromiseRace(cx, iter, C, resultPromise, resolve, reject); + bool done; + bool result = PerformPromiseRace(cx, iter, C, resultPromise, resolve, reject, &done); // Step 8. if (!result) { // Step 8.a. - // TODO: implement iterator closing. + if (!done) + iter.closeThrow(); // Step 8.b. return AbruptRejectPromise(cx, args, resultPromise, reject); @@ -1882,25 +1896,30 @@ Promise_static_race(JSContext* cx, unsigned argc, Value* vp) // ES2016, 25.4.4.3.1. static MOZ_MUST_USE bool PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, - HandleObject promiseObj, HandleObject resolve, HandleObject reject) + HandleObject promiseObj, HandleObject resolve, HandleObject reject, + bool* done) { + *done = false; MOZ_ASSERT(C->isConstructor()); RootedValue CVal(cx, ObjectValue(*C)); RootedValue nextValue(cx); RootedValue resolveFunVal(cx, ObjectOrNullValue(resolve)); RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); - bool done; while (true) { // Steps a-c, e-g. - if (!iterator.next(&nextValue, &done)) + if (!iterator.next(&nextValue, done)) { + // Steps b, f. + *done = true; + + // Steps c, g. return false; + } // Step d. - if (done) { - // Step d.i. - // TODO: implement iterator closing. + if (*done) { + // Step d.i (implicit). // Step d.ii. return true; diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 748ff7351..beff58e13 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2140,7 +2140,7 @@ ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst) MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_FROM) || pn->isKind(PNK_EXPORT_DEFAULT)); - MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY); + MOZ_ASSERT(pn->getArity() == (pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY)); MOZ_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_right->isKind(PNK_STRING)); RootedValue decl(cx, NullValue()); @@ -3415,7 +3415,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst) #endif RootedValue id(cx); - RootedAtom funcAtom(cx, func->name()); + RootedAtom funcAtom(cx, func->explicitName()); if (!optIdentifier(funcAtom, nullptr, &id)) return false; @@ -3423,7 +3423,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst) NodeVector defaults(cx); RootedValue body(cx), rest(cx); - if (func->hasRest()) + if (pn->pn_funbox->hasRest()) rest.setUndefined(); else rest.setNull(); diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 80a4bb5bd..b20f41c53 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -577,14 +577,29 @@ js::regexp_clone(JSContext* cx, unsigned argc, Value* vp) return true; } -/* ES6 draft rev32 21.2.5.4. */ +MOZ_ALWAYS_INLINE bool +IsRegExpInstanceOrPrototype(HandleValue v) +{ + if (!v.isObject()) + return false; + + return StandardProtoKeyOrNull(&v.toObject()) == JSProto_RegExp; +} + +// ES 2017 draft 21.2.5.4. MOZ_ALWAYS_INLINE bool regexp_global_impl(JSContext* cx, const CallArgs& args) { - MOZ_ASSERT(IsRegExpObject(args.thisv())); - Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); - /* Steps 4-6. */ + // Step 3.a. + if (!IsRegExpObject(args.thisv())) { + args.rval().setUndefined(); + return true; + } + + // Steps 4-6. + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); args.rval().setBoolean(reObj->global()); return true; } @@ -592,19 +607,25 @@ regexp_global_impl(JSContext* cx, const CallArgs& args) bool js::regexp_global(JSContext* cx, unsigned argc, JS::Value* vp) { - /* Steps 1-3. */ + // Steps 1-3. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsRegExpObject, regexp_global_impl>(cx, args); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_global_impl>(cx, args); } -/* ES6 draft rev32 21.2.5.5. */ +// ES 2017 draft 21.2.5.5. MOZ_ALWAYS_INLINE bool regexp_ignoreCase_impl(JSContext* cx, const CallArgs& args) { - MOZ_ASSERT(IsRegExpObject(args.thisv())); - Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); - /* Steps 4-6. */ + // Step 3.a + if (!IsRegExpObject(args.thisv())) { + args.rval().setUndefined(); + return true; + } + + // Steps 4-6. + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); args.rval().setBoolean(reObj->ignoreCase()); return true; } @@ -612,19 +633,25 @@ regexp_ignoreCase_impl(JSContext* cx, const CallArgs& args) bool js::regexp_ignoreCase(JSContext* cx, unsigned argc, JS::Value* vp) { - /* Steps 1-3. */ + // Steps 1-3. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsRegExpObject, regexp_ignoreCase_impl>(cx, args); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_ignoreCase_impl>(cx, args); } -/* ES6 draft rev32 21.2.5.7. */ +// ES 2017 draft 21.2.5.7. MOZ_ALWAYS_INLINE bool regexp_multiline_impl(JSContext* cx, const CallArgs& args) { - MOZ_ASSERT(IsRegExpObject(args.thisv())); - Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); - /* Steps 4-6. */ + // Step 3.a. + if (!IsRegExpObject(args.thisv())) { + args.rval().setUndefined(); + return true; + } + + // Steps 4-6. + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); args.rval().setBoolean(reObj->multiline()); return true; } @@ -632,24 +659,30 @@ regexp_multiline_impl(JSContext* cx, const CallArgs& args) bool js::regexp_multiline(JSContext* cx, unsigned argc, JS::Value* vp) { - /* Steps 1-3. */ + // Steps 1-3. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsRegExpObject, regexp_multiline_impl>(cx, args); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_multiline_impl>(cx, args); } -/* ES6 draft rev32 21.2.5.10. */ +// ES 2017 draft rev32 21.2.5.10. MOZ_ALWAYS_INLINE bool regexp_source_impl(JSContext* cx, const CallArgs& args) { - MOZ_ASSERT(IsRegExpObject(args.thisv())); - Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); + + // Step 3.a. + if (!IsRegExpObject(args.thisv())) { + args.rval().setString(cx->names().emptyRegExp); + return true; + } - /* Step 5. */ + // Step 5. + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); RootedAtom src(cx, reObj->getSource()); if (!src) return false; - /* Step 7. */ + // Step 7. RootedString str(cx, EscapeRegExpPattern(cx, src)); if (!str) return false; @@ -661,19 +694,25 @@ regexp_source_impl(JSContext* cx, const CallArgs& args) static bool regexp_source(JSContext* cx, unsigned argc, JS::Value* vp) { - /* Steps 1-4. */ + // Steps 1-4. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsRegExpObject, regexp_source_impl>(cx, args); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_source_impl>(cx, args); } -/* ES6 draft rev32 21.2.5.12. */ +// ES 2017 draft 21.2.5.12. MOZ_ALWAYS_INLINE bool regexp_sticky_impl(JSContext* cx, const CallArgs& args) { - MOZ_ASSERT(IsRegExpObject(args.thisv())); - Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); - /* Steps 4-6. */ + // Step 3.a. + if (!IsRegExpObject(args.thisv())) { + args.rval().setUndefined(); + return true; + } + + // Steps 4-6. + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); args.rval().setBoolean(reObj->sticky()); return true; } @@ -681,27 +720,35 @@ regexp_sticky_impl(JSContext* cx, const CallArgs& args) bool js::regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp) { - /* Steps 1-3. */ + // Steps 1-3. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsRegExpObject, regexp_sticky_impl>(cx, args); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_sticky_impl>(cx, args); } -/* ES6 21.2.5.15. */ +// ES 2017 draft 21.2.5.15. MOZ_ALWAYS_INLINE bool regexp_unicode_impl(JSContext* cx, const CallArgs& args) { - MOZ_ASSERT(IsRegExpObject(args.thisv())); - /* Steps 4-6. */ - args.rval().setBoolean(args.thisv().toObject().as<RegExpObject>().unicode()); + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); + + // Step 3.a. + if (!IsRegExpObject(args.thisv())) { + args.rval().setUndefined(); + return true; + } + + // Steps 4-6. + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + args.rval().setBoolean(reObj->unicode()); return true; } bool js::regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp) { - /* Steps 1-3. */ + // Steps 1-3. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsRegExpObject, regexp_unicode_impl>(cx, args); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_unicode_impl>(cx, args); } const JSPropertySpec js::regexp_properties[] = { @@ -829,25 +876,6 @@ const JSPropertySpec js::regexp_static_props[] = { JS_PS_END }; -JSObject* -js::CreateRegExpPrototype(JSContext* cx, JSProtoKey key) -{ - MOZ_ASSERT(key == JSProto_RegExp); - - Rooted<RegExpObject*> proto(cx, cx->global()->createBlankPrototype<RegExpObject>(cx)); - if (!proto) - return nullptr; - proto->NativeObject::setPrivate(nullptr); - - if (!RegExpObject::assignInitialShape(cx, proto)) - return nullptr; - - RootedAtom source(cx, cx->names().empty); - proto->initAndZeroLastIndex(source, RegExpFlag(0), cx); - - return proto; -} - template <typename CharT> static bool IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index) diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js index 1ffea0105..0b849292c 100644 --- a/js/src/builtin/RegExp.js +++ b/js/src/builtin/RegExp.js @@ -122,8 +122,7 @@ function RegExpMatch(string) { } // Step 5. - var sticky = !!(flags & REGEXP_STICKY_FLAG); - return RegExpLocalMatchOpt(rx, S, sticky); + return RegExpBuiltinExec(rx, S, false); } // Stes 4-6 @@ -220,37 +219,6 @@ function RegExpGlobalMatchOpt(rx, S, fullUnicode) { } } -// ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6 step 5. -// Optimized path for @@match without global flag. -function RegExpLocalMatchOpt(rx, S, sticky) { - // Step 4. - var lastIndex = ToLength(rx.lastIndex); - - // Step 8. - if (!sticky) { - lastIndex = 0; - } else { - if (lastIndex > S.length) { - // Steps 12.a.i-ii, 12.c.i.1-2. - rx.lastIndex = 0; - return null; - } - } - - // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15. - var result = RegExpMatcher(rx, S, lastIndex); - if (result === null) { - // Steps 12.a.i-ii, 12.c.i.1-2. - rx.lastIndex = 0; - } else { - // Step 15. - if (sticky) - rx.lastIndex = result.index + result[0].length; - } - - return result; -} - // Checks if following properties and getters are not modified, and accessing // them not observed by content script: // * flags @@ -318,9 +286,10 @@ function RegExpReplace(string, replaceValue) { if (functionalReplace) { var elemBase = GetElemBaseForLambda(replaceValue); - if (IsObject(elemBase)) + if (IsObject(elemBase)) { return RegExpGlobalReplaceOptElemBase(rx, S, lengthS, replaceValue, fullUnicode, elemBase); + } return RegExpGlobalReplaceOptFunc(rx, S, lengthS, replaceValue, fullUnicode); } @@ -336,18 +305,11 @@ function RegExpReplace(string, replaceValue) { fullUnicode); } - var sticky = !!(flags & REGEXP_STICKY_FLAG); - - if (functionalReplace) { - return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue, - sticky); - } - if (firstDollarIndex !== -1) { - return RegExpLocalReplaceOptSubst(rx, S, lengthS, replaceValue, - sticky, firstDollarIndex); - } - return RegExpLocalReplaceOpt(rx, S, lengthS, replaceValue, - sticky); + if (functionalReplace) + return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue); + if (firstDollarIndex !== -1) + return RegExpLocalReplaceOptSubst(rx, S, lengthS, replaceValue, firstDollarIndex); + return RegExpLocalReplaceOpt(rx, S, lengthS, replaceValue); } // Steps 8-16. @@ -647,7 +609,8 @@ function RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue, fullUnicode) #undef SUBSTITUTION #undef FUNC_NAME -// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.9. +// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210 +// 21.2.5.9 RegExp.prototype [ @@search ] ( string ) function RegExpSearch(string) { // Step 1. var rx = this; @@ -659,41 +622,69 @@ function RegExpSearch(string) { // Step 3. var S = ToString(string); + // Step 4. + var previousLastIndex = rx.lastIndex; + + // Step 5. + var lastIndexIsZero = SameValue(previousLastIndex, 0); + if (!lastIndexIsZero) + rx.lastIndex = 0; + if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) { // Step 6. var result = RegExpSearcher(rx, S, 0); - // Step 8. + // We need to consider two cases: + // + // 1. Neither global nor sticky is set: + // RegExpBuiltinExec doesn't modify lastIndex for local RegExps, that + // means |SameValue(rx.lastIndex, 0)| is true after calling exec. The + // comparison in steps 7-8 |SameValue(rx.lastIndex, previousLastIndex)| + // is therefore equal to the already computed |lastIndexIsZero| value. + // + // 2. Global or sticky flag is set. + // RegExpBuiltinExec will always update lastIndex and we need to + // restore the property to its original value. + + // Steps 7-8. + if (!lastIndexIsZero) { + rx.lastIndex = previousLastIndex; + } else { + var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); + if (flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)) + rx.lastIndex = previousLastIndex; + } + + // Step 9. if (result === -1) return -1; - // Step 9. + // Step 10. return result & 0x7fff; } - return RegExpSearchSlowPath(rx, S); + return RegExpSearchSlowPath(rx, S, previousLastIndex); } -// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.9 -// steps 4-9. -function RegExpSearchSlowPath(rx, S) { - // Step 4. - var previousLastIndex = rx.lastIndex; - - // Step 5. - rx.lastIndex = 0; - +// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210 +// 21.2.5.9 RegExp.prototype [ @@search ] ( string ) +// Steps 6-10. +function RegExpSearchSlowPath(rx, S, previousLastIndex) { // Step 6. var result = RegExpExec(rx, S, false); // Step 7. - rx.lastIndex = previousLastIndex; + var currentLastIndex = rx.lastIndex; // Step 8. + if (!SameValue(currentLastIndex, previousLastIndex)) + rx.lastIndex = previousLastIndex; + + // Step 9. if (result === null) return -1; - // Step 9. + // Step 10. return result.index; } @@ -942,15 +933,16 @@ function RegExpExec(R, S, forTest) { return forTest ? result !== null : result; } -// ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2. +// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210 +// 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) function RegExpBuiltinExec(R, S, forTest) { - // ES6 21.2.5.2.1 step 6. + // 21.2.5.2.1 Runtime Semantics: RegExpExec, step 5. // This check is here for RegExpTest. RegExp_prototype_Exec does same // thing already. if (!IsRegExpObject(R)) return UnwrapAndCallRegExpBuiltinExec(R, S, forTest); - // Steps 1-2 (skipped). + // Steps 1-3 (skipped). // Step 4. var lastIndex = ToLength(R.lastIndex); @@ -965,9 +957,11 @@ function RegExpBuiltinExec(R, S, forTest) { if (!globalOrSticky) { lastIndex = 0; } else { + // Step 12.a. if (lastIndex > S.length) { - // Steps 12.a.i-ii, 12.c.i.1-2. - R.lastIndex = 0; + // Steps 12.a.i-ii. + if (globalOrSticky) + R.lastIndex = 0; return forTest ? false : null; } } @@ -977,7 +971,8 @@ function RegExpBuiltinExec(R, S, forTest) { var endIndex = RegExpTester(R, S, lastIndex); if (endIndex == -1) { // Steps 12.a.i-ii, 12.c.i.1-2. - R.lastIndex = 0; + if (globalOrSticky) + R.lastIndex = 0; return false; } @@ -991,8 +986,9 @@ function RegExpBuiltinExec(R, S, forTest) { // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15. var result = RegExpMatcher(R, S, lastIndex); if (result === null) { - // Steps 12.a.i-ii, 12.c.i.1-2. - R.lastIndex = 0; + // Steps 12.a.i, 12.c.i. + if (globalOrSticky) + R.lastIndex = 0; } else { // Step 15. if (globalOrSticky) diff --git a/js/src/builtin/RegExpLocalReplaceOpt.h.js b/js/src/builtin/RegExpLocalReplaceOpt.h.js index edc2e2056..1acd6a73a 100644 --- a/js/src/builtin/RegExpLocalReplaceOpt.h.js +++ b/js/src/builtin/RegExpLocalReplaceOpt.h.js @@ -11,24 +11,39 @@ // * FUNCTIONAL -- replaceValue is a function // * neither of above -- replaceValue is a string without "$" -// ES 2017 draft 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8 +// ES 2017 draft 6390c2f1b34b309895d31d8c0512eac8660a0210 21.2.5.8 // steps 11.a-16. // Optimized path for @@replace with the following conditions: // * global flag is false -function FUNC_NAME(rx, S, lengthS, replaceValue, sticky +function FUNC_NAME(rx, S, lengthS, replaceValue #ifdef SUBSTITUTION , firstDollarIndex #endif ) { - var lastIndex; - if (sticky) { - lastIndex = ToLength(rx.lastIndex); + // 21.2.5.2.2 RegExpBuiltinExec, step 4. + var lastIndex = ToLength(rx.lastIndex); + + // 21.2.5.2.2 RegExpBuiltinExec, step 5. + // Side-effects in step 4 can recompile the RegExp, so we need to read the + // flags again and handle the case when global was enabled even though this + // function is optimized for non-global RegExps. + var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); + + // 21.2.5.2.2 RegExpBuiltinExec, steps 6-7. + var globalOrSticky = !!(flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)); + + if (globalOrSticky) { + // 21.2.5.2.2 RegExpBuiltinExec, step 12.a. if (lastIndex > lengthS) { - rx.lastIndex = 0; + if (globalOrSticky) + rx.lastIndex = 0; + + // Steps 12-16. return S; } } else { + // 21.2.5.2.2 RegExpBuiltinExec, step 8. lastIndex = 0; } @@ -37,7 +52,11 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, sticky // Step 11.b. if (result === null) { - rx.lastIndex = 0; + // 21.2.5.2.2 RegExpBuiltinExec, steps 12.a.i, 12.c.i. + if (globalOrSticky) + rx.lastIndex = 0; + + // Steps 12-16. return S; } @@ -61,7 +80,8 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, sticky // To set rx.lastIndex before RegExpGetComplexReplacement. var nextSourcePosition = position + matchLength; - if (sticky) + // 21.2.5.2.2 RegExpBuiltinExec, step 15. + if (globalOrSticky) rx.lastIndex = nextSourcePosition; var replacement; diff --git a/js/src/builtin/Set.js b/js/src/builtin/Set.js index accc70120..9af6cf8d1 100644 --- a/js/src/builtin/Set.js +++ b/js/src/builtin/Set.js @@ -14,34 +14,9 @@ function SetConstructorInit(iterable) { if (!IsCallable(adder)) ThrowTypeError(JSMSG_NOT_FUNCTION, typeof adder); - // Step 6.c. - var iterFn = iterable[std_iterator]; - if (!IsCallable(iterFn)) - ThrowTypeError(JSMSG_NOT_ITERABLE, DecompileArg(0, iterable)); - - var iter = callContentFunction(iterFn, iterable); - if (!IsObject(iter)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof iter); - - // Step 7 (not applicable). - - // Step 8. - while (true) { - // Step 8.a. - var next = callContentFunction(iter.next, iter); - if (!IsObject(next)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof next); - - // Step 8.b. - if (next.done) - return; - - // Step 8.c. - var nextValue = next.value; - - // Steps 8.d-e. + // Steps 6.c-8. + for (var nextValue of allowContentIter(iterable)) callContentFunction(adder, set, nextValue); - } } /* ES6 20121122 draft 15.16.4.6. */ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index acf449b7e..a14f9ba69 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -881,7 +881,7 @@ HasChild(JSContext* cx, unsigned argc, Value* vp) RootedValue parent(cx, args.get(0)); RootedValue child(cx, args.get(1)); - if (!parent.isMarkable() || !child.isMarkable()) { + if (!parent.isGCThing() || !child.isGCThing()) { args.rval().setBoolean(false); return true; } diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 4d2d6488f..a2205dc92 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -35,6 +35,10 @@ function IsDetachedBuffer(buffer) { return (flags & JS_ARRAYBUFFER_DETACHED_FLAG) !== 0; } +function TypedArrayLengthMethod() { + return TypedArrayLength(this); +} + function GetAttachedArrayBuffer(tarray) { var buffer = ViewedArrayBufferIfReified(tarray); if (IsDetachedBuffer(buffer)) @@ -42,6 +46,10 @@ function GetAttachedArrayBuffer(tarray) { return buffer; } +function GetAttachedArrayBufferMethod() { + return GetAttachedArrayBuffer(this); +} + // A function which ensures that the argument is either a typed array or a // cross-compartment wrapper for a typed array and that the typed array involved // has an attached array buffer. If one of those conditions doesn't hold (wrong @@ -54,10 +62,7 @@ function IsTypedArrayEnsuringArrayBuffer(arg) { return true; } - // This is a bit hacky but gets the job done: the first `arg` is used to - // test for a wrapped typed array, the second as an argument to - // GetAttachedArrayBuffer. - callFunction(CallTypedArrayMethodIfWrapped, arg, arg, "GetAttachedArrayBuffer"); + callFunction(CallTypedArrayMethodIfWrapped, arg, "GetAttachedArrayBufferMethod"); return false; } @@ -98,8 +103,8 @@ function TypedArrayCreateWithLength(constructor, length) { if (isTypedArray) { len = TypedArrayLength(newTypedArray); } else { - len = callFunction(CallTypedArrayMethodIfWrapped, newTypedArray, newTypedArray, - "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, newTypedArray, + "TypedArrayLengthMethod"); } if (len < length) @@ -259,15 +264,14 @@ function TypedArrayEvery(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -348,15 +352,14 @@ function TypedArrayFilter(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 3. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 4. if (arguments.length === 0) @@ -410,15 +413,14 @@ function TypedArrayFind(predicate/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -452,15 +454,14 @@ function TypedArrayFindIndex(predicate/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -492,15 +493,14 @@ function TypedArrayForEach(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 3-4. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 5. if (arguments.length === 0) @@ -686,15 +686,14 @@ function TypedArrayMap(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 3. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 4. if (arguments.length === 0) @@ -730,15 +729,14 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -776,15 +774,14 @@ function TypedArrayReduceRight(callbackfn/*, initialValue*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -1034,15 +1031,14 @@ function TypedArraySome(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -1137,7 +1133,7 @@ function TypedArraySort(comparefn) { if (isTypedArray) { buffer = GetAttachedArrayBuffer(obj); } else { - buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "GetAttachedArrayBuffer"); + buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, "GetAttachedArrayBufferMethod"); } // Step 3. @@ -1145,7 +1141,7 @@ function TypedArraySort(comparefn) { if (isTypedArray) { len = TypedArrayLength(obj); } else { - len = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, obj, "TypedArrayLengthMethod"); } if (comparefn === undefined) { @@ -1181,8 +1177,8 @@ function TypedArraySort(comparefn) { if (isTypedArray) { buffer = GetAttachedArrayBuffer(obj); } else { - buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, - "GetAttachedArrayBuffer"); + buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, + "GetAttachedArrayBufferMethod"); } } var bufferDetached; @@ -1217,15 +1213,14 @@ function TypedArrayToLocaleString(locales = undefined, options = undefined) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(array); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 2. var len; if (isTypedArray) len = TypedArrayLength(array); else - len = callFunction(CallTypedArrayMethodIfWrapped, array, array, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, array, "TypedArrayLengthMethod"); // Step 4. if (len === 0) @@ -1433,7 +1428,7 @@ function TypedArrayStaticFrom(source, mapfn = undefined, thisArg = undefined) { // 22.2.2.1.1 IterableToList, step 4.a. var next = callContentFunction(iterator.next, iterator); if (!IsObject(next)) - ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE); + ThrowTypeError(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next"); // 22.2.2.1.1 IterableToList, step 4.b. if (next.done) @@ -1560,7 +1555,7 @@ function IterableToList(items, method) { // Step 4.a. var next = callContentFunction(iterator.next, iterator); if (!IsObject(next)) - ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE); + ThrowTypeError(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next"); // Step 4.b. if (next.done) diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index b7297c894..ae74f01bf 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -230,7 +230,7 @@ const Class js::ScalarTypeDescr::class_ = { const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = { JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), - JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, JSFUN_HAS_REST), + JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0), JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), JS_FS_END }; diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index bfb1fe7f4..c73bc5e7f 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -106,7 +106,17 @@ function ToLength(v) { return std_Math_min(v, 0x1fffffffffffff); } -/* Spec: ECMAScript Draft, 6th edition Oct 14, 2014, 7.2.4 */ +// ES2017 draft rev aebf014403a3e641fb1622aec47c40f051943527 +// 7.2.9 SameValue ( x, y ) +function SameValue(x, y) { + if (x === y) { + return (x !== 0) || (1 / x === 1 / y); + } + return (x !== x && y !== y); +} + +// ES2017 draft rev aebf014403a3e641fb1622aec47c40f051943527 +// 7.2.10 SameValueZero ( x, y ) function SameValueZero(x, y) { return x === y || (x !== x && y !== y); } diff --git a/js/src/builtin/WeakMap.js b/js/src/builtin/WeakMap.js index 066a72bfe..6755b7a7b 100644 --- a/js/src/builtin/WeakMap.js +++ b/js/src/builtin/WeakMap.js @@ -14,31 +14,8 @@ function WeakMapConstructorInit(iterable) { if (!IsCallable(adder)) ThrowTypeError(JSMSG_NOT_FUNCTION, typeof adder); - // Step 6.c. - var iterFn = iterable[std_iterator]; - if (!IsCallable(iterFn)) - ThrowTypeError(JSMSG_NOT_ITERABLE, DecompileArg(0, iterable)); - - var iter = callContentFunction(iterFn, iterable); - if (!IsObject(iter)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof iter); - - // Step 7 (not applicable). - - // Step 8. - while (true) { - // Step 8.a. - var next = callContentFunction(iter.next, iter); - if (!IsObject(next)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof next); - - // Step 8.b. - if (next.done) - return; - - // Step 8.c. - var nextItem = next.value; - + // Steps 6.c-8. + for (var nextItem of allowContentIter(iterable)) { // Step 8.d. if (!IsObject(nextItem)) ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "WeakMap"); diff --git a/js/src/builtin/WeakSet.js b/js/src/builtin/WeakSet.js index eb7c2378f..b16b4634d 100644 --- a/js/src/builtin/WeakSet.js +++ b/js/src/builtin/WeakSet.js @@ -14,34 +14,9 @@ function WeakSetConstructorInit(iterable) { if (!IsCallable(adder)) ThrowTypeError(JSMSG_NOT_FUNCTION, typeof adder); - // Step 6.c. - var iterFn = iterable[std_iterator]; - if (!IsCallable(iterFn)) - ThrowTypeError(JSMSG_NOT_ITERABLE, DecompileArg(0, iterable)); - - var iter = callContentFunction(iterFn, iterable); - if (!IsObject(iter)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof iter); - - // Step 7 (not applicable). - - // Step 8. - while (true) { - // Step 8.a. - var next = callContentFunction(iter.next, iter); - if (!IsObject(next)) - ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof next); - - // Step 8.b. - if (next.done) - return; - - // Step 8.c. - var nextValue = next.value; - - // Steps 8.d-e. + // Steps 6.c-8. + for (var nextValue of allowContentIter(iterable)) callContentFunction(adder, set, nextValue); - } } // 23.4.3.1 |