diff options
Diffstat (limited to 'js')
29 files changed, 986 insertions, 565 deletions
diff --git a/js/moz.configure b/js/moz.configure index 0eeb2fc52..eadd0e9ab 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -236,3 +236,27 @@ def ctypes_and_compile_environment(ctypes, compile_environment, _): return ctypes and compile_environment include('ffi.configure', when=ctypes_and_compile_environment) + +# Support various fuzzing options +# ============================================================== +with only_when('--enable-compile-environment'): + option('--enable-fuzzing', help='Enable fuzzing support') + + @depends('--enable-fuzzing') + def enable_fuzzing(value): + if value: + return True + + @depends(enable_fuzzing, + try_compile(body='__AFL_COMPILER;', + check_msg='for AFL compiler', + when='--enable-fuzzing')) + def enable_libfuzzer(fuzzing, afl): + if fuzzing and not afl: + return True + + set_config('FUZZING', enable_fuzzing) + set_define('FUZZING', enable_fuzzing) + + set_config('LIBFUZZER', enable_libfuzzer) + set_define('LIBFUZZER', enable_libfuzzer) 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/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index bce2b74aa..aa50bf29e 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -121,6 +121,13 @@ js::Nursery::Nursery(JSRuntime* rt) bool js::Nursery::init(uint32_t maxNurseryBytes, AutoLockGC& lock) { + if (!mallocedBuffers.init()) + return false; + + freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp()); + if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init()) + return false; + /* maxNurseryBytes parameter is rounded down to a multiple of chunk size. */ maxNurseryChunks_ = maxNurseryBytes >> ChunkShift; @@ -128,16 +135,9 @@ js::Nursery::init(uint32_t maxNurseryBytes, AutoLockGC& lock) if (maxNurseryChunks_ == 0) return true; - if (!mallocedBuffers.init()) - return false; - if (!cellsWithUid_.init()) return false; - freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp()); - if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init()) - return false; - AutoMaybeStartBackgroundAllocation maybeBgAlloc; updateNumChunksLocked(1, maybeBgAlloc, lock); if (numChunks() == 0) diff --git a/js/src/jit-test/tests/debug/bug1353356.js b/js/src/jit-test/tests/debug/bug1353356.js new file mode 100644 index 000000000..389bb7860 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1353356.js @@ -0,0 +1,65 @@ +// |jit-test| allow-oom; --fuzzing-safe + +var lfLogBuffer = ` +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata + setJitCompilerOption("ion.warmup.trigger", 4); + var g = newGlobal(); + g.debuggeeGlobal = this; + g.eval("(" + function () { + dbg = new Debugger(debuggeeGlobal); + dbg.onExceptionUnwind = function (frame, exc) { + var s = '!'; + for (var f = frame; f; f = f.older) + debuggeeGlobal.log += s; + }; + } + ")();"); + j('Number.prototype.toSource.call([])'); +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata +//corefuzz-dcd-selectmode 4 +//corefuzz-dcd-endofdata +} +//corefuzz-dcd-endofdata +//corefuzz-dcd-selectmode 5 +//corefuzz-dcd-endofdata +oomTest(() => i({ + new : (true ), + thisprops: true +})); +`; +lfLogBuffer = lfLogBuffer.split('\n'); +var lfRunTypeId = -1; +var lfCodeBuffer = ""; +while (true) { + var line = lfLogBuffer.shift(); + if (line == null) { + break; + } else if (line == "//corefuzz-dcd-endofdata") { + loadFile(lfCodeBuffer); + lfCodeBuffer = ""; + loadFile(line); + } else { + lfCodeBuffer += line + "\n"; + } +} +if (lfCodeBuffer) loadFile(lfCodeBuffer); +function loadFile(lfVarx) { + try { + if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) { + lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % 6; + } else { + switch (lfRunTypeId) { + case 4: + oomTest(function() { + let m = parseModule(lfVarx); + }); + break; + default: + evaluate(lfVarx); + } + } + } catch (lfVare) {} +} diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 2a158ed7e..c61b414e0 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -568,9 +568,6 @@ jit::LinkIonScript(JSContext* cx, HandleScript calleeScript) // doesn't has code to handle it after linking happened. So it's // not OK to throw a catchable exception from there. cx->clearPendingException(); - - // Reset the TypeZone's compiler output for this script, if any. - InvalidateCompilerOutputsForScript(cx, calleeScript); } } diff --git a/js/src/jit/MoveResolver.cpp b/js/src/jit/MoveResolver.cpp index 5fd6c7bd5..383b45073 100644 --- a/js/src/jit/MoveResolver.cpp +++ b/js/src/jit/MoveResolver.cpp @@ -106,12 +106,111 @@ MoveResolver::findCycledMove(PendingMoveIterator* iter, PendingMoveIterator end, return nullptr; } +#ifdef JS_CODEGEN_ARM +static inline bool +MoveIsDouble(const MoveOperand& move) +{ + if (!move.isFloatReg()) + return false; + return move.floatReg().isDouble(); +} +#endif + +#ifdef JS_CODEGEN_ARM +static inline bool +MoveIsSingle(const MoveOperand& move) +{ + if (!move.isFloatReg()) + return false; + return move.floatReg().isSingle(); +} +#endif + +#ifdef JS_CODEGEN_ARM +bool +MoveResolver::isDoubleAliasedAsSingle(const MoveOperand& move) +{ + if (!MoveIsDouble(move)) + return false; + + for (auto iter = pending_.begin(); iter != pending_.end(); ++iter) { + PendingMove* other = *iter; + if (other->from().aliases(move) && MoveIsSingle(other->from())) + return true; + if (other->to().aliases(move) && MoveIsSingle(other->to())) + return true; + } + return false; +} +#endif + +#ifdef JS_CODEGEN_ARM +static MoveOperand +SplitIntoLowerHalf(const MoveOperand& move) +{ + if (MoveIsDouble(move)) { + FloatRegister lowerSingle = move.floatReg().asSingle(); + return MoveOperand(lowerSingle); + } + + MOZ_ASSERT(move.isMemoryOrEffectiveAddress()); + return move; +} +#endif + +#ifdef JS_CODEGEN_ARM +static MoveOperand +SplitIntoUpperHalf(const MoveOperand& move) +{ + if (MoveIsDouble(move)) { + FloatRegister lowerSingle = move.floatReg().asSingle(); + FloatRegister upperSingle = VFPRegister(lowerSingle.code() + 1, VFPRegister::Single); + return MoveOperand(upperSingle); + } + + MOZ_ASSERT(move.isMemoryOrEffectiveAddress()); + return MoveOperand(move.base(), move.disp() + sizeof(float)); +} +#endif + bool MoveResolver::resolve() { resetState(); orderedMoves_.clear(); +#ifdef JS_CODEGEN_ARM + // Some of ARM's double registers alias two of its single registers, + // but the algorithm below assumes that every register can participate + // in at most one cycle. To satisfy the algorithm, any double registers + // that may conflict are split into their single-register halves. + // + // This logic is only applicable because ARM only uses registers d0-d15, + // all of which alias s0-s31. Double registers d16-d31 are unused. + // Therefore there is never a double move that cannot be split. + // If this changes in the future, the algorithm will have to be fixed. + for (auto iter = pending_.begin(); iter != pending_.end(); ++iter) { + PendingMove* pm = *iter; + + if (isDoubleAliasedAsSingle(pm->from()) || isDoubleAliasedAsSingle(pm->to())) { + PendingMove* lower = movePool_.allocate(); + if (!lower) + return false; + + // Insert the new node before the current position to not affect iteration. + MoveOperand fromLower = SplitIntoLowerHalf(pm->from()); + MoveOperand toLower = SplitIntoLowerHalf(pm->to()); + new (lower) PendingMove(fromLower, toLower, MoveOp::FLOAT32); + pending_.insertBefore(pm, lower); + + // Overwrite pm in place for the upper move. Iteration proceeds as normal. + MoveOperand fromUpper = SplitIntoUpperHalf(pm->from()); + MoveOperand toUpper = SplitIntoUpperHalf(pm->to()); + pm->overwrite(fromUpper, toUpper, MoveOp::FLOAT32); + } + } +#endif + InlineList<PendingMove> stack; // This is a depth-first-search without recursion, which tries to find diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index fad2ba9e3..db045cfcf 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -252,6 +252,13 @@ class MoveOp bool aliases(const MoveOp& other) const { return aliases(other.from()) || aliases(other.to()); } +#ifdef JS_CODEGEN_ARM + void overwrite(MoveOperand& from, MoveOperand& to, Type type) { + from_ = from; + to_ = to; + type_ = type; + } +#endif }; class MoveResolver @@ -299,6 +306,10 @@ class MoveResolver // Internal reset function. Does not clear lists. void resetState(); +#ifdef JS_CODEGEN_ARM + bool isDoubleAliasedAsSingle(const MoveOperand& move); +#endif + public: MoveResolver(); diff --git a/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp b/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp index 416587293..c1c2baddd 100644 --- a/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp +++ b/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #if defined(JS_SIMULATOR_ARM) + #include "jit/arm/Assembler-arm.h" #include "jit/arm/MoveEmitter-arm.h" #include "jit/arm/Simulator-arm.h" @@ -528,5 +529,96 @@ BEGIN_TEST(testJitMoveEmitterCycles_autogen3) return true; } END_TEST(testJitMoveEmitterCycles_autogen3) +BEGIN_TEST(testJitMoveEmitterCycles_bug1299147_1) +{ + using namespace js; + using namespace js::jit; + LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE); + TempAllocator alloc(&lifo); + JitContext jc(cx, &alloc); + cx->runtime()->getJitRuntime(cx); + MacroAssembler masm; + MoveEmitter mover(masm); + MoveResolver mr; + mr.setAllocator(alloc); + Simulator* sim = Simulator::Current(); + // S2 -> S0 + // S2 -> S6 + // S3 -> S1 + // S3 -> S7 + // D0 -> D1 + // D0 -> D2 + TRY(mr.addMove(MoveOperand(s2), MoveOperand(s0), MoveOp::FLOAT32)); + TRY(mr.addMove(MoveOperand(s2), MoveOperand(s6), MoveOp::FLOAT32)); + sim->set_s_register_from_float(2, 2); + TRY(mr.addMove(MoveOperand(s3), MoveOperand(s1), MoveOp::FLOAT32)); + TRY(mr.addMove(MoveOperand(s3), MoveOperand(s7), MoveOp::FLOAT32)); + sim->set_s_register_from_float(3, 4); + TRY(mr.addMove(MoveOperand(d0), MoveOperand(d1), MoveOp::FLOAT32)); + TRY(mr.addMove(MoveOperand(d0), MoveOperand(d2), MoveOp::FLOAT32)); + sim->set_d_register_from_double(0, 1); + // don't explode! + TRY(mr.resolve()); + mover.emit(mr); + mover.finish(); + masm.abiret(); + JitCode* code = linkAndAllocate(cx, &masm); + sim->call(code->raw(), 1, 1); + float f; + double d; + sim->get_double_from_d_register(1, &d); + CHECK(d == 1); + sim->get_double_from_d_register(2, &d); + CHECK(d == 1); + sim->get_float_from_s_register(0, &f); + CHECK(int(f) == 2); + sim->get_float_from_s_register(6, &f); + CHECK(int(f) == 2); + sim->get_float_from_s_register(1, &f); + CHECK(int(f) == 4); + sim->get_float_from_s_register(7, &f); + CHECK(int(f) == 4); + return true; +} +END_TEST(testJitMoveEmitterCycles_bug1299147_1) +BEGIN_TEST(testJitMoveEmitterCycles_bug1299147) +{ + using namespace js; + using namespace js::jit; + LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE); + TempAllocator alloc(&lifo); + JitContext jc(cx, &alloc); + cx->runtime()->getJitRuntime(cx); + MacroAssembler masm; + MoveEmitter mover(masm); + MoveResolver mr; + mr.setAllocator(alloc); + Simulator* sim = Simulator::Current(); + // S2 -> S5 + // S2 -> S6 + // D0 -> D1 + TRY(mr.addMove(MoveOperand(s2), MoveOperand(s5), MoveOp::FLOAT32)); + TRY(mr.addMove(MoveOperand(s2), MoveOperand(s6), MoveOp::FLOAT32)); + sim->set_s_register_from_float(2, 2); + TRY(mr.addMove(MoveOperand(d0), MoveOperand(d1), MoveOp::FLOAT32)); + sim->set_d_register_from_double(0, 1); + // don't explode! + TRY(mr.resolve()); + mover.emit(mr); + mover.finish(); + masm.abiret(); + JitCode* code = linkAndAllocate(cx, &masm); + sim->call(code->raw(), 1, 1); + float f; + double d; + sim->get_double_from_d_register(1, &d); + CHECK(d == 1); + sim->get_float_from_s_register(5, &f); + CHECK(int(f) == 2); + sim->get_float_from_s_register(6, &f); + CHECK(int(f) == 2); + return true; +} +END_TEST(testJitMoveEmitterCycles_bug1299147) -#endif +#endif // JS_SIMULATOR_ARM diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 9ad3e757f..2d6ff462c 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5927,7 +5927,7 @@ enum TranscodeResult TranscodeResult_Failure_BadBuildId = TranscodeResult_Failure | 0x1, TranscodeResult_Failure_RunOnceNotSupported = TranscodeResult_Failure | 0x2, TranscodeResult_Failure_AsmJSNotSupported = TranscodeResult_Failure | 0x3, - TranscodeResult_Failure_UnknownClassKind = TranscodeResult_Failure | 0x4, + TranscodeResult_Failure_BadDecode = TranscodeResult_Failure | 0x4, // A error, the JSContext has a pending exception. TranscodeResult_Throw = 0x200 @@ -5999,7 +5999,6 @@ enum AsmJSCacheResult AsmJSCache_Disabled_JitInspector, AsmJSCache_InternalError, AsmJSCache_Disabled_PrivateBrowsing, - AsmJSCache_ESR52, AsmJSCache_LIMIT }; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9fffce448..c952441ad 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -641,6 +641,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope, objp.set(fun); } + // Verify marker at end of function to detect buffer truncation. + if (!xdr->codeMarker(0xA0129CD1)) + return false; + return true; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2e02aa63d..10821f26a 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -75,24 +75,19 @@ js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp) { JSContext* cx = xdr->cx(); - /* - * A script constant can be an arbitrary primitive value as they are used - * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see - * bug 407186. - */ enum ConstTag { - SCRIPT_INT = 0, - SCRIPT_DOUBLE = 1, - SCRIPT_ATOM = 2, - SCRIPT_TRUE = 3, - SCRIPT_FALSE = 4, - SCRIPT_NULL = 5, - SCRIPT_OBJECT = 6, - SCRIPT_VOID = 7, - SCRIPT_HOLE = 8 + SCRIPT_INT, + SCRIPT_DOUBLE, + SCRIPT_ATOM, + SCRIPT_TRUE, + SCRIPT_FALSE, + SCRIPT_NULL, + SCRIPT_OBJECT, + SCRIPT_VOID, + SCRIPT_HOLE }; - uint32_t tag; + ConstTag tag; if (mode == XDR_ENCODE) { if (vp.isInt32()) { tag = SCRIPT_INT; @@ -116,7 +111,7 @@ js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp) } } - if (!xdr->codeUint32(&tag)) + if (!xdr->codeEnum32(&tag)) return false; switch (tag) { @@ -182,6 +177,10 @@ js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp) if (mode == XDR_DECODE) vp.setMagic(JS_ELEMENTS_HOLE); break; + default: + // Fail in debug, but only soft-fail in release + MOZ_ASSERT(false, "Bad XDR value kind"); + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); } return true; } @@ -742,11 +741,20 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip case ScopeKind::Module: MOZ_CRASH("NYI"); break; + default: + // Fail in debug, but only soft-fail in release + MOZ_ASSERT(false, "Bad XDR scope kind"); + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); } if (mode == XDR_DECODE) vector[i].init(scope); } + + // Verify marker to detect data corruption after decoding scope data. A + // mismatch here indicates we will almost certainly crash in release. + if (!xdr->codeMarker(0xF81F7F5A)) + return false; } /* @@ -832,12 +840,18 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip } default: { - MOZ_ASSERT(false, "Unknown class kind."); - return xdr->fail(JS::TranscodeResult_Failure_UnknownClassKind); + // Fail in debug, but only soft-fail in release + MOZ_ASSERT(false, "Bad XDR class kind"); + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); } } } + // Verify marker to detect data corruption after decoding object data. A + // mismatch here indicates we will almost certainly crash in release. + if (!xdr->codeMarker(0x223DB179)) + return false; + if (ntrynotes != 0) { JSTryNote* tnfirst = script->trynotes()->vector; MOZ_ASSERT(script->trynotes()->length == ntrynotes); diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp index bb9f33df9..705d21975 100644 --- a/js/src/jsutil.cpp +++ b/js/src/jsutil.cpp @@ -39,7 +39,7 @@ mozilla::Atomic<AutoEnterOOMUnsafeRegion*> AutoEnterOOMUnsafeRegion::owner_; namespace oom { JS_PUBLIC_DATA(uint32_t) targetThread = 0; -JS_PUBLIC_DATA(MOZ_THREAD_LOCAL(uint32_t)) threadType; +MOZ_THREAD_LOCAL(uint32_t) threadType; JS_PUBLIC_DATA(uint64_t) maxAllocations = UINT64_MAX; JS_PUBLIC_DATA(uint64_t) counter = 0; JS_PUBLIC_DATA(bool) failAlways = true; diff --git a/js/src/old-configure.in b/js/src/old-configure.in index e4589b951..5da81ce3e 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -143,8 +143,7 @@ MOZ_TOOL_VARIABLES dnl Special win32 checks dnl ======================================================== -# Target the Windows 8.1 SDK by default -WINVER=502 +WINVER=601 case "$target" in *-mingw*) @@ -722,12 +721,7 @@ case "$target" in IMPORT_LIB_SUFFIX=lib MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)' MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)' - dnl Set subsystem version 5 for Windows XP. - if test "$CPU_ARCH" = "x86"; then - WIN32_SUBSYSTEM_VERSION=5.01 - else - WIN32_SUBSYSTEM_VERSION=6.01 - fi + WIN32_SUBSYSTEM_VERSION=6.01 WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION DSO_LDOPTS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 88d482a23..3f56981cd 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1546,9 +1546,9 @@ ConvertTranscodeResultToJSException(JSContext* cx, JS::TranscodeResult rv) MOZ_ASSERT(!cx->isExceptionPending()); JS_ReportErrorASCII(cx, "Asm.js is not supported by XDR"); return false; - case JS::TranscodeResult_Failure_UnknownClassKind: + case JS::TranscodeResult_Failure_BadDecode: MOZ_ASSERT(!cx->isExceptionPending()); - JS_ReportErrorASCII(cx, "Unknown class kind, go fix it."); + JS_ReportErrorASCII(cx, "XDR data corruption"); return false; case JS::TranscodeResult_Throw: diff --git a/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js b/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js new file mode 100644 index 000000000..f5f5b62a8 --- /dev/null +++ b/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras")) + +// Tests the PluralRules.resolvedOptions function for overriden Array[Symbol.species]. + +addIntlExtras(Intl); + +var pl = new Intl.PluralRules("de"); + +Object.defineProperty(Array, Symbol.species, { + value: function() { + return new Proxy(["?"], { + get(t, pk, r) { + return Reflect.get(t, pk, r); + }, + defineProperty(t, pk) { + return true; + } + }); + } +}); + +var pluralCategories = pl.resolvedOptions().pluralCategories; + +assertEqArray(pluralCategories, ["one", "other"]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/Intl/getCanonicalLocales-overridden-species.js b/js/src/tests/Intl/getCanonicalLocales-overridden-species.js new file mode 100644 index 000000000..858735b58 --- /dev/null +++ b/js/src/tests/Intl/getCanonicalLocales-overridden-species.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Tests the getCanonicalLocales function for overriden Array[Symbol.species]. + +Object.defineProperty(Array, Symbol.species, { + value: function() { + return new Proxy(["?"], { + get(t, pk, r) { + return Reflect.get(t, pk, r); + }, + defineProperty(t, pk) { + return true; + } + }); + } +}); + +var arr = Intl.getCanonicalLocales("de-x-private"); + +assertEqArray(arr, ["de-x-private"]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/threading/windows/ConditionVariable.cpp b/js/src/threading/windows/ConditionVariable.cpp index 868c35141..3c75a0f27 100644 --- a/js/src/threading/windows/ConditionVariable.cpp +++ b/js/src/threading/windows/ConditionVariable.cpp @@ -28,342 +28,34 @@ _InterlockedIncrement((volatile long*)(addend)) #endif -// Windows XP and Server 2003 don't support condition variables natively. The -// NativeImports class is responsible for detecting native support and -// retrieving the appropriate function pointers. It gets instantiated once, -// using a static initializer. -class ConditionVariableNativeImports -{ -public: - ConditionVariableNativeImports() { - HMODULE kernel32_dll = GetModuleHandle("kernel32.dll"); - MOZ_RELEASE_ASSERT(kernel32_dll != NULL); - -#define LOAD_SYMBOL(symbol) loadSymbol(kernel32_dll, #symbol, symbol) - supported_ = LOAD_SYMBOL(InitializeConditionVariable) && - LOAD_SYMBOL(WakeConditionVariable) && - LOAD_SYMBOL(WakeAllConditionVariable) && - LOAD_SYMBOL(SleepConditionVariableCS); -#undef LOAD_SYMBOL - } - - inline bool supported() const { - return supported_; - } - - void(WINAPI* InitializeConditionVariable)(CONDITION_VARIABLE* ConditionVariable); - void(WINAPI* WakeAllConditionVariable)(PCONDITION_VARIABLE ConditionVariable); - void(WINAPI* WakeConditionVariable)(CONDITION_VARIABLE* ConditionVariable); - BOOL(WINAPI* SleepConditionVariableCS)(CONDITION_VARIABLE* ConditionVariable, - CRITICAL_SECTION* CriticalSection, - DWORD dwMilliseconds); - -private: - template <typename T> - inline bool loadSymbol(HMODULE module, const char* name, T& fn) { - FARPROC ptr = GetProcAddress(module, name); - if (!ptr) - return false; - - fn = reinterpret_cast<T>(ptr); - return true; - } - - bool supported_; -}; - -static ConditionVariableNativeImports sNativeImports; - // Wrapper for native condition variable APIs. -struct ConditionVariableNative -{ - inline void initialize() { - sNativeImports.InitializeConditionVariable(&cv_); - } - - inline void destroy() { - // Native condition variables don't require cleanup. - } - - inline void notify_one() { sNativeImports.WakeConditionVariable(&cv_); } - - inline void notify_all() { sNativeImports.WakeAllConditionVariable(&cv_); } - - inline bool wait(CRITICAL_SECTION* cs, DWORD msec) { - return sNativeImports.SleepConditionVariableCS(&cv_, cs, msec); - } - -private: - CONDITION_VARIABLE cv_; -}; - -// Fallback condition variable support for Windows XP and Server 2003. Given the -// difficulty of testing on these antiquated platforms and their rapidly -// diminishing market share, this implementation trades performance for -// predictable behavior. -struct ConditionVariableFallback -{ - static const uint32_t WAKEUP_MODE_NONE = 0; - static const uint32_t WAKEUP_MODE_ONE = 0x40000000; - static const uint32_t WAKEUP_MODE_ALL = 0x80000000; - - static const uint32_t WAKEUP_MODE_MASK = WAKEUP_MODE_ONE | WAKEUP_MODE_ALL; - static const uint32_t SLEEPERS_COUNT_MASK = ~WAKEUP_MODE_MASK; - - void initialize() - { - // Initialize the state variable to 0 sleepers, no wakeup. - sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE; - - // Create a semaphore that prevents threads from entering sleep, - // or waking other threads while a wakeup is ongoing. - sleepWakeupSemaphore_ = CreateSemaphoreW(NULL, 1, 1, NULL); - MOZ_RELEASE_ASSERT(sleepWakeupSemaphore_); - - // Use an auto-reset event for waking up a single sleeper. - wakeOneEvent_ = CreateEventW(NULL, FALSE, FALSE, NULL); - MOZ_RELEASE_ASSERT(wakeOneEvent_); - - // Use a manual-reset event for waking up all sleepers. - wakeAllEvent_ = CreateEventW(NULL, TRUE, FALSE, NULL); - MOZ_RELEASE_ASSERT(wakeAllEvent_); - } - - void destroy() - { - BOOL r; - - MOZ_RELEASE_ASSERT(sleepersCountAndWakeupMode_ == (0 | WAKEUP_MODE_NONE)); - - r = CloseHandle(sleepWakeupSemaphore_); - MOZ_RELEASE_ASSERT(r); - - r = CloseHandle(wakeOneEvent_); - MOZ_RELEASE_ASSERT(r); - - r = CloseHandle(wakeAllEvent_); - MOZ_RELEASE_ASSERT(r); - } - -private: - void wakeup(uint32_t wakeupMode, HANDLE wakeEvent) - { - // Ensure that only one thread at a time can wake up others. - BOOL result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE); - MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0); - - // Atomically set the wakeup mode and retrieve the number of sleepers. - uint32_t wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_, - wakeupMode); - uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK; - MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE); - - if (sleepersCount > 0) { - // If there are any sleepers, set the wake event. The (last) woken - // up thread is responsible for releasing the semaphore. - BOOL success = SetEvent(wakeEvent); - MOZ_RELEASE_ASSERT(success); - - } else { - // If there are no sleepers, set the wakeup mode back to 'none' - // and release the semaphore ourselves. - sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE; - - BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL); - MOZ_RELEASE_ASSERT(success); - } - } - -public: - void notify_one() { wakeup(WAKEUP_MODE_ONE, wakeOneEvent_); } - - void notify_all() { wakeup(WAKEUP_MODE_ALL, wakeAllEvent_); } - - bool wait(CRITICAL_SECTION* userLock, DWORD msec) - { - // Make sure that we can't enter sleep when there are other threads - // that still need to wake up on either of the wake events being set. - DWORD result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE); - MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0); - - // Register ourselves as a sleeper. Use an atomic operation, because - // if another thread times out at the same time, it will decrement the - // sleepers count without acquiring the semaphore. - uint32_t wcwm = InterlockedIncrement(&sleepersCountAndWakeupMode_); - MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE); - - // Now that that this thread has been enlisted as a sleeper, it is safe - // again for other threads to do a wakeup. - BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL); - MOZ_RELEASE_ASSERT(success); - - // Release the caller's mutex. - LeaveCriticalSection(userLock); - - // Wait for either event to become signaled, which happens when - // notify_one() or notify_all() is called, or for a timeout. - HANDLE handles[2] = { wakeOneEvent_, wakeAllEvent_ }; - DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, msec); - MOZ_RELEASE_ASSERT(waitResult == WAIT_OBJECT_0 || - waitResult == WAIT_OBJECT_0 + 1 || - (waitResult == WAIT_TIMEOUT && msec != INFINITE)); - - // Atomically decrease the sleepers count and retrieve the wakeup mode - // and new sleepers count. - // If the wait returned because wakeOneEvent_ was set, we are certain - // that the wakeup mode will be WAKEUP_MODE_ONE. In that case, - // atomically reset the wakeup mode to 'none', because if another - // thread's sleep times out at same time and it finds that it was the - // last sleeper, it decides whether or not to reset the wakeOneEvent_ - // based on the current wakeup mode. - uint32_t sub; - if (waitResult == WAIT_OBJECT_0) - sub = 1 | WAKEUP_MODE_ONE; - else - sub = 1; - // Note that InterlockedExchangeAdd returns the old value, but it's - // easier to work with the new value. - wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_, -sub) - sub; - - uint32_t wakeupMode = wcwm & WAKEUP_MODE_MASK; - uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK; - - bool releaseSleepWakeupSemaphore = false; - - if (waitResult == WAIT_OBJECT_0) { - // The wake-one event is an auto-reset event so if we're woken by - // it, it should already have been reset. We also already removed - // the WAKEUP_MODE_ONE bit so the wakeup mode should now be 'none' - // again. - MOZ_RELEASE_ASSERT(wakeupMode == WAKEUP_MODE_NONE); - - // The signaling thread has acquired the enter-wakeup semaphore and - // expects the woken (this) thread to release it again. - releaseSleepWakeupSemaphore = true; - - } else if (waitResult == WAIT_TIMEOUT && wakeupMode == WAKEUP_MODE_ONE && - sleepersCount == 0) { - // In theory a race condition is possible where the last sleeper - // times out right at the moment that another thread signals it. - // If that just happened we now have a dangling signal event and - // mode, but no threads to be woken up by it, and we need to clean - // that up. - BOOL success = ResetEvent(wakeOneEvent_); - MOZ_RELEASE_ASSERT(success); - - // This is safe - we are certain there are no other sleepers that - // could wake up right now, and the semaphore ensures that no - // non-sleeping threads are messing with - // sleepersCountAndWakeupMode_. - sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE; - - // The signaling thread has acquired the sleep-wakeup semaphore and - // expects the woken thread to release it. But since there are no - // sleeping threads left this thread will do it instead. - releaseSleepWakeupSemaphore = true; - - } else if (wakeupMode == WAKEUP_MODE_ALL && sleepersCount == 0) { - // If this was the last thread waking up in response to a - // notify_all, clear the wakeup mode and reset the wake-all event. - // A race condition similar to the case described above could - // occur, so waitResult could be WAIT_TIMEOUT, but that doesn't - // matter for the actions that need to be taken. - MOZ_RELEASE_ASSERT(waitResult = WAIT_OBJECT_0 + 1 || - waitResult == WAIT_TIMEOUT); - - BOOL success = ResetEvent(wakeAllEvent_); - MOZ_RELEASE_ASSERT(success); - - sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE; - - // The broadcasting thread has acquired the enter-wakeup semaphore - // and expects the last thread that wakes up to release it. - releaseSleepWakeupSemaphore = true; - - } else if ((waitResult == WAIT_TIMEOUT && msec != INFINITE) || - (waitResult == WAIT_OBJECT_0 + 1 && - wakeupMode == WAKEUP_MODE_ALL)) { - // Either: - // * The wait timed out but found no active notify_one or notify_all - // the moment it decreased the wait count. - // * A notify_all woke up this thread but there are more threads - // that need to be woken up by the wake-all event. - // These are ordinary conditions in which we don't have to do - // anything. - - } else { - MOZ_CRASH("invalid wakeup condition"); - } - - // Release the enter-wakeup semaphore if the wakeup condition requires - // us to do it. - if (releaseSleepWakeupSemaphore) { - BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL); - MOZ_RELEASE_ASSERT(success); - } - - // Reacquire the user mutex. - EnterCriticalSection(userLock); - - // Return true if woken up, false when timed out. - if (waitResult == WAIT_TIMEOUT) { - SetLastError(ERROR_TIMEOUT); - return false; - } - return true; - } - -private: - uint32_t sleepersCountAndWakeupMode_; - HANDLE sleepWakeupSemaphore_; - HANDLE wakeOneEvent_; - HANDLE wakeAllEvent_; -}; - struct js::ConditionVariable::PlatformData { - union - { - ConditionVariableNative native; - ConditionVariableFallback fallback; - }; + CONDITION_VARIABLE cv_; }; js::ConditionVariable::ConditionVariable() { - if (sNativeImports.supported()) - platformData()->native.initialize(); - else - platformData()->fallback.initialize(); + InitializeConditionVariable(&platformData()->cv_); } void js::ConditionVariable::notify_one() { - if (sNativeImports.supported()) - platformData()->native.notify_one(); - else - platformData()->fallback.notify_one(); + WakeConditionVariable(&platformData()->cv_); } void js::ConditionVariable::notify_all() { - if (sNativeImports.supported()) - platformData()->native.notify_all(); - else - platformData()->fallback.notify_all(); + WakeAllConditionVariable(&platformData()->cv_); } void js::ConditionVariable::wait(UniqueLock<Mutex>& lock) { CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection; - bool r; - if (sNativeImports.supported()) - r = platformData()->native.wait(cs, INFINITE); - else - r = platformData()->fallback.wait(cs, INFINITE); + bool r = SleepConditionVariableCS(&platformData()->cv_, cs, INFINITE); MOZ_RELEASE_ASSERT(r); } @@ -390,11 +82,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock, ? INFINITE : static_cast<DWORD>(msecd); - BOOL r; - if (sNativeImports.supported()) - r = platformData()->native.wait(cs, msec); - else - r = platformData()->fallback.wait(cs, msec); + BOOL r = SleepConditionVariableCS(&platformData()->cv_, cs, msec); if (r) return CVStatus::NoTimeout; MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT); @@ -403,10 +91,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock, js::ConditionVariable::~ConditionVariable() { - if (sNativeImports.supported()) - platformData()->native.destroy(); - else - platformData()->fallback.destroy(); + // Native condition variables don't require cleanup. } inline js::ConditionVariable::PlatformData* diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index 730578cd4..c69306aac 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -116,22 +116,22 @@ SharedArrayRawBuffer::New(JSContext* cx, uint32_t length) if (allocSize <= length) return nullptr; + // Test >= to guard against the case where multiple extant runtimes + // race to allocate. + if (++numLive >= maxLive) { + JSRuntime* rt = cx->runtime(); + if (rt->largeAllocationFailureCallback) + rt->largeAllocationFailureCallback(rt->largeAllocationFailureCallbackData); + if (numLive >= maxLive) { + numLive--; + return nullptr; + } + } + bool preparedForAsmJS = jit::JitOptions.asmJSAtomicsEnable && IsValidAsmJSHeapLength(length); void* p = nullptr; if (preparedForAsmJS) { - // Test >= to guard against the case where multiple extant runtimes - // race to allocate. - if (++numLive >= maxLive) { - JSRuntime* rt = cx->runtime(); - if (rt->largeAllocationFailureCallback) - rt->largeAllocationFailureCallback(rt->largeAllocationFailureCallbackData); - if (numLive >= maxLive) { - numLive--; - return nullptr; - } - } - uint32_t mappedSize = SharedArrayMappedSize(allocSize); // Get the entire reserved region (with all pages inaccessible) @@ -154,8 +154,10 @@ SharedArrayRawBuffer::New(JSContext* cx, uint32_t length) # endif } else { p = MapMemory(allocSize, true); - if (!p) + if (!p) { + numLive--; return nullptr; + } } uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize(); @@ -189,8 +191,6 @@ SharedArrayRawBuffer::dropReference() uint32_t allocSize = SharedArrayAllocSize(this->length); if (this->preparedForAsmJS) { - numLive--; - uint32_t mappedSize = SharedArrayMappedSize(allocSize); UnmapMemory(address, mappedSize); @@ -202,6 +202,10 @@ SharedArrayRawBuffer::dropReference() } else { UnmapMemory(address, allocSize); } + + // Decrement the buffer counter at the end -- otherwise, a race condition + // could enable the creation of unlimited buffers. + numLive--; } diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 7978d8dbc..439bb1ed4 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1517,11 +1517,7 @@ jit::JitActivation::getRematerializedFrame(JSContext* cx, const JitFrameIterator uint8_t* top = iter.fp(); RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top); if (!p) { - RematerializedFrameVector empty(cx); - if (!rematerializedFrames_->add(p, top, Move(empty))) { - ReportOutOfMemory(cx); - return nullptr; - } + RematerializedFrameVector frames(cx); // The unit of rematerialization is an uninlined frame and its inlined // frames. Since inlined frames do not exist outside of snapshots, it @@ -1536,9 +1532,11 @@ jit::JitActivation::getRematerializedFrame(JSContext* cx, const JitFrameIterator // be in the activation's compartment. AutoCompartment ac(cx, compartment_); - if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover, - p->value())) - { + if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover, frames)) + return nullptr; + + if (!rematerializedFrames_->add(p, top, Move(frames))) { + ReportOutOfMemory(cx); return nullptr; } diff --git a/js/src/vm/Stopwatch.h b/js/src/vm/Stopwatch.h index a1b8bbbcb..38a3eb801 100644 --- a/js/src/vm/Stopwatch.h +++ b/js/src/vm/Stopwatch.h @@ -301,9 +301,9 @@ struct PerformanceMonitoring { #if WINVER >= 0x0600 struct cpuid_t { - WORD group_; - BYTE number_; - cpuid_t(WORD group, BYTE number) + uint16_t group_; + uint8_t number_; + cpuid_t(uint16_t group, uint8_t number) : group_(group), number_(number) { } diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 5b55ba947..2a7762e4f 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -1511,18 +1511,6 @@ js::FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList return true; } -void -js::InvalidateCompilerOutputsForScript(JSContext* cx, HandleScript script) -{ - TypeZone& types = cx->zone()->types; - if (types.compilerOutputs) { - for (auto& co : *types.compilerOutputs) { - if (co.script() == script) - co.invalidate(); - } - } -} - static void CheckDefinitePropertiesTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual) { diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 45b2711e2..9ba1c3cc8 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -1093,10 +1093,6 @@ bool FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList* constraints, RecompileInfo* precompileInfo, bool* isValidOut); -// Reset any CompilerOutput present for a script. -void -InvalidateCompilerOutputsForScript(JSContext* cx, HandleScript script); - // Update the actual types in any scripts queried by constraints with any // speculative types added during the definite properties analysis. void diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 8e8c5bf17..2a5c62480 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -143,13 +143,17 @@ class XDRState { template <typename T> bool codeEnum32(T* val, typename mozilla::EnableIf<mozilla::IsEnum<T>::value, T>::Type * = NULL) { + // Mix the enumeration value with a random magic number, such that a + // corruption with a low-ranged value (like 0) is less likely to cause a + // miss-interpretation of the XDR content and instead cause a failure. + const uint32_t MAGIC = 0xAF647BCE; uint32_t tmp; if (mode == XDR_ENCODE) - tmp = uint32_t(*val); + tmp = uint32_t(*val) ^ MAGIC; if (!codeUint32(&tmp)) return false; if (mode == XDR_DECODE) - *val = T(tmp); + *val = T(tmp ^ MAGIC); return true; } @@ -167,6 +171,18 @@ class XDRState { return true; } + bool codeMarker(uint32_t magic) { + uint32_t actual = magic; + if (!codeUint32(&actual)) + return false; + if (actual != magic) { + // Fail in debug, but only soft-fail in release + MOZ_ASSERT(false, "Bad XDR marker"); + return fail(JS::TranscodeResult_Failure_BadDecode); + } + return true; + } + bool codeBytes(void* bytes, size_t len) { if (len == 0) return true; diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 6964c5d62..34f5a8c0d 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -8661,9 +8661,6 @@ BuildConsoleMessage(ExclusiveContext* cx, unsigned time, JS::AsmJSCacheResult ca case JS::AsmJSCache_Disabled_PrivateBrowsing: cacheString = "caching disabled by private browsing mode"; break; - case JS::AsmJSCache_ESR52: - cacheString = "caching disabled in Firefox ESR52"; - break; case JS::AsmJSCache_LIMIT: MOZ_CRASH("bad AsmJSCacheResult"); break; diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 07d6331b8..0b030c844 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -1484,6 +1484,20 @@ const JSPropertySpec WasmTableObject::properties[] = JS_PS_END }; +static bool +ToTableIndex(JSContext* cx, HandleValue v, const Table& table, const char* noun, uint32_t* index) +{ + if (!ToNonWrappingUint32(cx, v, UINT32_MAX, "Table", noun, index)) + return false; + + if (*index >= table.length()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32, "Table", noun); + return false; + } + + return true; +} + /* static */ bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) { @@ -1491,7 +1505,7 @@ WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) const Table& table = tableObj->table(); uint32_t index; - if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "get index", &index)) + if (!ToTableIndex(cx, args.get(0), table, "get index", &index)) return false; ExternalTableElem& elem = table.externalArray()[index]; @@ -1530,7 +1544,7 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) return false; uint32_t index; - if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "set index", &index)) + if (!ToTableIndex(cx, args.get(0), table, "set index", &index)) return false; RootedFunction value(cx); diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index be7ddba8f..b24e01a40 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -1007,12 +1007,16 @@ Module::instantiate(JSContext* cx, maybeBytecode = bytecode_.get(); auto codeSegment = CodeSegment::create(cx, code_, linkData_, *metadata_, memory); - if (!codeSegment) + if (!codeSegment) { + ReportOutOfMemory(cx); return false; + } auto code = cx->make_unique<Code>(Move(codeSegment), *metadata_, maybeBytecode); - if (!code) + if (!code) { + ReportOutOfMemory(cx); return false; + } instance.set(WasmInstanceObject::create(cx, Move(code), diff --git a/js/xpconnect/shell/xpcshell.exe.manifest b/js/xpconnect/shell/xpcshell.exe.manifest index db2aa0861..c35b77af4 100644 --- a/js/xpconnect/shell/xpcshell.exe.manifest +++ b/js/xpconnect/shell/xpcshell.exe.manifest @@ -25,7 +25,6 @@ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> - <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> </application> </compatibility> </assembly> diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp index b9cbee7be..1e14c1bdf 100644 --- a/js/xpconnect/src/XPCJSID.cpp +++ b/js/xpconnect/src/XPCJSID.cpp @@ -456,27 +456,26 @@ nsJSIID::Enumerate(nsIXPConnectWrappedNative* wrapper, static nsresult FindObjectForHasInstance(JSContext* cx, HandleObject objArg, MutableHandleObject target) { + using namespace mozilla::jsipc; RootedObject obj(cx, objArg), proto(cx); - - while (obj && !IS_WN_REFLECTOR(obj) && - !IsDOMObject(obj) && !mozilla::jsipc::IsCPOW(obj)) - { - if (js::IsWrapper(obj)) { - obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false); - continue; + while (true) { + // Try the object, or the wrappee if allowed. + JSObject* o = js::IsWrapper(obj) ? js::CheckedUnwrap(obj, false) : obj; + if (o && (IS_WN_REFLECTOR(o) || IsDOMObject(o) || IsCPOW(o))) { + target.set(o); + return NS_OK; } - { - JSAutoCompartment ac(cx, obj); - if (!js::GetObjectProto(cx, obj, &proto)) - return NS_ERROR_FAILURE; + // Walk the prototype chain from the perspective of the callee (i.e. + // respecting Xrays if they exist). + if (!js::GetObjectProto(cx, obj, &proto)) + return NS_ERROR_FAILURE; + if (!proto) { + target.set(nullptr); + return NS_OK; } - obj = proto; } - - target.set(obj); - return NS_OK; } nsresult diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index 085e7100e..d17c0629e 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -307,6 +307,20 @@ ExposedPropertiesOnly::check(JSContext* cx, HandleObject wrapper, HandleId id, W // Unfortunately, |cx| can be in either compartment when we call ::check. :-( JSAutoCompartment ac(cx, wrappedObject); + // Proxies are not allowed in the proto chain. + RootedObject o(cx, wrappedObject); + while (o) { + JSObject* unwrapped = js::IsWrapper(o) ? js::CheckedUnwrap(o, false) : o; + if (!unwrapped || js::IsProxy(unwrapped)) + return false; + + RootedObject p(cx); + if (!js::GetObjectProto(cx, o, &p)) + return false; + + o = p; + } + bool found = false; if (!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found)) return false; |