From 51073d5f404354d89a3a988f8de28b1f0ea1b96a Mon Sep 17 00:00:00 2001 From: arv Date: Wed, 24 Jun 2015 13:54:08 -0700 Subject: [PATCH] i18n.js was not using original functions The i18n.js code was calling a lot of methods, which might have been removed or replaced by user code. Make sure we use the original functions. BUG=v8:4220 LOG=N R=adamk, littledan CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_chromium_rel_ng;tryserver.blink:linux_blink_rel Review URL: https://codereview.chromium.org/1199813004 Cr-Commit-Position: refs/heads/master@{#29268} --- src/array.js | 1 + src/i18n.js | 168 +++++++++++++++++++++++---------------- src/regexp.js | 1 + src/string.js | 16 +++- test/mjsunit/string-normalize.js | 11 +++ 5 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 test/mjsunit/string-normalize.js diff --git a/src/array.js b/src/array.js index 69f0910..9ecf0ac 100644 --- a/src/array.js +++ b/src/array.js @@ -1676,6 +1676,7 @@ utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [ // Exports utils.Export(function(to) { + to.ArrayIndexOf = ArrayIndexOf; to.ArrayJoin = ArrayJoin; to.ArrayToString = ArrayToString; to.InnerArrayEvery = InnerArrayEvery; diff --git a/src/i18n.js b/src/i18n.js index ae01cc4..79e9880 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -26,14 +26,34 @@ var ObjectDefineProperties = utils.ObjectDefineProperties; var ObjectDefineProperty = utils.ObjectDefineProperty; var SetFunctionName = utils.SetFunctionName; +var ArrayIndexOf; +var ArrayJoin; var IsFinite; var IsNaN; var MathFloor; +var RegExpTest; +var StringIndexOf; +var StringLastIndexOf; +var StringMatch; +var StringReplace; +var StringSplit; +var StringSubstr; +var StringSubstring; utils.Import(function(from) { + ArrayIndexOf = from.ArrayIndexOf; + ArrayJoin = from.ArrayJoin; IsFinite = from.IsFinite; IsNaN = from.IsNaN; MathFloor = from.MathFloor; + RegExpTest = from.RegExpTest; + StringIndexOf = from.StringIndexOf; + StringLastIndexOf = from.StringLastIndexOf; + StringMatch = from.StringMatch; + StringReplace = from.StringReplace; + StringSplit = from.StringSplit; + StringSubstr = from.StringSubstr; + StringSubstring = from.StringSubstring; }); // ------------------------------------------------------------------- @@ -223,7 +243,7 @@ function addBoundMethod(obj, methodName, implementation, length) { * Parameter locales is treated as a priority list. */ function supportedLocalesOf(service, locales, options) { - if (IS_NULL(service.match(GetServiceRE()))) { + if (IS_NULL(%_CallFunction(service, GetServiceRE(), StringMatch))) { throw MakeError(kWrongServiceType, service); } @@ -271,19 +291,20 @@ function lookupSupportedLocalesOf(requestedLocales, availableLocales) { var matchedLocales = []; for (var i = 0; i < requestedLocales.length; ++i) { // Remove -u- extension. - var locale = requestedLocales[i].replace(GetUnicodeExtensionRE(), ''); + var locale = %_CallFunction(requestedLocales[i], GetUnicodeExtensionRE(), + '', StringReplace); do { if (!IS_UNDEFINED(availableLocales[locale])) { // Push requested locale not the resolved one. - matchedLocales.push(requestedLocales[i]); + %_CallFunction(matchedLocales, requestedLocales[i], $arrayPush); break; } // Truncate locale if possible, if not break. - var pos = locale.lastIndexOf('-'); + var pos = %_CallFunction(locale, '-', StringLastIndexOf); if (pos === -1) { break; } - locale = locale.substring(0, pos); + locale = %_CallFunction(locale, 0, pos, StringSubstring); } while (true); } @@ -327,7 +348,9 @@ function getGetOption(options, caller) { default: throw MakeError(kWrongValueType); } - if (!IS_UNDEFINED(values) && values.indexOf(value) === -1) { + + if (!IS_UNDEFINED(values) && + %_CallFunction(values, value, ArrayIndexOf) === -1) { throw MakeRangeError(kValueOutOfRange, value, caller, property); } @@ -376,7 +399,7 @@ function resolveLocale(service, requestedLocales, options) { * lookup algorithm. */ function lookupMatcher(service, requestedLocales) { - if (IS_NULL(service.match(GetServiceRE()))) { + if (IS_NULL(%_CallFunction(service, GetServiceRE(), StringMatch))) { throw MakeError(kWrongServiceType, service); } @@ -387,20 +410,23 @@ function lookupMatcher(service, requestedLocales) { for (var i = 0; i < requestedLocales.length; ++i) { // Remove all extensions. - var locale = requestedLocales[i].replace(GetAnyExtensionRE(), ''); + var locale = %_CallFunction(requestedLocales[i], GetAnyExtensionRE(), '', + StringReplace); do { if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) { // Return the resolved locale and extension. - var extensionMatch = requestedLocales[i].match(GetUnicodeExtensionRE()); + var extensionMatch = + %_CallFunction(requestedLocales[i], GetUnicodeExtensionRE(), + StringMatch); var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0]; return {'locale': locale, 'extension': extension, 'position': i}; } // Truncate locale if possible. - var pos = locale.lastIndexOf('-'); + var pos = %_CallFunction(locale, '-', StringLastIndexOf); if (pos === -1) { break; } - locale = locale.substring(0, pos); + locale = %_CallFunction(locale, 0, pos, StringSubstring); } while (true); } @@ -429,7 +455,7 @@ function bestFitMatcher(service, requestedLocales) { * We are not concerned with the validity of the values at this point. */ function parseExtension(extension) { - var extensionSplit = extension.split('-'); + var extensionSplit = %_CallFunction(extension, '-', StringSplit); // Assume ['', 'u', ...] input, but don't throw. if (extensionSplit.length <= 2 || @@ -488,7 +514,7 @@ function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) { } for (var key in keyValues) { - if (keyValues.hasOwnProperty(key)) { + if (%HasOwnProperty(keyValues, key)) { var value = UNDEFINED; var map = keyValues[key]; if (!IS_UNDEFINED(map.property)) { @@ -504,7 +530,7 @@ function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) { // User options didn't have it, check Unicode extension. // Here we want to convert strings 'true', 'false' into proper Boolean // values (not a user error). - if (extensionMap.hasOwnProperty(key)) { + if (%HasOwnProperty(extensionMap, key)) { value = extensionMap[key]; if (!IS_UNDEFINED(value)) { updateProperty(map.property, map.type, value); @@ -528,15 +554,17 @@ function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) { * configurable: false, writable: false, enumerable: true. */ function freezeArray(array) { - array.forEach(function(element, index) { - ObjectDefineProperty(array, index, {value: element, - configurable: false, - writable: false, - enumerable: true}); - }); + var l = array.length; + for (var i = 0; i < l; i++) { + if (i in array) { + ObjectDefineProperty(array, i, {value: array[i], + configurable: false, + writable: false, + enumerable: true}); + } + } - ObjectDefineProperty(array, 'length', {value: array.length, - writable: false}); + ObjectDefineProperty(array, 'length', {value: l, writable: false}); return array; } @@ -564,7 +592,7 @@ function getOptimalLanguageTag(original, resolved) { // Preserve extensions of resolved locale, but swap base tags with original. var resolvedBase = new GlobalRegExp('^' + locales[1].base); - return resolved.replace(resolvedBase, locales[0].base); + return %_CallFunction(resolved, resolvedBase, locales[0].base, StringReplace); } @@ -578,8 +606,9 @@ function getAvailableLocalesOf(service) { var available = %AvailableLocalesOf(service); for (var i in available) { - if (available.hasOwnProperty(i)) { - var parts = i.match(/^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/); + if (%HasOwnProperty(available, i)) { + var parts = %_CallFunction(i, /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/, + StringMatch); if (parts !== null) { // Build xx-ZZ. We don't care about the actual value, // as long it's not undefined. @@ -639,7 +668,8 @@ function addWECPropertyIfDefined(object, property, value) { * Returns titlecased word, aMeRricA -> America. */ function toTitleCaseWord(word) { - return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase(); + return %StringToUpperCase(%_CallFunction(word, 0, 1, StringSubstr)) + + %StringToLowerCase(%_CallFunction(word, 1, StringSubstr)); } /** @@ -683,13 +713,12 @@ function initializeLocaleList(locales) { } else { // We allow single string localeID. if (typeof locales === 'string') { - seen.push(canonicalizeLanguageTag(locales)); + %_CallFunction(seen, canonicalizeLanguageTag(locales), $arrayPush); return freezeArray(seen); } var o = $toObject(locales); - // Converts it to UInt32 (>>> is shr on 32bit integers). - var len = o.length >>> 0; + var len = TO_UINT32(o.length); for (var k = 0; k < len; k++) { if (k in o) { @@ -697,8 +726,8 @@ function initializeLocaleList(locales) { var tag = canonicalizeLanguageTag(value); - if (seen.indexOf(tag) === -1) { - seen.push(tag); + if (%_CallFunction(seen, tag, ArrayIndexOf) === -1) { + %_CallFunction(seen, tag, $arrayPush); } } } @@ -719,39 +748,40 @@ function initializeLocaleList(locales) { */ function isValidLanguageTag(locale) { // Check if it's well-formed, including grandfadered tags. - if (GetLanguageTagRE().test(locale) === false) { + if (!%_CallFunction(GetLanguageTagRE(), locale, RegExpTest)) { return false; } // Just return if it's a x- form. It's all private. - if (locale.indexOf('x-') === 0) { + if (%_CallFunction(locale, 'x-', StringIndexOf) === 0) { return true; } // Check if there are any duplicate variants or singletons (extensions). // Remove private use section. - locale = locale.split(/-x-/)[0]; + locale = %_CallFunction(locale, /-x-/, StringSplit)[0]; // Skip language since it can match variant regex, so we start from 1. // We are matching i-klingon here, but that's ok, since i-klingon-klingon // is not valid and would fail LANGUAGE_TAG_RE test. var variants = []; var extensions = []; - var parts = locale.split(/-/); + var parts = %_CallFunction(locale, /-/, StringSplit); for (var i = 1; i < parts.length; i++) { var value = parts[i]; - if (GetLanguageVariantRE().test(value) === true && extensions.length === 0) { - if (variants.indexOf(value) === -1) { - variants.push(value); + if (%_CallFunction(GetLanguageVariantRE(), value, RegExpTest) && + extensions.length === 0) { + if (%_CallFunction(variants, value, ArrayIndexOf) === -1) { + %_CallFunction(variants, value, $arrayPush); } else { return false; } } - if (GetLanguageSingletonRE().test(value) === true) { - if (extensions.indexOf(value) === -1) { - extensions.push(value); + if (%_CallFunction(GetLanguageSingletonRE(), value, RegExpTest)) { + if (%_CallFunction(extensions, value, ArrayIndexOf) === -1) { + %_CallFunction(extensions, value, $arrayPush); } else { return false; } @@ -854,7 +884,7 @@ function initializeCollator(collator, locales, options) { var collation = 'default'; var extension = ''; - if (extensionMap.hasOwnProperty('co') && internalOptions.usage === 'sort') { + if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') { /** * Allowed -u-co- values. List taken from: @@ -865,7 +895,8 @@ function initializeCollator(collator, locales, options) { 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin' ]; - if (ALLOWED_CO_VALUES.indexOf(extensionMap.co) !== -1) { + if (%_CallFunction(ALLOWED_CO_VALUES, extensionMap.co, ArrayIndexOf) !== + -1) { extension = '-u-co-' + extensionMap.co; // ICU can't tell us what the collation is, so save user's input. collation = extensionMap.co; @@ -1005,7 +1036,7 @@ addBoundMethod(Intl.Collator, 'compare', compare, 2); function isWellFormedCurrencyCode(currency) { return typeof currency == "string" && currency.length == 3 && - currency.match(/[^A-Za-z]/) == null; + %_CallFunction(currency, /[^A-Za-z]/, StringMatch) == null; } @@ -1060,7 +1091,7 @@ function initializeNumberFormat(numberFormat, locales, options) { var currencyDisplay = getOption( 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol'); if (internalOptions.style === 'currency') { - defineWEProperty(internalOptions, 'currency', currency.toUpperCase()); + defineWEProperty(internalOptions, 'currency', %StringToUpperCase(currency)); defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay); } @@ -1116,10 +1147,10 @@ function initializeNumberFormat(numberFormat, locales, options) { style: {value: internalOptions.style, writable: true}, useGrouping: {writable: true} }); - if (internalOptions.hasOwnProperty('minimumSignificantDigits')) { + if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) { defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED); } - if (internalOptions.hasOwnProperty('maximumSignificantDigits')) { + if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) { defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED); } var formatter = %CreateNumberFormat(requestedLocale, @@ -1193,12 +1224,12 @@ function initializeNumberFormat(numberFormat, locales, options) { format.resolved.currencyDisplay); } - if (format.resolved.hasOwnProperty('minimumSignificantDigits')) { + if (%HasOwnProperty(format.resolved, 'minimumSignificantDigits')) { defineWECProperty(result, 'minimumSignificantDigits', format.resolved.minimumSignificantDigits); } - if (format.resolved.hasOwnProperty('maximumSignificantDigits')) { + if (%HasOwnProperty(format.resolved, 'maximumSignificantDigits')) { defineWECProperty(result, 'maximumSignificantDigits', format.resolved.maximumSignificantDigits); } @@ -1326,57 +1357,58 @@ function appendToLDMLString(option, pairs) { */ function fromLDMLString(ldmlString) { // First remove '' quoted text, so we lose 'Uhr' strings. - ldmlString = ldmlString.replace(GetQuotedStringRE(), ''); + ldmlString = %_CallFunction(ldmlString, GetQuotedStringRE(), '', + StringReplace); var options = {}; - var match = ldmlString.match(/E{3,5}/g); + var match = %_CallFunction(ldmlString, /E{3,5}/g, StringMatch); options = appendToDateTimeObject( options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'}); - match = ldmlString.match(/G{3,5}/g); + match = %_CallFunction(ldmlString, /G{3,5}/g, StringMatch); options = appendToDateTimeObject( options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'}); - match = ldmlString.match(/y{1,2}/g); + match = %_CallFunction(ldmlString, /y{1,2}/g, StringMatch); options = appendToDateTimeObject( options, 'year', match, {y: 'numeric', yy: '2-digit'}); - match = ldmlString.match(/M{1,5}/g); + match = %_CallFunction(ldmlString, /M{1,5}/g, StringMatch); options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit', M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'}); // Sometimes we get L instead of M for month - standalone name. - match = ldmlString.match(/L{1,5}/g); + match = %_CallFunction(ldmlString, /L{1,5}/g, StringMatch); options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit', L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'}); - match = ldmlString.match(/d{1,2}/g); + match = %_CallFunction(ldmlString, /d{1,2}/g, StringMatch); options = appendToDateTimeObject( options, 'day', match, {d: 'numeric', dd: '2-digit'}); - match = ldmlString.match(/h{1,2}/g); + match = %_CallFunction(ldmlString, /h{1,2}/g, StringMatch); if (match !== null) { options['hour12'] = true; } options = appendToDateTimeObject( options, 'hour', match, {h: 'numeric', hh: '2-digit'}); - match = ldmlString.match(/H{1,2}/g); + match = %_CallFunction(ldmlString, /H{1,2}/g, StringMatch); if (match !== null) { options['hour12'] = false; } options = appendToDateTimeObject( options, 'hour', match, {H: 'numeric', HH: '2-digit'}); - match = ldmlString.match(/m{1,2}/g); + match = %_CallFunction(ldmlString, /m{1,2}/g, StringMatch); options = appendToDateTimeObject( options, 'minute', match, {m: 'numeric', mm: '2-digit'}); - match = ldmlString.match(/s{1,2}/g); + match = %_CallFunction(ldmlString, /s{1,2}/g, StringMatch); options = appendToDateTimeObject( options, 'second', match, {s: 'numeric', ss: '2-digit'}); - match = ldmlString.match(/z|zzzz/g); + match = %_CallFunction(ldmlString, /z|zzzz/g, StringMatch); options = appendToDateTimeObject( options, 'timeZoneName', match, {z: 'short', zzzz: 'long'}); @@ -1386,7 +1418,7 @@ function fromLDMLString(ldmlString) { function appendToDateTimeObject(options, option, match, pairs) { if (IS_NULL(match)) { - if (!options.hasOwnProperty(option)) { + if (!%HasOwnProperty(options, option)) { defineWEProperty(options, option, UNDEFINED); } return options; @@ -1661,7 +1693,7 @@ SetFunctionName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf'); function formatDate(formatter, dateValue) { var dateMs; if (IS_UNDEFINED(dateValue)) { - dateMs = GlobalDate.now(); + dateMs = %DateCurrentTime(); } else { dateMs = $toNumber(dateValue); } @@ -1701,7 +1733,7 @@ function canonicalizeTimeZoneID(tzID) { } // Special case handling (UTC, GMT). - var upperID = tzID.toUpperCase(); + var upperID = %StringToUpperCase(tzID); if (upperID === 'UTC' || upperID === 'GMT' || upperID === 'ETC/UTC' || upperID === 'ETC/GMT') { return 'UTC'; @@ -1709,7 +1741,7 @@ function canonicalizeTimeZoneID(tzID) { // We expect only _ and / beside ASCII letters. // All inputs should conform to Area/Location from now on. - var match = GetTimezoneNameCheckRE().exec(tzID); + var match = %_CallFunction(tzID, GetTimezoneNameCheckRE(), StringMatch); if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID); var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]); @@ -1968,9 +2000,11 @@ OverrideFunction(GlobalString.prototype, 'normalize', function(that) { var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; - var normalizationForm = NORMALIZATION_FORMS.indexOf(form); + var normalizationForm = + %_CallFunction(NORMALIZATION_FORMS, form, ArrayIndexOf); if (normalizationForm === -1) { - throw MakeRangeError(kNormalizationForm, NORMALIZATION_FORMS.join(', ')); + throw MakeRangeError(kNormalizationForm, + %_CallFunction(NORMALIZATION_FORMS, ', ', ArrayJoin)); } return %StringNormalize(this, normalizationForm); diff --git a/src/regexp.js b/src/regexp.js index a0d7094..bf75ca1 100644 --- a/src/regexp.js +++ b/src/regexp.js @@ -447,6 +447,7 @@ utils.Export(function(to) { to.RegExpExec = DoRegExpExec; to.RegExpExecNoTests = RegExpExecNoTests; to.RegExpLastMatchInfo = RegExpLastMatchInfo; + to.RegExpTest = RegExpTest; }); }) diff --git a/src/string.js b/src/string.js index d8b37e2..3ddd6d2 100644 --- a/src/string.js +++ b/src/string.js @@ -14,6 +14,8 @@ var GlobalString = global.String; var InternalArray = utils.InternalArray; var InternalPackedArray = utils.InternalPackedArray; +var ArrayIndexOf; +var ArrayJoin; var MathMax; var MathMin; var RegExpExec; @@ -21,6 +23,8 @@ var RegExpExecNoTests; var RegExpLastMatchInfo; utils.Import(function(from) { + ArrayIndexOf = from.ArrayIndexOf; + ArrayJoin = from.ArrayJoin; MathMax = from.MathMax; MathMin = from.MathMin; RegExpExec = from.RegExpExec; @@ -191,10 +195,11 @@ function StringNormalizeJS(form) { var form = form ? TO_STRING_INLINE(form) : 'NFC'; var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; - - var normalizationForm = NORMALIZATION_FORMS.indexOf(form); + var normalizationForm = + %_CallFunction(NORMALIZATION_FORMS, form, ArrayIndexOf); if (normalizationForm === -1) { - throw MakeRangeError(kNormalizationForm, NORMALIZATION_FORMS.join(', ')); + throw MakeRangeError(kNormalizationForm, + %_CallFunction(NORMALIZATION_FORMS, ', ', ArrayJoin)); } return %_ValueOf(this); @@ -1191,6 +1196,11 @@ utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ utils.Export(function(to) { to.StringCharAt = StringCharAtJS; to.StringIndexOf = StringIndexOfJS; + to.StringLastIndexOf = StringLastIndexOfJS; + to.StringMatch = StringMatchJS; + to.StringReplace = StringReplace; + to.StringSplit = StringSplitJS; + to.StringSubstr = StringSubstr; to.StringSubstring = StringSubstring; }); diff --git a/test/mjsunit/string-normalize.js b/test/mjsunit/string-normalize.js new file mode 100644 index 0000000..f88f193 --- /dev/null +++ b/test/mjsunit/string-normalize.js @@ -0,0 +1,11 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +assertEquals('', ''.normalize()); +assertTrue(delete Array.prototype.indexOf); +assertEquals('', ''.normalize()); + +assertThrows(function() { ''.normalize('invalid'); }, RangeError); +assertTrue(delete Array.prototype.join); +assertThrows(function() { ''.normalize('invalid'); }, RangeError); -- 2.7.4