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 #
00150 # Under here: html extension, should only exist if KJS_PURE_ECMA is not defined
00151 # I guess we need to generate two hashtables in the .lut.h file, and use #ifdef
00152 # to select the right one... TODO. #####
00153   big           StringProtoFuncImp::Big     DontEnum|Function   0
00154   small         StringProtoFuncImp::Small   DontEnum|Function   0
00155   blink         StringProtoFuncImp::Blink   DontEnum|Function   0
00156   bold          StringProtoFuncImp::Bold    DontEnum|Function   0
00157   fixed         StringProtoFuncImp::Fixed   DontEnum|Function   0
00158   italics       StringProtoFuncImp::Italics DontEnum|Function   0
00159   strike        StringProtoFuncImp::Strike  DontEnum|Function   0
00160   sub           StringProtoFuncImp::Sub     DontEnum|Function   0
00161   sup           StringProtoFuncImp::Sup     DontEnum|Function   0
00162   fontcolor     StringProtoFuncImp::Fontcolor   DontEnum|Function   1
00163   fontsize      StringProtoFuncImp::Fontsize    DontEnum|Function   1
00164   anchor        StringProtoFuncImp::Anchor  DontEnum|Function   1
00165   link          StringProtoFuncImp::Link    DontEnum|Function   1
00166 @end
00167 */
00168 // ECMA 15.5.4
00169 StringPrototypeImp::StringPrototypeImp(ExecState * /*exec*/,
00170                                        ObjectPrototypeImp *objProto)
00171   : StringInstanceImp(objProto)
00172 {
00173   Value protect(this);
00174   // The constructor will be added later, after StringObjectImp has been built
00175   putDirect(lengthPropertyName, NumberImp::zero(), DontDelete|ReadOnly|DontEnum);
00176 
00177 }
00178 
00179 Value StringPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
00180 {
00181   return lookupGetFunction<StringProtoFuncImp, StringInstanceImp>( exec, propertyName, &stringTable, this );
00182 }
00183 
00184 // ------------------------------ StringProtoFuncImp ---------------------------
00185 
00186 StringProtoFuncImp::StringProtoFuncImp(ExecState *exec, int i, int len)
00187   : InternalFunctionImp(
00188     static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
00189     ), id(i)
00190 {
00191   Value protect(this);
00192   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
00193 }
00194 
00195 bool StringProtoFuncImp::implementsCall() const
00196 {
00197   return true;
00198 }
00199 
00200 // ECMA 15.5.4.2 - 15.5.4.20
00201 Value StringProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
00202 {
00203   Value result;
00204 
00205   // toString and valueOf are no generic functions.
00206   if (id == ToString || id == ValueOf) {
00207     KJS_CHECK_THIS( StringInstanceImp, thisObj );
00208 
00209     return String(thisObj.internalValue().toString(exec));
00210   }
00211 
00212   int n, m;
00213   UString u2, u3;
00214   double dpos;
00215   int pos, p0, i;
00216   double d = 0.0;
00217 
00218   UString s = thisObj.toString(exec);
00219 
00220   int len = s.size();
00221   Value a0 = args[0];
00222   Value a1 = args[1];
00223 
00224   switch (id) {
00225   case ToString:
00226   case ValueOf:
00227     // handled above
00228     break;
00229   case CharAt:
00230     pos = a0.type() == UndefinedType ? 0 : a0.toInteger(exec);
00231     if (pos < 0 || pos >= len)
00232       s = "";
00233     else
00234       s = s.substr(pos, 1);
00235     result = String(s);
00236     break;
00237   case CharCodeAt:
00238     pos = a0.type() == UndefinedType ? 0 : a0.toInteger(exec);
00239     if (pos < 0 || pos >= len)
00240       d = NaN;
00241     else {
00242       UChar c = s[pos];
00243       d = (c.high() << 8) + c.low();
00244     }
00245     result = Number(d);
00246     break;
00247   case Concat: {
00248     ListIterator it = args.begin();
00249     for ( ; it != args.end() ; ++it) {
00250         s += it->dispatchToString(exec);
00251     }
00252     result = String(s);
00253     break;
00254   }
00255   case IndexOf:
00256     u2 = a0.toString(exec);
00257     if (a1.type() == UndefinedType)
00258       pos = 0;
00259     else
00260       pos = a1.toInteger(exec);
00261     d = s.find(u2, pos);
00262     result = Number(d);
00263     break;
00264   case LastIndexOf:
00265     u2 = a0.toString(exec);
00266     d = a1.toNumber(exec);
00267     if (a1.type() == UndefinedType || KJS::isNaN(d))
00268       dpos = len;
00269     else {
00270       dpos = d;
00271       if (dpos < 0)
00272         dpos = 0;
00273       else if (dpos > len)
00274         dpos = len;
00275     }
00276     result = Number(s.rfind(u2, int(dpos)));
00277     break;
00278   case Match:
00279   case Search: {
00280     RegExp *reg, *tmpReg = 0;
00281     RegExpImp *imp = 0;
00282     if (a0.isA(ObjectType) && a0.toObject(exec).inherits(&RegExpImp::info))
00283     {
00284       imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
00285       reg = imp->regExp();
00286     }
00287     else
00288     { /*
00289        *  ECMA 15.5.4.12 String.prototype.search (regexp)
00290        *  If regexp is not an object whose [[Class]] property is "RegExp", it is
00291        *  replaced with the result of the expression new RegExp(regexp).
00292        */
00293       reg = tmpReg = new RegExp(a0.toString(exec), RegExp::None);
00294     }
00295     RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->interpreter()->builtinRegExp().imp());
00296     int **ovector = regExpObj->registerRegexp(reg, s);
00297     reg->prepareMatch(s);
00298     UString mstr = reg->match(s, -1, &pos, ovector);
00299     if (id == Search) {
00300       result = Number(pos);
00301     } else { // Match
00302       if (mstr.isNull()) {
00303         result = Null(); // no match
00304       } else if ((reg->flags() & RegExp::Global) == 0) {
00305     // case without 'g' flag is handled like RegExp.prototype.exec
00306     regExpObj->setSubPatterns(reg->subPatterns());
00307     result = regExpObj->arrayOfMatches(exec,mstr);
00308       } else {
00309     // return array of matches
00310     List list;
00311     while (pos >= 0) {
00312       list.append(String(mstr));
00313       pos += mstr.isEmpty() ? 1 : mstr.size();
00314       delete [] *ovector;
00315       mstr = reg->match(s, pos, &pos, ovector);
00316     }
00317     result = exec->lexicalInterpreter()->builtinArray().construct(exec, list);
00318       }
00319     }
00320     reg->doneMatch();
00321     delete tmpReg;
00322     break;
00323   }
00324   case Replace:
00325     if (a0.type() == ObjectType && a0.toObject(exec).inherits(&RegExpImp::info)) {
00326       RegExpImp* imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
00327       RegExp *reg = imp->regExp();
00328       bool global = false;
00329       Value tmp = imp->get(exec,"global");
00330       if (tmp.type() != UndefinedType && tmp.toBoolean(exec) == true)
00331         global = true;
00332 
00333       RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp().imp());
00334       int lastIndex = 0;
00335       Object o1;
00336       // Test if 2nd arg is a function (new in JS 1.3)
00337       if ( a1.type() == ObjectType && a1.toObject(exec).implementsCall() )
00338         o1 = a1.toObject(exec);
00339       else
00340         u3 = a1.toString(exec); // 2nd arg is the replacement string
00341 
00342       UString out;
00343       
00344       // This is either a loop (if global is set) or a one-way (if not).
00345       reg->prepareMatch(s);
00346       do {
00347         int **ovector = regExpObj->registerRegexp( reg, s );
00348         UString mstr = reg->match(s, lastIndex, &pos, ovector);
00349         regExpObj->setSubPatterns(reg->subPatterns());
00350         if (pos == -1)
00351           break;
00352           
00353         len = mstr.size();
00354 
00355         UString rstr;
00356         // Prepare replacement
00357         if (!o1.isValid())
00358         {
00359           rstr = u3;
00360           bool ok;
00361           // check if u3 matches $1 or $2 etc
00362           for (int i = 0; (i = rstr.find(UString("$"), i)) != -1; i++) {
00363             if (i+1<rstr.size() && rstr[i+1] == '$') {  // "$$" -> "$"
00364               rstr = rstr.substr(0,i) + "$" + rstr.substr(i+2);
00365               continue;
00366             }
00367             // Assume number part is one char exactly
00368             unsigned long pos = rstr.substr(i+1,1).toULong(&ok, false /* tolerate empty string */);
00369             if (ok && pos <= (unsigned)reg->subPatterns()) {
00370               rstr = rstr.substr(0,i)
00371                      + s.substr((*ovector)[2*pos],
00372                                 (*ovector)[2*pos+1]-(*ovector)[2*pos])
00373                      + rstr.substr(i+2);
00374               i += (*ovector)[2*pos+1]-(*ovector)[2*pos] - 1; // -1 offsets i++
00375             }
00376           }
00377         } else // 2nd arg is a function call. Spec from http://devedge.netscape.com/library/manuals/2000/javascript/1.5/reference/string.html#1194258
00378         {
00379           List l;
00380           l.append(String(mstr)); // First arg: complete matched substring
00381           // Then the submatch strings
00382           for ( unsigned int sub = 1; sub <= reg->subPatterns() ; ++sub )
00383             l.append( String( s.substr((*ovector)[2*sub],
00384                                (*ovector)[2*sub+1]-(*ovector)[2*sub]) ) );
00385           l.append(Number(pos)); // The offset within the string where the match occurred
00386           l.append(String(s)); // Last arg: the string itself. Can't see the difference with the 1st arg!
00387           Object thisObj = exec->interpreter()->globalObject();
00388           rstr = o1.call( exec, thisObj, l ).toString(exec);
00389         }
00390 
00391         // Append the stuff we skipped over to get to the match --
00392         // that would be [lastIndex, pos) of the original..
00393         if (pos != lastIndex)
00394           out += s.substr(lastIndex, pos - lastIndex);
00395 
00396         // Append the replacement..
00397         out += rstr;
00398         
00399         lastIndex = pos + len; // Skip over the matched stuff...
00400       } while (global);
00401 
00402       // Append the rest of the string to the output...
00403       if (lastIndex == 0 && out.size() == 0) // Don't copy stuff if nothing changed
00404         out = s;
00405       else
00406         out += s.substr(lastIndex, s.size() - lastIndex);
00407 
00408       reg->doneMatch();
00409 
00410       result = String(out);
00411     } else { // First arg is a string
00412       u2 = a0.toString(exec);
00413       pos = s.find(u2);
00414       len = u2.size();
00415       // Do the replacement
00416       if (pos == -1)
00417         result = String(s);
00418       else {
00419         u3 = s.substr(0, pos) + a1.toString(exec) +
00420              s.substr(pos + len);
00421         result = String(u3);
00422       }
00423     }
00424     break;
00425   case Slice: // http://developer.netscape.com/docs/manuals/js/client/jsref/string.htm#1194366 or 15.5.4.13
00426     {
00427         // The arg processing is very much like ArrayProtoFunc::Slice
00428         int begin = args[0].toUInt32(exec);
00429         int end = len;
00430         if (args[1].type() != UndefinedType) {
00431           end = args[1].toInteger(exec);
00432         }
00433         int from = begin < 0 ? len + begin : begin;
00434         int to = end < 0 ? len + end : end;
00435         if (to > from && to > 0 && from < len) {
00436           if (from < 0) {
00437             from = 0;
00438           }
00439           if (to > len) {
00440             to = len;
00441           }
00442           result = String(s.substr(from, to - from));
00443         } else {
00444           result = String("");
00445         }
00446         break;
00447     }
00448     case Split: {
00449     Object constructor = exec->lexicalInterpreter()->builtinArray();
00450     Object res = Object::dynamicCast(constructor.construct(exec,List::empty()));
00451     result = res;
00452     i = p0 = 0;
00453     uint32_t limit = (a1.type() != UndefinedType) ? a1.toUInt32(exec) : 0xFFFFFFFFU;
00454     if (a0.type() == ObjectType && Object::dynamicCast(a0).inherits(&RegExpImp::info)) {
00455       Object obj0 = Object::dynamicCast(a0);
00456       RegExp reg(obj0.get(exec,"source").toString(exec));
00457       reg.prepareMatch(s);
00458       if (s.isEmpty() && !reg.match(s, 0).isNull()) {
00459     // empty string matched by regexp -> empty array
00460         reg.doneMatch();
00461     res.put(exec, lengthPropertyName, Number(0), DontDelete|ReadOnly|DontEnum);
00462     break;
00463       }
00464       pos = 0;
00465       while (static_cast<uint32_t>(i) != limit && pos < s.size()) {
00466     // TODO: back references
00467         int mpos;
00468         int *ovector = 0L;
00469     UString mstr = reg.match(s, pos, &mpos, &ovector);
00470         delete [] ovector; ovector = 0L;
00471     if (mpos < 0)
00472       break;
00473     pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
00474     if (mpos != p0 || !mstr.isEmpty()) {
00475       res.put(exec,i, String(s.substr(p0, mpos-p0)));
00476       p0 = mpos + mstr.size();
00477       i++;
00478     }
00479       }
00480       reg.doneMatch();
00481     } else {
00482       u2 = a0.toString(exec);
00483       if (u2.isEmpty()) {
00484     if (s.isEmpty()) {
00485       // empty separator matches empty string -> empty array
00486       put(exec,lengthPropertyName, Number(0));
00487       break;
00488     } else {
00489       while (static_cast<uint32_t>(i) != limit && i < s.size()-1)
00490         res.put(exec,i++, String(s.substr(p0++, 1)));
00491     }
00492       } else {
00493     while (static_cast<uint32_t>(i) != limit && (pos = s.find(u2, p0)) >= 0) {
00494       res.put(exec,i, String(s.substr(p0, pos-p0)));
00495       p0 = pos + u2.size();
00496       i++;
00497     }
00498       }
00499     }
00500     // add remaining string, if any
00501     if (static_cast<uint32_t>(i) != limit)
00502       res.put(exec,i++, String(s.substr(p0)));
00503     res.put(exec,lengthPropertyName, Number(i));
00504     }
00505     break;
00506   case Substr: {
00507     n = a0.toInteger(exec);
00508     m = a1.toInteger(exec);
00509     int d, d2;
00510     if (n >= 0)
00511       d = n;
00512     else
00513       d = maxInt(len + n, 0);
00514     if (a1.type() == UndefinedType)
00515       d2 = len - d;
00516     else
00517       d2 = minInt(maxInt(m, 0), len - d);
00518     result = String(s.substr(d, d2));
00519     break;
00520   }
00521   case Substring: {
00522     double start = a0.toNumber(exec);
00523     double end = a1.toNumber(exec);
00524     if (KJS::isNaN(start))
00525       start = 0;
00526     if (KJS::isNaN(end))
00527       end = 0;
00528     if (start < 0)
00529       start = 0;
00530     if (end < 0)
00531       end = 0;
00532     if (start > len)
00533       start = len;
00534     if (end > len)
00535       end = len;
00536     if (a1.type() == UndefinedType)
00537       end = len;
00538     if (start > end) {
00539       double temp = end;
00540       end = start;
00541       start = temp;
00542     }
00543     result = String(s.substr((int)start, (int)end-(int)start));
00544     }
00545     break;
00546   case ToLowerCase:
00547   case ToLocaleLowerCase: // FIXME: To get this 100% right we need to detect Turkish and change I to lowercase i without a dot.
00548     for (i = 0; i < len; i++)
00549       s[i] = s[i].toLower();
00550     result = String(s);
00551     break;
00552   case ToUpperCase:
00553   case ToLocaleUpperCase: // FIXME: To get this 100% right we need to detect Turkish and change i to uppercase I with a dot.
00554     for (i = 0; i < len; i++)
00555       s[i] = s[i].toUpper();
00556     result = String(s);
00557     break;
00558 #ifndef KJS_PURE_ECMA
00559   case Big:
00560     result = String("<big>" + s + "</big>");
00561     break;
00562   case Small:
00563     result = String("<small>" + s + "</small>");
00564     break;
00565   case Blink:
00566     result = String("<blink>" + s + "</blink>");
00567     break;
00568   case Bold:
00569     result = String("<b>" + s + "</b>");
00570     break;
00571   case Fixed:
00572     result = String("<tt>" + s + "</tt>");
00573     break;
00574   case Italics:
00575     result = String("<i>" + s + "</i>");
00576     break;
00577   case Strike:
00578     result = String("<strike>" + s + "</strike>");
00579     break;
00580   case Sub:
00581     result = String("<sub>" + s + "</sub>");
00582     break;
00583   case Sup:
00584     result = String("<sup>" + s + "</sup>");
00585     break;
00586   case Fontcolor:
00587     result = String("<font color=\"" + a0.toString(exec) + "\">" + s + "</font>");
00588     break;
00589   case Fontsize:
00590     result = String("<font size=\"" + a0.toString(exec) + "\">" + s + "</font>");
00591     break;
00592   case Anchor:
00593     result = String("<a name=\"" + a0.toString(exec) + "\">" + s + "</a>");
00594     break;
00595   case Link:
00596     result = String("<a href=\"" + a0.toString(exec) + "\">" + s + "</a>");
00597     break;
00598 #endif
00599   }
00600 
00601   return result;
00602 }
00603 
00604 // ------------------------------ StringObjectImp ------------------------------
00605 
00606 StringObjectImp::StringObjectImp(ExecState *exec,
00607                                  FunctionPrototypeImp *funcProto,
00608                                  StringPrototypeImp *stringProto)
00609   : InternalFunctionImp(funcProto)
00610 {
00611   Value protect(this);
00612   // ECMA 15.5.3.1 String.prototype
00613   putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly);
00614 
00615   putDirect("fromCharCode", new StringObjectFuncImp(exec,funcProto), DontEnum);
00616 
00617   // no. of arguments for constructor
00618   putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
00619 }
00620 
00621 
00622 bool StringObjectImp::implementsConstruct() const
00623 {
00624   return true;
00625 }
00626 
00627 // ECMA 15.5.2
00628 Object StringObjectImp::construct(ExecState *exec, const List &args)
00629 {
00630   ObjectImp *proto = exec->lexicalInterpreter()->builtinStringPrototype().imp();
00631   if (args.size() == 0)
00632     return Object(new StringInstanceImp(proto));
00633   return Object(new StringInstanceImp(proto, args.begin()->dispatchToString(exec)));
00634 }
00635 
00636 bool StringObjectImp::implementsCall() const
00637 {
00638   return true;
00639 }
00640 
00641 // ECMA 15.5.1
00642 Value StringObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00643 {
00644   if (args.isEmpty())
00645     return String("");
00646   else {
00647     Value v = args[0];
00648     return String(v.toString(exec));
00649   }
00650 }
00651 
00652 // ------------------------------ StringObjectFuncImp --------------------------
00653 
00654 // ECMA 15.5.3.2 fromCharCode()
00655 StringObjectFuncImp::StringObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto)
00656   : InternalFunctionImp(funcProto)
00657 {
00658   Value protect(this);
00659   putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum);
00660 }
00661 
00662 bool StringObjectFuncImp::implementsCall() const
00663 {
00664   return true;
00665 }
00666 
00667 Value StringObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00668 {
00669   UString s;
00670   if (args.size()) {
00671     UChar *buf = new UChar[args.size()];
00672     UChar *p = buf;
00673     ListIterator it = args.begin();
00674     while (it != args.end()) {
00675       unsigned short u = it->toUInt16(exec);
00676       *p++ = UChar(u);
00677       it++;
00678     }
00679     s = UString(buf, args.size(), false);
00680   } else
00681     s = "";
00682 
00683   return String(s);
00684 }
KDE Home | KDE Accessibility Home | Description of Access Keys