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 }
KDE Home | KDE Accessibility Home | Description of Access Keys