| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 (function(global, utils) { | |
| 6 | |
| 7 %CheckIsBootstrapping(); | |
| 8 | |
| 9 // ------------------------------------------------------------------- | |
| 10 // Imports | |
| 11 | |
| 12 var ArrayIndexOf; | |
| 13 var ArrayJoin; | |
| 14 var GlobalRegExp = global.RegExp; | |
| 15 var GlobalString = global.String; | |
| 16 var InternalArray = utils.InternalArray; | |
| 17 var InternalPackedArray = utils.InternalPackedArray; | |
| 18 var RegExpExec; | |
| 19 var RegExpExecNoTests; | |
| 20 var RegExpLastMatchInfo; | |
| 21 | |
| 22 utils.Import(function(from) { | |
| 23 ArrayIndexOf = from.ArrayIndexOf; | |
| 24 ArrayJoin = from.ArrayJoin; | |
| 25 RegExpExec = from.RegExpExec; | |
| 26 RegExpExecNoTests = from.RegExpExecNoTests; | |
| 27 RegExpLastMatchInfo = from.RegExpLastMatchInfo; | |
| 28 }); | |
| 29 | |
| 30 //------------------------------------------------------------------- | |
| 31 | |
| 32 // ECMA-262 section 15.5.4.2 | |
| 33 function StringToString() { | |
| 34 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { | |
| 35 throw MakeTypeError(kNotGeneric, 'String.prototype.toString'); | |
| 36 } | |
| 37 return %_ValueOf(this); | |
| 38 } | |
| 39 | |
| 40 | |
| 41 // ECMA-262 section 15.5.4.3 | |
| 42 function StringValueOf() { | |
| 43 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { | |
| 44 throw MakeTypeError(kNotGeneric, 'String.prototype.valueOf'); | |
| 45 } | |
| 46 return %_ValueOf(this); | |
| 47 } | |
| 48 | |
| 49 | |
| 50 // ECMA-262, section 15.5.4.4 | |
| 51 function StringCharAtJS(pos) { | |
| 52 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt"); | |
| 53 | |
| 54 var result = %_StringCharAt(this, pos); | |
| 55 if (%_IsSmi(result)) { | |
| 56 result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos)); | |
| 57 } | |
| 58 return result; | |
| 59 } | |
| 60 | |
| 61 | |
| 62 // ECMA-262 section 15.5.4.5 | |
| 63 function StringCharCodeAtJS(pos) { | |
| 64 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt"); | |
| 65 | |
| 66 var result = %_StringCharCodeAt(this, pos); | |
| 67 if (!%_IsSmi(result)) { | |
| 68 result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos)); | |
| 69 } | |
| 70 return result; | |
| 71 } | |
| 72 | |
| 73 | |
| 74 // ECMA-262, section 15.5.4.6 | |
| 75 function StringConcat(other /* and more */) { // length == 1 | |
| 76 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat"); | |
| 77 var len = %_ArgumentsLength(); | |
| 78 var this_as_string = TO_STRING(this); | |
| 79 if (len === 1) { | |
| 80 return this_as_string + TO_STRING(other); | |
| 81 } | |
| 82 var parts = new InternalArray(len + 1); | |
| 83 parts[0] = this_as_string; | |
| 84 for (var i = 0; i < len; i++) { | |
| 85 var part = %_Arguments(i); | |
| 86 parts[i + 1] = TO_STRING(part); | |
| 87 } | |
| 88 return %StringBuilderConcat(parts, len + 1, ""); | |
| 89 } | |
| 90 | |
| 91 | |
| 92 // ECMA-262 section 15.5.4.7 | |
| 93 function StringIndexOfJS(pattern /* position */) { // length == 1 | |
| 94 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf"); | |
| 95 | |
| 96 var subject = TO_STRING(this); | |
| 97 pattern = TO_STRING(pattern); | |
| 98 var index = 0; | |
| 99 if (%_ArgumentsLength() > 1) { | |
| 100 index = %_Arguments(1); // position | |
| 101 index = TO_INTEGER(index); | |
| 102 if (index < 0) index = 0; | |
| 103 if (index > subject.length) index = subject.length; | |
| 104 } | |
| 105 return %StringIndexOf(subject, pattern, index); | |
| 106 } | |
| 107 | |
| 108 | |
| 109 // ECMA-262 section 15.5.4.8 | |
| 110 function StringLastIndexOfJS(pat /* position */) { // length == 1 | |
| 111 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf"); | |
| 112 | |
| 113 var sub = TO_STRING(this); | |
| 114 var subLength = sub.length; | |
| 115 var pat = TO_STRING(pat); | |
| 116 var patLength = pat.length; | |
| 117 var index = subLength - patLength; | |
| 118 if (%_ArgumentsLength() > 1) { | |
| 119 var position = TO_NUMBER(%_Arguments(1)); | |
| 120 if (!NUMBER_IS_NAN(position)) { | |
| 121 position = TO_INTEGER(position); | |
| 122 if (position < 0) { | |
| 123 position = 0; | |
| 124 } | |
| 125 if (position + patLength < subLength) { | |
| 126 index = position; | |
| 127 } | |
| 128 } | |
| 129 } | |
| 130 if (index < 0) { | |
| 131 return -1; | |
| 132 } | |
| 133 return %StringLastIndexOf(sub, pat, index); | |
| 134 } | |
| 135 | |
| 136 | |
| 137 // ECMA-262 section 15.5.4.9 | |
| 138 // | |
| 139 // This function is implementation specific. For now, we do not | |
| 140 // do anything locale specific. | |
| 141 function StringLocaleCompareJS(other) { | |
| 142 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare"); | |
| 143 | |
| 144 return %StringLocaleCompare(TO_STRING(this), TO_STRING(other)); | |
| 145 } | |
| 146 | |
| 147 | |
| 148 // ECMA-262 section 15.5.4.10 | |
| 149 function StringMatchJS(regexp) { | |
| 150 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match"); | |
| 151 | |
| 152 var subject = TO_STRING(this); | |
| 153 if (IS_REGEXP(regexp)) { | |
| 154 // Emulate RegExp.prototype.exec's side effect in step 5, even though | |
| 155 // value is discarded. | |
| 156 var lastIndex = TO_INTEGER(regexp.lastIndex); | |
| 157 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0); | |
| 158 var result = %StringMatch(subject, regexp, RegExpLastMatchInfo); | |
| 159 if (result !== null) $regexpLastMatchInfoOverride = null; | |
| 160 regexp.lastIndex = 0; | |
| 161 return result; | |
| 162 } | |
| 163 // Non-regexp argument. | |
| 164 regexp = new GlobalRegExp(regexp); | |
| 165 return RegExpExecNoTests(regexp, subject, 0); | |
| 166 } | |
| 167 | |
| 168 | |
| 169 // ECMA-262 v6, section 21.1.3.12 | |
| 170 // | |
| 171 // For now we do nothing, as proper normalization requires big tables. | |
| 172 // If Intl is enabled, then i18n.js will override it and provide the the | |
| 173 // proper functionality. | |
| 174 function StringNormalizeJS() { | |
| 175 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); | |
| 176 var s = TO_STRING(this); | |
| 177 | |
| 178 var formArg = %_Arguments(0); | |
| 179 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg); | |
| 180 | |
| 181 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; | |
| 182 var normalizationForm = | |
| 183 %_CallFunction(NORMALIZATION_FORMS, form, ArrayIndexOf); | |
| 184 if (normalizationForm === -1) { | |
| 185 throw MakeRangeError(kNormalizationForm, | |
| 186 %_CallFunction(NORMALIZATION_FORMS, ', ', ArrayJoin)); | |
| 187 } | |
| 188 | |
| 189 return s; | |
| 190 } | |
| 191 | |
| 192 | |
| 193 // This has the same size as the RegExpLastMatchInfo array, and can be used | |
| 194 // for functions that expect that structure to be returned. It is used when | |
| 195 // the needle is a string rather than a regexp. In this case we can't update | |
| 196 // lastMatchArray without erroneously affecting the properties on the global | |
| 197 // RegExp object. | |
| 198 var reusableMatchInfo = [2, "", "", -1, -1]; | |
| 199 | |
| 200 | |
| 201 // ECMA-262, section 15.5.4.11 | |
| 202 function StringReplace(search, replace) { | |
| 203 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); | |
| 204 | |
| 205 var subject = TO_STRING(this); | |
| 206 | |
| 207 // Decision tree for dispatch | |
| 208 // .. regexp search | |
| 209 // .... string replace | |
| 210 // ...... non-global search | |
| 211 // ........ empty string replace | |
| 212 // ........ non-empty string replace (with $-expansion) | |
| 213 // ...... global search | |
| 214 // ........ no need to circumvent last match info override | |
| 215 // ........ need to circument last match info override | |
| 216 // .... function replace | |
| 217 // ...... global search | |
| 218 // ...... non-global search | |
| 219 // .. string search | |
| 220 // .... special case that replaces with one single character | |
| 221 // ...... function replace | |
| 222 // ...... string replace (with $-expansion) | |
| 223 | |
| 224 if (IS_REGEXP(search)) { | |
| 225 // Emulate RegExp.prototype.exec's side effect in step 5, even if | |
| 226 // value is discarded. | |
| 227 var lastIndex = TO_INTEGER(search.lastIndex); | |
| 228 | |
| 229 if (!IS_CALLABLE(replace)) { | |
| 230 replace = TO_STRING(replace); | |
| 231 | |
| 232 if (!search.global) { | |
| 233 // Non-global regexp search, string replace. | |
| 234 var match = RegExpExec(search, subject, 0); | |
| 235 if (match == null) { | |
| 236 search.lastIndex = 0 | |
| 237 return subject; | |
| 238 } | |
| 239 if (replace.length == 0) { | |
| 240 return %_SubString(subject, 0, match[CAPTURE0]) + | |
| 241 %_SubString(subject, match[CAPTURE1], subject.length) | |
| 242 } | |
| 243 return ExpandReplacement(replace, subject, RegExpLastMatchInfo, | |
| 244 %_SubString(subject, 0, match[CAPTURE0])) + | |
| 245 %_SubString(subject, match[CAPTURE1], subject.length); | |
| 246 } | |
| 247 | |
| 248 // Global regexp search, string replace. | |
| 249 search.lastIndex = 0; | |
| 250 if ($regexpLastMatchInfoOverride == null) { | |
| 251 return %StringReplaceGlobalRegExpWithString( | |
| 252 subject, search, replace, RegExpLastMatchInfo); | |
| 253 } else { | |
| 254 // We use this hack to detect whether StringReplaceRegExpWithString | |
| 255 // found at least one hit. In that case we need to remove any | |
| 256 // override. | |
| 257 var saved_subject = RegExpLastMatchInfo[LAST_SUBJECT_INDEX]; | |
| 258 RegExpLastMatchInfo[LAST_SUBJECT_INDEX] = 0; | |
| 259 var answer = %StringReplaceGlobalRegExpWithString( | |
| 260 subject, search, replace, RegExpLastMatchInfo); | |
| 261 if (%_IsSmi(RegExpLastMatchInfo[LAST_SUBJECT_INDEX])) { | |
| 262 RegExpLastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; | |
| 263 } else { | |
| 264 $regexpLastMatchInfoOverride = null; | |
| 265 } | |
| 266 return answer; | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 if (search.global) { | |
| 271 // Global regexp search, function replace. | |
| 272 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | |
| 273 } | |
| 274 // Non-global regexp search, function replace. | |
| 275 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); | |
| 276 } | |
| 277 | |
| 278 search = TO_STRING(search); | |
| 279 | |
| 280 if (search.length == 1 && | |
| 281 subject.length > 0xFF && | |
| 282 IS_STRING(replace) && | |
| 283 %StringIndexOf(replace, '$', 0) < 0) { | |
| 284 // Searching by traversing a cons string tree and replace with cons of | |
| 285 // slices works only when the replaced string is a single character, being | |
| 286 // replaced by a simple string and only pays off for long strings. | |
| 287 return %StringReplaceOneCharWithString(subject, search, replace); | |
| 288 } | |
| 289 var start = %StringIndexOf(subject, search, 0); | |
| 290 if (start < 0) return subject; | |
| 291 var end = start + search.length; | |
| 292 | |
| 293 var result = %_SubString(subject, 0, start); | |
| 294 | |
| 295 // Compute the string to replace with. | |
| 296 if (IS_CALLABLE(replace)) { | |
| 297 result += replace(search, start, subject); | |
| 298 } else { | |
| 299 reusableMatchInfo[CAPTURE0] = start; | |
| 300 reusableMatchInfo[CAPTURE1] = end; | |
| 301 result = ExpandReplacement(TO_STRING(replace), | |
| 302 subject, | |
| 303 reusableMatchInfo, | |
| 304 result); | |
| 305 } | |
| 306 | |
| 307 return result + %_SubString(subject, end, subject.length); | |
| 308 } | |
| 309 | |
| 310 | |
| 311 // Expand the $-expressions in the string and return a new string with | |
| 312 // the result. | |
| 313 function ExpandReplacement(string, subject, matchInfo, result) { | |
| 314 var length = string.length; | |
| 315 var next = %StringIndexOf(string, '$', 0); | |
| 316 if (next < 0) { | |
| 317 if (length > 0) result += string; | |
| 318 return result; | |
| 319 } | |
| 320 | |
| 321 if (next > 0) result += %_SubString(string, 0, next); | |
| 322 | |
| 323 while (true) { | |
| 324 var expansion = '$'; | |
| 325 var position = next + 1; | |
| 326 if (position < length) { | |
| 327 var peek = %_StringCharCodeAt(string, position); | |
| 328 if (peek == 36) { // $$ | |
| 329 ++position; | |
| 330 result += '$'; | |
| 331 } else if (peek == 38) { // $& - match | |
| 332 ++position; | |
| 333 result += | |
| 334 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); | |
| 335 } else if (peek == 96) { // $` - prefix | |
| 336 ++position; | |
| 337 result += %_SubString(subject, 0, matchInfo[CAPTURE0]); | |
| 338 } else if (peek == 39) { // $' - suffix | |
| 339 ++position; | |
| 340 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length); | |
| 341 } else if (peek >= 48 && peek <= 57) { | |
| 342 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 | |
| 343 var scaled_index = (peek - 48) << 1; | |
| 344 var advance = 1; | |
| 345 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo); | |
| 346 if (position + 1 < string.length) { | |
| 347 var next = %_StringCharCodeAt(string, position + 1); | |
| 348 if (next >= 48 && next <= 57) { | |
| 349 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1); | |
| 350 if (new_scaled_index < number_of_captures) { | |
| 351 scaled_index = new_scaled_index; | |
| 352 advance = 2; | |
| 353 } | |
| 354 } | |
| 355 } | |
| 356 if (scaled_index != 0 && scaled_index < number_of_captures) { | |
| 357 var start = matchInfo[CAPTURE(scaled_index)]; | |
| 358 if (start >= 0) { | |
| 359 result += | |
| 360 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]); | |
| 361 } | |
| 362 position += advance; | |
| 363 } else { | |
| 364 result += '$'; | |
| 365 } | |
| 366 } else { | |
| 367 result += '$'; | |
| 368 } | |
| 369 } else { | |
| 370 result += '$'; | |
| 371 } | |
| 372 | |
| 373 // Go the the next $ in the string. | |
| 374 next = %StringIndexOf(string, '$', position); | |
| 375 | |
| 376 // Return if there are no more $ characters in the string. If we | |
| 377 // haven't reached the end, we need to append the suffix. | |
| 378 if (next < 0) { | |
| 379 if (position < length) { | |
| 380 result += %_SubString(string, position, length); | |
| 381 } | |
| 382 return result; | |
| 383 } | |
| 384 | |
| 385 // Append substring between the previous and the next $ character. | |
| 386 if (next > position) { | |
| 387 result += %_SubString(string, position, next); | |
| 388 } | |
| 389 } | |
| 390 return result; | |
| 391 } | |
| 392 | |
| 393 | |
| 394 // Compute the string of a given regular expression capture. | |
| 395 function CaptureString(string, lastCaptureInfo, index) { | |
| 396 // Scale the index. | |
| 397 var scaled = index << 1; | |
| 398 // Compute start and end. | |
| 399 var start = lastCaptureInfo[CAPTURE(scaled)]; | |
| 400 // If start isn't valid, return undefined. | |
| 401 if (start < 0) return; | |
| 402 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; | |
| 403 return %_SubString(string, start, end); | |
| 404 } | |
| 405 | |
| 406 | |
| 407 // TODO(lrn): This array will survive indefinitely if replace is never | |
| 408 // called again. However, it will be empty, since the contents are cleared | |
| 409 // in the finally block. | |
| 410 var reusableReplaceArray = new InternalArray(4); | |
| 411 | |
| 412 // Helper function for replacing regular expressions with the result of a | |
| 413 // function application in String.prototype.replace. | |
| 414 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { | |
| 415 var resultArray = reusableReplaceArray; | |
| 416 if (resultArray) { | |
| 417 reusableReplaceArray = null; | |
| 418 } else { | |
| 419 // Inside a nested replace (replace called from the replacement function | |
| 420 // of another replace) or we have failed to set the reusable array | |
| 421 // back due to an exception in a replacement function. Create a new | |
| 422 // array to use in the future, or until the original is written back. | |
| 423 resultArray = new InternalArray(16); | |
| 424 } | |
| 425 var res = %RegExpExecMultiple(regexp, | |
| 426 subject, | |
| 427 RegExpLastMatchInfo, | |
| 428 resultArray); | |
| 429 regexp.lastIndex = 0; | |
| 430 if (IS_NULL(res)) { | |
| 431 // No matches at all. | |
| 432 reusableReplaceArray = resultArray; | |
| 433 return subject; | |
| 434 } | |
| 435 var len = res.length; | |
| 436 if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) { | |
| 437 // If the number of captures is two then there are no explicit captures in | |
| 438 // the regexp, just the implicit capture that captures the whole match. In | |
| 439 // this case we can simplify quite a bit and end up with something faster. | |
| 440 // The builder will consist of some integers that indicate slices of the | |
| 441 // input string and some replacements that were returned from the replace | |
| 442 // function. | |
| 443 var match_start = 0; | |
| 444 var override = new InternalPackedArray(null, 0, subject); | |
| 445 for (var i = 0; i < len; i++) { | |
| 446 var elem = res[i]; | |
| 447 if (%_IsSmi(elem)) { | |
| 448 // Integers represent slices of the original string. Use these to | |
| 449 // get the offsets we need for the override array (so things like | |
| 450 // RegExp.leftContext work during the callback function. | |
| 451 if (elem > 0) { | |
| 452 match_start = (elem >> 11) + (elem & 0x7ff); | |
| 453 } else { | |
| 454 match_start = res[++i] - elem; | |
| 455 } | |
| 456 } else { | |
| 457 override[0] = elem; | |
| 458 override[1] = match_start; | |
| 459 $regexpLastMatchInfoOverride = override; | |
| 460 var func_result = replace(elem, match_start, subject); | |
| 461 // Overwrite the i'th element in the results with the string we got | |
| 462 // back from the callback function. | |
| 463 res[i] = TO_STRING(func_result); | |
| 464 match_start += elem.length; | |
| 465 } | |
| 466 } | |
| 467 } else { | |
| 468 for (var i = 0; i < len; i++) { | |
| 469 var elem = res[i]; | |
| 470 if (!%_IsSmi(elem)) { | |
| 471 // elem must be an Array. | |
| 472 // Use the apply argument as backing for global RegExp properties. | |
| 473 $regexpLastMatchInfoOverride = elem; | |
| 474 var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length); | |
| 475 // Overwrite the i'th element in the results with the string we got | |
| 476 // back from the callback function. | |
| 477 res[i] = TO_STRING(func_result); | |
| 478 } | |
| 479 } | |
| 480 } | |
| 481 var result = %StringBuilderConcat(res, res.length, subject); | |
| 482 resultArray.length = 0; | |
| 483 reusableReplaceArray = resultArray; | |
| 484 return result; | |
| 485 } | |
| 486 | |
| 487 | |
| 488 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { | |
| 489 var matchInfo = RegExpExec(regexp, subject, 0); | |
| 490 if (IS_NULL(matchInfo)) { | |
| 491 regexp.lastIndex = 0; | |
| 492 return subject; | |
| 493 } | |
| 494 var index = matchInfo[CAPTURE0]; | |
| 495 var result = %_SubString(subject, 0, index); | |
| 496 var endOfMatch = matchInfo[CAPTURE1]; | |
| 497 // Compute the parameter list consisting of the match, captures, index, | |
| 498 // and subject for the replace function invocation. | |
| 499 // The number of captures plus one for the match. | |
| 500 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | |
| 501 var replacement; | |
| 502 if (m == 1) { | |
| 503 // No captures, only the match, which is always valid. | |
| 504 var s = %_SubString(subject, index, endOfMatch); | |
| 505 // Don't call directly to avoid exposing the built-in global object. | |
| 506 replacement = replace(s, index, subject); | |
| 507 } else { | |
| 508 var parameters = new InternalArray(m + 2); | |
| 509 for (var j = 0; j < m; j++) { | |
| 510 parameters[j] = CaptureString(subject, matchInfo, j); | |
| 511 } | |
| 512 parameters[j] = index; | |
| 513 parameters[j + 1] = subject; | |
| 514 | |
| 515 replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2); | |
| 516 } | |
| 517 | |
| 518 result += replacement; // The add method converts to string if necessary. | |
| 519 // Can't use matchInfo any more from here, since the function could | |
| 520 // overwrite it. | |
| 521 return result + %_SubString(subject, endOfMatch, subject.length); | |
| 522 } | |
| 523 | |
| 524 | |
| 525 // ECMA-262 section 15.5.4.12 | |
| 526 function StringSearch(re) { | |
| 527 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); | |
| 528 | |
| 529 var regexp; | |
| 530 if (IS_REGEXP(re)) { | |
| 531 regexp = re; | |
| 532 } else { | |
| 533 regexp = new GlobalRegExp(re); | |
| 534 } | |
| 535 var match = RegExpExec(regexp, TO_STRING(this), 0); | |
| 536 if (match) { | |
| 537 return match[CAPTURE0]; | |
| 538 } | |
| 539 return -1; | |
| 540 } | |
| 541 | |
| 542 | |
| 543 // ECMA-262 section 15.5.4.13 | |
| 544 function StringSlice(start, end) { | |
| 545 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice"); | |
| 546 | |
| 547 var s = TO_STRING(this); | |
| 548 var s_len = s.length; | |
| 549 var start_i = TO_INTEGER(start); | |
| 550 var end_i = s_len; | |
| 551 if (!IS_UNDEFINED(end)) { | |
| 552 end_i = TO_INTEGER(end); | |
| 553 } | |
| 554 | |
| 555 if (start_i < 0) { | |
| 556 start_i += s_len; | |
| 557 if (start_i < 0) { | |
| 558 start_i = 0; | |
| 559 } | |
| 560 } else { | |
| 561 if (start_i > s_len) { | |
| 562 return ''; | |
| 563 } | |
| 564 } | |
| 565 | |
| 566 if (end_i < 0) { | |
| 567 end_i += s_len; | |
| 568 if (end_i < 0) { | |
| 569 return ''; | |
| 570 } | |
| 571 } else { | |
| 572 if (end_i > s_len) { | |
| 573 end_i = s_len; | |
| 574 } | |
| 575 } | |
| 576 | |
| 577 if (end_i <= start_i) { | |
| 578 return ''; | |
| 579 } | |
| 580 | |
| 581 return %_SubString(s, start_i, end_i); | |
| 582 } | |
| 583 | |
| 584 | |
| 585 // ECMA-262 section 15.5.4.14 | |
| 586 function StringSplitJS(separator, limit) { | |
| 587 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split"); | |
| 588 | |
| 589 var subject = TO_STRING(this); | |
| 590 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit); | |
| 591 | |
| 592 var length = subject.length; | |
| 593 if (!IS_REGEXP(separator)) { | |
| 594 var separator_string = TO_STRING(separator); | |
| 595 | |
| 596 if (limit === 0) return []; | |
| 597 | |
| 598 // ECMA-262 says that if separator is undefined, the result should | |
| 599 // be an array of size 1 containing the entire string. | |
| 600 if (IS_UNDEFINED(separator)) return [subject]; | |
| 601 | |
| 602 var separator_length = separator_string.length; | |
| 603 | |
| 604 // If the separator string is empty then return the elements in the subject. | |
| 605 if (separator_length === 0) return %StringToArray(subject, limit); | |
| 606 | |
| 607 var result = %StringSplit(subject, separator_string, limit); | |
| 608 | |
| 609 return result; | |
| 610 } | |
| 611 | |
| 612 if (limit === 0) return []; | |
| 613 | |
| 614 // Separator is a regular expression. | |
| 615 return StringSplitOnRegExp(subject, separator, limit, length); | |
| 616 } | |
| 617 | |
| 618 | |
| 619 function StringSplitOnRegExp(subject, separator, limit, length) { | |
| 620 if (length === 0) { | |
| 621 if (RegExpExec(separator, subject, 0, 0) != null) { | |
| 622 return []; | |
| 623 } | |
| 624 return [subject]; | |
| 625 } | |
| 626 | |
| 627 var currentIndex = 0; | |
| 628 var startIndex = 0; | |
| 629 var startMatch = 0; | |
| 630 var result = new InternalArray(); | |
| 631 | |
| 632 outer_loop: | |
| 633 while (true) { | |
| 634 | |
| 635 if (startIndex === length) { | |
| 636 result[result.length] = %_SubString(subject, currentIndex, length); | |
| 637 break; | |
| 638 } | |
| 639 | |
| 640 var matchInfo = RegExpExec(separator, subject, startIndex); | |
| 641 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { | |
| 642 result[result.length] = %_SubString(subject, currentIndex, length); | |
| 643 break; | |
| 644 } | |
| 645 var endIndex = matchInfo[CAPTURE1]; | |
| 646 | |
| 647 // We ignore a zero-length match at the currentIndex. | |
| 648 if (startIndex === endIndex && endIndex === currentIndex) { | |
| 649 startIndex++; | |
| 650 continue; | |
| 651 } | |
| 652 | |
| 653 result[result.length] = %_SubString(subject, currentIndex, startMatch); | |
| 654 | |
| 655 if (result.length === limit) break; | |
| 656 | |
| 657 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE; | |
| 658 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) { | |
| 659 var start = matchInfo[i++]; | |
| 660 var end = matchInfo[i++]; | |
| 661 if (end != -1) { | |
| 662 result[result.length] = %_SubString(subject, start, end); | |
| 663 } else { | |
| 664 result[result.length] = UNDEFINED; | |
| 665 } | |
| 666 if (result.length === limit) break outer_loop; | |
| 667 } | |
| 668 | |
| 669 startIndex = currentIndex = endIndex; | |
| 670 } | |
| 671 var array_result = []; | |
| 672 %MoveArrayContents(result, array_result); | |
| 673 return array_result; | |
| 674 } | |
| 675 | |
| 676 | |
| 677 // ECMA-262 section 15.5.4.15 | |
| 678 function StringSubstring(start, end) { | |
| 679 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString"); | |
| 680 | |
| 681 var s = TO_STRING(this); | |
| 682 var s_len = s.length; | |
| 683 | |
| 684 var start_i = TO_INTEGER(start); | |
| 685 if (start_i < 0) { | |
| 686 start_i = 0; | |
| 687 } else if (start_i > s_len) { | |
| 688 start_i = s_len; | |
| 689 } | |
| 690 | |
| 691 var end_i = s_len; | |
| 692 if (!IS_UNDEFINED(end)) { | |
| 693 end_i = TO_INTEGER(end); | |
| 694 if (end_i > s_len) { | |
| 695 end_i = s_len; | |
| 696 } else { | |
| 697 if (end_i < 0) end_i = 0; | |
| 698 if (start_i > end_i) { | |
| 699 var tmp = end_i; | |
| 700 end_i = start_i; | |
| 701 start_i = tmp; | |
| 702 } | |
| 703 } | |
| 704 } | |
| 705 | |
| 706 return %_SubString(s, start_i, end_i); | |
| 707 } | |
| 708 | |
| 709 | |
| 710 // ES6 draft, revision 26 (2014-07-18), section B.2.3.1 | |
| 711 function StringSubstr(start, n) { | |
| 712 CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr"); | |
| 713 | |
| 714 var s = TO_STRING(this); | |
| 715 var len; | |
| 716 | |
| 717 // Correct n: If not given, set to string length; if explicitly | |
| 718 // set to undefined, zero, or negative, returns empty string. | |
| 719 if (IS_UNDEFINED(n)) { | |
| 720 len = s.length; | |
| 721 } else { | |
| 722 len = TO_INTEGER(n); | |
| 723 if (len <= 0) return ''; | |
| 724 } | |
| 725 | |
| 726 // Correct start: If not given (or undefined), set to zero; otherwise | |
| 727 // convert to integer and handle negative case. | |
| 728 if (IS_UNDEFINED(start)) { | |
| 729 start = 0; | |
| 730 } else { | |
| 731 start = TO_INTEGER(start); | |
| 732 // If positive, and greater than or equal to the string length, | |
| 733 // return empty string. | |
| 734 if (start >= s.length) return ''; | |
| 735 // If negative and absolute value is larger than the string length, | |
| 736 // use zero. | |
| 737 if (start < 0) { | |
| 738 start += s.length; | |
| 739 if (start < 0) start = 0; | |
| 740 } | |
| 741 } | |
| 742 | |
| 743 var end = start + len; | |
| 744 if (end > s.length) end = s.length; | |
| 745 | |
| 746 return %_SubString(s, start, end); | |
| 747 } | |
| 748 | |
| 749 | |
| 750 // ECMA-262, 15.5.4.16 | |
| 751 function StringToLowerCaseJS() { | |
| 752 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase"); | |
| 753 | |
| 754 return %StringToLowerCase(TO_STRING(this)); | |
| 755 } | |
| 756 | |
| 757 | |
| 758 // ECMA-262, 15.5.4.17 | |
| 759 function StringToLocaleLowerCase() { | |
| 760 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase"); | |
| 761 | |
| 762 return %StringToLowerCase(TO_STRING(this)); | |
| 763 } | |
| 764 | |
| 765 | |
| 766 // ECMA-262, 15.5.4.18 | |
| 767 function StringToUpperCaseJS() { | |
| 768 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase"); | |
| 769 | |
| 770 return %StringToUpperCase(TO_STRING(this)); | |
| 771 } | |
| 772 | |
| 773 | |
| 774 // ECMA-262, 15.5.4.19 | |
| 775 function StringToLocaleUpperCase() { | |
| 776 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase"); | |
| 777 | |
| 778 return %StringToUpperCase(TO_STRING(this)); | |
| 779 } | |
| 780 | |
| 781 // ES5, 15.5.4.20 | |
| 782 function StringTrimJS() { | |
| 783 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim"); | |
| 784 | |
| 785 return %StringTrim(TO_STRING(this), true, true); | |
| 786 } | |
| 787 | |
| 788 function StringTrimLeft() { | |
| 789 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft"); | |
| 790 | |
| 791 return %StringTrim(TO_STRING(this), true, false); | |
| 792 } | |
| 793 | |
| 794 function StringTrimRight() { | |
| 795 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight"); | |
| 796 | |
| 797 return %StringTrim(TO_STRING(this), false, true); | |
| 798 } | |
| 799 | |
| 800 | |
| 801 // ECMA-262, section 15.5.3.2 | |
| 802 function StringFromCharCode(code) { | |
| 803 var n = %_ArgumentsLength(); | |
| 804 if (n == 1) { | |
| 805 if (!%_IsSmi(code)) code = TO_NUMBER(code); | |
| 806 return %_StringCharFromCode(code & 0xffff); | |
| 807 } | |
| 808 | |
| 809 var one_byte = %NewString(n, NEW_ONE_BYTE_STRING); | |
| 810 var i; | |
| 811 for (i = 0; i < n; i++) { | |
| 812 var code = %_Arguments(i); | |
| 813 if (!%_IsSmi(code)) code = TO_NUMBER(code) & 0xffff; | |
| 814 if (code < 0) code = code & 0xffff; | |
| 815 if (code > 0xff) break; | |
| 816 %_OneByteSeqStringSetChar(i, code, one_byte); | |
| 817 } | |
| 818 if (i == n) return one_byte; | |
| 819 one_byte = %TruncateString(one_byte, i); | |
| 820 | |
| 821 var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING); | |
| 822 for (var j = 0; i < n; i++, j++) { | |
| 823 var code = %_Arguments(i); | |
| 824 if (!%_IsSmi(code)) code = TO_NUMBER(code) & 0xffff; | |
| 825 %_TwoByteSeqStringSetChar(j, code, two_byte); | |
| 826 } | |
| 827 return one_byte + two_byte; | |
| 828 } | |
| 829 | |
| 830 | |
| 831 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1 | |
| 832 function HtmlEscape(str) { | |
| 833 return %_CallFunction(TO_STRING(str), /"/g, """, StringReplace); | |
| 834 } | |
| 835 | |
| 836 | |
| 837 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2 | |
| 838 function StringAnchor(name) { | |
| 839 CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor"); | |
| 840 return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) + | |
| 841 "</a>"; | |
| 842 } | |
| 843 | |
| 844 | |
| 845 // ES6 draft, revision 26 (2014-07-18), section B.2.3.3 | |
| 846 function StringBig() { | |
| 847 CHECK_OBJECT_COERCIBLE(this, "String.prototype.big"); | |
| 848 return "<big>" + TO_STRING(this) + "</big>"; | |
| 849 } | |
| 850 | |
| 851 | |
| 852 // ES6 draft, revision 26 (2014-07-18), section B.2.3.4 | |
| 853 function StringBlink() { | |
| 854 CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink"); | |
| 855 return "<blink>" + TO_STRING(this) + "</blink>"; | |
| 856 } | |
| 857 | |
| 858 | |
| 859 // ES6 draft, revision 26 (2014-07-18), section B.2.3.5 | |
| 860 function StringBold() { | |
| 861 CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold"); | |
| 862 return "<b>" + TO_STRING(this) + "</b>"; | |
| 863 } | |
| 864 | |
| 865 | |
| 866 // ES6 draft, revision 26 (2014-07-18), section B.2.3.6 | |
| 867 function StringFixed() { | |
| 868 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed"); | |
| 869 return "<tt>" + TO_STRING(this) + "</tt>"; | |
| 870 } | |
| 871 | |
| 872 | |
| 873 // ES6 draft, revision 26 (2014-07-18), section B.2.3.7 | |
| 874 function StringFontcolor(color) { | |
| 875 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor"); | |
| 876 return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) + | |
| 877 "</font>"; | |
| 878 } | |
| 879 | |
| 880 | |
| 881 // ES6 draft, revision 26 (2014-07-18), section B.2.3.8 | |
| 882 function StringFontsize(size) { | |
| 883 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize"); | |
| 884 return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) + | |
| 885 "</font>"; | |
| 886 } | |
| 887 | |
| 888 | |
| 889 // ES6 draft, revision 26 (2014-07-18), section B.2.3.9 | |
| 890 function StringItalics() { | |
| 891 CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics"); | |
| 892 return "<i>" + TO_STRING(this) + "</i>"; | |
| 893 } | |
| 894 | |
| 895 | |
| 896 // ES6 draft, revision 26 (2014-07-18), section B.2.3.10 | |
| 897 function StringLink(s) { | |
| 898 CHECK_OBJECT_COERCIBLE(this, "String.prototype.link"); | |
| 899 return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>"; | |
| 900 } | |
| 901 | |
| 902 | |
| 903 // ES6 draft, revision 26 (2014-07-18), section B.2.3.11 | |
| 904 function StringSmall() { | |
| 905 CHECK_OBJECT_COERCIBLE(this, "String.prototype.small"); | |
| 906 return "<small>" + TO_STRING(this) + "</small>"; | |
| 907 } | |
| 908 | |
| 909 | |
| 910 // ES6 draft, revision 26 (2014-07-18), section B.2.3.12 | |
| 911 function StringStrike() { | |
| 912 CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike"); | |
| 913 return "<strike>" + TO_STRING(this) + "</strike>"; | |
| 914 } | |
| 915 | |
| 916 | |
| 917 // ES6 draft, revision 26 (2014-07-18), section B.2.3.13 | |
| 918 function StringSub() { | |
| 919 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub"); | |
| 920 return "<sub>" + TO_STRING(this) + "</sub>"; | |
| 921 } | |
| 922 | |
| 923 | |
| 924 // ES6 draft, revision 26 (2014-07-18), section B.2.3.14 | |
| 925 function StringSup() { | |
| 926 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup"); | |
| 927 return "<sup>" + TO_STRING(this) + "</sup>"; | |
| 928 } | |
| 929 | |
| 930 // ES6 draft 01-20-14, section 21.1.3.13 | |
| 931 function StringRepeat(count) { | |
| 932 CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat"); | |
| 933 | |
| 934 var s = TO_STRING(this); | |
| 935 var n = TO_INTEGER(count); | |
| 936 // The maximum string length is stored in a smi, so a longer repeat | |
| 937 // must result in a range error. | |
| 938 if (n < 0 || n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue); | |
| 939 | |
| 940 var r = ""; | |
| 941 while (true) { | |
| 942 if (n & 1) r += s; | |
| 943 n >>= 1; | |
| 944 if (n === 0) return r; | |
| 945 s += s; | |
| 946 } | |
| 947 } | |
| 948 | |
| 949 | |
| 950 // ES6 draft 04-05-14, section 21.1.3.18 | |
| 951 function StringStartsWith(searchString /* position */) { // length == 1 | |
| 952 CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith"); | |
| 953 | |
| 954 var s = TO_STRING(this); | |
| 955 | |
| 956 if (IS_REGEXP(searchString)) { | |
| 957 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith"); | |
| 958 } | |
| 959 | |
| 960 var ss = TO_STRING(searchString); | |
| 961 var pos = 0; | |
| 962 if (%_ArgumentsLength() > 1) { | |
| 963 var arg = %_Arguments(1); // position | |
| 964 if (!IS_UNDEFINED(arg)) { | |
| 965 pos = TO_INTEGER(arg); | |
| 966 } | |
| 967 } | |
| 968 | |
| 969 var s_len = s.length; | |
| 970 if (pos < 0) pos = 0; | |
| 971 if (pos > s_len) pos = s_len; | |
| 972 var ss_len = ss.length; | |
| 973 | |
| 974 if (ss_len + pos > s_len) { | |
| 975 return false; | |
| 976 } | |
| 977 | |
| 978 for (var i = 0; i < ss_len; i++) { | |
| 979 if (%_StringCharCodeAt(s, pos + i) !== %_StringCharCodeAt(ss, i)) { | |
| 980 return false; | |
| 981 } | |
| 982 } | |
| 983 | |
| 984 return true; | |
| 985 } | |
| 986 | |
| 987 | |
| 988 // ES6 draft 04-05-14, section 21.1.3.7 | |
| 989 function StringEndsWith(searchString /* position */) { // length == 1 | |
| 990 CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith"); | |
| 991 | |
| 992 var s = TO_STRING(this); | |
| 993 | |
| 994 if (IS_REGEXP(searchString)) { | |
| 995 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith"); | |
| 996 } | |
| 997 | |
| 998 var ss = TO_STRING(searchString); | |
| 999 var s_len = s.length; | |
| 1000 var pos = s_len; | |
| 1001 if (%_ArgumentsLength() > 1) { | |
| 1002 var arg = %_Arguments(1); // position | |
| 1003 if (!IS_UNDEFINED(arg)) { | |
| 1004 pos = TO_INTEGER(arg); | |
| 1005 } | |
| 1006 } | |
| 1007 | |
| 1008 if (pos < 0) pos = 0; | |
| 1009 if (pos > s_len) pos = s_len; | |
| 1010 var ss_len = ss.length; | |
| 1011 pos = pos - ss_len; | |
| 1012 | |
| 1013 if (pos < 0) { | |
| 1014 return false; | |
| 1015 } | |
| 1016 | |
| 1017 for (var i = 0; i < ss_len; i++) { | |
| 1018 if (%_StringCharCodeAt(s, pos + i) !== %_StringCharCodeAt(ss, i)) { | |
| 1019 return false; | |
| 1020 } | |
| 1021 } | |
| 1022 | |
| 1023 return true; | |
| 1024 } | |
| 1025 | |
| 1026 | |
| 1027 // ES6 draft 04-05-14, section 21.1.3.6 | |
| 1028 function StringIncludes(searchString /* position */) { // length == 1 | |
| 1029 CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes"); | |
| 1030 | |
| 1031 var string = TO_STRING(this); | |
| 1032 | |
| 1033 if (IS_REGEXP(searchString)) { | |
| 1034 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes"); | |
| 1035 } | |
| 1036 | |
| 1037 searchString = TO_STRING(searchString); | |
| 1038 var pos = 0; | |
| 1039 if (%_ArgumentsLength() > 1) { | |
| 1040 pos = %_Arguments(1); // position | |
| 1041 pos = TO_INTEGER(pos); | |
| 1042 } | |
| 1043 | |
| 1044 var stringLength = string.length; | |
| 1045 if (pos < 0) pos = 0; | |
| 1046 if (pos > stringLength) pos = stringLength; | |
| 1047 var searchStringLength = searchString.length; | |
| 1048 | |
| 1049 if (searchStringLength + pos > stringLength) { | |
| 1050 return false; | |
| 1051 } | |
| 1052 | |
| 1053 return %StringIndexOf(string, searchString, pos) !== -1; | |
| 1054 } | |
| 1055 | |
| 1056 | |
| 1057 // ES6 Draft 05-22-2014, section 21.1.3.3 | |
| 1058 function StringCodePointAt(pos) { | |
| 1059 CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt"); | |
| 1060 | |
| 1061 var string = TO_STRING(this); | |
| 1062 var size = string.length; | |
| 1063 pos = TO_INTEGER(pos); | |
| 1064 if (pos < 0 || pos >= size) { | |
| 1065 return UNDEFINED; | |
| 1066 } | |
| 1067 var first = %_StringCharCodeAt(string, pos); | |
| 1068 if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) { | |
| 1069 return first; | |
| 1070 } | |
| 1071 var second = %_StringCharCodeAt(string, pos + 1); | |
| 1072 if (second < 0xDC00 || second > 0xDFFF) { | |
| 1073 return first; | |
| 1074 } | |
| 1075 return (first - 0xD800) * 0x400 + second + 0x2400; | |
| 1076 } | |
| 1077 | |
| 1078 | |
| 1079 // ES6 Draft 05-22-2014, section 21.1.2.2 | |
| 1080 function StringFromCodePoint(_) { // length = 1 | |
| 1081 var code; | |
| 1082 var length = %_ArgumentsLength(); | |
| 1083 var index; | |
| 1084 var result = ""; | |
| 1085 for (index = 0; index < length; index++) { | |
| 1086 code = %_Arguments(index); | |
| 1087 if (!%_IsSmi(code)) { | |
| 1088 code = TO_NUMBER(code); | |
| 1089 } | |
| 1090 if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) { | |
| 1091 throw MakeRangeError(kInvalidCodePoint, code); | |
| 1092 } | |
| 1093 if (code <= 0xFFFF) { | |
| 1094 result += %_StringCharFromCode(code); | |
| 1095 } else { | |
| 1096 code -= 0x10000; | |
| 1097 result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800); | |
| 1098 result += %_StringCharFromCode(code & 0x3FF | 0xDC00); | |
| 1099 } | |
| 1100 } | |
| 1101 return result; | |
| 1102 } | |
| 1103 | |
| 1104 | |
| 1105 // ------------------------------------------------------------------- | |
| 1106 // String methods related to templates | |
| 1107 | |
| 1108 // ES6 Draft 03-17-2015, section 21.1.2.4 | |
| 1109 function StringRaw(callSite) { | |
| 1110 // TODO(caitp): Use rest parameters when implemented | |
| 1111 var numberOfSubstitutions = %_ArgumentsLength(); | |
| 1112 var cooked = TO_OBJECT(callSite); | |
| 1113 var raw = TO_OBJECT(cooked.raw); | |
| 1114 var literalSegments = TO_LENGTH(raw.length); | |
| 1115 if (literalSegments <= 0) return ""; | |
| 1116 | |
| 1117 var result = TO_STRING(raw[0]); | |
| 1118 | |
| 1119 for (var i = 1; i < literalSegments; ++i) { | |
| 1120 if (i < numberOfSubstitutions) { | |
| 1121 result += TO_STRING(%_Arguments(i)); | |
| 1122 } | |
| 1123 result += TO_STRING(raw[i]); | |
| 1124 } | |
| 1125 | |
| 1126 return result; | |
| 1127 } | |
| 1128 | |
| 1129 // ------------------------------------------------------------------- | |
| 1130 | |
| 1131 // Set the String function and constructor. | |
| 1132 %FunctionSetPrototype(GlobalString, new GlobalString()); | |
| 1133 | |
| 1134 // Set up the constructor property on the String prototype object. | |
| 1135 %AddNamedProperty( | |
| 1136 GlobalString.prototype, "constructor", GlobalString, DONT_ENUM); | |
| 1137 | |
| 1138 // Set up the non-enumerable functions on the String object. | |
| 1139 utils.InstallFunctions(GlobalString, DONT_ENUM, [ | |
| 1140 "fromCharCode", StringFromCharCode, | |
| 1141 "fromCodePoint", StringFromCodePoint, | |
| 1142 "raw", StringRaw | |
| 1143 ]); | |
| 1144 | |
| 1145 // Set up the non-enumerable functions on the String prototype object. | |
| 1146 utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ | |
| 1147 "valueOf", StringValueOf, | |
| 1148 "toString", StringToString, | |
| 1149 "charAt", StringCharAtJS, | |
| 1150 "charCodeAt", StringCharCodeAtJS, | |
| 1151 "codePointAt", StringCodePointAt, | |
| 1152 "concat", StringConcat, | |
| 1153 "endsWith", StringEndsWith, | |
| 1154 "includes", StringIncludes, | |
| 1155 "indexOf", StringIndexOfJS, | |
| 1156 "lastIndexOf", StringLastIndexOfJS, | |
| 1157 "localeCompare", StringLocaleCompareJS, | |
| 1158 "match", StringMatchJS, | |
| 1159 "normalize", StringNormalizeJS, | |
| 1160 "repeat", StringRepeat, | |
| 1161 "replace", StringReplace, | |
| 1162 "search", StringSearch, | |
| 1163 "slice", StringSlice, | |
| 1164 "split", StringSplitJS, | |
| 1165 "substring", StringSubstring, | |
| 1166 "substr", StringSubstr, | |
| 1167 "startsWith", StringStartsWith, | |
| 1168 "toLowerCase", StringToLowerCaseJS, | |
| 1169 "toLocaleLowerCase", StringToLocaleLowerCase, | |
| 1170 "toUpperCase", StringToUpperCaseJS, | |
| 1171 "toLocaleUpperCase", StringToLocaleUpperCase, | |
| 1172 "trim", StringTrimJS, | |
| 1173 "trimLeft", StringTrimLeft, | |
| 1174 "trimRight", StringTrimRight, | |
| 1175 | |
| 1176 "link", StringLink, | |
| 1177 "anchor", StringAnchor, | |
| 1178 "fontcolor", StringFontcolor, | |
| 1179 "fontsize", StringFontsize, | |
| 1180 "big", StringBig, | |
| 1181 "blink", StringBlink, | |
| 1182 "bold", StringBold, | |
| 1183 "fixed", StringFixed, | |
| 1184 "italics", StringItalics, | |
| 1185 "small", StringSmall, | |
| 1186 "strike", StringStrike, | |
| 1187 "sub", StringSub, | |
| 1188 "sup", StringSup | |
| 1189 ]); | |
| 1190 | |
| 1191 // ------------------------------------------------------------------- | |
| 1192 // Exports | |
| 1193 | |
| 1194 utils.Export(function(to) { | |
| 1195 to.StringCharAt = StringCharAtJS; | |
| 1196 to.StringIndexOf = StringIndexOfJS; | |
| 1197 to.StringLastIndexOf = StringLastIndexOfJS; | |
| 1198 to.StringMatch = StringMatchJS; | |
| 1199 to.StringReplace = StringReplace; | |
| 1200 to.StringSlice = StringSlice; | |
| 1201 to.StringSplit = StringSplitJS; | |
| 1202 to.StringSubstr = StringSubstr; | |
| 1203 to.StringSubstring = StringSubstring; | |
| 1204 }); | |
| 1205 | |
| 1206 }) | |
| OLD | NEW |