2 * Copyright (C) 2015 Andy VanWagoner <thetalecrafter@gmail.com>.
3 * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>
4 * Copyright (C) 2016 Apple Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 function match(regexp)
33 @throwTypeError("String.prototype.match requires that |this| not be null or undefined");
36 var matcher = regexp.@matchSymbol;
37 if (matcher != @undefined)
38 return matcher.@call(regexp, this);
41 let thisString = @toString(this);
42 let createdRegExp = @regExpCreate(regexp, @undefined);
43 return createdRegExp.@matchSymbol(thisString);
47 function repeatSlowPath(string, count)
51 // Return an empty string.
52 if (count === 0 || string.length === 0)
55 // Return the original string.
59 if (string.length * count > @MAX_STRING_LENGTH)
60 @throwOutOfMemoryError();
62 // Bit operation onto |count| is safe because |count| should be within Int32 range,
63 // Repeat log N times to generate the repeated string rope.
77 function repeatCharactersSlowPath(string, count)
80 var repeatCount = (count / string.length) | 0;
81 var remainingCharacters = count - repeatCount * string.length;
84 // Bit operation onto |repeatCount| is safe because |repeatCount| should be within Int32 range,
85 // Repeat log N times to generate the repeated string rope.
94 if (remainingCharacters)
95 result += @stringSubstrInternal.@call(string, 0, remainingCharacters);
100 function repeat(count)
105 @throwTypeError("String.prototype.repeat requires that |this| not be null or undefined");
107 var string = @toString(this);
108 count = @toInteger(count);
110 if (count < 0 || count === @Infinity)
111 @throwRangeError("String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity");
113 if (string.length === 1)
114 return @repeatCharacter(string, count);
116 return @repeatSlowPath(string, count);
119 function padStart(maxLength/*, fillString*/)
124 @throwTypeError("String.prototype.padStart requires that |this| not be null or undefined");
126 var string = @toString(this);
127 maxLength = @toLength(maxLength);
129 var stringLength = string.length;
130 if (maxLength <= stringLength)
134 var fillString = @argument(1);
135 if (fillString === @undefined)
138 filler = @toString(fillString);
143 if (maxLength > @MAX_STRING_LENGTH)
144 @throwOutOfMemoryError();
146 var fillLength = maxLength - stringLength;
147 var truncatedStringFiller;
149 if (filler.length === 1)
150 truncatedStringFiller = @repeatCharacter(filler, fillLength);
152 truncatedStringFiller = @repeatCharactersSlowPath(filler, fillLength);
153 return truncatedStringFiller + string;
156 function padEnd(maxLength/*, fillString*/)
161 @throwTypeError("String.prototype.padEnd requires that |this| not be null or undefined");
163 var string = @toString(this);
164 maxLength = @toLength(maxLength);
166 var stringLength = string.length;
167 if (maxLength <= stringLength)
171 var fillString = @argument(1);
172 if (fillString === @undefined)
175 filler = @toString(fillString);
180 if (maxLength > @MAX_STRING_LENGTH)
181 @throwOutOfMemoryError();
183 var fillLength = maxLength - stringLength;
184 var truncatedStringFiller;
186 if (filler.length === 1)
187 truncatedStringFiller = @repeatCharacter(filler, fillLength);
189 truncatedStringFiller = @repeatCharactersSlowPath(filler, fillLength);
190 return string + truncatedStringFiller;
194 function hasObservableSideEffectsForStringReplace(regexp, replacer) {
195 if (replacer !== @regExpPrototypeSymbolReplace)
198 let regexpExec = @tryGetById(regexp, "exec");
199 if (regexpExec !== @regExpBuiltinExec)
202 let regexpGlobal = @tryGetById(regexp, "global");
203 if (regexpGlobal !== @regExpProtoGlobalGetter)
206 let regexpUnicode = @tryGetById(regexp, "unicode");
207 if (regexpUnicode !== @regExpProtoUnicodeGetter)
210 return !@isRegExpObject(regexp);
213 @intrinsic=StringPrototypeReplaceIntrinsic
214 function replace(search, replace)
219 @throwTypeError("String.prototype.replace requires that |this| not be null or undefined");
221 if (search != null) {
222 let replacer = search.@replaceSymbol;
223 if (replacer !== @undefined) {
224 if (!@hasObservableSideEffectsForStringReplace(search, replacer))
225 return @toString(this).@replaceUsingRegExp(search, replace);
226 return replacer.@call(search, this, replace);
230 let thisString = @toString(this);
231 let searchString = @toString(search);
232 return thisString.@replaceUsingStringSearch(searchString, replace);
236 function getDefaultCollator()
238 return @getDefaultCollator.collator || (@getDefaultCollator.collator = new @Collator());
241 function localeCompare(that/*, locales, options */)
245 // 13.1.1 String.prototype.localeCompare (that [, locales [, options ]]) (ECMA-402 2.0)
246 // http://ecma-international.org/publications/standards/Ecma-402.htm
248 // 1. Let O be RequireObjectCoercible(this value).
250 @throwTypeError("String.prototype.localeCompare requires that |this| not be null or undefined");
252 // 2. Let S be ToString(O).
253 // 3. ReturnIfAbrupt(S).
254 var thisString = @toString(this);
256 // 4. Let That be ToString(that).
257 // 5. ReturnIfAbrupt(That).
258 var thatString = @toString(that);
260 // Avoid creating a new collator every time for defaults.
261 var locales = @argument(1);
262 var options = @argument(2);
263 if (locales === @undefined && options === @undefined)
264 return @getDefaultCollator().compare(thisString, thatString);
266 // 6. Let collator be Construct(%Collator%, «locales, options»).
267 // 7. ReturnIfAbrupt(collator).
268 var collator = new @Collator(locales, options);
270 // 8. Return CompareStrings(collator, S, That).
271 return collator.compare(thisString, thatString);
274 function search(regexp)
279 @throwTypeError("String.prototype.search requires that |this| not be null or undefined");
281 if (regexp != null) {
282 var searcher = regexp.@searchSymbol;
283 if (searcher != @undefined)
284 return searcher.@call(regexp, this);
287 var thisString = @toString(this);
288 var createdRegExp = @regExpCreate(regexp, @undefined);
289 return createdRegExp.@searchSymbol(thisString);
292 function split(separator, limit)
297 @throwTypeError("String.prototype.split requires that |this| not be null or undefined");
299 if (separator != null) {
300 var splitter = separator.@splitSymbol;
301 if (splitter != @undefined)
302 return splitter.@call(separator, this, limit);
305 return @stringSplitFast.@call(this, separator, limit);
309 function createHTML(func, string, tag, attribute, value)
313 @throwTypeError(`${func} requires that |this| not be null or undefined`);
314 let S = @toString(string);
317 let V = @toString(value);
318 let escapedV = V.@replaceUsingRegExp(/"/g, '"');
319 p1 = p1 + " " + @toString(attribute) + '="' + escapedV + '"'
323 let p4 = p3 + "</" + tag + ">";
330 return @createHTML("String.prototype.link", this, "a", "name", url)
336 return @createHTML("String.prototype.big", this, "big", "", "");
342 return @createHTML("String.prototype.blink", this, "blink", "", "");
348 return @createHTML("String.prototype.bold", this, "b", "", "");
354 return @createHTML("String.prototype.fixed", this, "tt", "", "");
357 function fontcolor(color)
360 return @createHTML("String.prototype.fontcolor", this, "font", "color", color);
363 function fontsize(size)
366 return @createHTML("String.prototype.fontsize", this, "font", "size", size);
372 return @createHTML("String.prototype.italics", this, "i", "", "");
378 return @createHTML("String.prototype.link", this, "a", "href", url)
384 return @createHTML("String.prototype.small", this, "small", "", "");
390 return @createHTML("String.prototype.strike", this, "strike", "", "");
396 return @createHTML("String.prototype.sub", this, "sub", "", "");
402 return @createHTML("String.prototype.sup", this, "sup", "", "");