• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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