/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ function StringProtoHasNoMatch() { var ObjectProto = GetBuiltinPrototype("Object"); var StringProto = GetBuiltinPrototype("String"); if (!ObjectHasPrototype(StringProto, ObjectProto)) { return false; } return !(GetBuiltinSymbol("match") in StringProto); } function IsStringMatchOptimizable() { var RegExpProto = GetBuiltinPrototype("RegExp"); // If RegExpPrototypeOptimizable succeeds, `exec` and `@@match` are // guaranteed to be data properties. return ( RegExpPrototypeOptimizable(RegExpProto) && RegExpProto.exec === RegExp_prototype_Exec && RegExpProto[GetBuiltinSymbol("match")] === RegExpMatch ); } function ThrowIncompatibleMethod(name, thisv) { ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "String", name, ToString(thisv)); } // ES 2016 draft Mar 25, 2016 21.1.3.11. function String_match(regexp) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("match", this); } // Step 2. var isPatternString = typeof regexp === "string"; if ( !(isPatternString && StringProtoHasNoMatch()) && !IsNullOrUndefined(regexp) ) { // Step 2.a. var matcher = GetMethod(regexp, GetBuiltinSymbol("match")); // Step 2.b. if (matcher !== undefined) { return callContentFunction(matcher, regexp, this); } } // Step 3. var S = ToString(this); if (isPatternString && IsStringMatchOptimizable()) { var flatResult = FlatStringMatch(S, regexp); if (flatResult !== undefined) { return flatResult; } } // Step 4. var rx = RegExpCreate(regexp); // Step 5 (optimized case). if (IsStringMatchOptimizable()) { return RegExpMatcher(rx, S, 0); } // Step 5. return callContentFunction(GetMethod(rx, GetBuiltinSymbol("match")), rx, S); } // String.prototype.matchAll proposal. // // String.prototype.matchAll ( regexp ) function String_matchAll(regexp) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("matchAll", this); } // Step 2. if (!IsNullOrUndefined(regexp)) { // Steps 2.a-b. if (IsRegExp(regexp)) { // Step 2.b.i. var flags = regexp.flags; // Step 2.b.ii. if (IsNullOrUndefined(flags)) { ThrowTypeError(JSMSG_FLAGS_UNDEFINED_OR_NULL); } // Step 2.b.iii. if (!callFunction(std_String_includes, ToString(flags), "g")) { ThrowTypeError(JSMSG_REQUIRES_GLOBAL_REGEXP, "matchAll"); } } // Step 2.c. var matcher = GetMethod(regexp, GetBuiltinSymbol("matchAll")); // Step 2.d. if (matcher !== undefined) { return callContentFunction(matcher, regexp, this); } } // Step 3. var string = ToString(this); // Step 4. var rx = RegExpCreate(regexp, "g"); // Step 5. return callContentFunction( GetMethod(rx, GetBuiltinSymbol("matchAll")), rx, string ); } /** * A helper function implementing the logic for both String.prototype.padStart * and String.prototype.padEnd as described in ES7 Draft March 29, 2016 */ function String_pad(maxLength, fillString, padEnd) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod(padEnd ? "padEnd" : "padStart", this); } // Step 2. let str = ToString(this); // Steps 3-4. let intMaxLength = ToLength(maxLength); let strLen = str.length; // Step 5. if (intMaxLength <= strLen) { return str; } // Steps 6-7. assert(fillString !== undefined, "never called when fillString is undefined"); let filler = ToString(fillString); // Step 8. if (filler === "") { return str; } // Throw an error if the final string length exceeds the maximum string // length. Perform this check early so we can use int32 operations below. if (intMaxLength > MAX_STRING_LENGTH) { ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE); } // Step 9. let fillLen = intMaxLength - strLen; // Step 10. // Perform an int32 division to ensure String_repeat is not called with a // double to avoid repeated bailouts in ToInteger. let truncatedStringFiller = callFunction( String_repeat, filler, (fillLen / filler.length) | 0 ); truncatedStringFiller += Substring(filler, 0, fillLen % filler.length); // Step 11. if (padEnd === true) { return str + truncatedStringFiller; } return truncatedStringFiller + str; } function String_pad_start(maxLength, fillString = " ") { return callFunction(String_pad, this, maxLength, fillString, false); } function String_pad_end(maxLength, fillString = " ") { return callFunction(String_pad, this, maxLength, fillString, true); } function StringProtoHasNoReplace() { var ObjectProto = GetBuiltinPrototype("Object"); var StringProto = GetBuiltinPrototype("String"); if (!ObjectHasPrototype(StringProto, ObjectProto)) { return false; } return !(GetBuiltinSymbol("replace") in StringProto); } // A thin wrapper to call SubstringKernel with int32-typed arguments. // Caller should check the range of |from| and |length|. function Substring(str, from, length) { assert(typeof str === "string", "|str| should be a string"); assert( (from | 0) === from, "coercing |from| into int32 should not change the value" ); assert( (length | 0) === length, "coercing |length| into int32 should not change the value" ); return SubstringKernel(str, from | 0, length | 0); } // ES 2016 draft Mar 25, 2016 21.1.3.14. function String_replace(searchValue, replaceValue) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("replace", this); } // Step 2. if ( !(typeof searchValue === "string" && StringProtoHasNoReplace()) && !IsNullOrUndefined(searchValue) ) { // Step 2.a. var replacer = GetMethod(searchValue, GetBuiltinSymbol("replace")); // Step 2.b. if (replacer !== undefined) { return callContentFunction(replacer, searchValue, this, replaceValue); } } // Step 3. var string = ToString(this); // Step 4. var searchString = ToString(searchValue); if (typeof replaceValue === "string") { // Steps 6-12: Optimized for string case. return StringReplaceString(string, searchString, replaceValue); } // Step 5. if (!IsCallable(replaceValue)) { // Steps 6-12. return StringReplaceString(string, searchString, ToString(replaceValue)); } // Step 7. var pos = callFunction(std_String_indexOf, string, searchString); if (pos === -1) { return string; } // Step 8. var replStr = ToString( callContentFunction(replaceValue, undefined, searchString, pos, string) ); // Step 10. var tailPos = pos + searchString.length; // Step 11. var newString; if (pos === 0) { newString = ""; } else { newString = Substring(string, 0, pos); } newString += replStr; var stringLength = string.length; if (tailPos < stringLength) { newString += Substring(string, tailPos, stringLength - tailPos); } // Step 12. return newString; } // String.prototype.replaceAll (Stage 3 proposal) // https://tc39.es/proposal-string-replaceall/ // // String.prototype.replaceAll ( searchValue, replaceValue ) function String_replaceAll(searchValue, replaceValue) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("replaceAll", this); } // Step 2. if (!IsNullOrUndefined(searchValue)) { // Steps 2.a-b. if (IsRegExp(searchValue)) { // Step 2.b.i. var flags = searchValue.flags; // Step 2.b.ii. if (IsNullOrUndefined(flags)) { ThrowTypeError(JSMSG_FLAGS_UNDEFINED_OR_NULL); } // Step 2.b.iii. if (!callFunction(std_String_includes, ToString(flags), "g")) { ThrowTypeError(JSMSG_REQUIRES_GLOBAL_REGEXP, "replaceAll"); } } // Step 2.c. var replacer = GetMethod(searchValue, GetBuiltinSymbol("replace")); // Step 2.b. if (replacer !== undefined) { return callContentFunction(replacer, searchValue, this, replaceValue); } } // Step 3. var string = ToString(this); // Step 4. var searchString = ToString(searchValue); // Steps 5-6. if (!IsCallable(replaceValue)) { // Steps 7-16. return StringReplaceAllString(string, searchString, ToString(replaceValue)); } // Step 7. var searchLength = searchString.length; // Step 8. var advanceBy = std_Math_max(1, searchLength); // Step 9 (not needed in this implementation). // Step 12. var endOfLastMatch = 0; // Step 13. var result = ""; // Steps 10-11, 14. var position = 0; while (true) { // Steps 10-11. // // StringIndexOf doesn't clamp the |position| argument to the input // string length, i.e. |StringIndexOf("abc", "", 4)| returns -1, // whereas |"abc".indexOf("", 4)| returns 3. That means we need to // exit the loop when |nextPosition| is smaller than |position| and // not just when |nextPosition| is -1. var nextPosition = callFunction( std_String_indexOf, string, searchString, position ); if (nextPosition < position) { break; } position = nextPosition; // Step 14.a. var replacement = ToString( callContentFunction( replaceValue, undefined, searchString, position, string ) ); // Step 14.b (not applicable). // Step 14.c. var stringSlice = Substring( string, endOfLastMatch, position - endOfLastMatch ); // Step 14.d. result += stringSlice + replacement; // Step 14.e. endOfLastMatch = position + searchLength; // Step 11.b. position += advanceBy; } // Step 15. if (endOfLastMatch < string.length) { // Step 15.a. result += Substring(string, endOfLastMatch, string.length - endOfLastMatch); } // Step 16. return result; } function StringProtoHasNoSearch() { var ObjectProto = GetBuiltinPrototype("Object"); var StringProto = GetBuiltinPrototype("String"); if (!ObjectHasPrototype(StringProto, ObjectProto)) { return false; } return !(GetBuiltinSymbol("search") in StringProto); } function IsStringSearchOptimizable() { var RegExpProto = GetBuiltinPrototype("RegExp"); // If RegExpPrototypeOptimizable succeeds, `exec` and `@@search` are // guaranteed to be data properties. return ( RegExpPrototypeOptimizable(RegExpProto) && RegExpProto.exec === RegExp_prototype_Exec && RegExpProto[GetBuiltinSymbol("search")] === RegExpSearch ); } // ES 2016 draft Mar 25, 2016 21.1.3.15. function String_search(regexp) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("search", this); } // Step 2. var isPatternString = typeof regexp === "string"; if ( !(isPatternString && StringProtoHasNoSearch()) && !IsNullOrUndefined(regexp) ) { // Step 2.a. var searcher = GetMethod(regexp, GetBuiltinSymbol("search")); // Step 2.b. if (searcher !== undefined) { return callContentFunction(searcher, regexp, this); } } // Step 3. var string = ToString(this); if (isPatternString && IsStringSearchOptimizable()) { var flatResult = FlatStringSearch(string, regexp); if (flatResult !== -2) { return flatResult; } } // Step 4. var rx = RegExpCreate(regexp); // Step 5. return callContentFunction( GetMethod(rx, GetBuiltinSymbol("search")), rx, string ); } function StringProtoHasNoSplit() { var ObjectProto = GetBuiltinPrototype("Object"); var StringProto = GetBuiltinPrototype("String"); if (!ObjectHasPrototype(StringProto, ObjectProto)) { return false; } return !(GetBuiltinSymbol("split") in StringProto); } // ES 2016 draft Mar 25, 2016 21.1.3.17. function String_split(separator, limit) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("split", this); } // Optimized path for string.split(string), especially when both strings // are constants. Following sequence of if's cannot be put together in // order that IonMonkey sees the constant if present (bug 1246141). if (typeof this === "string") { if (StringProtoHasNoSplit()) { if (typeof separator === "string") { if (limit === undefined) { // inlineConstantStringSplitString needs both arguments to // be MConstant, so pass them directly. return StringSplitString(this, separator); } } } } // Step 2. if ( !(typeof separator === "string" && StringProtoHasNoSplit()) && !IsNullOrUndefined(separator) ) { // Step 2.a. var splitter = GetMethod(separator, GetBuiltinSymbol("split")); // Step 2.b. if (splitter !== undefined) { return callContentFunction(splitter, separator, this, limit); } } // Step 3. var S = ToString(this); // Step 6. var R; if (limit !== undefined) { var lim = limit >>> 0; // Step 9. R = ToString(separator); // Step 10. if (lim === 0) { return []; } // Step 11. if (separator === undefined) { return [S]; } // Steps 4, 8, 12-18. return StringSplitStringLimit(S, R, lim); } // Step 9. R = ToString(separator); // Step 11. if (separator === undefined) { return [S]; } // Optimized path. // Steps 4, 8, 12-18. return StringSplitString(S, R); } // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 // 21.1.3.22 String.prototype.substring ( start, end ) function String_substring(start, end) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("substring", this); } // Step 2. var str = ToString(this); // Step 3. var len = str.length; // Step 4. var intStart = ToInteger(start); // Step 5. var intEnd = end === undefined ? len : ToInteger(end); // Step 6. var finalStart = std_Math_min(std_Math_max(intStart, 0), len); // Step 7. var finalEnd = std_Math_min(std_Math_max(intEnd, 0), len); // Step 8. var from = std_Math_min(finalStart, finalEnd); // Step 9. var to = std_Math_max(finalStart, finalEnd); // Step 10. // While |from| and |to - from| are bounded to the length of |str| and this // and thus definitely in the int32 range, they can still be typed as // double. Eagerly truncate since SubstringKernel only accepts int32. return SubstringKernel(str, from | 0, (to - from) | 0); } SetIsInlinableLargeFunction(String_substring); // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 // B.2.3.1 String.prototype.substr ( start, length ) function String_substr(start, length) { // Steps 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("substr", this); } // Step 2. var str = ToString(this); // Step 3. var intStart = ToInteger(start); // Steps 4-5. var size = str.length; // Use |size| instead of +Infinity to avoid performing calculations with // doubles. (The result is the same either way.) var end = length === undefined ? size : ToInteger(length); // Step 6. if (intStart < 0) { intStart = std_Math_max(intStart + size, 0); } else { // Restrict the input range to allow better Ion optimizations. intStart = std_Math_min(intStart, size); } // Step 7. var resultLength = std_Math_min(std_Math_max(end, 0), size - intStart); // Step 8. assert( 0 <= resultLength && resultLength <= size - intStart, "resultLength is a valid substring length value" ); // Step 9. // While |intStart| and |resultLength| are bounded to the length of |str| // and thus definitely in the int32 range, they can still be typed as // double. Eagerly truncate since SubstringKernel only accepts int32. return SubstringKernel(str, intStart | 0, resultLength | 0); } SetIsInlinableLargeFunction(String_substr); // ES2021 draft rev 12a546b92275a0e2f834017db2727bb9c6f6c8fd // 21.1.3.4 String.prototype.concat ( ...args ) // Note: String.prototype.concat.length is 1. function String_concat(arg1) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("concat", this); } // Step 2. var str = ToString(this); // Specialize for the most common number of arguments for better inlining. if (ArgumentsLength() === 0) { return str; } if (ArgumentsLength() === 1) { return str + ToString(GetArgument(0)); } if (ArgumentsLength() === 2) { return str + ToString(GetArgument(0)) + ToString(GetArgument(1)); } // Step 3. (implicit) // Step 4. var result = str; // Step 5. for (var i = 0; i < ArgumentsLength(); i++) { // Steps 5.a-b. var nextString = ToString(GetArgument(i)); // Step 5.c. result += nextString; } // Step 6. return result; } // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 // 21.1.3.19 String.prototype.slice ( start, end ) function String_slice(start, end) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("slice", this); } // Step 2. var str = ToString(this); // Step 3. var len = str.length; // Step 4. var intStart = ToInteger(start); // Step 5. var intEnd = end === undefined ? len : ToInteger(end); // Step 6. var from = intStart < 0 ? std_Math_max(len + intStart, 0) : std_Math_min(intStart, len); // Step 7. var to = intEnd < 0 ? std_Math_max(len + intEnd, 0) : std_Math_min(intEnd, len); // Step 8. var span = std_Math_max(to - from, 0); // Step 9. // While |from| and |span| are bounded to the length of |str| // and thus definitely in the int32 range, they can still be typed as // double. Eagerly truncate since SubstringKernel only accepts int32. return SubstringKernel(str, from | 0, span | 0); } SetIsInlinableLargeFunction(String_slice); // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 // 21.1.3.3 String.prototype.codePointAt ( pos ) function String_codePointAt(pos) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("codePointAt", this); } // Step 2. var S = ToString(this); // Step 3. var position = ToInteger(pos); // Step 4. var size = S.length; // Step 5. if (position < 0 || position >= size) { return undefined; } // Steps 6-7. var first = callFunction(std_String_charCodeAt, S, position); if (first < 0xd800 || first > 0xdbff || position + 1 === size) { return first; } // Steps 8-9. var second = callFunction(std_String_charCodeAt, S, position + 1); if (second < 0xdc00 || second > 0xdfff) { return first; } // Step 10. return (first - 0xd800) * 0x400 + (second - 0xdc00) + 0x10000; } // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 // 21.1.3.16 String.prototype.repeat ( count ) function String_repeat(count) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("repeat", this); } // Step 2. var S = ToString(this); // Step 3. var n = ToInteger(count); // Step 4. if (n < 0) { ThrowRangeError(JSMSG_NEGATIVE_REPETITION_COUNT); } // Step 5. // Inverted condition to handle |Infinity * 0 = NaN| correctly. if (!(n * S.length <= MAX_STRING_LENGTH)) { ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE); } // Communicate |n|'s possible range to the compiler. We actually use // MAX_STRING_LENGTH + 1 as range because that's a valid bit mask. That's // fine because it's only used as optimization hint. assert( TO_INT32(MAX_STRING_LENGTH + 1) === MAX_STRING_LENGTH + 1, "MAX_STRING_LENGTH + 1 must fit in int32" ); assert( ((MAX_STRING_LENGTH + 1) & (MAX_STRING_LENGTH + 2)) === 0, "MAX_STRING_LENGTH + 1 can be used as a bitmask" ); n = n & (MAX_STRING_LENGTH + 1); // Steps 6-7. var T = ""; for (;;) { if (n & 1) { T += S; } n >>= 1; if (n) { S += S; } else { break; } } return T; } // ES6 draft specification, section 21.1.3.27, version 2013-09-27. function String_iterator() { // Step 1. if (IsNullOrUndefined(this)) { ThrowTypeError( JSMSG_INCOMPATIBLE_PROTO2, "String", "Symbol.iterator", ToString(this) ); } // Step 2. var S = ToString(this); // Step 3. var iterator = NewStringIterator(); UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, S); UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0); return iterator; } function StringIteratorNext() { var obj = this; if (!IsObject(obj) || (obj = GuardToStringIterator(obj)) === null) { return callFunction( CallStringIteratorMethodIfWrapped, this, "StringIteratorNext" ); } var S = UnsafeGetStringFromReservedSlot(obj, ITERATOR_SLOT_TARGET); // We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in // SelfHostring.cpp) so our current index can never be anything other than // an Int32Value. var index = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX); var size = S.length; var result = { value: undefined, done: false }; if (index >= size) { result.done = true; return result; } var charCount = 1; var first = callFunction(std_String_charCodeAt, S, index); if (first >= 0xd800 && first <= 0xdbff && index + 1 < size) { var second = callFunction(std_String_charCodeAt, S, index + 1); if (second >= 0xdc00 && second <= 0xdfff) { first = (first - 0xd800) * 0x400 + (second - 0xdc00) + 0x10000; charCount = 2; } } UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + charCount); // Communicate |first|'s possible range to the compiler. result.value = callFunction(std_String_fromCodePoint, null, first & 0x1fffff); return result; } #if JS_HAS_INTL_API var collatorCache = new_Record(); /** * Compare this String against that String, using the locale and collation * options provided. * * Spec: ECMAScript Internationalization API Specification, 13.1.1. */ function String_localeCompare(that) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("localeCompare", this); } // Steps 2-3. var S = ToString(this); var That = ToString(that); // Steps 4-5. var locales = ArgumentsLength() > 1 ? GetArgument(1) : undefined; var options = ArgumentsLength() > 2 ? GetArgument(2) : undefined; // Step 6. var collator; if (locales === undefined && options === undefined) { // This cache only optimizes for the old ES5 localeCompare without // locales and options. if (!intl_IsRuntimeDefaultLocale(collatorCache.runtimeDefaultLocale)) { collatorCache.collator = intl_Collator(locales, options); collatorCache.runtimeDefaultLocale = intl_RuntimeDefaultLocale(); } collator = collatorCache.collator; } else { collator = intl_Collator(locales, options); } // Step 7. return intl_CompareStrings(collator, S, That); } /** * 13.1.2 String.prototype.toLocaleLowerCase ( [ locales ] ) * * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ function String_toLocaleLowerCase() { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("toLocaleLowerCase", this); } // Step 2. var string = ToString(this); // Handle the common cases (no locales argument or a single string // argument) first. var locales = ArgumentsLength() ? GetArgument(0) : undefined; var requestedLocale; if (locales === undefined) { // Steps 3, 6. requestedLocale = undefined; } else if (typeof locales === "string") { // Steps 3, 5. requestedLocale = intl_ValidateAndCanonicalizeLanguageTag(locales, false); } else { // Step 3. var requestedLocales = CanonicalizeLocaleList(locales); // Steps 4-6. requestedLocale = requestedLocales.length ? requestedLocales[0] : undefined; } // Trivial case: When the input is empty, directly return the empty string. if (string.length === 0) { return ""; } if (requestedLocale === undefined) { requestedLocale = DefaultLocale(); } // Steps 7-16. return intl_toLocaleLowerCase(string, requestedLocale); } /** * 13.1.3 String.prototype.toLocaleUpperCase ( [ locales ] ) * * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ function String_toLocaleUpperCase() { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("toLocaleUpperCase", this); } // Step 2. var string = ToString(this); // Handle the common cases (no locales argument or a single string // argument) first. var locales = ArgumentsLength() ? GetArgument(0) : undefined; var requestedLocale; if (locales === undefined) { // Steps 3, 6. requestedLocale = undefined; } else if (typeof locales === "string") { // Steps 3, 5. requestedLocale = intl_ValidateAndCanonicalizeLanguageTag(locales, false); } else { // Step 3. var requestedLocales = CanonicalizeLocaleList(locales); // Steps 4-6. requestedLocale = requestedLocales.length ? requestedLocales[0] : undefined; } // Trivial case: When the input is empty, directly return the empty string. if (string.length === 0) { return ""; } if (requestedLocale === undefined) { requestedLocale = DefaultLocale(); } // Steps 7-16. return intl_toLocaleUpperCase(string, requestedLocale); } #endif // JS_HAS_INTL_API // ES2018 draft rev 8fadde42cf6a9879b4ab0cb6142b31c4ee501667 // 21.1.2.4 String.raw ( template, ...substitutions ) function String_static_raw(callSite /*, ...substitutions*/) { // Steps 1-2 (not applicable). // Step 3. var cooked = ToObject(callSite); // Step 4. var raw = ToObject(cooked.raw); // Step 5. var literalSegments = ToLength(raw.length); // Step 6. if (literalSegments === 0) { return ""; } // Special case for |String.raw ``| callers to avoid falling into // the loop code below. if (literalSegments === 1) { return ToString(raw[0]); } // Steps 7-9 were reordered to use ArgumentsLength/GetArgument instead of a // rest parameter, because the former is currently more optimized. // // String.raw intersperses the substitution elements between the literal // segments, i.e. a substitution is added iff there are still pending // literal segments. Furthermore by moving the access to |raw[0]| outside // of the loop, we can use |nextIndex| to index into both, the |raw| array // and the arguments. // Steps 7 (implicit) and 9.a-c. var resultString = ToString(raw[0]); // Steps 8-9, 9.d, and 9.i. for (var nextIndex = 1; nextIndex < literalSegments; nextIndex++) { // Steps 9.e-h. if (nextIndex < ArgumentsLength()) { resultString += ToString(GetArgument(nextIndex)); } // Steps 9.a-c. resultString += ToString(raw[nextIndex]); } // Step 9.d.i. return resultString; } // https://github.com/tc39/proposal-relative-indexing-method // String.prototype.at ( index ) function String_at(index) { // Step 1. if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("at", this); } // Step 2. var string = ToString(this); // Step 3. var len = string.length; // Step 4. var relativeIndex = ToInteger(index); // Steps 5-6. var k; if (relativeIndex >= 0) { k = relativeIndex; } else { k = len + relativeIndex; } // Step 7. if (k < 0 || k >= len) { return undefined; } // Step 8. return string[k]; } // ES6 draft 2014-04-27 B.2.3.3 function String_big() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("big", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.4 function String_blink() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("blink", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.5 function String_bold() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("bold", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.6 function String_fixed() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("fixed", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.9 function String_italics() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("italics", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.11 function String_small() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("small", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.12 function String_strike() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("strike", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.13 function String_sub() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("sub", this); } return "" + ToString(this) + ""; } // ES6 draft 2014-04-27 B.2.3.14 function String_sup() { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("sup", this); } return "" + ToString(this) + ""; } function EscapeAttributeValue(v) { var inputStr = ToString(v); return StringReplaceAllString(inputStr, '"', """); } // ES6 draft 2014-04-27 B.2.3.2 function String_anchor(name) { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("anchor", this); } var S = ToString(this); return '' + S + ""; } // ES6 draft 2014-04-27 B.2.3.7 function String_fontcolor(color) { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("fontcolor", this); } var S = ToString(this); return '' + S + ""; } // ES6 draft 2014-04-27 B.2.3.8 function String_fontsize(size) { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("fontsize", this); } var S = ToString(this); return '' + S + ""; } // ES6 draft 2014-04-27 B.2.3.10 function String_link(url) { if (IsNullOrUndefined(this)) { ThrowIncompatibleMethod("link", this); } var S = ToString(this); return '' + S + ""; }