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