OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils) { | 5 (function(global, utils) { |
6 | 6 |
7 %CheckIsBootstrapping(); | 7 %CheckIsBootstrapping(); |
8 | 8 |
9 // ------------------------------------------------------------------- | 9 // ------------------------------------------------------------------- |
10 // Imports | 10 // Imports |
11 | 11 |
| 12 var ArrayJoin; |
| 13 var GlobalRegExp = global.RegExp; |
12 var GlobalString = global.String; | 14 var GlobalString = global.String; |
| 15 var MaxSimple; |
| 16 var MinSimple; |
13 var matchSymbol = utils.ImportNow("match_symbol"); | 17 var matchSymbol = utils.ImportNow("match_symbol"); |
| 18 var replaceSymbol = utils.ImportNow("replace_symbol"); |
14 var searchSymbol = utils.ImportNow("search_symbol"); | 19 var searchSymbol = utils.ImportNow("search_symbol"); |
| 20 var splitSymbol = utils.ImportNow("split_symbol"); |
| 21 |
| 22 utils.Import(function(from) { |
| 23 ArrayJoin = from.ArrayJoin; |
| 24 MaxSimple = from.MaxSimple; |
| 25 MinSimple = from.MinSimple; |
| 26 }); |
15 | 27 |
16 //------------------------------------------------------------------- | 28 //------------------------------------------------------------------- |
17 | 29 |
18 // ECMA-262, section 15.5.4.6 | 30 // ECMA-262, section 15.5.4.6 |
19 function StringConcat(other /* and more */) { // length == 1 | 31 function StringConcat(other /* and more */) { // length == 1 |
20 "use strict"; | 32 "use strict"; |
21 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat"); | 33 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat"); |
22 var s = TO_STRING(this); | 34 var s = TO_STRING(this); |
23 var len = arguments.length; | 35 var len = arguments.length; |
24 for (var i = 0; i < len; ++i) { | 36 for (var i = 0; i < len; ++i) { |
(...skipping 14 matching lines...) Expand all Loading... |
39 } | 51 } |
40 } | 52 } |
41 | 53 |
42 var subject = TO_STRING(this); | 54 var subject = TO_STRING(this); |
43 | 55 |
44 // Equivalent to RegExpCreate (ES#sec-regexpcreate) | 56 // Equivalent to RegExpCreate (ES#sec-regexpcreate) |
45 var regexp = %RegExpCreate(pattern); | 57 var regexp = %RegExpCreate(pattern); |
46 return regexp[matchSymbol](subject); | 58 return regexp[matchSymbol](subject); |
47 } | 59 } |
48 | 60 |
| 61 // ES#sec-getsubstitution |
| 62 // GetSubstitution(matched, str, position, captures, replacement) |
| 63 // Expand the $-expressions in the string and return a new string with |
| 64 // the result. |
| 65 function GetSubstitution(matched, string, position, captures, replacement) { |
| 66 var matchLength = matched.length; |
| 67 var stringLength = string.length; |
| 68 var capturesLength = captures.length; |
| 69 var tailPos = position + matchLength; |
| 70 var result = ""; |
| 71 var pos, expansion, peek, next, scaledIndex, advance, newScaledIndex; |
| 72 |
| 73 var next = %StringIndexOf(replacement, '$', 0); |
| 74 if (next < 0) { |
| 75 result += replacement; |
| 76 return result; |
| 77 } |
| 78 |
| 79 if (next > 0) result += %_SubString(replacement, 0, next); |
| 80 |
| 81 while (true) { |
| 82 expansion = '$'; |
| 83 pos = next + 1; |
| 84 if (pos < replacement.length) { |
| 85 peek = %_StringCharCodeAt(replacement, pos); |
| 86 if (peek == 36) { // $$ |
| 87 ++pos; |
| 88 result += '$'; |
| 89 } else if (peek == 38) { // $& - match |
| 90 ++pos; |
| 91 result += matched; |
| 92 } else if (peek == 96) { // $` - prefix |
| 93 ++pos; |
| 94 result += %_SubString(string, 0, position); |
| 95 } else if (peek == 39) { // $' - suffix |
| 96 ++pos; |
| 97 result += %_SubString(string, tailPos, stringLength); |
| 98 } else if (peek >= 48 && peek <= 57) { |
| 99 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 |
| 100 scaledIndex = (peek - 48); |
| 101 advance = 1; |
| 102 if (pos + 1 < replacement.length) { |
| 103 next = %_StringCharCodeAt(replacement, pos + 1); |
| 104 if (next >= 48 && next <= 57) { |
| 105 newScaledIndex = scaledIndex * 10 + ((next - 48)); |
| 106 if (newScaledIndex < capturesLength) { |
| 107 scaledIndex = newScaledIndex; |
| 108 advance = 2; |
| 109 } |
| 110 } |
| 111 } |
| 112 if (scaledIndex != 0 && scaledIndex < capturesLength) { |
| 113 var capture = captures.at(scaledIndex); |
| 114 if (!IS_UNDEFINED(capture)) result += capture; |
| 115 pos += advance; |
| 116 } else { |
| 117 result += '$'; |
| 118 } |
| 119 } else { |
| 120 result += '$'; |
| 121 } |
| 122 } else { |
| 123 result += '$'; |
| 124 } |
| 125 |
| 126 // Go the the next $ in the replacement. |
| 127 next = %StringIndexOf(replacement, '$', pos); |
| 128 |
| 129 // Return if there are no more $ characters in the replacement. If we |
| 130 // haven't reached the end, we need to append the suffix. |
| 131 if (next < 0) { |
| 132 if (pos < replacement.length) { |
| 133 result += %_SubString(replacement, pos, replacement.length); |
| 134 } |
| 135 return result; |
| 136 } |
| 137 |
| 138 // Append substring between the previous and the next $ character. |
| 139 if (next > pos) { |
| 140 result += %_SubString(replacement, pos, next); |
| 141 } |
| 142 } |
| 143 return result; |
| 144 } |
| 145 |
| 146 // ES6, section 21.1.3.14 |
| 147 function StringReplace(search, replace) { |
| 148 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); |
| 149 |
| 150 // Decision tree for dispatch |
| 151 // .. regexp search (in src/js/regexp.js, RegExpReplace) |
| 152 // .... string replace |
| 153 // ...... non-global search |
| 154 // ........ empty string replace |
| 155 // ........ non-empty string replace (with $-expansion) |
| 156 // ...... global search |
| 157 // ........ no need to circumvent last match info override |
| 158 // ........ need to circument last match info override |
| 159 // .... function replace |
| 160 // ...... global search |
| 161 // ...... non-global search |
| 162 // .. string search |
| 163 // .... special case that replaces with one single character |
| 164 // ...... function replace |
| 165 // ...... string replace (with $-expansion) |
| 166 |
| 167 if (!IS_NULL_OR_UNDEFINED(search)) { |
| 168 var replacer = search[replaceSymbol]; |
| 169 if (!IS_UNDEFINED(replacer)) { |
| 170 return %_Call(replacer, search, this, replace); |
| 171 } |
| 172 } |
| 173 |
| 174 var subject = TO_STRING(this); |
| 175 |
| 176 search = TO_STRING(search); |
| 177 |
| 178 if (search.length == 1 && |
| 179 subject.length > 0xFF && |
| 180 IS_STRING(replace) && |
| 181 %StringIndexOf(replace, '$', 0) < 0) { |
| 182 // Searching by traversing a cons string tree and replace with cons of |
| 183 // slices works only when the replaced string is a single character, being |
| 184 // replaced by a simple string and only pays off for long strings. |
| 185 return %StringReplaceOneCharWithString(subject, search, replace); |
| 186 } |
| 187 var start = %StringIndexOf(subject, search, 0); |
| 188 if (start < 0) return subject; |
| 189 var end = start + search.length; |
| 190 |
| 191 var result = %_SubString(subject, 0, start); |
| 192 |
| 193 // Compute the string to replace with. |
| 194 if (IS_CALLABLE(replace)) { |
| 195 result += replace(search, start, subject); |
| 196 } else { |
| 197 // In this case, we don't have any capture groups and can get away with |
| 198 // faking the captures object by simply setting its length to 1. |
| 199 const captures = { length: 1 }; |
| 200 const matched = %_SubString(subject, start, end); |
| 201 result += GetSubstitution(matched, subject, start, captures, |
| 202 TO_STRING(replace)); |
| 203 } |
| 204 |
| 205 return result + %_SubString(subject, end, subject.length); |
| 206 } |
| 207 |
| 208 |
49 // ES6 21.1.3.15. | 209 // ES6 21.1.3.15. |
50 function StringSearch(pattern) { | 210 function StringSearch(pattern) { |
51 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); | 211 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); |
52 | 212 |
53 if (!IS_NULL_OR_UNDEFINED(pattern)) { | 213 if (!IS_NULL_OR_UNDEFINED(pattern)) { |
54 var searcher = pattern[searchSymbol]; | 214 var searcher = pattern[searchSymbol]; |
55 if (!IS_UNDEFINED(searcher)) { | 215 if (!IS_UNDEFINED(searcher)) { |
56 return %_Call(searcher, pattern, this); | 216 return %_Call(searcher, pattern, this); |
57 } | 217 } |
58 } | 218 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 } | 259 } |
100 } | 260 } |
101 | 261 |
102 if (end_i <= start_i) { | 262 if (end_i <= start_i) { |
103 return ''; | 263 return ''; |
104 } | 264 } |
105 | 265 |
106 return %_SubString(s, start_i, end_i); | 266 return %_SubString(s, start_i, end_i); |
107 } | 267 } |
108 | 268 |
| 269 |
| 270 // ES6 21.1.3.17. |
| 271 function StringSplitJS(separator, limit) { |
| 272 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split"); |
| 273 |
| 274 if (!IS_NULL_OR_UNDEFINED(separator)) { |
| 275 var splitter = separator[splitSymbol]; |
| 276 if (!IS_UNDEFINED(splitter)) { |
| 277 return %_Call(splitter, separator, this, limit); |
| 278 } |
| 279 } |
| 280 |
| 281 var subject = TO_STRING(this); |
| 282 limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit); |
| 283 |
| 284 var length = subject.length; |
| 285 var separator_string = TO_STRING(separator); |
| 286 |
| 287 if (limit === 0) return []; |
| 288 |
| 289 // ECMA-262 says that if separator is undefined, the result should |
| 290 // be an array of size 1 containing the entire string. |
| 291 if (IS_UNDEFINED(separator)) return [subject]; |
| 292 |
| 293 var separator_length = separator_string.length; |
| 294 |
| 295 // If the separator string is empty then return the elements in the subject. |
| 296 if (separator_length === 0) return %StringToArray(subject, limit); |
| 297 |
| 298 return %StringSplit(subject, separator_string, limit); |
| 299 } |
| 300 |
| 301 |
109 // ECMA-262, 15.5.4.16 | 302 // ECMA-262, 15.5.4.16 |
110 function StringToLowerCaseJS() { | 303 function StringToLowerCaseJS() { |
111 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase"); | 304 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase"); |
112 | 305 |
113 return %StringToLowerCase(TO_STRING(this)); | 306 return %StringToLowerCase(TO_STRING(this)); |
114 } | 307 } |
115 | 308 |
116 | 309 |
117 // ECMA-262, 15.5.4.17 | 310 // ECMA-262, 15.5.4.17 |
118 function StringToLocaleLowerCase() { | 311 function StringToLocaleLowerCase() { |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 utils.InstallFunctions(GlobalString, DONT_ENUM, [ | 508 utils.InstallFunctions(GlobalString, DONT_ENUM, [ |
316 "raw", StringRaw | 509 "raw", StringRaw |
317 ]); | 510 ]); |
318 | 511 |
319 // Set up the non-enumerable functions on the String prototype object. | 512 // Set up the non-enumerable functions on the String prototype object. |
320 utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ | 513 utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ |
321 "codePointAt", StringCodePointAt, | 514 "codePointAt", StringCodePointAt, |
322 "concat", StringConcat, | 515 "concat", StringConcat, |
323 "match", StringMatchJS, | 516 "match", StringMatchJS, |
324 "repeat", StringRepeat, | 517 "repeat", StringRepeat, |
| 518 "replace", StringReplace, |
325 "search", StringSearch, | 519 "search", StringSearch, |
326 "slice", StringSlice, | 520 "slice", StringSlice, |
| 521 "split", StringSplitJS, |
327 "toLowerCase", StringToLowerCaseJS, | 522 "toLowerCase", StringToLowerCaseJS, |
328 "toLocaleLowerCase", StringToLocaleLowerCase, | 523 "toLocaleLowerCase", StringToLocaleLowerCase, |
329 "toUpperCase", StringToUpperCaseJS, | 524 "toUpperCase", StringToUpperCaseJS, |
330 "toLocaleUpperCase", StringToLocaleUpperCase, | 525 "toLocaleUpperCase", StringToLocaleUpperCase, |
331 | 526 |
332 "link", StringLink, | 527 "link", StringLink, |
333 "anchor", StringAnchor, | 528 "anchor", StringAnchor, |
334 "fontcolor", StringFontcolor, | 529 "fontcolor", StringFontcolor, |
335 "fontsize", StringFontsize, | 530 "fontsize", StringFontsize, |
336 "big", StringBig, | 531 "big", StringBig, |
337 "blink", StringBlink, | 532 "blink", StringBlink, |
338 "bold", StringBold, | 533 "bold", StringBold, |
339 "fixed", StringFixed, | 534 "fixed", StringFixed, |
340 "italics", StringItalics, | 535 "italics", StringItalics, |
341 "small", StringSmall, | 536 "small", StringSmall, |
342 "strike", StringStrike, | 537 "strike", StringStrike, |
343 "sub", StringSub, | 538 "sub", StringSub, |
344 "sup", StringSup | 539 "sup", StringSup |
345 ]); | 540 ]); |
346 | 541 |
| 542 // ------------------------------------------------------------------- |
| 543 // Exports |
| 544 |
| 545 utils.Export(function(to) { |
| 546 to.StringMatch = StringMatchJS; |
| 547 to.StringReplace = StringReplace; |
| 548 to.StringSlice = StringSlice; |
| 549 to.StringSplit = StringSplitJS; |
| 550 }); |
| 551 |
347 }) | 552 }) |
OLD | NEW |