Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 222 | 222 |
| 223 | 223 |
| 224 // ECMA-262, section 15.5.4.11 | 224 // ECMA-262, section 15.5.4.11 |
| 225 function StringReplace(search, replace) { | 225 function StringReplace(search, replace) { |
| 226 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 226 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
| 227 throw MakeTypeError("called_on_null_or_undefined", | 227 throw MakeTypeError("called_on_null_or_undefined", |
| 228 ["String.prototype.replace"]); | 228 ["String.prototype.replace"]); |
| 229 } | 229 } |
| 230 var subject = TO_STRING_INLINE(this); | 230 var subject = TO_STRING_INLINE(this); |
| 231 | 231 |
| 232 // Delegate to one of the regular expression variants if necessary. | 232 // Decision tree for dispatch |
| 233 // .. regexp search | |
| 234 // .... string replace | |
| 235 // ...... non-global search | |
| 236 // ........ empty string replace | |
| 237 // ........ non-empty string replace (with $-expansion) | |
| 238 // ...... global search | |
| 239 // ........ no need to circumvent last match info override | |
| 240 // ........ need to circument last match info override | |
| 241 // .... function replace | |
| 242 // ...... global search | |
| 243 // ...... non-global search | |
| 244 // .. string search | |
| 245 // .... special case that replaces with one single character | |
| 246 // ...... function replace | |
| 247 // ...... string replace (with $-expansion) | |
| 248 | |
| 233 if (IS_REGEXP(search)) { | 249 if (IS_REGEXP(search)) { |
| 234 // Emulate RegExp.prototype.exec's side effect in step 5, even though | 250 // Emulate RegExp.prototype.exec's side effect in step 5, even if |
| 235 // value is discarded. | 251 // value is discarded. |
| 236 ToInteger(search.lastIndex); | 252 ToInteger(search.lastIndex); |
| 237 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); | 253 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); |
| 238 if (IS_SPEC_FUNCTION(replace)) { | 254 |
| 239 if (search.global) { | 255 if (!IS_SPEC_FUNCTION(replace)) { |
| 240 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | 256 if (!search.global) { |
| 241 } else { | 257 // Non-global regexp search, string replace. |
| 242 return StringReplaceNonGlobalRegExpWithFunction(subject, | 258 var match = DoRegExpExec(search, subject, 0); |
| 243 search, | 259 if (match == null) { |
| 244 replace); | 260 search.lastIndex = 0 |
| 245 } | |
| 246 } else { | |
| 247 if (lastMatchInfoOverride == null) { | |
| 248 var answer = %StringReplaceRegExpWithString(subject, | |
| 249 search, | |
| 250 TO_STRING_INLINE(replace), | |
| 251 lastMatchInfo); | |
| 252 if (IS_UNDEFINED(answer)) { // No match. Return subject string. | |
| 253 search.lastIndex = 0; | |
| 254 return subject; | 261 return subject; |
| 255 } | 262 } |
| 256 if (search.global) search.lastIndex = 0; | 263 if (replace.length == 0) { |
| 257 return answer; | 264 return SubString(subject, 0, match[CAPTURE0]) + |
| 265 SubString(subject, match[CAPTURE1], subject.length) | |
| 266 } | |
| 267 return ExpandReplacement(TO_STRING_INLINE(replace), | |
| 268 subject, | |
| 269 lastMatchInfo, | |
| 270 SubString(subject, 0, match[CAPTURE0])) + | |
| 271 SubString(subject, match[CAPTURE1], subject.length); | |
|
ulan
2013/02/05 09:30:58
I would reformat by putting the first argument of
Yang
2013/02/05 10:47:32
Not entirely sure what you mean here.
| |
| 272 } | |
| 273 | |
| 274 // Global regexp search, string replace. | |
| 275 search.lastIndex = 0; | |
| 276 if (lastMatchInfoOverride == null) { | |
| 277 return %StringReplaceGlobalRegExpWithString( | |
| 278 subject, search, replace, lastMatchInfo); | |
| 258 } else { | 279 } else { |
| 259 // We use this hack to detect whether StringReplaceRegExpWithString | 280 // We use this hack to detect whether StringReplaceRegExpWithString |
| 260 // found at least one hit. In that case we need to remove any | 281 // found at least one hit. In that case we need to remove any |
| 261 // override. | 282 // override. |
| 262 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX]; | 283 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX]; |
| 263 lastMatchInfo[LAST_SUBJECT_INDEX] = 0; | 284 lastMatchInfo[LAST_SUBJECT_INDEX] = 0; |
| 264 var answer = %StringReplaceRegExpWithString(subject, | 285 var answer = %StringReplaceGlobalRegExpWithString( |
| 265 search, | 286 subject, search, replace, lastMatchInfo); |
| 266 TO_STRING_INLINE(replace), | |
| 267 lastMatchInfo); | |
| 268 if (IS_UNDEFINED(answer)) { // No match. Return subject string. | |
| 269 search.lastIndex = 0; | |
| 270 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; | |
| 271 return subject; | |
| 272 } | |
| 273 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) { | 287 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) { |
| 274 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; | 288 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; |
| 275 } else { | 289 } else { |
| 276 lastMatchInfoOverride = null; | 290 lastMatchInfoOverride = null; |
| 277 } | 291 } |
| 278 if (search.global) search.lastIndex = 0; | |
| 279 return answer; | 292 return answer; |
| 280 } | 293 } |
| 281 } | 294 } |
| 295 | |
| 296 if (search.global) { | |
| 297 // Global regexp search, function replace. | |
| 298 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | |
| 299 } | |
| 300 // Non-global regexp search, function replace. | |
| 301 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); | |
| 282 } | 302 } |
| 283 | 303 |
| 284 // Convert the search argument to a string and search for it. | |
| 285 search = TO_STRING_INLINE(search); | |
|
ulan
2013/02/05 09:30:58
Looks like it was remove accidentally.
| |
| 286 if (search.length == 1 && | 304 if (search.length == 1 && |
| 287 subject.length > 0xFF && | 305 subject.length > 0xFF && |
| 288 IS_STRING(replace) && | 306 IS_STRING(replace) && |
| 289 %StringIndexOf(replace, '$', 0) < 0) { | 307 %StringIndexOf(replace, '$', 0) < 0) { |
| 290 // Searching by traversing a cons string tree and replace with cons of | 308 // Searching by traversing a cons string tree and replace with cons of |
| 291 // slices works only when the replaced string is a single character, being | 309 // slices works only when the replaced string is a single character, being |
| 292 // replaced by a simple string and only pays off for long strings. | 310 // replaced by a simple string and only pays off for long strings. |
| 293 return %StringReplaceOneCharWithString(subject, search, replace); | 311 return %StringReplaceOneCharWithString(subject, search, replace); |
| 294 } | 312 } |
| 295 var start = %StringIndexOf(subject, search, 0); | 313 var start = %StringIndexOf(subject, search, 0); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 result += '$'; | 353 result += '$'; |
| 336 } else if (peek == 38) { // $& - match | 354 } else if (peek == 38) { // $& - match |
| 337 ++position; | 355 ++position; |
| 338 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); | 356 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); |
| 339 } else if (peek == 96) { // $` - prefix | 357 } else if (peek == 96) { // $` - prefix |
| 340 ++position; | 358 ++position; |
| 341 result += SubString(subject, 0, matchInfo[CAPTURE0]); | 359 result += SubString(subject, 0, matchInfo[CAPTURE0]); |
| 342 } else if (peek == 39) { // $' - suffix | 360 } else if (peek == 39) { // $' - suffix |
| 343 ++position; | 361 ++position; |
| 344 result += SubString(subject, matchInfo[CAPTURE1], subject.length); | 362 result += SubString(subject, matchInfo[CAPTURE1], subject.length); |
| 363 } else if (peek >= 48 && peek <= 57) { // between '0' and '9' | |
| 364 // Convert to digit by subtracting '0'. | |
| 365 var scaled_index; | |
| 366 var advance; | |
| 367 if (peek == 48) { // $0... | |
| 368 if (position + 1 == string.length || // ends with '$0', invalid. | |
| 369 %_StringCharCodeAt(string, position + 1) != 49) { // not '$01' | |
|
ulan
2013/02/05 09:30:58
As discussed offline, we should check $02, ... $nn
| |
| 370 scaled_index = 1 << 18; | |
| 371 } else { | |
| 372 scaled_index = 2; // 1 * 2 | |
| 373 advance = 2; | |
| 374 } | |
| 375 } else if (peek == 49 && | |
| 376 position + 1 < string.length && | |
| 377 %_StringCharCodeAt(string, position + 1) == 48) { // $10 | |
| 378 scaled_index = 20; // 10 * 2 | |
| 379 advance = 2; | |
| 380 } else { | |
| 381 scaled_index = (peek - 48) << 1; // index * 2 | |
| 382 advance = 1; | |
| 383 } | |
| 384 | |
| 385 if (NUMBER_OF_CAPTURES(matchInfo) > scaled_index) { | |
| 386 var start = matchInfo[CAPTURE(scaled_index)]; | |
| 387 if (start >= 0) { | |
| 388 result += | |
| 389 SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]); | |
| 390 } | |
| 391 position += advance; | |
| 392 } else { | |
| 393 result += '$'; | |
| 394 } | |
| 345 } else { | 395 } else { |
| 346 result += '$'; | 396 result += '$'; |
| 347 } | 397 } |
| 348 } else { | 398 } else { |
| 349 result += '$'; | 399 result += '$'; |
| 350 } | 400 } |
| 351 | 401 |
| 352 // Go the the next $ in the string. | 402 // Go the the next $ in the string. |
| 353 next = %StringIndexOf(string, '$', position); | 403 next = %StringIndexOf(string, '$', position); |
| 354 | 404 |
| (...skipping 660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1015 "fixed", StringFixed, | 1065 "fixed", StringFixed, |
| 1016 "italics", StringItalics, | 1066 "italics", StringItalics, |
| 1017 "small", StringSmall, | 1067 "small", StringSmall, |
| 1018 "strike", StringStrike, | 1068 "strike", StringStrike, |
| 1019 "sub", StringSub, | 1069 "sub", StringSub, |
| 1020 "sup", StringSup | 1070 "sup", StringSup |
| 1021 )); | 1071 )); |
| 1022 } | 1072 } |
| 1023 | 1073 |
| 1024 SetUpString(); | 1074 SetUpString(); |
| OLD | NEW |