1 // -*- c-basic-offset: 2 -*-
3 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
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.
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.
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
23 #include "string_object.h"
24 #include "string_object.lut.h"
26 #include "JSWrapperObject.h"
27 #include "PropertyNameArray.h"
28 #include "array_object.h"
29 #include "error_object.h"
30 #include "operations.h"
31 #include "regexp_object.h"
32 #include <wtf/MathExtras.h>
33 #include <wtf/unicode/Unicode.h>
36 #include <CoreFoundation/CoreFoundation.h>
37 #elif PLATFORM(WIN_OS)
45 // ------------------------------ StringInstance ----------------------------
47 const ClassInfo
StringInstance::info
= { "String", 0, 0 };
49 StringInstance::StringInstance(JSObject
*proto
)
50 : JSWrapperObject(proto
)
52 setInternalValue(jsString(""));
55 StringInstance::StringInstance(JSObject
*proto
, StringImp
* string
)
56 : JSWrapperObject(proto
)
58 setInternalValue(string
);
61 StringInstance::StringInstance(JSObject
*proto
, const UString
&string
)
62 : JSWrapperObject(proto
)
64 setInternalValue(jsString(string
));
67 JSValue
*StringInstance::lengthGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
&slot
)
69 return jsNumber(static_cast<StringInstance
*>(slot
.slotBase())->internalValue()->value().size());
72 JSValue
* StringInstance::indexGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
& slot
)
74 return jsString(static_cast<StringInstance
*>(slot
.slotBase())->internalValue()->value().substr(slot
.index(), 1));
77 static JSValue
* stringInstanceNumericPropertyGetter(ExecState
*, JSObject
*, unsigned index
, const PropertySlot
& slot
)
79 return jsString(static_cast<StringInstance
*>(slot
.slotBase())->internalValue()->value().substr(index
, 1));
82 bool StringInstance::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
84 if (propertyName
== exec
->propertyNames().length
) {
85 slot
.setCustom(this, lengthGetter
);
90 unsigned i
= propertyName
.toStrictUInt32(&isStrictUInt32
);
91 unsigned length
= internalValue()->value().size();
92 if (isStrictUInt32
&& i
< length
) {
93 slot
.setCustomIndex(this, i
, indexGetter
);
97 return JSObject::getOwnPropertySlot(exec
, propertyName
, slot
);
100 bool StringInstance::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
102 unsigned length
= internalValue()->value().size();
103 if (propertyName
< length
) {
104 slot
.setCustomNumeric(this, stringInstanceNumericPropertyGetter
);
108 return JSObject::getOwnPropertySlot(exec
, Identifier::from(propertyName
), slot
);
111 void StringInstance::put(ExecState
*exec
, const Identifier
&propertyName
, JSValue
*value
, int attr
)
113 if (propertyName
== exec
->propertyNames().length
)
115 JSObject::put(exec
, propertyName
, value
, attr
);
118 bool StringInstance::deleteProperty(ExecState
*exec
, const Identifier
&propertyName
)
120 if (propertyName
== exec
->propertyNames().length
)
122 return JSObject::deleteProperty(exec
, propertyName
);
125 void StringInstance::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
127 int size
= internalValue()->getString().size();
128 for (int i
= 0; i
< size
; i
++)
129 propertyNames
.add(Identifier(UString::from(i
)));
130 return JSObject::getPropertyNames(exec
, propertyNames
);
133 // ------------------------------ StringPrototype ---------------------------
134 const ClassInfo
StringPrototype::info
= { "String", &StringInstance::info
, &stringTable
};
135 /* Source for string_object.lut.h
136 @begin stringTable 26
137 toString &stringProtoFuncToString DontEnum|Function 0
138 valueOf &stringProtoFuncValueOf DontEnum|Function 0
139 charAt &stringProtoFuncCharAt DontEnum|Function 1
140 charCodeAt &stringProtoFuncCharCodeAt DontEnum|Function 1
141 concat &stringProtoFuncConcat DontEnum|Function 1
142 indexOf &stringProtoFuncIndexOf DontEnum|Function 1
143 lastIndexOf &stringProtoFuncLastIndexOf DontEnum|Function 1
144 match &stringProtoFuncMatch DontEnum|Function 1
145 replace &stringProtoFuncReplace DontEnum|Function 2
146 search &stringProtoFuncSearch DontEnum|Function 1
147 slice &stringProtoFuncSlice DontEnum|Function 2
148 split &stringProtoFuncSplit DontEnum|Function 2
149 substr &stringProtoFuncSubstr DontEnum|Function 2
150 substring &stringProtoFuncSubstring DontEnum|Function 2
151 toLowerCase &stringProtoFuncToLowerCase DontEnum|Function 0
152 toUpperCase &stringProtoFuncToUpperCase DontEnum|Function 0
153 toLocaleLowerCase &stringProtoFuncToLocaleLowerCase DontEnum|Function 0
154 toLocaleUpperCase &stringProtoFuncToLocaleUpperCase DontEnum|Function 0
155 localeCompare &stringProtoFuncLocaleCompare DontEnum|Function 1
157 big &stringProtoFuncBig DontEnum|Function 0
158 small &stringProtoFuncSmall DontEnum|Function 0
159 blink &stringProtoFuncBlink DontEnum|Function 0
160 bold &stringProtoFuncBold DontEnum|Function 0
161 fixed &stringProtoFuncFixed DontEnum|Function 0
162 italics &stringProtoFuncItalics DontEnum|Function 0
163 strike &stringProtoFuncStrike DontEnum|Function 0
164 sub &stringProtoFuncSub DontEnum|Function 0
165 sup &stringProtoFuncSup DontEnum|Function 0
166 fontcolor &stringProtoFuncFontcolor DontEnum|Function 1
167 fontsize &stringProtoFuncFontsize DontEnum|Function 1
168 anchor &stringProtoFuncAnchor DontEnum|Function 1
169 link &stringProtoFuncLink DontEnum|Function 1
173 StringPrototype::StringPrototype(ExecState
* exec
, ObjectPrototype
* objProto
)
174 : StringInstance(objProto
)
176 // The constructor will be added later, after StringObjectImp has been built
177 putDirect(exec
->propertyNames().length
, jsNumber(0), DontDelete
| ReadOnly
| DontEnum
);
180 bool StringPrototype::getOwnPropertySlot(ExecState
*exec
, const Identifier
& propertyName
, PropertySlot
&slot
)
182 return getStaticFunctionSlot
<StringInstance
>(exec
, &stringTable
, this, propertyName
, slot
);
185 // ------------------------------ Functions --------------------------
187 static inline void expandSourceRanges(UString::Range
* & array
, int& count
, int& capacity
)
193 newCapacity
= capacity
* 2;
196 UString::Range
*newArray
= new UString::Range
[newCapacity
];
197 for (int i
= 0; i
< count
; i
++) {
198 newArray
[i
] = array
[i
];
203 capacity
= newCapacity
;
207 static void pushSourceRange(UString::Range
* & array
, int& count
, int& capacity
, UString::Range range
)
209 if (count
+ 1 > capacity
)
210 expandSourceRanges(array
, count
, capacity
);
212 array
[count
] = range
;
216 static inline void expandReplacements(UString
* & array
, int& count
, int& capacity
)
222 newCapacity
= capacity
* 2;
225 UString
*newArray
= new UString
[newCapacity
];
226 for (int i
= 0; i
< count
; i
++) {
227 newArray
[i
] = array
[i
];
232 capacity
= newCapacity
;
236 static void pushReplacement(UString
* & array
, int& count
, int& capacity
, UString replacement
)
238 if (count
+ 1 > capacity
)
239 expandReplacements(array
, count
, capacity
);
241 array
[count
] = replacement
;
245 static inline UString
substituteBackreferences(const UString
&replacement
, const UString
&source
, int *ovector
, RegExp
*reg
)
247 UString substitutedReplacement
= replacement
;
250 while ((i
= substitutedReplacement
.find(UString("$"), i
+ 1)) != -1) {
251 if (i
+1 == substitutedReplacement
.size())
254 unsigned short ref
= substitutedReplacement
[i
+1].unicode();
255 int backrefStart
= 0;
256 int backrefLength
= 0;
259 if (ref
== '$') { // "$$" -> "$"
260 substitutedReplacement
= substitutedReplacement
.substr(0, i
+ 1) + substitutedReplacement
.substr(i
+ 2);
262 } else if (ref
== '&') {
263 backrefStart
= ovector
[0];
264 backrefLength
= ovector
[1] - backrefStart
;
265 } else if (ref
== '`') {
267 backrefLength
= ovector
[0];
268 } else if (ref
== '\'') {
269 backrefStart
= ovector
[1];
270 backrefLength
= source
.size() - backrefStart
;
271 } else if (ref
>= '0' && ref
<= '9') {
272 // 1- and 2-digit back references are allowed
273 unsigned backrefIndex
= ref
- '0';
274 if (backrefIndex
> reg
->numSubpatterns())
276 if (substitutedReplacement
.size() > i
+ 2) {
277 ref
= substitutedReplacement
[i
+2].unicode();
278 if (ref
>= '0' && ref
<= '9') {
279 backrefIndex
= 10 * backrefIndex
+ ref
- '0';
280 if (backrefIndex
> reg
->numSubpatterns())
281 backrefIndex
= backrefIndex
/ 10; // Fall back to the 1-digit reference
286 backrefStart
= ovector
[2 * backrefIndex
];
287 backrefLength
= ovector
[2 * backrefIndex
+ 1] - backrefStart
;
291 substitutedReplacement
= substitutedReplacement
.substr(0, i
) + source
.substr(backrefStart
, backrefLength
) + substitutedReplacement
.substr(i
+ 2 + advance
);
292 i
+= backrefLength
- 1; // - 1 offsets 'i + 1'
295 return substitutedReplacement
;
297 static inline int localeCompare(const UString
& a
, const UString
& b
)
300 int retval
= CompareStringW(LOCALE_USER_DEFAULT
, 0,
301 reinterpret_cast<LPCWSTR
>(a
.data()), a
.size(),
302 reinterpret_cast<LPCWSTR
>(b
.data()), b
.size());
303 return !retval
? retval
: retval
- 2;
305 CFStringRef sa
= CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault
, reinterpret_cast<const UniChar
*>(a
.data()), a
.size(), kCFAllocatorNull
);
306 CFStringRef sb
= CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault
, reinterpret_cast<const UniChar
*>(b
.data()), b
.size(), kCFAllocatorNull
);
308 int retval
= CFStringCompare(sa
, sb
, kCFCompareLocalized
);
315 return compare(a
, b
);
319 static JSValue
*replace(ExecState
*exec
, StringImp
* sourceVal
, JSValue
*pattern
, JSValue
*replacement
)
321 UString source
= sourceVal
->value();
322 JSObject
*replacementFunction
= 0;
323 UString replacementString
;
325 if (replacement
->isObject() && replacement
->toObject(exec
)->implementsCall())
326 replacementFunction
= replacement
->toObject(exec
);
328 replacementString
= replacement
->toString(exec
);
330 if (pattern
->isObject() && static_cast<JSObject
*>(pattern
)->inherits(&RegExpImp::info
)) {
331 RegExp
*reg
= static_cast<RegExpImp
*>(pattern
)->regExp();
332 bool global
= reg
->global();
334 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalGlobalObject()->regExpConstructor());
337 int startPosition
= 0;
339 UString::Range
*sourceRanges
= 0;
340 int sourceRangeCount
= 0;
341 int sourceRangeCapacity
= 0;
342 UString
*replacements
= 0;
343 int replacementCount
= 0;
344 int replacementCapacity
= 0;
346 // This is either a loop (if global is set) or a one-way (if not).
351 regExpObj
->performMatch(reg
, source
, startPosition
, matchIndex
, matchLen
, &ovector
);
355 pushSourceRange(sourceRanges
, sourceRangeCount
, sourceRangeCapacity
, UString::Range(lastIndex
, matchIndex
- lastIndex
));
357 UString substitutedReplacement
;
358 if (replacementFunction
) {
359 int completeMatchStart
= ovector
[0];
362 for (unsigned i
= 0; i
< reg
->numSubpatterns() + 1; i
++) {
363 int matchStart
= ovector
[i
* 2];
364 int matchLen
= ovector
[i
* 2 + 1] - matchStart
;
367 args
.append(jsUndefined());
369 args
.append(jsString(source
.substr(matchStart
, matchLen
)));
372 args
.append(jsNumber(completeMatchStart
));
373 args
.append(sourceVal
);
375 substitutedReplacement
= replacementFunction
->call(exec
, exec
->dynamicGlobalObject(),
376 args
)->toString(exec
);
378 substitutedReplacement
= substituteBackreferences(replacementString
, source
, ovector
, reg
);
380 pushReplacement(replacements
, replacementCount
, replacementCapacity
, substitutedReplacement
);
382 lastIndex
= matchIndex
+ matchLen
;
383 startPosition
= lastIndex
;
385 // special case of empty match
388 if (startPosition
> source
.size())
393 if (lastIndex
< source
.size())
394 pushSourceRange(sourceRanges
, sourceRangeCount
, sourceRangeCapacity
, UString::Range(lastIndex
, source
.size() - lastIndex
));
399 result
= source
.spliceSubstringsWithSeparators(sourceRanges
, sourceRangeCount
, replacements
, replacementCount
);
401 delete [] sourceRanges
;
402 delete [] replacements
;
404 if (result
== source
)
407 return jsString(result
);
410 // First arg is a string
411 UString patternString
= pattern
->toString(exec
);
412 int matchPos
= source
.find(patternString
);
413 int matchLen
= patternString
.size();
414 // Do the replacement
418 if (replacementFunction
) {
421 args
.append(jsString(source
.substr(matchPos
, matchLen
)));
422 args
.append(jsNumber(matchPos
));
423 args
.append(sourceVal
);
425 replacementString
= replacementFunction
->call(exec
, exec
->dynamicGlobalObject(),
426 args
)->toString(exec
);
429 return jsString(source
.substr(0, matchPos
) + replacementString
+ source
.substr(matchPos
+ matchLen
));
432 JSValue
* stringProtoFuncToString(ExecState
* exec
, JSObject
* thisObj
, const List
&)
434 if (!thisObj
->inherits(&StringInstance::info
))
435 return throwError(exec
, TypeError
);
437 return static_cast<StringInstance
*>(thisObj
)->internalValue();
440 JSValue
* stringProtoFuncValueOf(ExecState
* exec
, JSObject
* thisObj
, const List
&)
442 if (!thisObj
->inherits(&StringInstance::info
))
443 return throwError(exec
, TypeError
);
445 return static_cast<StringInstance
*>(thisObj
)->internalValue();
448 JSValue
* stringProtoFuncCharAt(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
450 // This optimizes the common case that thisObj is a StringInstance
451 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
455 JSValue
* a0
= args
[0];
456 double dpos
= a0
->toInteger(exec
);
457 if (dpos
>= 0 && dpos
< len
)
458 u
= s
.substr(static_cast<int>(dpos
), 1);
464 JSValue
* stringProtoFuncCharCodeAt(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
466 // This optimizes the common case that thisObj is a StringInstance
467 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
472 JSValue
* a0
= args
[0];
473 double dpos
= a0
->toInteger(exec
);
474 if (dpos
>= 0 && dpos
< len
)
475 result
= jsNumber(s
[static_cast<int>(dpos
)].unicode());
481 JSValue
* stringProtoFuncConcat(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
483 // This optimizes the common case that thisObj is a StringInstance
484 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
486 List::const_iterator end
= args
.end();
487 for (List::const_iterator it
= args
.begin(); it
!= end
; ++it
) {
488 s
+= (*it
)->toString(exec
);
493 JSValue
* stringProtoFuncIndexOf(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
495 // This optimizes the common case that thisObj is a StringInstance
496 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
499 JSValue
* a0
= args
[0];
500 JSValue
* a1
= args
[1];
501 UString u2
= a0
->toString(exec
);
502 double dpos
= a1
->toInteger(exec
);
507 return jsNumber(s
.find(u2
, static_cast<int>(dpos
)));
510 JSValue
* stringProtoFuncLastIndexOf(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
512 // This optimizes the common case that thisObj is a StringInstance
513 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
516 JSValue
* a0
= args
[0];
517 JSValue
* a1
= args
[1];
519 UString u2
= a0
->toString(exec
);
520 double dpos
= a1
->toIntegerPreserveNaN(exec
);
523 else if (!(dpos
<= len
)) // true for NaN
525 return jsNumber(s
.rfind(u2
, static_cast<int>(dpos
)));
528 JSValue
* stringProtoFuncMatch(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
530 // This optimizes the common case that thisObj is a StringInstance
531 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
533 JSValue
* a0
= args
[0];
539 if (a0
->isObject() && static_cast<JSObject
*>(a0
)->inherits(&RegExpImp::info
)) {
540 reg
= static_cast<RegExpImp
*>(a0
)->regExp();
543 * ECMA 15.5.4.12 String.prototype.search (regexp)
544 * If regexp is not an object whose [[Class]] property is "RegExp", it is
545 * replaced with the result of the expression new RegExp(regexp).
547 reg
= tmpReg
= new RegExp(a0
->toString(exec
));
549 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalGlobalObject()->regExpConstructor());
552 regExpObj
->performMatch(reg
, u
, 0, pos
, matchLength
);
554 if (!(reg
->global())) {
555 // case without 'g' flag is handled like RegExp.prototype.exec
559 result
= regExpObj
->arrayOfMatches(exec
);
561 // return array of matches
565 list
.append(jsString(u
.substr(pos
, matchLength
)));
567 pos
+= matchLength
== 0 ? 1 : matchLength
;
568 regExpObj
->performMatch(reg
, u
, pos
, pos
, matchLength
);
571 imp
->setLastIndex(lastIndex
);
572 if (list
.isEmpty()) {
573 // if there are no matches at all, it's important to return
574 // Null instead of an empty array, because this matches
575 // other browsers and because Null is a false value.
578 result
= exec
->lexicalGlobalObject()->arrayConstructor()->construct(exec
, list
);
585 JSValue
* stringProtoFuncSearch(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
587 // This optimizes the common case that thisObj is a StringInstance
588 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
590 JSValue
* a0
= args
[0];
595 if (a0
->isObject() && static_cast<JSObject
*>(a0
)->inherits(&RegExpImp::info
)) {
596 reg
= static_cast<RegExpImp
*>(a0
)->regExp();
599 * ECMA 15.5.4.12 String.prototype.search (regexp)
600 * If regexp is not an object whose [[Class]] property is "RegExp", it is
601 * replaced with the result of the expression new RegExp(regexp).
603 reg
= tmpReg
= new RegExp(a0
->toString(exec
));
605 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalGlobalObject()->regExpConstructor());
608 regExpObj
->performMatch(reg
, u
, 0, pos
, matchLength
);
610 return jsNumber(pos
);
613 JSValue
* stringProtoFuncReplace(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
615 // This optimizes the common case that thisObj is a StringInstance
616 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
618 StringImp
* sVal
= thisObj
->inherits(&StringInstance::info
) ?
619 static_cast<StringInstance
*>(thisObj
)->internalValue() :
620 static_cast<StringImp
*>(jsString(s
));
622 JSValue
* a0
= args
[0];
623 JSValue
* a1
= args
[1];
625 return replace(exec
, sVal
, a0
, a1
);
628 JSValue
* stringProtoFuncSlice(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
630 // This optimizes the common case that thisObj is a StringInstance
631 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
634 JSValue
* a0
= args
[0];
635 JSValue
* a1
= args
[1];
637 // The arg processing is very much like ArrayProtoFunc::Slice
638 double start
= a0
->toInteger(exec
);
639 double end
= a1
->isUndefined() ? len
: a1
->toInteger(exec
);
640 double from
= start
< 0 ? len
+ start
: start
;
641 double to
= end
< 0 ? len
+ end
: end
;
642 if (to
> from
&& to
> 0 && from
< len
) {
647 return jsString(s
.substr(static_cast<int>(from
), static_cast<int>(to
- from
)));
653 JSValue
* stringProtoFuncSplit(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
655 // This optimizes the common case that thisObj is a StringInstance
656 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
658 JSValue
* a0
= args
[0];
659 JSValue
* a1
= args
[1];
661 JSObject
*constructor
= exec
->lexicalGlobalObject()->arrayConstructor();
662 JSObject
* res
= static_cast<JSObject
*>(constructor
->construct(exec
, exec
->emptyList()));
663 JSValue
* result
= res
;
668 uint32_t limit
= a1
->isUndefined() ? 0xFFFFFFFFU
: a1
->toUInt32(exec
);
669 if (a0
->isObject() && static_cast<JSObject
*>(a0
)->inherits(&RegExpImp::info
)) {
670 RegExp
*reg
= static_cast<RegExpImp
*>(a0
)->regExp();
671 if (u
.isEmpty() && reg
->match(u
, 0) >= 0) {
672 // empty string matched by regexp -> empty array
673 res
->put(exec
, exec
->propertyNames().length
, jsNumber(0));
677 while (static_cast<uint32_t>(i
) != limit
&& pos
< u
.size()) {
678 OwnArrayPtr
<int> ovector
;
679 int mpos
= reg
->match(u
, pos
, &ovector
);
682 int mlen
= ovector
[1] - ovector
[0];
683 pos
= mpos
+ (mlen
== 0 ? 1 : mlen
);
684 if (mpos
!= p0
|| mlen
) {
685 res
->put(exec
,i
, jsString(u
.substr(p0
, mpos
-p0
)));
689 for (unsigned si
= 1; si
<= reg
->numSubpatterns(); ++si
) {
690 int spos
= ovector
[si
* 2];
692 res
->put(exec
, i
++, jsUndefined());
694 res
->put(exec
, i
++, jsString(u
.substr(spos
, ovector
[si
* 2 + 1] - spos
)));
698 UString u2
= a0
->toString(exec
);
701 // empty separator matches empty string -> empty array
702 res
->put(exec
, exec
->propertyNames().length
, jsNumber(0));
705 while (static_cast<uint32_t>(i
) != limit
&& i
< u
.size()-1)
706 res
->put(exec
, i
++, jsString(u
.substr(p0
++, 1)));
709 while (static_cast<uint32_t>(i
) != limit
&& (pos
= u
.find(u2
, p0
)) >= 0) {
710 res
->put(exec
, i
, jsString(u
.substr(p0
, pos
-p0
)));
711 p0
= pos
+ u2
.size();
716 // add remaining string, if any
717 if (static_cast<uint32_t>(i
) != limit
)
718 res
->put(exec
, i
++, jsString(u
.substr(p0
)));
719 res
->put(exec
, exec
->propertyNames().length
, jsNumber(i
));
723 JSValue
* stringProtoFuncSubstr(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
725 // This optimizes the common case that thisObj is a StringInstance
726 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
729 JSValue
* a0
= args
[0];
730 JSValue
* a1
= args
[1];
732 double start
= a0
->toInteger(exec
);
733 double length
= a1
->isUndefined() ? len
: a1
->toInteger(exec
);
745 return jsString(s
.substr(static_cast<int>(start
), static_cast<int>(length
)));
748 JSValue
* stringProtoFuncSubstring(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
750 // This optimizes the common case that thisObj is a StringInstance
751 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
754 JSValue
* a0
= args
[0];
755 JSValue
* a1
= args
[1];
757 double start
= a0
->toNumber(exec
);
758 double end
= a1
->toNumber(exec
);
771 if (a1
->isUndefined())
778 return jsString(s
.substr((int)start
, (int)end
-(int)start
));
781 JSValue
* stringProtoFuncToLowerCase(ExecState
* exec
, JSObject
* thisObj
, const List
&)
783 // This optimizes the common case that thisObj is a StringInstance
784 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
786 StringImp
* sVal
= thisObj
->inherits(&StringInstance::info
)
787 ? static_cast<StringInstance
*>(thisObj
)->internalValue()
788 : static_cast<StringImp
*>(jsString(s
));
789 int ssize
= s
.size();
792 Vector
< ::UChar
> buffer(ssize
);
794 int length
= Unicode::toLower(buffer
.data(), ssize
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
796 buffer
.resize(length
);
797 length
= Unicode::toLower(buffer
.data(), length
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
801 if (length
== ssize
&& memcmp(buffer
.data(), s
.data(), length
* sizeof(UChar
)) == 0)
803 return jsString(UString(reinterpret_cast<UChar
*>(buffer
.releaseBuffer()), length
, false));
806 JSValue
* stringProtoFuncToUpperCase(ExecState
* exec
, JSObject
* thisObj
, const List
&)
808 // This optimizes the common case that thisObj is a StringInstance
809 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
811 StringImp
* sVal
= thisObj
->inherits(&StringInstance::info
)
812 ? static_cast<StringInstance
*>(thisObj
)->internalValue()
813 : static_cast<StringImp
*>(jsString(s
));
814 int ssize
= s
.size();
817 Vector
< ::UChar
> buffer(ssize
);
819 int length
= Unicode::toUpper(buffer
.data(), ssize
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
821 buffer
.resize(length
);
822 length
= Unicode::toUpper(buffer
.data(), length
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
826 if (length
== ssize
&& memcmp(buffer
.data(), s
.data(), length
* sizeof(UChar
)) == 0)
828 return jsString(UString(reinterpret_cast<UChar
*>(buffer
.releaseBuffer()), length
, false));
831 JSValue
* stringProtoFuncToLocaleLowerCase(ExecState
* exec
, JSObject
* thisObj
, const List
&)
833 // This optimizes the common case that thisObj is a StringInstance
834 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
836 // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
837 StringImp
* sVal
= thisObj
->inherits(&StringInstance::info
)
838 ? static_cast<StringInstance
*>(thisObj
)->internalValue()
839 : static_cast<StringImp
*>(jsString(s
));
840 int ssize
= s
.size();
843 Vector
< ::UChar
> buffer(ssize
);
845 int length
= Unicode::toLower(buffer
.data(), ssize
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
847 buffer
.resize(length
);
848 length
= Unicode::toLower(buffer
.data(), length
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
852 if (length
== ssize
&& memcmp(buffer
.data(), s
.data(), length
* sizeof(UChar
)) == 0)
854 return jsString(UString(reinterpret_cast<UChar
*>(buffer
.releaseBuffer()), length
, false));
857 JSValue
* stringProtoFuncToLocaleUpperCase(ExecState
* exec
, JSObject
* thisObj
, const List
&)
859 // This optimizes the common case that thisObj is a StringInstance
860 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
862 StringImp
* sVal
= thisObj
->inherits(&StringInstance::info
)
863 ? static_cast<StringInstance
*>(thisObj
)->internalValue()
864 : static_cast<StringImp
*>(jsString(s
));
865 int ssize
= s
.size();
868 Vector
< ::UChar
> buffer(ssize
);
870 int length
= Unicode::toUpper(buffer
.data(), ssize
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
872 buffer
.resize(length
);
873 length
= Unicode::toUpper(buffer
.data(), length
, reinterpret_cast<const ::UChar
*>(s
.data()), ssize
, &error
);
877 if (length
== ssize
&& memcmp(buffer
.data(), s
.data(), length
* sizeof(UChar
)) == 0)
879 return jsString(UString(reinterpret_cast<UChar
*>(buffer
.releaseBuffer()), length
, false));
882 JSValue
* stringProtoFuncLocaleCompare(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
887 // This optimizes the common case that thisObj is a StringInstance
888 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
889 JSValue
* a0
= args
[0];
890 return jsNumber(localeCompare(s
, a0
->toString(exec
)));
893 JSValue
* stringProtoFuncBig(ExecState
* exec
, JSObject
* thisObj
, const List
&)
895 // This optimizes the common case that thisObj is a StringInstance
896 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
897 return jsString("<big>" + s
+ "</big>");
900 JSValue
* stringProtoFuncSmall(ExecState
* exec
, JSObject
* thisObj
, const List
&)
902 // This optimizes the common case that thisObj is a StringInstance
903 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
904 return jsString("<small>" + s
+ "</small>");
907 JSValue
* stringProtoFuncBlink(ExecState
* exec
, JSObject
* thisObj
, const List
&)
909 // This optimizes the common case that thisObj is a StringInstance
910 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
911 return jsString("<blink>" + s
+ "</blink>");
914 JSValue
* stringProtoFuncBold(ExecState
* exec
, JSObject
* thisObj
, const List
&)
916 // This optimizes the common case that thisObj is a StringInstance
917 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
918 return jsString("<b>" + s
+ "</b>");
921 JSValue
* stringProtoFuncFixed(ExecState
* exec
, JSObject
* thisObj
, const List
&)
923 // This optimizes the common case that thisObj is a StringInstance
924 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
925 return jsString("<tt>" + s
+ "</tt>");
928 JSValue
* stringProtoFuncItalics(ExecState
* exec
, JSObject
* thisObj
, const List
&)
930 // This optimizes the common case that thisObj is a StringInstance
931 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
932 return jsString("<i>" + s
+ "</i>");
935 JSValue
* stringProtoFuncStrike(ExecState
* exec
, JSObject
* thisObj
, const List
&)
937 // This optimizes the common case that thisObj is a StringInstance
938 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
939 return jsString("<strike>" + s
+ "</strike>");
942 JSValue
* stringProtoFuncSub(ExecState
* exec
, JSObject
* thisObj
, const List
&)
944 // This optimizes the common case that thisObj is a StringInstance
945 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
946 return jsString("<sub>" + s
+ "</sub>");
949 JSValue
* stringProtoFuncSup(ExecState
* exec
, JSObject
* thisObj
, const List
&)
951 // This optimizes the common case that thisObj is a StringInstance
952 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
953 return jsString("<sup>" + s
+ "</sup>");
956 JSValue
* stringProtoFuncFontcolor(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
958 // This optimizes the common case that thisObj is a StringInstance
959 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
960 JSValue
* a0
= args
[0];
961 return jsString("<font color=\"" + a0
->toString(exec
) + "\">" + s
+ "</font>");
964 JSValue
* stringProtoFuncFontsize(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
966 // This optimizes the common case that thisObj is a StringInstance
967 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
968 JSValue
* a0
= args
[0];
969 return jsString("<font size=\"" + a0
->toString(exec
) + "\">" + s
+ "</font>");
972 JSValue
* stringProtoFuncAnchor(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
974 // This optimizes the common case that thisObj is a StringInstance
975 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
976 JSValue
* a0
= args
[0];
977 return jsString("<a name=\"" + a0
->toString(exec
) + "\">" + s
+ "</a>");
980 JSValue
* stringProtoFuncLink(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
982 // This optimizes the common case that thisObj is a StringInstance
983 UString s
= thisObj
->inherits(&StringInstance::info
) ? static_cast<StringInstance
*>(thisObj
)->internalValue()->value() : thisObj
->toString(exec
);
984 JSValue
* a0
= args
[0];
985 return jsString("<a href=\"" + a0
->toString(exec
) + "\">" + s
+ "</a>");
988 // ------------------------------ StringObjectImp ------------------------------
990 StringObjectImp::StringObjectImp(ExecState
* exec
, FunctionPrototype
* funcProto
, StringPrototype
* stringProto
)
991 : InternalFunctionImp(funcProto
, stringProto
->classInfo()->className
)
993 // ECMA 15.5.3.1 String.prototype
994 putDirect(exec
->propertyNames().prototype
, stringProto
, DontEnum
|DontDelete
|ReadOnly
);
996 putDirectFunction(new StringObjectFuncImp(exec
, funcProto
, exec
->propertyNames().fromCharCode
), DontEnum
);
998 // no. of arguments for constructor
999 putDirect(exec
->propertyNames().length
, jsNumber(1), ReadOnly
|DontDelete
|DontEnum
);
1003 bool StringObjectImp::implementsConstruct() const
1009 JSObject
*StringObjectImp::construct(ExecState
*exec
, const List
&args
)
1011 JSObject
*proto
= exec
->lexicalGlobalObject()->stringPrototype();
1012 if (args
.size() == 0)
1013 return new StringInstance(proto
);
1014 return new StringInstance(proto
, args
[0]->toString(exec
));
1018 JSValue
*StringObjectImp::callAsFunction(ExecState
*exec
, JSObject
* /*thisObj*/, const List
&args
)
1021 return jsString("");
1023 JSValue
*v
= args
[0];
1024 return jsString(v
->toString(exec
));
1028 // ------------------------------ StringObjectFuncImp --------------------------
1030 // ECMA 15.5.3.2 fromCharCode()
1031 StringObjectFuncImp::StringObjectFuncImp(ExecState
* exec
, FunctionPrototype
* funcProto
, const Identifier
& name
)
1032 : InternalFunctionImp(funcProto
, name
)
1034 putDirect(exec
->propertyNames().length
, jsNumber(1), DontDelete
|ReadOnly
|DontEnum
);
1037 JSValue
*StringObjectFuncImp::callAsFunction(ExecState
*exec
, JSObject
* /*thisObj*/, const List
&args
)
1041 UChar
*buf
= static_cast<UChar
*>(fastMalloc(args
.size() * sizeof(UChar
)));
1043 List::const_iterator end
= args
.end();
1044 for (List::const_iterator it
= args
.begin(); it
!= end
; ++it
) {
1045 unsigned short u
= static_cast<unsigned short>((*it
)->toUInt32(exec
));
1048 s
= UString(buf
, args
.size(), false);