1 /*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "config.h"
22 #include "StringPrototype.h"
23
24 #include "JSArray.h"
25 #include "JSFunction.h"
26 #include "ObjectPrototype.h"
27 #include "PropertyNameArray.h"
28 #include "RegExpConstructor.h"
29 #include "RegExpObject.h"
30 #include <wtf/ASCIICType.h>
31 #include <wtf/MathExtras.h>
32 #include <wtf/unicode/Collator.h>
33
34 using namespace WTF;
35
36 namespace JSC {
37
38 ASSERT_CLASS_FITS_IN_CELL(StringPrototype);
39
40 static JSValuePtr stringProtoFuncToString(ExecState*, JSObject*, JSValuePtr, const ArgList&);
41 static JSValuePtr stringProtoFuncCharAt(ExecState*, JSObject*, JSValuePtr, const ArgList&);
42 static JSValuePtr stringProtoFuncCharCodeAt(ExecState*, JSObject*, JSValuePtr, const ArgList&);
43 static JSValuePtr stringProtoFuncConcat(ExecState*, JSObject*, JSValuePtr, const ArgList&);
44 static JSValuePtr stringProtoFuncIndexOf(ExecState*, JSObject*, JSValuePtr, const ArgList&);
45 static JSValuePtr stringProtoFuncLastIndexOf(ExecState*, JSObject*, JSValuePtr, const ArgList&);
46 static JSValuePtr stringProtoFuncMatch(ExecState*, JSObject*, JSValuePtr, const ArgList&);
47 static JSValuePtr stringProtoFuncReplace(ExecState*, JSObject*, JSValuePtr, const ArgList&);
48 static JSValuePtr stringProtoFuncSearch(ExecState*, JSObject*, JSValuePtr, const ArgList&);
49 static JSValuePtr stringProtoFuncSlice(ExecState*, JSObject*, JSValuePtr, const ArgList&);
50 static JSValuePtr stringProtoFuncSplit(ExecState*, JSObject*, JSValuePtr, const ArgList&);
51 static JSValuePtr stringProtoFuncSubstr(ExecState*, JSObject*, JSValuePtr, const ArgList&);
52 static JSValuePtr stringProtoFuncSubstring(ExecState*, JSObject*, JSValuePtr, const ArgList&);
53 static JSValuePtr stringProtoFuncToLowerCase(ExecState*, JSObject*, JSValuePtr, const ArgList&);
54 static JSValuePtr stringProtoFuncToUpperCase(ExecState*, JSObject*, JSValuePtr, const ArgList&);
55 static JSValuePtr stringProtoFuncLocaleCompare(ExecState*, JSObject*, JSValuePtr, const ArgList&);
56
57 static JSValuePtr stringProtoFuncBig(ExecState*, JSObject*, JSValuePtr, const ArgList&);
58 static JSValuePtr stringProtoFuncSmall(ExecState*, JSObject*, JSValuePtr, const ArgList&);
59 static JSValuePtr stringProtoFuncBlink(ExecState*, JSObject*, JSValuePtr, const ArgList&);
60 static JSValuePtr stringProtoFuncBold(ExecState*, JSObject*, JSValuePtr, const ArgList&);
61 static JSValuePtr stringProtoFuncFixed(ExecState*, JSObject*, JSValuePtr, const ArgList&);
62 static JSValuePtr stringProtoFuncItalics(ExecState*, JSObject*, JSValuePtr, const ArgList&);
63 static JSValuePtr stringProtoFuncStrike(ExecState*, JSObject*, JSValuePtr, const ArgList&);
64 static JSValuePtr stringProtoFuncSub(ExecState*, JSObject*, JSValuePtr, const ArgList&);
65 static JSValuePtr stringProtoFuncSup(ExecState*, JSObject*, JSValuePtr, const ArgList&);
66 static JSValuePtr stringProtoFuncFontcolor(ExecState*, JSObject*, JSValuePtr, const ArgList&);
67 static JSValuePtr stringProtoFuncFontsize(ExecState*, JSObject*, JSValuePtr, const ArgList&);
68 static JSValuePtr stringProtoFuncAnchor(ExecState*, JSObject*, JSValuePtr, const ArgList&);
69 static JSValuePtr stringProtoFuncLink(ExecState*, JSObject*, JSValuePtr, const ArgList&);
70
71 }
72
73 #include "StringPrototype.lut.h"
74
75 namespace JSC {
76
77 const ClassInfo StringPrototype::info = { "String", &StringObject::info, 0, ExecState::stringTable };
78
79 /* Source for StringPrototype.lut.h
80 @begin stringTable 26
81 toString stringProtoFuncToString DontEnum|Function 0
82 valueOf stringProtoFuncToString DontEnum|Function 0
83 charAt stringProtoFuncCharAt DontEnum|Function 1
84 charCodeAt stringProtoFuncCharCodeAt DontEnum|Function 1
85 concat stringProtoFuncConcat DontEnum|Function 1
86 indexOf stringProtoFuncIndexOf DontEnum|Function 1
87 lastIndexOf stringProtoFuncLastIndexOf DontEnum|Function 1
88 match stringProtoFuncMatch DontEnum|Function 1
89 replace stringProtoFuncReplace DontEnum|Function 2
90 search stringProtoFuncSearch DontEnum|Function 1
91 slice stringProtoFuncSlice DontEnum|Function 2
92 split stringProtoFuncSplit DontEnum|Function 2
93 substr stringProtoFuncSubstr DontEnum|Function 2
94 substring stringProtoFuncSubstring DontEnum|Function 2
95 toLowerCase stringProtoFuncToLowerCase DontEnum|Function 0
96 toUpperCase stringProtoFuncToUpperCase DontEnum|Function 0
97 localeCompare stringProtoFuncLocaleCompare DontEnum|Function 1
98
99 # toLocaleLowerCase and toLocaleUpperCase are currently identical to toLowerCase and toUpperCase
100 toLocaleLowerCase stringProtoFuncToLowerCase DontEnum|Function 0
101 toLocaleUpperCase stringProtoFuncToUpperCase DontEnum|Function 0
102
103 big stringProtoFuncBig DontEnum|Function 0
104 small stringProtoFuncSmall DontEnum|Function 0
105 blink stringProtoFuncBlink DontEnum|Function 0
106 bold stringProtoFuncBold DontEnum|Function 0
107 fixed stringProtoFuncFixed DontEnum|Function 0
108 italics stringProtoFuncItalics DontEnum|Function 0
109 strike stringProtoFuncStrike DontEnum|Function 0
110 sub stringProtoFuncSub DontEnum|Function 0
111 sup stringProtoFuncSup DontEnum|Function 0
112 fontcolor stringProtoFuncFontcolor DontEnum|Function 1
113 fontsize stringProtoFuncFontsize DontEnum|Function 1
114 anchor stringProtoFuncAnchor DontEnum|Function 1
115 link stringProtoFuncLink DontEnum|Function 1
116 @end
117 */
118
119 // ECMA 15.5.4
StringPrototype(ExecState * exec,PassRefPtr<Structure> structure)120 StringPrototype::StringPrototype(ExecState* exec, PassRefPtr<Structure> structure)
121 : StringObject(exec, structure)
122 {
123 // The constructor will be added later, after StringConstructor has been built
124 putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum);
125 }
126
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)127 bool StringPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot)
128 {
129 return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), this, propertyName, slot);
130 }
131
132 // ------------------------------ Functions --------------------------
133
substituteBackreferences(const UString & replacement,const UString & source,const int * ovector,RegExp * reg)134 static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg)
135 {
136 UString substitutedReplacement;
137 int offset = 0;
138 int i = -1;
139 while ((i = replacement.find('$', i + 1)) != -1) {
140 if (i + 1 == replacement.size())
141 break;
142
143 UChar ref = replacement[i + 1];
144 if (ref == '$') {
145 // "$$" -> "$"
146 ++i;
147 substitutedReplacement.append(replacement.data() + offset, i - offset);
148 offset = i + 1;
149 continue;
150 }
151
152 int backrefStart;
153 int backrefLength;
154 int advance = 0;
155 if (ref == '&') {
156 backrefStart = ovector[0];
157 backrefLength = ovector[1] - backrefStart;
158 } else if (ref == '`') {
159 backrefStart = 0;
160 backrefLength = ovector[0];
161 } else if (ref == '\'') {
162 backrefStart = ovector[1];
163 backrefLength = source.size() - backrefStart;
164 } else if (reg && ref >= '0' && ref <= '9') {
165 // 1- and 2-digit back references are allowed
166 unsigned backrefIndex = ref - '0';
167 if (backrefIndex > reg->numSubpatterns())
168 continue;
169 if (replacement.size() > i + 2) {
170 ref = replacement[i + 2];
171 if (ref >= '0' && ref <= '9') {
172 backrefIndex = 10 * backrefIndex + ref - '0';
173 if (backrefIndex > reg->numSubpatterns())
174 backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
175 else
176 advance = 1;
177 }
178 }
179 if (!backrefIndex)
180 continue;
181 backrefStart = ovector[2 * backrefIndex];
182 backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
183 } else
184 continue;
185
186 if (i - offset)
187 substitutedReplacement.append(replacement.data() + offset, i - offset);
188 i += 1 + advance;
189 offset = i + 1;
190 substitutedReplacement.append(source.data() + backrefStart, backrefLength);
191 }
192
193 if (!offset)
194 return replacement;
195
196 if (replacement.size() - offset)
197 substitutedReplacement.append(replacement.data() + offset, replacement.size() - offset);
198
199 return substitutedReplacement;
200 }
201
localeCompare(const UString & a,const UString & b)202 static inline int localeCompare(const UString& a, const UString& b)
203 {
204 return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.data()), a.size(), reinterpret_cast<const ::UChar*>(b.data()), b.size());
205 }
206
stringProtoFuncReplace(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)207 JSValuePtr stringProtoFuncReplace(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
208 {
209 JSString* sourceVal = thisValue.toThisJSString(exec);
210 const UString& source = sourceVal->value();
211
212 JSValuePtr pattern = args.at(exec, 0);
213
214 JSValuePtr replacement = args.at(exec, 1);
215 UString replacementString;
216 CallData callData;
217 CallType callType = replacement.getCallData(callData);
218 if (callType == CallTypeNone)
219 replacementString = replacement.toString(exec);
220
221 if (pattern.isObject(&RegExpObject::info)) {
222 RegExp* reg = asRegExpObject(pattern)->regExp();
223 bool global = reg->global();
224
225 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
226
227 int lastIndex = 0;
228 int startPosition = 0;
229
230 Vector<UString::Range, 16> sourceRanges;
231 Vector<UString, 16> replacements;
232
233 // This is either a loop (if global is set) or a one-way (if not).
234 do {
235 int matchIndex;
236 int matchLen;
237 int* ovector;
238 regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector);
239 if (matchIndex < 0)
240 break;
241
242 sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex));
243
244 if (callType != CallTypeNone) {
245 int completeMatchStart = ovector[0];
246 ArgList args;
247
248 for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) {
249 int matchStart = ovector[i * 2];
250 int matchLen = ovector[i * 2 + 1] - matchStart;
251
252 if (matchStart < 0)
253 args.append(jsUndefined());
254 else
255 args.append(jsSubstring(exec, source, matchStart, matchLen));
256 }
257
258 args.append(jsNumber(exec, completeMatchStart));
259 args.append(sourceVal);
260
261 replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec));
262 if (exec->hadException())
263 break;
264 } else
265 replacements.append(substituteBackreferences(replacementString, source, ovector, reg));
266
267 lastIndex = matchIndex + matchLen;
268 startPosition = lastIndex;
269
270 // special case of empty match
271 if (matchLen == 0) {
272 startPosition++;
273 if (startPosition > source.size())
274 break;
275 }
276 } while (global);
277
278 if (lastIndex < source.size())
279 sourceRanges.append(UString::Range(lastIndex, source.size() - lastIndex));
280
281 UString result = source.spliceSubstringsWithSeparators(sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size());
282
283 if (result == source)
284 return sourceVal;
285
286 return jsString(exec, result);
287 }
288
289 // First arg is a string
290 UString patternString = pattern.toString(exec);
291 int matchPos = source.find(patternString);
292 int matchLen = patternString.size();
293 // Do the replacement
294 if (matchPos == -1)
295 return sourceVal;
296
297 if (callType != CallTypeNone) {
298 ArgList args;
299 args.append(jsSubstring(exec, source, matchPos, matchLen));
300 args.append(jsNumber(exec, matchPos));
301 args.append(sourceVal);
302
303 replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec);
304 }
305
306 int ovector[2] = { matchPos, matchPos + matchLen };
307 return jsString(exec, source.substr(0, matchPos)
308 + substituteBackreferences(replacementString, source, ovector, 0)
309 + source.substr(matchPos + matchLen));
310 }
311
stringProtoFuncToString(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)312 JSValuePtr stringProtoFuncToString(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
313 {
314 // Also used for valueOf.
315
316 if (thisValue.isString())
317 return thisValue;
318
319 if (thisValue.isObject(&StringObject::info))
320 return asStringObject(thisValue)->internalValue();
321
322 return throwError(exec, TypeError);
323 }
324
stringProtoFuncCharAt(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)325 JSValuePtr stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
326 {
327 UString s = thisValue.toThisString(exec);
328 unsigned len = s.size();
329 JSValuePtr a0 = args.at(exec, 0);
330 if (a0.isUInt32Fast()) {
331 uint32_t i = a0.getUInt32Fast();
332 if (i < len)
333 return jsSingleCharacterSubstring(exec, s, i);
334 return jsEmptyString(exec);
335 }
336 double dpos = a0.toInteger(exec);
337 if (dpos >= 0 && dpos < len)
338 return jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos));
339 return jsEmptyString(exec);
340 }
341
stringProtoFuncCharCodeAt(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)342 JSValuePtr stringProtoFuncCharCodeAt(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
343 {
344 UString s = thisValue.toThisString(exec);
345 unsigned len = s.size();
346 JSValuePtr a0 = args.at(exec, 0);
347 if (a0.isUInt32Fast()) {
348 uint32_t i = a0.getUInt32Fast();
349 if (i < len)
350 return jsNumber(exec, s.data()[i]);
351 return jsNaN(exec);
352 }
353 double dpos = a0.toInteger(exec);
354 if (dpos >= 0 && dpos < len)
355 return jsNumber(exec, s[static_cast<int>(dpos)]);
356 return jsNaN(exec);
357 }
358
stringProtoFuncConcat(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)359 JSValuePtr stringProtoFuncConcat(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
360 {
361 UString s = thisValue.toThisString(exec);
362
363 ArgList::const_iterator end = args.end();
364 for (ArgList::const_iterator it = args.begin(); it != end; ++it)
365 s += (*it).jsValue(exec).toString(exec);
366 return jsString(exec, s);
367 }
368
stringProtoFuncIndexOf(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)369 JSValuePtr stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
370 {
371 UString s = thisValue.toThisString(exec);
372 int len = s.size();
373
374 JSValuePtr a0 = args.at(exec, 0);
375 JSValuePtr a1 = args.at(exec, 1);
376 UString u2 = a0.toString(exec);
377 int pos;
378 if (a1.isUndefined())
379 pos = 0;
380 else if (a1.isUInt32Fast())
381 pos = min<uint32_t>(a1.getUInt32Fast(), len);
382 else {
383 double dpos = a1.toInteger(exec);
384 if (dpos < 0)
385 dpos = 0;
386 else if (dpos > len)
387 dpos = len;
388 pos = static_cast<int>(dpos);
389 }
390
391 return jsNumber(exec, s.find(u2, pos));
392 }
393
stringProtoFuncLastIndexOf(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)394 JSValuePtr stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
395 {
396 UString s = thisValue.toThisString(exec);
397 int len = s.size();
398
399 JSValuePtr a0 = args.at(exec, 0);
400 JSValuePtr a1 = args.at(exec, 1);
401
402 UString u2 = a0.toString(exec);
403 double dpos = a1.toIntegerPreserveNaN(exec);
404 if (dpos < 0)
405 dpos = 0;
406 else if (!(dpos <= len)) // true for NaN
407 dpos = len;
408 return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos)));
409 }
410
stringProtoFuncMatch(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)411 JSValuePtr stringProtoFuncMatch(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
412 {
413 UString s = thisValue.toThisString(exec);
414
415 JSValuePtr a0 = args.at(exec, 0);
416
417 UString u = s;
418 RefPtr<RegExp> reg;
419 RegExpObject* imp = 0;
420 if (a0.isObject(&RegExpObject::info))
421 reg = asRegExpObject(a0)->regExp();
422 else {
423 /*
424 * ECMA 15.5.4.12 String.prototype.search (regexp)
425 * If regexp is not an object whose [[Class]] property is "RegExp", it is
426 * replaced with the result of the expression new RegExp(regexp).
427 */
428 reg = RegExp::create(&exec->globalData(), a0.toString(exec));
429 }
430 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
431 int pos;
432 int matchLength;
433 regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength);
434 if (!(reg->global())) {
435 // case without 'g' flag is handled like RegExp.prototype.exec
436 if (pos < 0)
437 return jsNull();
438 return regExpConstructor->arrayOfMatches(exec);
439 }
440
441 // return array of matches
442 ArgList list;
443 int lastIndex = 0;
444 while (pos >= 0) {
445 list.append(jsSubstring(exec, u, pos, matchLength));
446 lastIndex = pos;
447 pos += matchLength == 0 ? 1 : matchLength;
448 regExpConstructor->performMatch(reg.get(), u, pos, pos, matchLength);
449 }
450 if (imp)
451 imp->setLastIndex(lastIndex);
452 if (list.isEmpty()) {
453 // if there are no matches at all, it's important to return
454 // Null instead of an empty array, because this matches
455 // other browsers and because Null is a false value.
456 return jsNull();
457 }
458
459 return constructArray(exec, list);
460 }
461
stringProtoFuncSearch(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)462 JSValuePtr stringProtoFuncSearch(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
463 {
464 UString s = thisValue.toThisString(exec);
465
466 JSValuePtr a0 = args.at(exec, 0);
467
468 UString u = s;
469 RefPtr<RegExp> reg;
470 if (a0.isObject(&RegExpObject::info))
471 reg = asRegExpObject(a0)->regExp();
472 else {
473 /*
474 * ECMA 15.5.4.12 String.prototype.search (regexp)
475 * If regexp is not an object whose [[Class]] property is "RegExp", it is
476 * replaced with the result of the expression new RegExp(regexp).
477 */
478 reg = RegExp::create(&exec->globalData(), a0.toString(exec));
479 }
480 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
481 int pos;
482 int matchLength;
483 regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength);
484 return jsNumber(exec, pos);
485 }
486
stringProtoFuncSlice(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)487 JSValuePtr stringProtoFuncSlice(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
488 {
489 UString s = thisValue.toThisString(exec);
490 int len = s.size();
491
492 JSValuePtr a0 = args.at(exec, 0);
493 JSValuePtr a1 = args.at(exec, 1);
494
495 // The arg processing is very much like ArrayProtoFunc::Slice
496 double start = a0.toInteger(exec);
497 double end = a1.isUndefined() ? len : a1.toInteger(exec);
498 double from = start < 0 ? len + start : start;
499 double to = end < 0 ? len + end : end;
500 if (to > from && to > 0 && from < len) {
501 if (from < 0)
502 from = 0;
503 if (to > len)
504 to = len;
505 return jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from));
506 }
507
508 return jsEmptyString(exec);
509 }
510
stringProtoFuncSplit(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)511 JSValuePtr stringProtoFuncSplit(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
512 {
513 UString s = thisValue.toThisString(exec);
514
515 JSValuePtr a0 = args.at(exec, 0);
516 JSValuePtr a1 = args.at(exec, 1);
517
518 JSArray* result = constructEmptyArray(exec);
519 unsigned i = 0;
520 int p0 = 0;
521 unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec);
522 if (a0.isObject(&RegExpObject::info)) {
523 RegExp* reg = asRegExpObject(a0)->regExp();
524 if (s.isEmpty() && reg->match(s, 0) >= 0) {
525 // empty string matched by regexp -> empty array
526 return result;
527 }
528 int pos = 0;
529 while (i != limit && pos < s.size()) {
530 OwnArrayPtr<int> ovector;
531 int mpos = reg->match(s, pos, &ovector);
532 if (mpos < 0)
533 break;
534 int mlen = ovector[1] - ovector[0];
535 pos = mpos + (mlen == 0 ? 1 : mlen);
536 if (mpos != p0 || mlen) {
537 result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0));
538 p0 = mpos + mlen;
539 }
540 for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) {
541 int spos = ovector[si * 2];
542 if (spos < 0)
543 result->put(exec, i++, jsUndefined());
544 else
545 result->put(exec, i++, jsSubstring(exec, s, spos, ovector[si * 2 + 1] - spos));
546 }
547 }
548 } else {
549 UString u2 = a0.toString(exec);
550 if (u2.isEmpty()) {
551 if (s.isEmpty()) {
552 // empty separator matches empty string -> empty array
553 return result;
554 }
555 while (i != limit && p0 < s.size() - 1)
556 result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++));
557 } else {
558 int pos;
559 while (i != limit && (pos = s.find(u2, p0)) >= 0) {
560 result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0));
561 p0 = pos + u2.size();
562 }
563 }
564 }
565
566 // add remaining string
567 if (i != limit)
568 result->put(exec, i++, jsSubstring(exec, s, p0, s.size() - p0));
569
570 return result;
571 }
572
stringProtoFuncSubstr(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)573 JSValuePtr stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
574 {
575 UString s = thisValue.toThisString(exec);
576 int len = s.size();
577
578 JSValuePtr a0 = args.at(exec, 0);
579 JSValuePtr a1 = args.at(exec, 1);
580
581 double start = a0.toInteger(exec);
582 double length = a1.isUndefined() ? len : a1.toInteger(exec);
583 if (start >= len || length <= 0)
584 return jsEmptyString(exec);
585 if (start < 0) {
586 start += len;
587 if (start < 0)
588 start = 0;
589 }
590 if (start + length > len)
591 length = len - start;
592 return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length));
593 }
594
stringProtoFuncSubstring(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)595 JSValuePtr stringProtoFuncSubstring(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
596 {
597 UString s = thisValue.toThisString(exec);
598 int len = s.size();
599
600 JSValuePtr a0 = args.at(exec, 0);
601 JSValuePtr a1 = args.at(exec, 1);
602
603 double start = a0.toNumber(exec);
604 double end = a1.toNumber(exec);
605 if (isnan(start))
606 start = 0;
607 if (isnan(end))
608 end = 0;
609 if (start < 0)
610 start = 0;
611 if (end < 0)
612 end = 0;
613 if (start > len)
614 start = len;
615 if (end > len)
616 end = len;
617 if (a1.isUndefined())
618 end = len;
619 if (start > end) {
620 double temp = end;
621 end = start;
622 start = temp;
623 }
624 return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(end) - static_cast<unsigned>(start));
625 }
626
stringProtoFuncToLowerCase(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)627 JSValuePtr stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
628 {
629 JSString* sVal = thisValue.toThisJSString(exec);
630 const UString& s = sVal->value();
631
632 int sSize = s.size();
633 if (!sSize)
634 return sVal;
635
636 const UChar* sData = s.data();
637 Vector<UChar> buffer(sSize);
638
639 UChar ored = 0;
640 for (int i = 0; i < sSize; i++) {
641 UChar c = sData[i];
642 ored |= c;
643 buffer[i] = toASCIILower(c);
644 }
645 if (!(ored & ~0x7f))
646 return jsString(exec, UString(buffer.releaseBuffer(), sSize, false));
647
648 bool error;
649 int length = Unicode::toLower(buffer.data(), sSize, sData, sSize, &error);
650 if (error) {
651 buffer.resize(length);
652 length = Unicode::toLower(buffer.data(), length, sData, sSize, &error);
653 if (error)
654 return sVal;
655 }
656 if (length == sSize && memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
657 return sVal;
658 return jsString(exec, UString(buffer.releaseBuffer(), length, false));
659 }
660
stringProtoFuncToUpperCase(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)661 JSValuePtr stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
662 {
663 JSString* sVal = thisValue.toThisJSString(exec);
664 const UString& s = sVal->value();
665
666 int sSize = s.size();
667 if (!sSize)
668 return sVal;
669
670 const UChar* sData = s.data();
671 Vector<UChar> buffer(sSize);
672
673 UChar ored = 0;
674 for (int i = 0; i < sSize; i++) {
675 UChar c = sData[i];
676 ored |= c;
677 buffer[i] = toASCIIUpper(c);
678 }
679 if (!(ored & ~0x7f))
680 return jsString(exec, UString(buffer.releaseBuffer(), sSize, false));
681
682 bool error;
683 int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error);
684 if (error) {
685 buffer.resize(length);
686 length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error);
687 if (error)
688 return sVal;
689 }
690 if (length == sSize && memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
691 return sVal;
692 return jsString(exec, UString(buffer.releaseBuffer(), length, false));
693 }
694
stringProtoFuncLocaleCompare(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)695 JSValuePtr stringProtoFuncLocaleCompare(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
696 {
697 if (args.size() < 1)
698 return jsNumber(exec, 0);
699
700 UString s = thisValue.toThisString(exec);
701 JSValuePtr a0 = args.at(exec, 0);
702 return jsNumber(exec, localeCompare(s, a0.toString(exec)));
703 }
704
stringProtoFuncBig(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)705 JSValuePtr stringProtoFuncBig(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
706 {
707 UString s = thisValue.toThisString(exec);
708 return jsNontrivialString(exec, "<big>" + s + "</big>");
709 }
710
stringProtoFuncSmall(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)711 JSValuePtr stringProtoFuncSmall(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
712 {
713 UString s = thisValue.toThisString(exec);
714 return jsNontrivialString(exec, "<small>" + s + "</small>");
715 }
716
stringProtoFuncBlink(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)717 JSValuePtr stringProtoFuncBlink(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
718 {
719 UString s = thisValue.toThisString(exec);
720 return jsNontrivialString(exec, "<blink>" + s + "</blink>");
721 }
722
stringProtoFuncBold(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)723 JSValuePtr stringProtoFuncBold(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
724 {
725 UString s = thisValue.toThisString(exec);
726 return jsNontrivialString(exec, "<b>" + s + "</b>");
727 }
728
stringProtoFuncFixed(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)729 JSValuePtr stringProtoFuncFixed(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
730 {
731 UString s = thisValue.toThisString(exec);
732 return jsString(exec, "<tt>" + s + "</tt>");
733 }
734
stringProtoFuncItalics(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)735 JSValuePtr stringProtoFuncItalics(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
736 {
737 UString s = thisValue.toThisString(exec);
738 return jsNontrivialString(exec, "<i>" + s + "</i>");
739 }
740
stringProtoFuncStrike(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)741 JSValuePtr stringProtoFuncStrike(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
742 {
743 UString s = thisValue.toThisString(exec);
744 return jsNontrivialString(exec, "<strike>" + s + "</strike>");
745 }
746
stringProtoFuncSub(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)747 JSValuePtr stringProtoFuncSub(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
748 {
749 UString s = thisValue.toThisString(exec);
750 return jsNontrivialString(exec, "<sub>" + s + "</sub>");
751 }
752
stringProtoFuncSup(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList &)753 JSValuePtr stringProtoFuncSup(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&)
754 {
755 UString s = thisValue.toThisString(exec);
756 return jsNontrivialString(exec, "<sup>" + s + "</sup>");
757 }
758
stringProtoFuncFontcolor(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)759 JSValuePtr stringProtoFuncFontcolor(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
760 {
761 UString s = thisValue.toThisString(exec);
762 JSValuePtr a0 = args.at(exec, 0);
763 return jsNontrivialString(exec, "<font color=\"" + a0.toString(exec) + "\">" + s + "</font>");
764 }
765
stringProtoFuncFontsize(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)766 JSValuePtr stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
767 {
768 UString s = thisValue.toThisString(exec);
769 JSValuePtr a0 = args.at(exec, 0);
770
771 uint32_t smallInteger;
772 if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
773 unsigned stringSize = s.size();
774 unsigned bufferSize = 22 + stringSize;
775 UChar* buffer = static_cast<UChar*>(tryFastMalloc(bufferSize * sizeof(UChar)));
776 if (!buffer)
777 return jsUndefined();
778 buffer[0] = '<';
779 buffer[1] = 'f';
780 buffer[2] = 'o';
781 buffer[3] = 'n';
782 buffer[4] = 't';
783 buffer[5] = ' ';
784 buffer[6] = 's';
785 buffer[7] = 'i';
786 buffer[8] = 'z';
787 buffer[9] = 'e';
788 buffer[10] = '=';
789 buffer[11] = '"';
790 buffer[12] = '0' + smallInteger;
791 buffer[13] = '"';
792 buffer[14] = '>';
793 memcpy(&buffer[15], s.data(), stringSize * sizeof(UChar));
794 buffer[15 + stringSize] = '<';
795 buffer[16 + stringSize] = '/';
796 buffer[17 + stringSize] = 'f';
797 buffer[18 + stringSize] = 'o';
798 buffer[19 + stringSize] = 'n';
799 buffer[20 + stringSize] = 't';
800 buffer[21 + stringSize] = '>';
801 return jsNontrivialString(exec, UString(buffer, bufferSize, false));
802 }
803
804 return jsNontrivialString(exec, "<font size=\"" + a0.toString(exec) + "\">" + s + "</font>");
805 }
806
stringProtoFuncAnchor(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)807 JSValuePtr stringProtoFuncAnchor(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
808 {
809 UString s = thisValue.toThisString(exec);
810 JSValuePtr a0 = args.at(exec, 0);
811 return jsNontrivialString(exec, "<a name=\"" + a0.toString(exec) + "\">" + s + "</a>");
812 }
813
stringProtoFuncLink(ExecState * exec,JSObject *,JSValuePtr thisValue,const ArgList & args)814 JSValuePtr stringProtoFuncLink(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args)
815 {
816 UString s = thisValue.toThisString(exec);
817 JSValuePtr a0 = args.at(exec, 0);
818 UString linkText = a0.toString(exec);
819
820 unsigned linkTextSize = linkText.size();
821 unsigned stringSize = s.size();
822 unsigned bufferSize = 15 + linkTextSize + stringSize;
823 UChar* buffer = static_cast<UChar*>(tryFastMalloc(bufferSize * sizeof(UChar)));
824 if (!buffer)
825 return jsUndefined();
826 buffer[0] = '<';
827 buffer[1] = 'a';
828 buffer[2] = ' ';
829 buffer[3] = 'h';
830 buffer[4] = 'r';
831 buffer[5] = 'e';
832 buffer[6] = 'f';
833 buffer[7] = '=';
834 buffer[8] = '"';
835 memcpy(&buffer[9], linkText.data(), linkTextSize * sizeof(UChar));
836 buffer[9 + linkTextSize] = '"';
837 buffer[10 + linkTextSize] = '>';
838 memcpy(&buffer[11 + linkTextSize], s.data(), stringSize * sizeof(UChar));
839 buffer[11 + linkTextSize + stringSize] = '<';
840 buffer[12 + linkTextSize + stringSize] = '/';
841 buffer[13 + linkTextSize + stringSize] = 'a';
842 buffer[14 + linkTextSize + stringSize] = '>';
843 return jsNontrivialString(exec, UString(buffer, bufferSize, false));
844 }
845
846 } // namespace JSC
847