string_object.cpp
00001 // -*- c-basic-offset: 2 -*- 00002 /* 00003 * This file is part of the KDE libraries 00004 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 00005 * Copyright (C) 2003 Apple Computer, Inc. 00006 * 00007 * This library is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2 of the License, or (at your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 * 00021 */ 00022 00023 #include "value.h" 00024 #include "object.h" 00025 #include "types.h" 00026 #include "interpreter.h" 00027 #include "operations.h" 00028 #include "regexp.h" 00029 #include "regexp_object.h" 00030 #include "string_object.h" 00031 #include "error_object.h" 00032 #include <stdio.h> 00033 #include "string_object.lut.h" 00034 00035 #ifdef HAVE_STDINT_H 00036 #include <stdint.h> 00037 #endif 00038 #ifdef HAVE_SYS_TYPES_H 00039 #include <sys/types.h> 00040 #endif 00041 #ifdef HAVE_SYS_BITYPES_H 00042 #include <sys/bitypes.h> /* For uintXX_t on Tru64 */ 00043 #endif 00044 00045 using namespace KJS; 00046 00047 // ------------------------------ StringInstanceImp ---------------------------- 00048 00049 const ClassInfo StringInstanceImp::info = {"String", 0, 0, 0}; 00050 00051 StringInstanceImp::StringInstanceImp(ObjectImp *proto) 00052 : ObjectImp(proto) 00053 { 00054 setInternalValue(String("")); 00055 } 00056 00057 StringInstanceImp::StringInstanceImp(ObjectImp *proto, const UString &string) 00058 : ObjectImp(proto) 00059 { 00060 setInternalValue(String(string)); 00061 } 00062 00063 Value StringInstanceImp::get(ExecState *exec, const Identifier &propertyName) const 00064 { 00065 if (propertyName == lengthPropertyName) 00066 return Number(internalValue().toString(exec).size()); 00067 00068 bool ok; 00069 const unsigned index = propertyName.toArrayIndex(&ok); 00070 if (ok) { 00071 const UString s = internalValue().toString(exec); 00072 const unsigned length = s.size(); 00073 if (index < length) { 00074 const UChar c = s[index]; 00075 return String(UString(&c, 1)); 00076 } 00077 } 00078 00079 return ObjectImp::get(exec, propertyName); 00080 } 00081 00082 void StringInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr) 00083 { 00084 if (propertyName == lengthPropertyName) 00085 return; 00086 ObjectImp::put(exec, propertyName, value, attr); 00087 } 00088 00089 bool StringInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const 00090 { 00091 if (propertyName == lengthPropertyName) 00092 return true; 00093 00094 bool ok; 00095 unsigned index = propertyName.toULong(&ok); 00096 if (ok && index < (unsigned)internalValue().toString(exec).size()) 00097 return true; 00098 00099 return ObjectImp::hasProperty(exec, propertyName); 00100 } 00101 00102 bool StringInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName) 00103 { 00104 if (propertyName == lengthPropertyName) 00105 return false; 00106 00107 bool ok; 00108 unsigned index = propertyName.toULong(&ok); 00109 if (ok && index < (unsigned)internalValue().toString(exec).size()) 00110 return false; 00111 00112 return ObjectImp::deleteProperty(exec, propertyName); 00113 } 00114 00115 ReferenceList StringInstanceImp::propList(ExecState *exec, bool recursive) 00116 { 00117 ReferenceList properties = ObjectImp::propList(exec,recursive); 00118 00119 UString str = internalValue().toString(exec); 00120 for (int i = 0; i < str.size(); i++) 00121 if (!ObjectImp::hasProperty(exec,Identifier::from(i))) 00122 properties.append(Reference(this, i)); 00123 00124 return properties; 00125 } 00126 00127 // ------------------------------ StringPrototypeImp --------------------------- 00128 const ClassInfo StringPrototypeImp::info = {"String", &StringInstanceImp::info, &stringTable, 0}; 00129 /* Source for string_object.lut.h 00130 @begin stringTable 28 00131 toString StringProtoFuncImp::ToString DontEnum|Function 0 00132 valueOf StringProtoFuncImp::ValueOf DontEnum|Function 0 00133 charAt StringProtoFuncImp::CharAt DontEnum|Function 1 00134 charCodeAt StringProtoFuncImp::CharCodeAt DontEnum|Function 1 00135 concat StringProtoFuncImp::Concat DontEnum|Function 1 00136 indexOf StringProtoFuncImp::IndexOf DontEnum|Function 1 00137 lastIndexOf StringProtoFuncImp::LastIndexOf DontEnum|Function 1 00138 match StringProtoFuncImp::Match DontEnum|Function 1 00139 replace StringProtoFuncImp::Replace DontEnum|Function 2 00140 search StringProtoFuncImp::Search DontEnum|Function 1 00141 slice StringProtoFuncImp::Slice DontEnum|Function 2 00142 split StringProtoFuncImp::Split DontEnum|Function 2 00143 substr StringProtoFuncImp::Substr DontEnum|Function 2 00144 substring StringProtoFuncImp::Substring DontEnum|Function 2 00145 toLowerCase StringProtoFuncImp::ToLowerCase DontEnum|Function 0 00146 toUpperCase StringProtoFuncImp::ToUpperCase DontEnum|Function 0 00147 toLocaleLowerCase StringProtoFuncImp::ToLocaleLowerCase DontEnum|Function 0 00148 toLocaleUpperCase StringProtoFuncImp::ToLocaleUpperCase DontEnum|Function 0 00149 localeCompare StringProtoFuncImp::LocaleCompare DontEnum|Function 1 00150 # 00151 # Under here: html extension, should only exist if KJS_PURE_ECMA is not defined 00152 # I guess we need to generate two hashtables in the .lut.h file, and use #ifdef 00153 # to select the right one... TODO. ##### 00154 big StringProtoFuncImp::Big DontEnum|Function 0 00155 small StringProtoFuncImp::Small DontEnum|Function 0 00156 blink StringProtoFuncImp::Blink DontEnum|Function 0 00157 bold StringProtoFuncImp::Bold DontEnum|Function 0 00158 fixed StringProtoFuncImp::Fixed DontEnum|Function 0 00159 italics StringProtoFuncImp::Italics DontEnum|Function 0 00160 strike StringProtoFuncImp::Strike DontEnum|Function 0 00161 sub StringProtoFuncImp::Sub DontEnum|Function 0 00162 sup StringProtoFuncImp::Sup DontEnum|Function 0 00163 fontcolor StringProtoFuncImp::Fontcolor DontEnum|Function 1 00164 fontsize StringProtoFuncImp::Fontsize DontEnum|Function 1 00165 anchor StringProtoFuncImp::Anchor DontEnum|Function 1 00166 link StringProtoFuncImp::Link DontEnum|Function 1 00167 @end 00168 */ 00169 // ECMA 15.5.4 00170 StringPrototypeImp::StringPrototypeImp(ExecState * /*exec*/, 00171 ObjectPrototypeImp *objProto) 00172 : StringInstanceImp(objProto) 00173 { 00174 Value protect(this); 00175 // The constructor will be added later, after StringObjectImp has been built 00176 putDirect(lengthPropertyName, NumberImp::zero(), DontDelete|ReadOnly|DontEnum); 00177 00178 } 00179 00180 Value StringPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const 00181 { 00182 return lookupGetFunction<StringProtoFuncImp, StringInstanceImp>( exec, propertyName, &stringTable, this ); 00183 } 00184 00185 // ------------------------------ StringProtoFuncImp --------------------------- 00186 00187 StringProtoFuncImp::StringProtoFuncImp(ExecState *exec, int i, int len) 00188 : InternalFunctionImp( 00189 static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp()) 00190 ), id(i) 00191 { 00192 Value protect(this); 00193 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum); 00194 } 00195 00196 bool StringProtoFuncImp::implementsCall() const 00197 { 00198 return true; 00199 } 00200 00201 // ### use as fallback only. implement locale aware version. 00202 static inline int localeCompare(const UString &a, const UString &b) 00203 { 00204 // ### other browsers have more detailed return values than -1, 0 and 1 00205 return compare(a, b); 00206 } 00207 00208 // ECMA 15.5.4.2 - 15.5.4.20 00209 Value StringProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args) 00210 { 00211 Value result; 00212 00213 // toString and valueOf are no generic functions. 00214 if (id == ToString || id == ValueOf) { 00215 KJS_CHECK_THIS( StringInstanceImp, thisObj ); 00216 00217 return String(thisObj.internalValue().toString(exec)); 00218 } 00219 00220 int n, m; 00221 UString u2, u3; 00222 double dpos; 00223 int pos, p0, i; 00224 double d = 0.0; 00225 00226 UString s = thisObj.toString(exec); 00227 00228 int len = s.size(); 00229 Value a0 = args[0]; 00230 Value a1 = args[1]; 00231 00232 switch (id) { 00233 case ToString: 00234 case ValueOf: 00235 // handled above 00236 break; 00237 case CharAt: 00238 pos = a0.type() == UndefinedType ? 0 : a0.toInteger(exec); 00239 if (pos < 0 || pos >= len) 00240 s = ""; 00241 else 00242 s = s.substr(pos, 1); 00243 result = String(s); 00244 break; 00245 case CharCodeAt: 00246 pos = a0.type() == UndefinedType ? 0 : a0.toInteger(exec); 00247 if (pos < 0 || pos >= len) 00248 d = NaN; 00249 else { 00250 UChar c = s[pos]; 00251 d = (c.high() << 8) + c.low(); 00252 } 00253 result = Number(d); 00254 break; 00255 case Concat: { 00256 ListIterator it = args.begin(); 00257 for ( ; it != args.end() ; ++it) { 00258 s += it->dispatchToString(exec); 00259 } 00260 result = String(s); 00261 break; 00262 } 00263 case IndexOf: 00264 u2 = a0.toString(exec); 00265 if (a1.type() == UndefinedType) 00266 pos = 0; 00267 else 00268 pos = a1.toInteger(exec); 00269 d = s.find(u2, pos); 00270 result = Number(d); 00271 break; 00272 case LastIndexOf: 00273 u2 = a0.toString(exec); 00274 d = a1.toNumber(exec); 00275 if (a1.type() == UndefinedType || KJS::isNaN(d)) 00276 dpos = len; 00277 else { 00278 dpos = d; 00279 if (dpos < 0) 00280 dpos = 0; 00281 else if (dpos > len) 00282 dpos = len; 00283 } 00284 result = Number(s.rfind(u2, int(dpos))); 00285 break; 00286 case Match: 00287 case Search: { 00288 RegExp *reg, *tmpReg = 0; 00289 RegExpImp *imp = 0; 00290 if (a0.isA(ObjectType) && a0.toObject(exec).inherits(&RegExpImp::info)) 00291 { 00292 imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() ); 00293 reg = imp->regExp(); 00294 } 00295 else 00296 { /* 00297 * ECMA 15.5.4.12 String.prototype.search (regexp) 00298 * If regexp is not an object whose [[Class]] property is "RegExp", it is 00299 * replaced with the result of the expression new RegExp(regexp). 00300 */ 00301 reg = tmpReg = new RegExp(a0.toString(exec), RegExp::None); 00302 } 00303 if (!reg->isValid()) { 00304 delete tmpReg; 00305 Object err = Error::create(exec, SyntaxError, 00306 "Invalid regular expression"); 00307 exec->setException(err); 00308 return err; 00309 } 00310 RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->interpreter()->builtinRegExp().imp()); 00311 int **ovector = regExpObj->registerRegexp(reg, s); 00312 reg->prepareMatch(s); 00313 UString mstr = reg->match(s, -1, &pos, ovector); 00314 if (id == Search) { 00315 result = Number(pos); 00316 } else { // Match 00317 if (mstr.isNull()) { 00318 result = Null(); // no match 00319 } else if ((reg->flags() & RegExp::Global) == 0) { 00320 // case without 'g' flag is handled like RegExp.prototype.exec 00321 regExpObj->setSubPatterns(reg->subPatterns()); 00322 result = regExpObj->arrayOfMatches(exec,mstr); 00323 } else { 00324 // return array of matches 00325 List list; 00326 while (pos >= 0) { 00327 list.append(String(mstr)); 00328 pos += mstr.isEmpty() ? 1 : mstr.size(); 00329 delete [] *ovector; 00330 mstr = reg->match(s, pos, &pos, ovector); 00331 } 00332 result = exec->lexicalInterpreter()->builtinArray().construct(exec, list); 00333 } 00334 } 00335 reg->doneMatch(); 00336 delete tmpReg; 00337 break; 00338 } 00339 case Replace: 00340 if (a0.type() == ObjectType && a0.toObject(exec).inherits(&RegExpImp::info)) { 00341 RegExpImp* imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() ); 00342 RegExp *reg = imp->regExp(); 00343 bool global = false; 00344 Value tmp = imp->get(exec,"global"); 00345 if (tmp.type() != UndefinedType && tmp.toBoolean(exec) == true) 00346 global = true; 00347 00348 RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp().imp()); 00349 int lastIndex = 0; 00350 Object o1; 00351 // Test if 2nd arg is a function (new in JS 1.3) 00352 if ( a1.type() == ObjectType && a1.toObject(exec).implementsCall() ) 00353 o1 = a1.toObject(exec); 00354 else 00355 u3 = a1.toString(exec); // 2nd arg is the replacement string 00356 00357 UString out; 00358 00359 // This is either a loop (if global is set) or a one-way (if not). 00360 reg->prepareMatch(s); 00361 do { 00362 int **ovector = regExpObj->registerRegexp( reg, s ); 00363 UString mstr = reg->match(s, lastIndex, &pos, ovector); 00364 regExpObj->setSubPatterns(reg->subPatterns()); 00365 if (pos == -1) 00366 break; 00367 00368 len = mstr.size(); 00369 00370 UString rstr; 00371 // Prepare replacement 00372 if (!o1.isValid()) 00373 { 00374 rstr = u3; 00375 bool ok; 00376 // check if u3 matches $1 or $2 etc 00377 for (int i = 0; (i = rstr.find(UString("$"), i)) != -1; i++) { 00378 if (i+1<rstr.size() && rstr[i+1] == '$') { // "$$" -> "$" 00379 rstr = rstr.substr(0,i) + "$" + rstr.substr(i+2); 00380 continue; 00381 } 00382 // Assume number part is one char exactly 00383 unsigned long pos = rstr.substr(i+1,1).toULong(&ok, false /* tolerate empty string */); 00384 if (ok && pos <= (unsigned)reg->subPatterns()) { 00385 rstr = rstr.substr(0,i) 00386 + s.substr((*ovector)[2*pos], 00387 (*ovector)[2*pos+1]-(*ovector)[2*pos]) 00388 + rstr.substr(i+2); 00389 i += (*ovector)[2*pos+1]-(*ovector)[2*pos] - 1; // -1 offsets i++ 00390 } 00391 } 00392 } else // 2nd arg is a function call. Spec from http://devedge.netscape.com/library/manuals/2000/javascript/1.5/reference/string.html#1194258 00393 { 00394 List l; 00395 l.append(String(mstr)); // First arg: complete matched substring 00396 // Then the submatch strings 00397 for ( unsigned int sub = 1; sub <= reg->subPatterns() ; ++sub ) 00398 l.append( String( s.substr((*ovector)[2*sub], 00399 (*ovector)[2*sub+1]-(*ovector)[2*sub]) ) ); 00400 l.append(Number(pos)); // The offset within the string where the match occurred 00401 l.append(String(s)); // Last arg: the string itself. Can't see the difference with the 1st arg! 00402 Object thisObj = exec->interpreter()->globalObject(); 00403 rstr = o1.call( exec, thisObj, l ).toString(exec); 00404 } 00405 00406 // Append the stuff we skipped over to get to the match -- 00407 // that would be [lastIndex, pos) of the original.. 00408 if (pos != lastIndex) 00409 out += s.substr(lastIndex, pos - lastIndex); 00410 00411 // Append the replacement.. 00412 out += rstr; 00413 00414 lastIndex = pos + len; // Skip over the matched stuff... 00415 } while (global); 00416 00417 // Append the rest of the string to the output... 00418 if (lastIndex == 0 && out.size() == 0) // Don't copy stuff if nothing changed 00419 out = s; 00420 else 00421 out += s.substr(lastIndex, s.size() - lastIndex); 00422 00423 reg->doneMatch(); 00424 00425 result = String(out); 00426 } else { // First arg is a string 00427 u2 = a0.toString(exec); 00428 pos = s.find(u2); 00429 len = u2.size(); 00430 // Do the replacement 00431 if (pos == -1) 00432 result = String(s); 00433 else { 00434 u3 = s.substr(0, pos) + a1.toString(exec) + 00435 s.substr(pos + len); 00436 result = String(u3); 00437 } 00438 } 00439 break; 00440 case Slice: // http://developer.netscape.com/docs/manuals/js/client/jsref/string.htm#1194366 or 15.5.4.13 00441 { 00442 // The arg processing is very much like ArrayProtoFunc::Slice 00443 int begin = args[0].toUInt32(exec); 00444 int end = len; 00445 if (args[1].type() != UndefinedType) { 00446 end = args[1].toInteger(exec); 00447 } 00448 int from = begin < 0 ? len + begin : begin; 00449 int to = end < 0 ? len + end : end; 00450 if (to > from && to > 0 && from < len) { 00451 if (from < 0) { 00452 from = 0; 00453 } 00454 if (to > len) { 00455 to = len; 00456 } 00457 result = String(s.substr(from, to - from)); 00458 } else { 00459 result = String(""); 00460 } 00461 break; 00462 } 00463 case Split: { 00464 Object constructor = exec->lexicalInterpreter()->builtinArray(); 00465 Object res = Object::dynamicCast(constructor.construct(exec,List::empty())); 00466 result = res; 00467 i = p0 = 0; 00468 uint32_t limit = (a1.type() != UndefinedType) ? a1.toUInt32(exec) : 0xFFFFFFFFU; 00469 if (a0.type() == ObjectType && Object::dynamicCast(a0).inherits(&RegExpImp::info)) { 00470 Object obj0 = Object::dynamicCast(a0); 00471 RegExp reg(obj0.get(exec,"source").toString(exec)); 00472 reg.prepareMatch(s); 00473 if (s.isEmpty() && !reg.match(s, 0).isNull()) { 00474 // empty string matched by regexp -> empty array 00475 reg.doneMatch(); 00476 res.put(exec, lengthPropertyName, Number(0), DontDelete|ReadOnly|DontEnum); 00477 break; 00478 } 00479 pos = 0; 00480 while (static_cast<uint32_t>(i) != limit && pos < s.size()) { 00481 // TODO: back references 00482 int mpos; 00483 int *ovector = 0L; 00484 UString mstr = reg.match(s, pos, &mpos, &ovector); 00485 delete [] ovector; ovector = 0L; 00486 if (mpos < 0) 00487 break; 00488 pos = mpos + (mstr.isEmpty() ? 1 : mstr.size()); 00489 if (mpos != p0 || !mstr.isEmpty()) { 00490 res.put(exec,i, String(s.substr(p0, mpos-p0))); 00491 p0 = mpos + mstr.size(); 00492 i++; 00493 } 00494 } 00495 reg.doneMatch(); 00496 } else { 00497 u2 = a0.toString(exec); 00498 if (u2.isEmpty()) { 00499 if (s.isEmpty()) { 00500 // empty separator matches empty string -> empty array 00501 put(exec,lengthPropertyName, Number(0)); 00502 break; 00503 } else { 00504 while (static_cast<uint32_t>(i) != limit && i < s.size()-1) 00505 res.put(exec,i++, String(s.substr(p0++, 1))); 00506 } 00507 } else { 00508 while (static_cast<uint32_t>(i) != limit && (pos = s.find(u2, p0)) >= 0) { 00509 res.put(exec,i, String(s.substr(p0, pos-p0))); 00510 p0 = pos + u2.size(); 00511 i++; 00512 } 00513 } 00514 } 00515 // add remaining string, if any 00516 if (static_cast<uint32_t>(i) != limit) 00517 res.put(exec,i++, String(s.substr(p0))); 00518 res.put(exec,lengthPropertyName, Number(i)); 00519 } 00520 break; 00521 case Substr: { 00522 n = a0.toInteger(exec); 00523 m = a1.toInteger(exec); 00524 int d, d2; 00525 if (n >= 0) 00526 d = n; 00527 else 00528 d = maxInt(len + n, 0); 00529 if (a1.type() == UndefinedType) 00530 d2 = len - d; 00531 else 00532 d2 = minInt(maxInt(m, 0), len - d); 00533 result = String(s.substr(d, d2)); 00534 break; 00535 } 00536 case Substring: { 00537 double start = a0.toNumber(exec); 00538 double end = a1.toNumber(exec); 00539 if (KJS::isNaN(start)) 00540 start = 0; 00541 if (KJS::isNaN(end)) 00542 end = 0; 00543 if (start < 0) 00544 start = 0; 00545 if (end < 0) 00546 end = 0; 00547 if (start > len) 00548 start = len; 00549 if (end > len) 00550 end = len; 00551 if (a1.type() == UndefinedType) 00552 end = len; 00553 if (start > end) { 00554 double temp = end; 00555 end = start; 00556 start = temp; 00557 } 00558 result = String(s.substr((int)start, (int)end-(int)start)); 00559 } 00560 break; 00561 case ToLowerCase: 00562 case ToLocaleLowerCase: // FIXME: To get this 100% right we need to detect Turkish and change I to lowercase i without a dot. 00563 for (i = 0; i < len; i++) 00564 s[i] = s[i].toLower(); 00565 result = String(s); 00566 break; 00567 case ToUpperCase: 00568 case ToLocaleUpperCase: // FIXME: To get this 100% right we need to detect Turkish and change i to uppercase I with a dot. 00569 for (i = 0; i < len; i++) 00570 s[i] = s[i].toUpper(); 00571 result = String(s); 00572 break; 00573 case LocaleCompare: 00574 return Number(localeCompare(s, a0.toString(exec))); 00575 #ifndef KJS_PURE_ECMA 00576 case Big: 00577 result = String("<big>" + s + "</big>"); 00578 break; 00579 case Small: 00580 result = String("<small>" + s + "</small>"); 00581 break; 00582 case Blink: 00583 result = String("<blink>" + s + "</blink>"); 00584 break; 00585 case Bold: 00586 result = String("<b>" + s + "</b>"); 00587 break; 00588 case Fixed: 00589 result = String("<tt>" + s + "</tt>"); 00590 break; 00591 case Italics: 00592 result = String("<i>" + s + "</i>"); 00593 break; 00594 case Strike: 00595 result = String("<strike>" + s + "</strike>"); 00596 break; 00597 case Sub: 00598 result = String("<sub>" + s + "</sub>"); 00599 break; 00600 case Sup: 00601 result = String("<sup>" + s + "</sup>"); 00602 break; 00603 case Fontcolor: 00604 result = String("<font color=\"" + a0.toString(exec) + "\">" + s + "</font>"); 00605 break; 00606 case Fontsize: 00607 result = String("<font size=\"" + a0.toString(exec) + "\">" + s + "</font>"); 00608 break; 00609 case Anchor: 00610 result = String("<a name=\"" + a0.toString(exec) + "\">" + s + "</a>"); 00611 break; 00612 case Link: 00613 result = String("<a href=\"" + a0.toString(exec) + "\">" + s + "</a>"); 00614 break; 00615 #endif 00616 } 00617 00618 return result; 00619 } 00620 00621 // ------------------------------ StringObjectImp ------------------------------ 00622 00623 StringObjectImp::StringObjectImp(ExecState *exec, 00624 FunctionPrototypeImp *funcProto, 00625 StringPrototypeImp *stringProto) 00626 : InternalFunctionImp(funcProto) 00627 { 00628 Value protect(this); 00629 // ECMA 15.5.3.1 String.prototype 00630 putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly); 00631 00632 putDirect("fromCharCode", new StringObjectFuncImp(exec,funcProto), DontEnum); 00633 00634 // no. of arguments for constructor 00635 putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum); 00636 } 00637 00638 00639 bool StringObjectImp::implementsConstruct() const 00640 { 00641 return true; 00642 } 00643 00644 // ECMA 15.5.2 00645 Object StringObjectImp::construct(ExecState *exec, const List &args) 00646 { 00647 ObjectImp *proto = exec->lexicalInterpreter()->builtinStringPrototype().imp(); 00648 if (args.size() == 0) 00649 return Object(new StringInstanceImp(proto)); 00650 return Object(new StringInstanceImp(proto, args.begin()->dispatchToString(exec))); 00651 } 00652 00653 bool StringObjectImp::implementsCall() const 00654 { 00655 return true; 00656 } 00657 00658 // ECMA 15.5.1 00659 Value StringObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args) 00660 { 00661 if (args.isEmpty()) 00662 return String(""); 00663 else { 00664 Value v = args[0]; 00665 return String(v.toString(exec)); 00666 } 00667 } 00668 00669 // ------------------------------ StringObjectFuncImp -------------------------- 00670 00671 // ECMA 15.5.3.2 fromCharCode() 00672 StringObjectFuncImp::StringObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto) 00673 : InternalFunctionImp(funcProto) 00674 { 00675 Value protect(this); 00676 putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum); 00677 } 00678 00679 bool StringObjectFuncImp::implementsCall() const 00680 { 00681 return true; 00682 } 00683 00684 Value StringObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args) 00685 { 00686 UString s; 00687 if (args.size()) { 00688 UChar *buf = new UChar[args.size()]; 00689 UChar *p = buf; 00690 ListIterator it = args.begin(); 00691 while (it != args.end()) { 00692 unsigned short u = it->toUInt16(exec); 00693 *p++ = UChar(u); 00694 it++; 00695 } 00696 s = UString(buf, args.size(), false); 00697 } else 00698 s = ""; 00699 00700 return String(s); 00701 }