1 // -*- c-basic-offset: 2 -*-
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5 * Copyright (C) 2004 Apple Computer, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "string_object.h"
24 #include "string_object.lut.h"
27 #include "JSWrapperObject.h"
28 #include "error_object.h"
29 #include "operations.h"
30 #include "PropertyNameArray.h"
31 #include "regexp_object.h"
32 #include <wtf/unicode/libc/UnicodeLibC.h>
42 // ------------------------------ StringInstance ----------------------------
44 const ClassInfo
StringInstance::info
= {"String", 0, 0, 0};
46 StringInstance::StringInstance(JSObject
*proto
)
47 : JSWrapperObject(proto
), m_conversionsCustomized(false)
49 setInternalValue(jsString(""));
52 StringInstance::StringInstance(JSObject
*proto
, StringImp
* string
)
53 : JSWrapperObject(proto
), m_conversionsCustomized(false)
55 setInternalValue(string
);
58 StringInstance::StringInstance(JSObject
*proto
, const UString
&string
)
59 : JSWrapperObject(proto
), m_conversionsCustomized(false)
61 setInternalValue(jsString(string
));
64 JSValue
*StringInstance::lengthGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
&slot
)
66 return jsNumber(static_cast<StringInstance
*>(slot
.slotBase())->internalValue()->value().size());
69 JSValue
*StringInstance::indexGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
&slot
)
71 const UChar c
= static_cast<StringInstance
*>(slot
.slotBase())->internalValue()->value()[slot
.index()];
72 return jsString(UString(&c
, 1));
75 bool StringInstance::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
77 if (propertyName
== exec
->propertyNames().length
) {
78 slot
.setCustom(this, lengthGetter
);
83 unsigned i
= propertyName
.toStrictUInt32(&isStrictUInt32
);
84 unsigned length
= internalValue()->value().size();
85 if (isStrictUInt32
&& i
< length
) {
86 slot
.setCustomIndex(this, i
, indexGetter
);
90 return JSObject::getOwnPropertySlot(exec
, propertyName
, slot
);
93 bool StringInstance::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
95 unsigned length
= internalValue()->value().size();
96 if (propertyName
< length
) {
97 slot
.setCustomIndex(this, propertyName
, indexGetter
);
101 return JSObject::getOwnPropertySlot(exec
, Identifier::from(propertyName
), slot
);
104 void StringInstance::put(ExecState
*exec
, const Identifier
&propertyName
, JSValue
*value
, int attr
)
106 if (propertyName
== exec
->propertyNames().length
)
109 if (propertyName
== exec
->propertyNames().valueOf
|| propertyName
== exec
->propertyNames().toString
)
110 m_conversionsCustomized
= true;
112 JSObject::put(exec
, propertyName
, value
, attr
);
115 bool StringInstance::deleteProperty(ExecState
*exec
, const Identifier
&propertyName
)
117 if (propertyName
== exec
->propertyNames().length
)
119 return JSObject::deleteProperty(exec
, propertyName
);
122 void StringInstance::getOwnPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
124 int size
= internalValue()->getString().size();
125 for (int i
= 0; i
< size
; i
++)
126 propertyNames
.add(Identifier(UString::from(i
)));
127 return JSObject::getOwnPropertyNames(exec
, propertyNames
);
130 UString
StringInstance::toString(ExecState
*exec
) const
132 if (prototype() == originalProto() && !conversionsCustomized() &&
133 !static_cast<StringPrototype
*>(prototype())->conversionsCustomized())
134 return internalValue()->value();
136 return JSObject::toString(exec
);
139 // ------------------------------ StringPrototype ---------------------------
140 const ClassInfo
StringPrototype::info
= {"String", &StringInstance::info
, &stringTable
, 0};
141 /* Source for string_object.lut.h
142 @begin stringTable 26
143 toString StringProtoFunc::ToString DontEnum|Function 0
144 valueOf StringProtoFunc::ValueOf DontEnum|Function 0
145 charAt StringProtoFunc::CharAt DontEnum|Function 1
146 charCodeAt StringProtoFunc::CharCodeAt DontEnum|Function 1
147 concat StringProtoFunc::Concat DontEnum|Function 1
148 indexOf StringProtoFunc::IndexOf DontEnum|Function 1
149 lastIndexOf StringProtoFunc::LastIndexOf DontEnum|Function 1
150 match StringProtoFunc::Match DontEnum|Function 1
151 replace StringProtoFunc::Replace DontEnum|Function 2
152 search StringProtoFunc::Search DontEnum|Function 1
153 slice StringProtoFunc::Slice DontEnum|Function 2
154 split StringProtoFunc::Split DontEnum|Function 2
155 substr StringProtoFunc::Substr DontEnum|Function 2
156 substring StringProtoFunc::Substring DontEnum|Function 2
157 toLowerCase StringProtoFunc::ToLowerCase DontEnum|Function 0
158 toUpperCase StringProtoFunc::ToUpperCase DontEnum|Function 0
159 toLocaleLowerCase StringProtoFunc::ToLocaleLowerCase DontEnum|Function 0
160 toLocaleUpperCase StringProtoFunc::ToLocaleUpperCase DontEnum|Function 0
161 localeCompare StringProtoFunc::LocaleCompare DontEnum|Function 1
163 # Under here: html extension, should only exist if KJS_PURE_ECMA is not defined
164 # I guess we need to generate two hashtables in the .lut.h file, and use #ifdef
165 # to select the right one... TODO. #####
166 big StringProtoFunc::Big DontEnum|Function 0
167 small StringProtoFunc::Small DontEnum|Function 0
168 blink StringProtoFunc::Blink DontEnum|Function 0
169 bold StringProtoFunc::Bold DontEnum|Function 0
170 fixed StringProtoFunc::Fixed DontEnum|Function 0
171 italics StringProtoFunc::Italics DontEnum|Function 0
172 strike StringProtoFunc::Strike DontEnum|Function 0
173 sub StringProtoFunc::Sub DontEnum|Function 0
174 sup StringProtoFunc::Sup DontEnum|Function 0
175 fontcolor StringProtoFunc::Fontcolor DontEnum|Function 1
176 fontsize StringProtoFunc::Fontsize DontEnum|Function 1
177 anchor StringProtoFunc::Anchor DontEnum|Function 1
178 link StringProtoFunc::Link DontEnum|Function 1
182 StringPrototype::StringPrototype(ExecState
* exec
, ObjectPrototype
* objProto
)
183 : StringInstance(objProto
)
185 // The constructor will be added later, after StringObjectImp has been built
186 putDirect(exec
->propertyNames().length
, jsNumber(0), DontDelete
| ReadOnly
| DontEnum
);
189 bool StringPrototype::getOwnPropertySlot(ExecState
*exec
, const Identifier
& propertyName
, PropertySlot
&slot
)
191 return getStaticFunctionSlot
<StringProtoFunc
, StringInstance
>(exec
, &stringTable
, this, propertyName
, slot
);
194 // ------------------------------ StringProtoFunc ---------------------------
196 StringProtoFunc::StringProtoFunc(ExecState
* exec
, int i
, int len
, const Identifier
& name
)
197 : InternalFunctionImp(static_cast<FunctionPrototype
*>(exec
->lexicalInterpreter()->builtinFunctionPrototype()), name
)
200 putDirect(exec
->propertyNames().length
, len
, DontDelete
| ReadOnly
| DontEnum
);
203 static inline void expandSourceRanges(UString::Range
* & array
, int& count
, int& capacity
)
209 newCapacity
= capacity
* 2;
212 UString::Range
*newArray
= new UString::Range
[newCapacity
];
213 for (int i
= 0; i
< count
; i
++) {
214 newArray
[i
] = array
[i
];
219 capacity
= newCapacity
;
223 static void pushSourceRange(UString::Range
* & array
, int& count
, int& capacity
, UString::Range range
)
225 if (count
+ 1 > capacity
)
226 expandSourceRanges(array
, count
, capacity
);
228 array
[count
] = range
;
232 static inline void expandReplacements(UString
* & array
, int& count
, int& capacity
)
238 newCapacity
= capacity
* 2;
241 UString
*newArray
= new UString
[newCapacity
];
242 for (int i
= 0; i
< count
; i
++) {
243 newArray
[i
] = array
[i
];
248 capacity
= newCapacity
;
252 static void pushReplacement(UString
* & array
, int& count
, int& capacity
, UString replacement
)
254 if (count
+ 1 > capacity
)
255 expandReplacements(array
, count
, capacity
);
257 array
[count
] = replacement
;
261 static inline UString
substituteBackreferences(const UString
&replacement
, const UString
&source
, int *ovector
, RegExp
*reg
)
263 UString substitutedReplacement
= replacement
;
266 while ((i
= substitutedReplacement
.find(UString("$"), i
+ 1)) != -1) {
267 if (i
+1 == substitutedReplacement
.size())
270 unsigned short ref
= substitutedReplacement
[i
+1].unicode();
271 int backrefStart
= 0;
272 int backrefLength
= 0;
275 if (ref
== '$') { // "$$" -> "$"
276 substitutedReplacement
= substitutedReplacement
.substr(0, i
+ 1) + substitutedReplacement
.substr(i
+ 2);
278 } else if (ref
== '&') {
279 backrefStart
= ovector
[0];
280 backrefLength
= ovector
[1] - backrefStart
;
281 } else if (ref
== '`') {
283 backrefLength
= ovector
[0];
284 } else if (ref
== '\'') {
285 backrefStart
= ovector
[1];
286 backrefLength
= source
.size() - backrefStart
;
287 } else if (ref
>= '0' && ref
<= '9') {
288 // 1- and 2-digit back references are allowed
289 unsigned backrefIndex
= ref
- '0';
290 if (backrefIndex
> (unsigned)reg
->subPatterns())
292 if (substitutedReplacement
.size() > i
+ 2) {
293 ref
= substitutedReplacement
[i
+2].unicode();
294 if (ref
>= '0' && ref
<= '9') {
295 backrefIndex
= 10 * backrefIndex
+ ref
- '0';
296 if (backrefIndex
> (unsigned)reg
->subPatterns())
297 backrefIndex
= backrefIndex
/ 10; // Fall back to the 1-digit reference
302 backrefStart
= ovector
[2 * backrefIndex
];
303 backrefLength
= ovector
[2 * backrefIndex
+ 1] - backrefStart
;
307 substitutedReplacement
= substitutedReplacement
.substr(0, i
) + source
.substr(backrefStart
, backrefLength
) + substitutedReplacement
.substr(i
+ 2 + advance
);
308 i
+= backrefLength
- 1; // - 1 offsets 'i + 1'
311 return substitutedReplacement
;
314 static inline int localeCompare(const UString
& a
, const UString
& b
)
317 int retval
= CompareStringW(LOCALE_USER_DEFAULT
, 0,
318 reinterpret_cast<LPCWSTR
>(a
.data()), a
.size(),
319 reinterpret_cast<LPCWSTR
>(b
.data()), b
.size());
320 return !retval
? retval
: retval
- 2;
322 CFStringRef sa
= CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault
, reinterpret_cast<const UniChar
*>(a
.data()), a
.size(), kCFAllocatorNull
);
323 CFStringRef sb
= CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault
, reinterpret_cast<const UniChar
*>(b
.data()), b
.size(), kCFAllocatorNull
);
325 int retval
= CFStringCompare(sa
, sb
, kCFCompareLocalized
);
332 // ### use as fallback only. implement locale aware version.
333 // ### other browsers have more detailed return values than -1, 0 and 1
334 return compare(a
, b
);
338 static JSValue
*replace(ExecState
*exec
, const UString
&source
, JSValue
*pattern
, JSValue
*replacement
)
340 JSObject
*replacementFunction
= 0;
341 UString replacementString
;
343 if (replacement
->isObject() && replacement
->toObject(exec
)->implementsCall())
344 replacementFunction
= replacement
->toObject(exec
);
346 replacementString
= replacement
->toString(exec
);
348 if (pattern
->isObject() && static_cast<JSObject
*>(pattern
)->inherits(&RegExpImp::info
)) {
349 RegExp
*reg
= static_cast<RegExpImp
*>(pattern
)->regExp();
350 bool global
= reg
->flags() & RegExp::Global
;
352 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalInterpreter()->builtinRegExp());
356 int startPosition
= 0;
358 UString::Range
*sourceRanges
= 0;
359 int sourceRangeCount
= 0;
360 int sourceRangeCapacity
= 0;
361 UString
*replacements
= 0;
362 int replacementCount
= 0;
363 int replacementCapacity
= 0;
365 // This is either a loop (if global is set) or a one-way (if not).
366 reg
->prepareMatch(source
);
369 UString matchString
= regExpObj
->performMatch(reg
, exec
, source
, startPosition
, &matchIndex
, &ovector
);
370 if (matchIndex
== -1)
372 int matchLen
= matchString
.size();
374 pushSourceRange(sourceRanges
, sourceRangeCount
, sourceRangeCapacity
, UString::Range(lastIndex
, matchIndex
- lastIndex
));
375 UString substitutedReplacement
;
376 if (replacementFunction
) {
377 int completeMatchStart
= ovector
[0];
380 args
.append(jsString(matchString
));
382 for (unsigned i
= 0; i
< reg
->subPatterns(); i
++) {
383 int matchStart
= ovector
[(i
+ 1) * 2];
384 int matchLen
= ovector
[(i
+ 1) * 2 + 1] - matchStart
;
386 args
.append(jsString(source
.substr(matchStart
, matchLen
)));
389 args
.append(jsNumber(completeMatchStart
));
390 args
.append(jsString(source
));
392 substitutedReplacement
= replacementFunction
->call(exec
, exec
->dynamicInterpreter()->globalObject(),
393 args
)->toString(exec
);
395 substitutedReplacement
= substituteBackreferences(replacementString
, source
, ovector
, reg
);
397 pushReplacement(replacements
, replacementCount
, replacementCapacity
, substitutedReplacement
);
399 lastIndex
= matchIndex
+ matchLen
;
400 startPosition
= lastIndex
;
402 // special case of empty match
405 if (startPosition
> source
.size())
412 if (lastIndex
< source
.size())
413 pushSourceRange(sourceRanges
, sourceRangeCount
, sourceRangeCapacity
, UString::Range(lastIndex
, source
.size() - lastIndex
));
417 result
= source
.spliceSubstringsWithSeparators(sourceRanges
, sourceRangeCount
, replacements
, replacementCount
);
419 delete [] sourceRanges
;
420 delete [] replacements
;
422 return jsString(result
);
425 // First arg is a string
426 UString patternString
= pattern
->toString(exec
);
427 int matchPos
= source
.find(patternString
);
428 int matchLen
= patternString
.size();
429 // Do the replacement
431 return jsString(source
);
433 if (replacementFunction
) {
436 args
.append(jsString(source
.substr(matchPos
, matchLen
)));
437 args
.append(jsNumber(matchPos
));
438 args
.append(jsString(source
));
440 replacementString
= replacementFunction
->call(exec
, exec
->dynamicInterpreter()->globalObject(),
441 args
)->toString(exec
);
444 return jsString(source
.substr(0, matchPos
) + replacementString
+ source
.substr(matchPos
+ matchLen
));
447 static UnicodeSupport::StringConversionFunction toUpperF
= Unicode::toUpper
;
448 static UnicodeSupport::StringConversionFunction toLowerF
= Unicode::toLower
;
450 void StringProtoFunc::setToLowerFunction(UnicodeSupport::StringConversionFunction f
)
455 void StringProtoFunc::setToUpperFunction(UnicodeSupport::StringConversionFunction f
)
461 // ECMA 15.5.4.2 - 15.5.4.20
462 JSValue
*StringProtoFunc::callAsFunction(ExecState
* exec
, JSObject
* thisObj
, const List
& args
)
464 JSValue
* result
= NULL
;
466 // toString and valueOf are no generic function.
467 if (id
== ToString
|| id
== ValueOf
) {
468 if (!thisObj
|| !thisObj
->inherits(&StringInstance::info
))
469 return throwError(exec
, TypeError
);
471 return jsString(static_cast<StringInstance
*>(thisObj
)->internalValue()->toString(exec
));
479 UString s
= thisObj
->toString(exec
);
482 JSValue
*a0
= args
[0];
483 JSValue
*a1
= args
[1];
491 // Other browsers treat an omitted parameter as 0 rather than NaN.
492 // That doesn't match the ECMA standard, but is needed for site compatibility.
493 dpos
= a0
->isUndefined() ? 0 : a0
->toInteger(exec
);
494 if (dpos
>= 0 && dpos
< len
) // false for NaN
495 u
= s
.substr(static_cast<int>(dpos
), 1);
498 result
= jsString(u
);
501 // Other browsers treat an omitted parameter as 0 rather than NaN.
502 // That doesn't match the ECMA standard, but is needed for site compatibility.
503 dpos
= a0
->isUndefined() ? 0 : a0
->toInteger(exec
);
504 if (dpos
>= 0 && dpos
< len
) // false for NaN
505 result
= jsNumber(s
[static_cast<int>(dpos
)].unicode());
510 ListIterator it
= args
.begin();
511 for ( ; it
!= args
.end() ; ++it
) {
512 s
+= it
->toString(exec
);
514 result
= jsString(s
);
518 u2
= a0
->toString(exec
);
519 if (a1
->isUndefined())
522 dpos
= a1
->toInteger(exec
);
523 if (dpos
>= 0) { // false for NaN
529 result
= jsNumber(s
.find(u2
, static_cast<int>(dpos
)));
532 u2
= a0
->toString(exec
);
533 d
= a1
->toNumber(exec
);
534 if (a1
->isUndefined() || KJS::isNaN(d
))
537 dpos
= a1
->toInteger(exec
);
538 if (dpos
>= 0) { // false for NaN
544 result
= jsNumber(s
.rfind(u2
, static_cast<int>(dpos
)));
549 RegExp
*reg
, *tmpReg
= 0;
551 if (a0
->isObject() && static_cast<JSObject
*>(a0
)->inherits(&RegExpImp::info
)) {
552 reg
= static_cast<RegExpImp
*>(a0
)->regExp();
555 * ECMA 15.5.4.12 String.prototype.search (regexp)
556 * If regexp is not an object whose [[Class]] property is "RegExp", it is
557 * replaced with the result of the expression new RegExp(regexp).
559 reg
= tmpReg
= new RegExp(a0
->toString(exec
), RegExp::None
);
561 if (!reg
->isValid()) {
563 return throwError(exec
, SyntaxError
, "Invalid regular expression");
565 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalInterpreter()->builtinRegExp());
566 reg
->prepareMatch(u
);
567 UString mstr
= regExpObj
->performMatch(reg
, exec
, u
, 0, &pos
);
570 result
= jsNumber(pos
);
573 if ((reg
->flags() & RegExp::Global
) == 0) {
574 // case without 'g' flag is handled like RegExp.prototype.exec
578 result
= regExpObj
->arrayOfMatches(exec
,mstr
);
581 // return array of matches
586 list
.append(jsUndefined());
588 list
.append(jsString(mstr
));
590 pos
+= mstr
.isEmpty() ? 1 : mstr
.size();
591 mstr
= regExpObj
->performMatch(reg
, exec
, u
, pos
, &pos
);
594 imp
->put(exec
, "lastIndex", jsNumber(lastIndex
), DontDelete
|DontEnum
);
595 if (list
.isEmpty()) {
596 // if there are no matches at all, it's important to return
597 // Null instead of an empty array, because this matches
598 // other browsers and because Null is a false value.
601 result
= exec
->lexicalInterpreter()->builtinArray()->construct(exec
, list
);
611 result
= replace(exec
, s
, a0
, a1
);
615 // The arg processing is very much like ArrayProtoFunc::Slice
616 double start
= a0
->toInteger(exec
);
617 double end
= a1
->isUndefined() ? len
: a1
->toInteger(exec
);
618 double from
= start
< 0 ? len
+ start
: start
;
619 double to
= end
< 0 ? len
+ end
: end
;
620 if (to
> from
&& to
> 0 && from
< len
) {
625 result
= jsString(s
.substr(static_cast<int>(from
), static_cast<int>(to
- from
)));
627 result
= jsString("");
632 JSObject
*constructor
= exec
->lexicalInterpreter()->builtinArray();
633 JSObject
*res
= static_cast<JSObject
*>(constructor
->construct(exec
,List::empty()));
637 uint32_t limit
= a1
->isUndefined() ? 0xFFFFFFFFU
: a1
->toUInt32(exec
);
638 if (a0
->isObject() && static_cast<JSObject
*>(a0
)->inherits(&RegExpImp::info
)) {
639 RegExp
*reg
= static_cast<RegExpImp
*>(a0
)->regExp();
640 reg
->prepareMatch(u
);
642 if (u
.isEmpty() && !reg
->match(u
, &error
, 0).isNull()) {
645 // empty string matched by regexp -> empty array
646 res
->put(exec
, exec
->propertyNames().length
, jsNumber(0));
650 while (!error
&& static_cast<uint32_t>(i
) != limit
&& pos
< u
.size()) {
651 // TODO: back references
654 UString mstr
= reg
->match(u
, &error
, pos
, &mpos
, &ovector
);
655 delete [] ovector
; ovector
= 0L;
658 pos
= mpos
+ (mstr
.isEmpty() ? 1 : mstr
.size());
659 if (mpos
!= p0
|| !mstr
.isEmpty()) {
660 res
->put(exec
,i
, jsString(u
.substr(p0
, mpos
-p0
)));
661 p0
= mpos
+ mstr
.size();
667 RegExpObjectImp::throwRegExpError(exec
);
670 u2
= a0
->toString(exec
);
673 // empty separator matches empty string -> empty array
674 put(exec
, exec
->propertyNames().length
, jsNumber(0));
677 while (static_cast<uint32_t>(i
) != limit
&& i
< u
.size()-1)
678 res
->put(exec
, i
++, jsString(u
.substr(p0
++, 1)));
681 while (static_cast<uint32_t>(i
) != limit
&& (pos
= u
.find(u2
, p0
)) >= 0) {
682 res
->put(exec
, i
, jsString(u
.substr(p0
, pos
-p0
)));
683 p0
= pos
+ u2
.size();
688 // add remaining string, if any
689 if (static_cast<uint32_t>(i
) != limit
)
690 res
->put(exec
, i
++, jsString(u
.substr(p0
)));
691 res
->put(exec
, exec
->propertyNames().length
, jsNumber(i
));
694 case Substr
: { //B.2.3
695 // Note: NaN is effectively handled as 0 here for both length
696 // and start, hence toInteger does fine, and removes worries
697 // about weird comparison results below.
699 double start
= a0
->toInteger(exec
);
700 double length
= a1
->isUndefined() ? len
: a1
->toInteger(exec
);
712 if (length
> len
- start
)
713 length
= len
- start
;
715 result
= jsString(s
.substr(static_cast<int>(start
), static_cast<int>(length
)));
719 double start
= a0
->toNumber(exec
);
720 double end
= a1
->toNumber(exec
);
733 if (a1
->isUndefined())
740 result
= jsString(s
.substr((int)start
, (int)end
-(int)start
));
744 case ToLocaleLowerCase
: { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
747 uint16_t* dataPtr
= reinterpret_cast<uint16_t*>(u
.rep()->data());
748 uint16_t* destIfNeeded
;
750 int len
= toLowerF(dataPtr
, u
.size(), destIfNeeded
);
752 result
= jsString(UString(reinterpret_cast<UChar
*>(destIfNeeded
? destIfNeeded
: dataPtr
), len
));
754 result
= jsString(s
);
760 case ToLocaleUpperCase
: { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
763 uint16_t* dataPtr
= reinterpret_cast<uint16_t*>(u
.rep()->data());
764 uint16_t* destIfNeeded
;
766 int len
= toUpperF(dataPtr
, u
.size(), destIfNeeded
);
768 result
= jsString(UString(reinterpret_cast<UChar
*>(destIfNeeded
? destIfNeeded
: dataPtr
), len
));
770 result
= jsString(s
);
778 return jsNumber(localeCompare(s
, a0
->toString(exec
)));
779 #ifndef KJS_PURE_ECMA
781 result
= jsString("<big>" + s
+ "</big>");
784 result
= jsString("<small>" + s
+ "</small>");
787 result
= jsString("<blink>" + s
+ "</blink>");
790 result
= jsString("<b>" + s
+ "</b>");
793 result
= jsString("<tt>" + s
+ "</tt>");
796 result
= jsString("<i>" + s
+ "</i>");
799 result
= jsString("<strike>" + s
+ "</strike>");
802 result
= jsString("<sub>" + s
+ "</sub>");
805 result
= jsString("<sup>" + s
+ "</sup>");
808 result
= jsString("<font color=\"" + a0
->toString(exec
) + "\">" + s
+ "</font>");
811 result
= jsString("<font size=\"" + a0
->toString(exec
) + "\">" + s
+ "</font>");
814 result
= jsString("<a name=\"" + a0
->toString(exec
) + "\">" + s
+ "</a>");
817 result
= jsString("<a href=\"" + a0
->toString(exec
) + "\">" + s
+ "</a>");
825 // ------------------------------ StringObjectImp ------------------------------
827 StringObjectImp::StringObjectImp(ExecState
* exec
,
828 FunctionPrototype
* funcProto
,
829 StringPrototype
* stringProto
)
830 : InternalFunctionImp(funcProto
)
832 // ECMA 15.5.3.1 String.prototype
833 putDirect(exec
->propertyNames().prototype
, stringProto
, DontEnum
|DontDelete
|ReadOnly
);
835 putDirectFunction(new StringObjectFuncImp(exec
, funcProto
, exec
->propertyNames().fromCharCode
), DontEnum
);
837 // no. of arguments for constructor
838 putDirect(exec
->propertyNames().length
, jsNumber(1), ReadOnly
|DontDelete
|DontEnum
);
842 bool StringObjectImp::implementsConstruct() const
848 JSObject
*StringObjectImp::construct(ExecState
*exec
, const List
&args
)
850 JSObject
*proto
= exec
->lexicalInterpreter()->builtinStringPrototype();
851 if (args
.size() == 0)
852 return new StringInstance(proto
);
853 return new StringInstance(proto
, args
.begin()->toString(exec
));
857 JSValue
*StringObjectImp::callAsFunction(ExecState
*exec
, JSObject
* /*thisObj*/, const List
&args
)
862 JSValue
*v
= args
[0];
863 return jsString(v
->toString(exec
));
867 // ------------------------------ StringObjectFuncImp --------------------------
869 // ECMA 15.5.3.2 fromCharCode()
870 StringObjectFuncImp::StringObjectFuncImp(ExecState
* exec
, FunctionPrototype
* funcProto
, const Identifier
& name
)
871 : InternalFunctionImp(funcProto
, name
)
873 putDirect(exec
->propertyNames().length
, jsNumber(1), DontDelete
|ReadOnly
|DontEnum
);
876 JSValue
*StringObjectFuncImp::callAsFunction(ExecState
*exec
, JSObject
* /*thisObj*/, const List
&args
)
880 UChar
*buf
= static_cast<UChar
*>(fastMalloc(args
.size() * sizeof(UChar
)));
882 ListIterator it
= args
.begin();
883 while (it
!= args
.end()) {
884 unsigned short u
= it
->toUInt16(exec
);
888 s
= UString(buf
, args
.size(), false);