]>
Commit | Line | Data |
---|---|---|
b37bf2e1 A |
1 | // -*- c-basic-offset: 2 -*- |
2 | /* | |
3 | * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) | |
4 | * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | |
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 "string_object.h" | |
24 | #include "string_object.lut.h" | |
25 | ||
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> | |
34 | ||
35 | #if PLATFORM(CF) | |
36 | #include <CoreFoundation/CoreFoundation.h> | |
37 | #elif PLATFORM(WIN_OS) | |
38 | #include <windows.h> | |
39 | #endif | |
40 | ||
41 | using namespace WTF; | |
42 | ||
43 | namespace KJS { | |
44 | ||
45 | // ------------------------------ StringInstance ---------------------------- | |
46 | ||
47 | const ClassInfo StringInstance::info = { "String", 0, 0 }; | |
48 | ||
49 | StringInstance::StringInstance(JSObject *proto) | |
50 | : JSWrapperObject(proto) | |
51 | { | |
52 | setInternalValue(jsString("")); | |
53 | } | |
54 | ||
55 | StringInstance::StringInstance(JSObject *proto, StringImp* string) | |
56 | : JSWrapperObject(proto) | |
57 | { | |
58 | setInternalValue(string); | |
59 | } | |
60 | ||
61 | StringInstance::StringInstance(JSObject *proto, const UString &string) | |
62 | : JSWrapperObject(proto) | |
63 | { | |
64 | setInternalValue(jsString(string)); | |
65 | } | |
66 | ||
67 | JSValue *StringInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot &slot) | |
68 | { | |
69 | return jsNumber(static_cast<StringInstance*>(slot.slotBase())->internalValue()->value().size()); | |
70 | } | |
71 | ||
72 | JSValue* StringInstance::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) | |
73 | { | |
74 | return jsString(static_cast<StringInstance*>(slot.slotBase())->internalValue()->value().substr(slot.index(), 1)); | |
75 | } | |
76 | ||
77 | static JSValue* stringInstanceNumericPropertyGetter(ExecState*, JSObject*, unsigned index, const PropertySlot& slot) | |
78 | { | |
79 | return jsString(static_cast<StringInstance*>(slot.slotBase())->internalValue()->value().substr(index, 1)); | |
80 | } | |
81 | ||
82 | bool StringInstance::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) | |
83 | { | |
84 | if (propertyName == exec->propertyNames().length) { | |
85 | slot.setCustom(this, lengthGetter); | |
86 | return true; | |
87 | } | |
88 | ||
89 | bool isStrictUInt32; | |
90 | unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); | |
91 | unsigned length = internalValue()->value().size(); | |
92 | if (isStrictUInt32 && i < length) { | |
93 | slot.setCustomIndex(this, i, indexGetter); | |
94 | return true; | |
95 | } | |
96 | ||
97 | return JSObject::getOwnPropertySlot(exec, propertyName, slot); | |
98 | } | |
99 | ||
100 | bool StringInstance::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) | |
101 | { | |
102 | unsigned length = internalValue()->value().size(); | |
103 | if (propertyName < length) { | |
104 | slot.setCustomNumeric(this, stringInstanceNumericPropertyGetter); | |
105 | return true; | |
106 | } | |
107 | ||
108 | return JSObject::getOwnPropertySlot(exec, Identifier::from(propertyName), slot); | |
109 | } | |
110 | ||
111 | void StringInstance::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr) | |
112 | { | |
113 | if (propertyName == exec->propertyNames().length) | |
114 | return; | |
115 | JSObject::put(exec, propertyName, value, attr); | |
116 | } | |
117 | ||
118 | bool StringInstance::deleteProperty(ExecState *exec, const Identifier &propertyName) | |
119 | { | |
120 | if (propertyName == exec->propertyNames().length) | |
121 | return false; | |
122 | return JSObject::deleteProperty(exec, propertyName); | |
123 | } | |
124 | ||
125 | void StringInstance::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) | |
126 | { | |
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); | |
131 | } | |
132 | ||
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 | |
156 | ||
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 | |
170 | @end | |
171 | */ | |
172 | // ECMA 15.5.4 | |
173 | StringPrototype::StringPrototype(ExecState* exec, ObjectPrototype* objProto) | |
174 | : StringInstance(objProto) | |
175 | { | |
176 | // The constructor will be added later, after StringObjectImp has been built | |
177 | putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); | |
178 | } | |
179 | ||
180 | bool StringPrototype::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot &slot) | |
181 | { | |
182 | return getStaticFunctionSlot<StringInstance>(exec, &stringTable, this, propertyName, slot); | |
183 | } | |
184 | ||
185 | // ------------------------------ Functions -------------------------- | |
186 | ||
187 | static inline void expandSourceRanges(UString::Range * & array, int& count, int& capacity) | |
188 | { | |
189 | int newCapacity; | |
190 | if (capacity == 0) { | |
191 | newCapacity = 16; | |
192 | } else { | |
193 | newCapacity = capacity * 2; | |
194 | } | |
195 | ||
196 | UString::Range *newArray = new UString::Range[newCapacity]; | |
197 | for (int i = 0; i < count; i++) { | |
198 | newArray[i] = array[i]; | |
199 | } | |
200 | ||
201 | delete [] array; | |
202 | ||
203 | capacity = newCapacity; | |
204 | array = newArray; | |
205 | } | |
206 | ||
207 | static void pushSourceRange(UString::Range * & array, int& count, int& capacity, UString::Range range) | |
208 | { | |
209 | if (count + 1 > capacity) | |
210 | expandSourceRanges(array, count, capacity); | |
211 | ||
212 | array[count] = range; | |
213 | count++; | |
214 | } | |
215 | ||
216 | static inline void expandReplacements(UString * & array, int& count, int& capacity) | |
217 | { | |
218 | int newCapacity; | |
219 | if (capacity == 0) { | |
220 | newCapacity = 16; | |
221 | } else { | |
222 | newCapacity = capacity * 2; | |
223 | } | |
224 | ||
225 | UString *newArray = new UString[newCapacity]; | |
226 | for (int i = 0; i < count; i++) { | |
227 | newArray[i] = array[i]; | |
228 | } | |
229 | ||
230 | delete [] array; | |
231 | ||
232 | capacity = newCapacity; | |
233 | array = newArray; | |
234 | } | |
235 | ||
236 | static void pushReplacement(UString * & array, int& count, int& capacity, UString replacement) | |
237 | { | |
238 | if (count + 1 > capacity) | |
239 | expandReplacements(array, count, capacity); | |
240 | ||
241 | array[count] = replacement; | |
242 | count++; | |
243 | } | |
244 | ||
245 | static inline UString substituteBackreferences(const UString &replacement, const UString &source, int *ovector, RegExp *reg) | |
246 | { | |
247 | UString substitutedReplacement = replacement; | |
248 | ||
249 | int i = -1; | |
250 | while ((i = substitutedReplacement.find(UString("$"), i + 1)) != -1) { | |
251 | if (i+1 == substitutedReplacement.size()) | |
252 | break; | |
253 | ||
254 | unsigned short ref = substitutedReplacement[i+1].unicode(); | |
255 | int backrefStart = 0; | |
256 | int backrefLength = 0; | |
257 | int advance = 0; | |
258 | ||
259 | if (ref == '$') { // "$$" -> "$" | |
260 | substitutedReplacement = substitutedReplacement.substr(0, i + 1) + substitutedReplacement.substr(i + 2); | |
261 | continue; | |
262 | } else if (ref == '&') { | |
263 | backrefStart = ovector[0]; | |
264 | backrefLength = ovector[1] - backrefStart; | |
265 | } else if (ref == '`') { | |
266 | backrefStart = 0; | |
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()) | |
275 | continue; | |
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 | |
282 | else | |
283 | advance = 1; | |
284 | } | |
285 | } | |
286 | backrefStart = ovector[2 * backrefIndex]; | |
287 | backrefLength = ovector[2 * backrefIndex + 1] - backrefStart; | |
288 | } else | |
289 | continue; | |
290 | ||
291 | substitutedReplacement = substitutedReplacement.substr(0, i) + source.substr(backrefStart, backrefLength) + substitutedReplacement.substr(i + 2 + advance); | |
292 | i += backrefLength - 1; // - 1 offsets 'i + 1' | |
293 | } | |
294 | ||
295 | return substitutedReplacement; | |
296 | } | |
297 | static inline int localeCompare(const UString& a, const UString& b) | |
298 | { | |
299 | #if PLATFORM(WIN_OS) | |
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; | |
304 | #elif PLATFORM(CF) | |
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); | |
307 | ||
308 | int retval = CFStringCompare(sa, sb, kCFCompareLocalized); | |
309 | ||
310 | CFRelease(sa); | |
311 | CFRelease(sb); | |
312 | ||
313 | return retval; | |
314 | #else | |
315 | return compare(a, b); | |
316 | #endif | |
317 | } | |
318 | ||
319 | static JSValue *replace(ExecState *exec, StringImp* sourceVal, JSValue *pattern, JSValue *replacement) | |
320 | { | |
321 | UString source = sourceVal->value(); | |
322 | JSObject *replacementFunction = 0; | |
323 | UString replacementString; | |
324 | ||
325 | if (replacement->isObject() && replacement->toObject(exec)->implementsCall()) | |
326 | replacementFunction = replacement->toObject(exec); | |
327 | else | |
328 | replacementString = replacement->toString(exec); | |
329 | ||
330 | if (pattern->isObject() && static_cast<JSObject *>(pattern)->inherits(&RegExpImp::info)) { | |
331 | RegExp *reg = static_cast<RegExpImp *>(pattern)->regExp(); | |
332 | bool global = reg->global(); | |
333 | ||
334 | RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); | |
335 | ||
336 | int lastIndex = 0; | |
337 | int startPosition = 0; | |
338 | ||
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; | |
345 | ||
346 | // This is either a loop (if global is set) or a one-way (if not). | |
347 | do { | |
348 | int matchIndex; | |
349 | int matchLen; | |
350 | int* ovector; | |
351 | regExpObj->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); | |
352 | if (matchIndex < 0) | |
353 | break; | |
354 | ||
355 | pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, matchIndex - lastIndex)); | |
356 | ||
357 | UString substitutedReplacement; | |
358 | if (replacementFunction) { | |
359 | int completeMatchStart = ovector[0]; | |
360 | List args; | |
361 | ||
362 | for (unsigned i = 0; i < reg->numSubpatterns() + 1; i++) { | |
363 | int matchStart = ovector[i * 2]; | |
364 | int matchLen = ovector[i * 2 + 1] - matchStart; | |
365 | ||
366 | if (matchStart < 0) | |
367 | args.append(jsUndefined()); | |
368 | else | |
369 | args.append(jsString(source.substr(matchStart, matchLen))); | |
370 | } | |
371 | ||
372 | args.append(jsNumber(completeMatchStart)); | |
373 | args.append(sourceVal); | |
374 | ||
375 | substitutedReplacement = replacementFunction->call(exec, exec->dynamicGlobalObject(), | |
376 | args)->toString(exec); | |
377 | } else | |
378 | substitutedReplacement = substituteBackreferences(replacementString, source, ovector, reg); | |
379 | ||
380 | pushReplacement(replacements, replacementCount, replacementCapacity, substitutedReplacement); | |
381 | ||
382 | lastIndex = matchIndex + matchLen; | |
383 | startPosition = lastIndex; | |
384 | ||
385 | // special case of empty match | |
386 | if (matchLen == 0) { | |
387 | startPosition++; | |
388 | if (startPosition > source.size()) | |
389 | break; | |
390 | } | |
391 | } while (global); | |
392 | ||
393 | if (lastIndex < source.size()) | |
394 | pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, source.size() - lastIndex)); | |
395 | ||
396 | UString result; | |
397 | ||
398 | if (sourceRanges) | |
399 | result = source.spliceSubstringsWithSeparators(sourceRanges, sourceRangeCount, replacements, replacementCount); | |
400 | ||
401 | delete [] sourceRanges; | |
402 | delete [] replacements; | |
403 | ||
404 | if (result == source) | |
405 | return sourceVal; | |
406 | ||
407 | return jsString(result); | |
408 | } | |
409 | ||
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 | |
415 | if (matchPos == -1) | |
416 | return sourceVal; | |
417 | ||
418 | if (replacementFunction) { | |
419 | List args; | |
420 | ||
421 | args.append(jsString(source.substr(matchPos, matchLen))); | |
422 | args.append(jsNumber(matchPos)); | |
423 | args.append(sourceVal); | |
424 | ||
425 | replacementString = replacementFunction->call(exec, exec->dynamicGlobalObject(), | |
426 | args)->toString(exec); | |
427 | } | |
428 | ||
429 | return jsString(source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen)); | |
430 | } | |
431 | ||
432 | JSValue* stringProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) | |
433 | { | |
434 | if (!thisObj->inherits(&StringInstance::info)) | |
435 | return throwError(exec, TypeError); | |
436 | ||
437 | return static_cast<StringInstance*>(thisObj)->internalValue(); | |
438 | } | |
439 | ||
440 | JSValue* stringProtoFuncValueOf(ExecState* exec, JSObject* thisObj, const List&) | |
441 | { | |
442 | if (!thisObj->inherits(&StringInstance::info)) | |
443 | return throwError(exec, TypeError); | |
444 | ||
445 | return static_cast<StringInstance*>(thisObj)->internalValue(); | |
446 | } | |
447 | ||
448 | JSValue* stringProtoFuncCharAt(ExecState* exec, JSObject* thisObj, const List& args) | |
449 | { | |
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); | |
452 | int len = s.size(); | |
453 | ||
454 | UString u; | |
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); | |
459 | else | |
460 | u = ""; | |
461 | return jsString(u); | |
462 | } | |
463 | ||
464 | JSValue* stringProtoFuncCharCodeAt(ExecState* exec, JSObject* thisObj, const List& args) | |
465 | { | |
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); | |
468 | int len = s.size(); | |
469 | ||
470 | JSValue* result = 0; | |
471 | ||
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()); | |
476 | else | |
477 | result = jsNaN(); | |
478 | return result; | |
479 | } | |
480 | ||
481 | JSValue* stringProtoFuncConcat(ExecState* exec, JSObject* thisObj, const List& args) | |
482 | { | |
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); | |
485 | ||
486 | List::const_iterator end = args.end(); | |
487 | for (List::const_iterator it = args.begin(); it != end; ++it) { | |
488 | s += (*it)->toString(exec); | |
489 | } | |
490 | return jsString(s); | |
491 | } | |
492 | ||
493 | JSValue* stringProtoFuncIndexOf(ExecState* exec, JSObject* thisObj, const List& args) | |
494 | { | |
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); | |
497 | int len = s.size(); | |
498 | ||
499 | JSValue* a0 = args[0]; | |
500 | JSValue* a1 = args[1]; | |
501 | UString u2 = a0->toString(exec); | |
502 | double dpos = a1->toInteger(exec); | |
503 | if (dpos < 0) | |
504 | dpos = 0; | |
505 | else if (dpos > len) | |
506 | dpos = len; | |
507 | return jsNumber(s.find(u2, static_cast<int>(dpos))); | |
508 | } | |
509 | ||
510 | JSValue* stringProtoFuncLastIndexOf(ExecState* exec, JSObject* thisObj, const List& args) | |
511 | { | |
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); | |
514 | int len = s.size(); | |
515 | ||
516 | JSValue* a0 = args[0]; | |
517 | JSValue* a1 = args[1]; | |
518 | ||
519 | UString u2 = a0->toString(exec); | |
520 | double dpos = a1->toIntegerPreserveNaN(exec); | |
521 | if (dpos < 0) | |
522 | dpos = 0; | |
523 | else if (!(dpos <= len)) // true for NaN | |
524 | dpos = len; | |
525 | return jsNumber(s.rfind(u2, static_cast<int>(dpos))); | |
526 | } | |
527 | ||
528 | JSValue* stringProtoFuncMatch(ExecState* exec, JSObject* thisObj, const List& args) | |
529 | { | |
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); | |
532 | ||
533 | JSValue* a0 = args[0]; | |
534 | ||
535 | UString u = s; | |
536 | RegExp* reg; | |
537 | RegExp* tmpReg = 0; | |
538 | RegExpImp* imp = 0; | |
539 | if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) { | |
540 | reg = static_cast<RegExpImp *>(a0)->regExp(); | |
541 | } else { | |
542 | /* | |
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). | |
546 | */ | |
547 | reg = tmpReg = new RegExp(a0->toString(exec)); | |
548 | } | |
549 | RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); | |
550 | int pos; | |
551 | int matchLength; | |
552 | regExpObj->performMatch(reg, u, 0, pos, matchLength); | |
553 | JSValue* result; | |
554 | if (!(reg->global())) { | |
555 | // case without 'g' flag is handled like RegExp.prototype.exec | |
556 | if (pos < 0) | |
557 | result = jsNull(); | |
558 | else | |
559 | result = regExpObj->arrayOfMatches(exec); | |
560 | } else { | |
561 | // return array of matches | |
562 | List list; | |
563 | int lastIndex = 0; | |
564 | while (pos >= 0) { | |
565 | list.append(jsString(u.substr(pos, matchLength))); | |
566 | lastIndex = pos; | |
567 | pos += matchLength == 0 ? 1 : matchLength; | |
568 | regExpObj->performMatch(reg, u, pos, pos, matchLength); | |
569 | } | |
570 | if (imp) | |
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. | |
576 | result = jsNull(); | |
577 | } else { | |
578 | result = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, list); | |
579 | } | |
580 | } | |
581 | delete tmpReg; | |
582 | return result; | |
583 | } | |
584 | ||
585 | JSValue* stringProtoFuncSearch(ExecState* exec, JSObject* thisObj, const List& args) | |
586 | { | |
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); | |
589 | ||
590 | JSValue* a0 = args[0]; | |
591 | ||
592 | UString u = s; | |
593 | RegExp* reg; | |
594 | RegExp* tmpReg = 0; | |
595 | if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) { | |
596 | reg = static_cast<RegExpImp *>(a0)->regExp(); | |
597 | } else { | |
598 | /* | |
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). | |
602 | */ | |
603 | reg = tmpReg = new RegExp(a0->toString(exec)); | |
604 | } | |
605 | RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); | |
606 | int pos; | |
607 | int matchLength; | |
608 | regExpObj->performMatch(reg, u, 0, pos, matchLength); | |
609 | delete tmpReg; | |
610 | return jsNumber(pos); | |
611 | } | |
612 | ||
613 | JSValue* stringProtoFuncReplace(ExecState* exec, JSObject* thisObj, const List& args) | |
614 | { | |
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); | |
617 | ||
618 | StringImp* sVal = thisObj->inherits(&StringInstance::info) ? | |
619 | static_cast<StringInstance*>(thisObj)->internalValue() : | |
620 | static_cast<StringImp*>(jsString(s)); | |
621 | ||
622 | JSValue* a0 = args[0]; | |
623 | JSValue* a1 = args[1]; | |
624 | ||
625 | return replace(exec, sVal, a0, a1); | |
626 | } | |
627 | ||
628 | JSValue* stringProtoFuncSlice(ExecState* exec, JSObject* thisObj, const List& args) | |
629 | { | |
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); | |
632 | int len = s.size(); | |
633 | ||
634 | JSValue* a0 = args[0]; | |
635 | JSValue* a1 = args[1]; | |
636 | ||
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) { | |
643 | if (from < 0) | |
644 | from = 0; | |
645 | if (to > len) | |
646 | to = len; | |
647 | return jsString(s.substr(static_cast<int>(from), static_cast<int>(to - from))); | |
648 | } | |
649 | ||
650 | return jsString(""); | |
651 | } | |
652 | ||
653 | JSValue* stringProtoFuncSplit(ExecState* exec, JSObject* thisObj, const List& args) | |
654 | { | |
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); | |
657 | ||
658 | JSValue* a0 = args[0]; | |
659 | JSValue* a1 = args[1]; | |
660 | ||
661 | JSObject *constructor = exec->lexicalGlobalObject()->arrayConstructor(); | |
662 | JSObject* res = static_cast<JSObject*>(constructor->construct(exec, exec->emptyList())); | |
663 | JSValue* result = res; | |
664 | UString u = s; | |
665 | int pos; | |
666 | int i = 0; | |
667 | int p0 = 0; | |
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)); | |
674 | return result; | |
675 | } | |
676 | pos = 0; | |
677 | while (static_cast<uint32_t>(i) != limit && pos < u.size()) { | |
678 | OwnArrayPtr<int> ovector; | |
679 | int mpos = reg->match(u, pos, &ovector); | |
680 | if (mpos < 0) | |
681 | break; | |
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))); | |
686 | p0 = mpos + mlen; | |
687 | i++; | |
688 | } | |
689 | for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) { | |
690 | int spos = ovector[si * 2]; | |
691 | if (spos < 0) | |
692 | res->put(exec, i++, jsUndefined()); | |
693 | else | |
694 | res->put(exec, i++, jsString(u.substr(spos, ovector[si * 2 + 1] - spos))); | |
695 | } | |
696 | } | |
697 | } else { | |
698 | UString u2 = a0->toString(exec); | |
699 | if (u2.isEmpty()) { | |
700 | if (u.isEmpty()) { | |
701 | // empty separator matches empty string -> empty array | |
702 | res->put(exec, exec->propertyNames().length, jsNumber(0)); | |
703 | return result; | |
704 | } else { | |
705 | while (static_cast<uint32_t>(i) != limit && i < u.size()-1) | |
706 | res->put(exec, i++, jsString(u.substr(p0++, 1))); | |
707 | } | |
708 | } else { | |
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(); | |
712 | i++; | |
713 | } | |
714 | } | |
715 | } | |
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)); | |
720 | return result; | |
721 | } | |
722 | ||
723 | JSValue* stringProtoFuncSubstr(ExecState* exec, JSObject* thisObj, const List& args) | |
724 | { | |
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); | |
727 | int len = s.size(); | |
728 | ||
729 | JSValue* a0 = args[0]; | |
730 | JSValue* a1 = args[1]; | |
731 | ||
732 | double start = a0->toInteger(exec); | |
733 | double length = a1->isUndefined() ? len : a1->toInteger(exec); | |
734 | if (start >= len) | |
735 | return jsString(""); | |
736 | if (length < 0) | |
737 | return jsString(""); | |
738 | if (start < 0) { | |
739 | start += len; | |
740 | if (start < 0) | |
741 | start = 0; | |
742 | } | |
743 | if (length > len) | |
744 | length = len; | |
745 | return jsString(s.substr(static_cast<int>(start), static_cast<int>(length))); | |
746 | } | |
747 | ||
748 | JSValue* stringProtoFuncSubstring(ExecState* exec, JSObject* thisObj, const List& args) | |
749 | { | |
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); | |
752 | int len = s.size(); | |
753 | ||
754 | JSValue* a0 = args[0]; | |
755 | JSValue* a1 = args[1]; | |
756 | ||
757 | double start = a0->toNumber(exec); | |
758 | double end = a1->toNumber(exec); | |
759 | if (isnan(start)) | |
760 | start = 0; | |
761 | if (isnan(end)) | |
762 | end = 0; | |
763 | if (start < 0) | |
764 | start = 0; | |
765 | if (end < 0) | |
766 | end = 0; | |
767 | if (start > len) | |
768 | start = len; | |
769 | if (end > len) | |
770 | end = len; | |
771 | if (a1->isUndefined()) | |
772 | end = len; | |
773 | if (start > end) { | |
774 | double temp = end; | |
775 | end = start; | |
776 | start = temp; | |
777 | } | |
778 | return jsString(s.substr((int)start, (int)end-(int)start)); | |
779 | } | |
780 | ||
781 | JSValue* stringProtoFuncToLowerCase(ExecState* exec, JSObject* thisObj, const List&) | |
782 | { | |
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); | |
785 | ||
786 | StringImp* sVal = thisObj->inherits(&StringInstance::info) | |
787 | ? static_cast<StringInstance*>(thisObj)->internalValue() | |
788 | : static_cast<StringImp*>(jsString(s)); | |
789 | int ssize = s.size(); | |
790 | if (!ssize) | |
791 | return sVal; | |
792 | Vector< ::UChar> buffer(ssize); | |
793 | bool error; | |
794 | int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
795 | if (error) { | |
796 | buffer.resize(length); | |
797 | length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
798 | if (error) | |
799 | return sVal; | |
800 | } | |
801 | if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) | |
802 | return sVal; | |
803 | return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); | |
804 | } | |
805 | ||
806 | JSValue* stringProtoFuncToUpperCase(ExecState* exec, JSObject* thisObj, const List&) | |
807 | { | |
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); | |
810 | ||
811 | StringImp* sVal = thisObj->inherits(&StringInstance::info) | |
812 | ? static_cast<StringInstance*>(thisObj)->internalValue() | |
813 | : static_cast<StringImp*>(jsString(s)); | |
814 | int ssize = s.size(); | |
815 | if (!ssize) | |
816 | return sVal; | |
817 | Vector< ::UChar> buffer(ssize); | |
818 | bool error; | |
819 | int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
820 | if (error) { | |
821 | buffer.resize(length); | |
822 | length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
823 | if (error) | |
824 | return sVal; | |
825 | } | |
826 | if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) | |
827 | return sVal; | |
828 | return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); | |
829 | } | |
830 | ||
831 | JSValue* stringProtoFuncToLocaleLowerCase(ExecState* exec, JSObject* thisObj, const List&) | |
832 | { | |
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); | |
835 | ||
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(); | |
841 | if (!ssize) | |
842 | return sVal; | |
843 | Vector< ::UChar> buffer(ssize); | |
844 | bool error; | |
845 | int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
846 | if (error) { | |
847 | buffer.resize(length); | |
848 | length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
849 | if (error) | |
850 | return sVal; | |
851 | } | |
852 | if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) | |
853 | return sVal; | |
854 | return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); | |
855 | } | |
856 | ||
857 | JSValue* stringProtoFuncToLocaleUpperCase(ExecState* exec, JSObject* thisObj, const List&) | |
858 | { | |
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); | |
861 | ||
862 | StringImp* sVal = thisObj->inherits(&StringInstance::info) | |
863 | ? static_cast<StringInstance*>(thisObj)->internalValue() | |
864 | : static_cast<StringImp*>(jsString(s)); | |
865 | int ssize = s.size(); | |
866 | if (!ssize) | |
867 | return sVal; | |
868 | Vector< ::UChar> buffer(ssize); | |
869 | bool error; | |
870 | int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
871 | if (error) { | |
872 | buffer.resize(length); | |
873 | length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); | |
874 | if (error) | |
875 | return sVal; | |
876 | } | |
877 | if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) | |
878 | return sVal; | |
879 | return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); | |
880 | } | |
881 | ||
882 | JSValue* stringProtoFuncLocaleCompare(ExecState* exec, JSObject* thisObj, const List& args) | |
883 | { | |
884 | if (args.size() < 1) | |
885 | return jsNumber(0); | |
886 | ||
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))); | |
891 | } | |
892 | ||
893 | JSValue* stringProtoFuncBig(ExecState* exec, JSObject* thisObj, const List&) | |
894 | { | |
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>"); | |
898 | } | |
899 | ||
900 | JSValue* stringProtoFuncSmall(ExecState* exec, JSObject* thisObj, const List&) | |
901 | { | |
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>"); | |
905 | } | |
906 | ||
907 | JSValue* stringProtoFuncBlink(ExecState* exec, JSObject* thisObj, const List&) | |
908 | { | |
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>"); | |
912 | } | |
913 | ||
914 | JSValue* stringProtoFuncBold(ExecState* exec, JSObject* thisObj, const List&) | |
915 | { | |
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>"); | |
919 | } | |
920 | ||
921 | JSValue* stringProtoFuncFixed(ExecState* exec, JSObject* thisObj, const List&) | |
922 | { | |
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>"); | |
926 | } | |
927 | ||
928 | JSValue* stringProtoFuncItalics(ExecState* exec, JSObject* thisObj, const List&) | |
929 | { | |
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>"); | |
933 | } | |
934 | ||
935 | JSValue* stringProtoFuncStrike(ExecState* exec, JSObject* thisObj, const List&) | |
936 | { | |
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>"); | |
940 | } | |
941 | ||
942 | JSValue* stringProtoFuncSub(ExecState* exec, JSObject* thisObj, const List&) | |
943 | { | |
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>"); | |
947 | } | |
948 | ||
949 | JSValue* stringProtoFuncSup(ExecState* exec, JSObject* thisObj, const List&) | |
950 | { | |
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>"); | |
954 | } | |
955 | ||
956 | JSValue* stringProtoFuncFontcolor(ExecState* exec, JSObject* thisObj, const List& args) | |
957 | { | |
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>"); | |
962 | } | |
963 | ||
964 | JSValue* stringProtoFuncFontsize(ExecState* exec, JSObject* thisObj, const List& args) | |
965 | { | |
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>"); | |
970 | } | |
971 | ||
972 | JSValue* stringProtoFuncAnchor(ExecState* exec, JSObject* thisObj, const List& args) | |
973 | { | |
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>"); | |
978 | } | |
979 | ||
980 | JSValue* stringProtoFuncLink(ExecState* exec, JSObject* thisObj, const List& args) | |
981 | { | |
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>"); | |
986 | } | |
987 | ||
988 | // ------------------------------ StringObjectImp ------------------------------ | |
989 | ||
990 | StringObjectImp::StringObjectImp(ExecState* exec, FunctionPrototype* funcProto, StringPrototype* stringProto) | |
991 | : InternalFunctionImp(funcProto, stringProto->classInfo()->className) | |
992 | { | |
993 | // ECMA 15.5.3.1 String.prototype | |
994 | putDirect(exec->propertyNames().prototype, stringProto, DontEnum|DontDelete|ReadOnly); | |
995 | ||
996 | putDirectFunction(new StringObjectFuncImp(exec, funcProto, exec->propertyNames().fromCharCode), DontEnum); | |
997 | ||
998 | // no. of arguments for constructor | |
999 | putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); | |
1000 | } | |
1001 | ||
1002 | ||
1003 | bool StringObjectImp::implementsConstruct() const | |
1004 | { | |
1005 | return true; | |
1006 | } | |
1007 | ||
1008 | // ECMA 15.5.2 | |
1009 | JSObject *StringObjectImp::construct(ExecState *exec, const List &args) | |
1010 | { | |
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)); | |
1015 | } | |
1016 | ||
1017 | // ECMA 15.5.1 | |
1018 | JSValue *StringObjectImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args) | |
1019 | { | |
1020 | if (args.isEmpty()) | |
1021 | return jsString(""); | |
1022 | else { | |
1023 | JSValue *v = args[0]; | |
1024 | return jsString(v->toString(exec)); | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | // ------------------------------ StringObjectFuncImp -------------------------- | |
1029 | ||
1030 | // ECMA 15.5.3.2 fromCharCode() | |
1031 | StringObjectFuncImp::StringObjectFuncImp(ExecState* exec, FunctionPrototype* funcProto, const Identifier& name) | |
1032 | : InternalFunctionImp(funcProto, name) | |
1033 | { | |
1034 | putDirect(exec->propertyNames().length, jsNumber(1), DontDelete|ReadOnly|DontEnum); | |
1035 | } | |
1036 | ||
1037 | JSValue *StringObjectFuncImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args) | |
1038 | { | |
1039 | UString s; | |
1040 | if (args.size()) { | |
1041 | UChar *buf = static_cast<UChar *>(fastMalloc(args.size() * sizeof(UChar))); | |
1042 | UChar *p = buf; | |
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)); | |
1046 | *p++ = UChar(u); | |
1047 | } | |
1048 | s = UString(buf, args.size(), false); | |
1049 | } else | |
1050 | s = ""; | |
1051 | ||
1052 | return jsString(s); | |
1053 | } | |
1054 | ||
1055 | } // namespace KJS |