1 // -*- c-basic-offset: 2 -*-
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5 * Copyright (C) 2003 Apple Computer, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "interpreter.h"
27 #include "operations.h"
29 #include "regexp_object.h"
30 #include "string_object.h"
31 #include "error_object.h"
33 #include "string_object.lut.h"
37 // ------------------------------ StringInstanceImp ----------------------------
39 const ClassInfo StringInstanceImp::info = {"String", 0, 0, 0};
41 StringInstanceImp::StringInstanceImp(ObjectImp *proto)
44 setInternalValue(String(""));
47 StringInstanceImp::StringInstanceImp(ObjectImp *proto, const UString &string)
50 setInternalValue(String(string));
53 Value StringInstanceImp::get(ExecState *exec, const Identifier &propertyName) const
55 if (propertyName == lengthPropertyName)
56 return Number(internalValue().toString(exec).size());
59 const unsigned index = propertyName.toArrayIndex(&ok);
61 const UString s = internalValue().toString(exec);
62 const unsigned length = s.size();
65 const UChar c = s[index];
66 return String(UString(&c, 1));
69 return ObjectImp::get(exec, propertyName);
72 void StringInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
74 if (propertyName == lengthPropertyName)
76 ObjectImp::put(exec, propertyName, value, attr);
79 bool StringInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
81 if (propertyName == lengthPropertyName)
85 const unsigned index = propertyName.toArrayIndex(&ok);
87 const unsigned length = internalValue().toString(exec).size();
92 return ObjectImp::hasProperty(exec, propertyName);
95 bool StringInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
97 if (propertyName == lengthPropertyName)
99 return ObjectImp::deleteProperty(exec, propertyName);
102 // ------------------------------ StringPrototypeImp ---------------------------
103 const ClassInfo StringPrototypeImp::info = {"String", &StringInstanceImp::info, &stringTable, 0};
104 /* Source for string_object.lut.h
105 @begin stringTable 26
106 toString StringProtoFuncImp::ToString DontEnum|Function 0
107 valueOf StringProtoFuncImp::ValueOf DontEnum|Function 0
108 charAt StringProtoFuncImp::CharAt DontEnum|Function 1
109 charCodeAt StringProtoFuncImp::CharCodeAt DontEnum|Function 1
110 concat StringProtoFuncImp::Concat DontEnum|Function 1
111 indexOf StringProtoFuncImp::IndexOf DontEnum|Function 1
112 lastIndexOf StringProtoFuncImp::LastIndexOf DontEnum|Function 1
113 match StringProtoFuncImp::Match DontEnum|Function 1
114 replace StringProtoFuncImp::Replace DontEnum|Function 2
115 search StringProtoFuncImp::Search DontEnum|Function 1
116 slice StringProtoFuncImp::Slice DontEnum|Function 2
117 split StringProtoFuncImp::Split DontEnum|Function 2
118 substr StringProtoFuncImp::Substr DontEnum|Function 2
119 substring StringProtoFuncImp::Substring DontEnum|Function 2
120 toLowerCase StringProtoFuncImp::ToLowerCase DontEnum|Function 0
121 toUpperCase StringProtoFuncImp::ToUpperCase DontEnum|Function 0
123 # Under here: html extension, should only exist if KJS_PURE_ECMA is not defined
124 # I guess we need to generate two hashtables in the .lut.h file, and use #ifdef
125 # to select the right one... TODO. #####
126 big StringProtoFuncImp::Big DontEnum|Function 0
127 small StringProtoFuncImp::Small DontEnum|Function 0
128 blink StringProtoFuncImp::Blink DontEnum|Function 0
129 bold StringProtoFuncImp::Bold DontEnum|Function 0
130 fixed StringProtoFuncImp::Fixed DontEnum|Function 0
131 italics StringProtoFuncImp::Italics DontEnum|Function 0
132 strike StringProtoFuncImp::Strike DontEnum|Function 0
133 sub StringProtoFuncImp::Sub DontEnum|Function 0
134 sup StringProtoFuncImp::Sup DontEnum|Function 0
135 fontcolor StringProtoFuncImp::Fontcolor DontEnum|Function 1
136 fontsize StringProtoFuncImp::Fontsize DontEnum|Function 1
137 anchor StringProtoFuncImp::Anchor DontEnum|Function 1
138 link StringProtoFuncImp::Link DontEnum|Function 1
142 StringPrototypeImp::StringPrototypeImp(ExecState *exec,
143 ObjectPrototypeImp *objProto)
144 : StringInstanceImp(objProto)
147 // The constructor will be added later, after StringObjectImp has been built
148 putDirect(lengthPropertyName, NumberImp::zero(), DontDelete|ReadOnly|DontEnum);
152 Value StringPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
154 return lookupGetFunction<StringProtoFuncImp, StringInstanceImp>( exec, propertyName, &stringTable, this );
157 // ------------------------------ StringProtoFuncImp ---------------------------
159 StringProtoFuncImp::StringProtoFuncImp(ExecState *exec, int i, int len)
160 : InternalFunctionImp(
161 static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
165 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
168 bool StringProtoFuncImp::implementsCall() const
173 // ECMA 15.5.4.2 - 15.5.4.20
174 Value StringProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
178 // toString and valueOf are no generic function.
179 if (id == ToString || id == ValueOf) {
180 if (thisObj.isNull() || !thisObj.inherits(&StringInstanceImp::info)) {
181 Object err = Error::create(exec,TypeError);
182 exec->setException(err);
186 return String(thisObj.internalValue().toString(exec));
194 UString s = thisObj.toString(exec);
206 dpos = a0.toInteger(exec);
207 if (dpos >= 0 && dpos < len) // false for NaN
208 u = s.substr(static_cast<int>(dpos), 1);
214 dpos = a0.toInteger(exec);
215 if (dpos >= 0 && dpos < len) {// false for NaN
216 UChar c = s[static_cast<int>(dpos)];
217 d = (c.high() << 8) + c.low();
223 ListIterator it = args.begin();
224 for ( ; it != args.end() ; ++it) {
225 s += it->dispatchToString(exec);
231 u2 = a0.toString(exec);
232 if (a1.type() == UndefinedType)
235 dpos = a1.toInteger(exec);
236 if (dpos >= 0) { // false for NaN
242 d = s.find(u2, static_cast<int>(dpos));
246 u2 = a0.toString(exec);
247 d = a1.toNumber(exec);
248 if (a1.type() == UndefinedType || KJS::isNaN(d))
251 dpos = a1.toInteger(exec);
252 if (dpos >= 0) { // false for NaN
258 d = s.rfind(u2, static_cast<int>(dpos));
264 RegExp *reg, *tmpReg = 0;
266 if (a0.isA(ObjectType) && a0.toObject(exec).inherits(&RegExpImp::info))
268 imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
273 * ECMA 15.5.4.12 String.prototype.search (regexp)
274 * If regexp is not an object whose [[Class]] property is "RegExp", it is
275 * replaced with the result of the expression new RegExp(regexp).
277 reg = tmpReg = new RegExp(a0.toString(exec), RegExp::None);
279 RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp().imp());
280 int **ovector = regExpObj->registerRegexp(reg, u);
281 UString mstr = reg->match(u, -1, &pos, ovector);
283 result = Number(pos);
286 if ((reg->flags() & RegExp::Global) == 0) {
287 // case without 'g' flag is handled like RegExp.prototype.exec
291 regExpObj->setSubPatterns(reg->subPatterns());
292 result = regExpObj->arrayOfMatches(exec,mstr);
295 // return array of matches
300 list.append(UndefinedImp::staticUndefined);
302 list.append(String(mstr));
304 pos += mstr.isEmpty() ? 1 : mstr.size();
306 mstr = reg->match(u, pos, &pos, ovector);
309 imp->put(exec, "lastIndex", Number(lastIndex), DontDelete|DontEnum);
310 if (list.isEmpty()) {
311 // if there are no matches at all, it's important to return
312 // Null instead of an empty array, because this matches
313 // other browsers and because Null is a false value.
316 result = exec->lexicalInterpreter()->builtinArray().construct(exec, list);
325 if (a0.type() == ObjectType && a0.toObject(exec).inherits(&RegExpImp::info)) {
326 RegExpImp* imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
327 RegExp *reg = imp->regExp();
329 Value tmp = imp->get(exec,"global");
330 if (tmp.type() != UndefinedType && tmp.toBoolean(exec) == true)
333 RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp().imp());
335 u3 = a1.toString(exec); // replacement string
336 // This is either a loop (if global is set) or a one-way (if not).
338 int **ovector = regExpObj->registerRegexp( reg, u );
339 UString mstr = reg->match(u, lastIndex, &pos, ovector);
340 regExpObj->setSubPatterns(reg->subPatterns());
346 // check if u3 matches $1 or $2 etc
347 for (int i = 0; (i = rstr.find(UString("$"), i)) != -1; i++) {
348 if (i+1<rstr.size() && rstr[i+1] == '$') { // "$$" -> "$"
349 rstr = rstr.substr(0,i) + "$" + rstr.substr(i+2);
352 // Assume number part is one char exactly
353 unsigned long pos = rstr.substr(i+1,1).toULong(&ok, false /* tolerate empty string */);
354 if (ok && pos <= (unsigned)reg->subPatterns()) {
355 rstr = rstr.substr(0,i)
356 + u.substr((*ovector)[2*pos],
357 (*ovector)[2*pos+1]-(*ovector)[2*pos])
359 i += (*ovector)[2*pos+1]-(*ovector)[2*pos] - 1; // -1 offsets i++
362 lastIndex = pos + rstr.size();
363 u = u.substr(0, pos) + rstr + u.substr(pos + len);
364 //fprintf(stderr,"pos=%d,len=%d,lastIndex=%d,u=%s\n",pos,len,lastIndex,u.ascii());
366 // special case of empty match
368 lastIndex = lastIndex + 1;
369 if (lastIndex > u.size())
375 } else { // First arg is a string
376 u2 = a0.toString(exec);
379 // Do the replacement
383 u3 = u.substr(0, pos) + a1.toString(exec) +
389 case Slice: // http://developer.netscape.com/docs/manuals/js/client/jsref/string.htm#1194366
391 // The arg processing is very much like ArrayProtoFunc::Slice
392 double begin = args[0].toInteger(exec);
393 if (begin >= 0) { // false for NaN
398 if (!(begin >= 0)) // true for NaN
402 if (args[1].type() != UndefinedType) {
403 end = args[1].toInteger(exec);
404 if (end >= 0) { // false for NaN
409 if (!(end >= 0)) // true for NaN
413 //printf( "Slicing from %d to %d \n", begin, end );
414 result = String(s.substr(static_cast<int>(begin), static_cast<int>(end-begin)));
418 Object constructor = exec->lexicalInterpreter()->builtinArray();
419 Object res = Object::dynamicCast(constructor.construct(exec,List::empty()));
423 uint32_t limit = a1.type() == UndefinedType ? 0xFFFFFFFFU : a1.toUInt32(exec);
424 if (a0.type() == ObjectType && Object::dynamicCast(a0).inherits(&RegExpImp::info)) {
425 Object obj0 = Object::dynamicCast(a0);
426 RegExp reg(obj0.get(exec,"source").toString(exec));
427 if (u.isEmpty() && !reg.match(u, 0).isNull()) {
428 // empty string matched by regexp -> empty array
429 res.put(exec,lengthPropertyName, Number(0));
433 while (static_cast<uint32_t>(i) != limit && pos < u.size()) {
434 // TODO: back references
437 UString mstr = reg.match(u, pos, &mpos, &ovector);
438 delete [] ovector; ovector = 0L;
441 pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
442 if (mpos != p0 || !mstr.isEmpty()) {
443 res.put(exec,i, String(u.substr(p0, mpos-p0)));
444 p0 = mpos + mstr.size();
449 u2 = a0.toString(exec);
452 // empty separator matches empty string -> empty array
453 put(exec,lengthPropertyName, Number(0));
456 while (static_cast<uint32_t>(i) != limit && i < u.size()-1)
457 res.put(exec, i++, String(u.substr(p0++, 1)));
460 while (static_cast<uint32_t>(i) != limit && (pos = u.find(u2, p0)) >= 0) {
461 res.put(exec, i, String(u.substr(p0, pos-p0)));
462 p0 = pos + u2.size();
467 // add remaining string, if any
468 if (static_cast<uint32_t>(i) != limit)
469 res.put(exec, i++, String(u.substr(p0)));
470 res.put(exec,lengthPropertyName, Number(i));
474 double d = a0.toInteger(exec);
475 double d2 = a1.toInteger(exec);
476 if (!(d >= 0)) { // true for NaN
478 if (!(d >= 0)) // true for NaN
489 result = String(s.substr(static_cast<int>(d), static_cast<int>(d2)));
493 double start = a0.toNumber(exec);
494 double end = a1.toNumber(exec);
495 if (KJS::isNaN(start))
507 if (a1.type() == UndefinedType)
514 result = String(s.substr((int)start, (int)end-(int)start));
519 for (i = 0; i < len; i++)
520 u[i] = u[i].toLower();
525 for (i = 0; i < len; i++)
526 u[i] = u[i].toUpper();
529 #ifndef KJS_PURE_ECMA
531 result = String("<big>" + s + "</big>");
534 result = String("<small>" + s + "</small>");
537 result = String("<blink>" + s + "</blink>");
540 result = String("<b>" + s + "</b>");
543 result = String("<tt>" + s + "</tt>");
546 result = String("<i>" + s + "</i>");
549 result = String("<strike>" + s + "</strike>");
552 result = String("<sub>" + s + "</sub>");
555 result = String("<sup>" + s + "</sup>");
558 result = String("<font color=" + a0.toString(exec) + ">"
562 result = String("<font size=" + a0.toString(exec) + ">"
566 result = String("<a name=" + a0.toString(exec) + ">"
570 result = String("<a href=" + a0.toString(exec) + ">"
579 // ------------------------------ StringObjectImp ------------------------------
581 StringObjectImp::StringObjectImp(ExecState *exec,
582 FunctionPrototypeImp *funcProto,
583 StringPrototypeImp *stringProto)
584 : InternalFunctionImp(funcProto)
587 // ECMA 15.5.3.1 String.prototype
588 putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly);
590 static Identifier fromCharCode("fromCharCode");
591 putDirect(fromCharCode, new StringObjectFuncImp(exec,funcProto), DontEnum);
593 // no. of arguments for constructor
594 putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
598 bool StringObjectImp::implementsConstruct() const
604 Object StringObjectImp::construct(ExecState *exec, const List &args)
606 ObjectImp *proto = exec->lexicalInterpreter()->builtinStringPrototype().imp();
607 if (args.size() == 0)
608 return Object(new StringInstanceImp(proto));
609 return Object(new StringInstanceImp(proto, args.begin()->dispatchToString(exec)));
612 bool StringObjectImp::implementsCall() const
618 Value StringObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
624 return String(v.toString(exec));
628 // ------------------------------ StringObjectFuncImp --------------------------
630 // ECMA 15.5.3.2 fromCharCode()
631 StringObjectFuncImp::StringObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto)
632 : InternalFunctionImp(funcProto)
635 putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum);
638 bool StringObjectFuncImp::implementsCall() const
643 Value StringObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
647 UChar *buf = new UChar[args.size()];
649 ListIterator it = args.begin();
650 while (it != args.end()) {
651 unsigned short u = it->toUInt16(exec);
655 s = UString(buf, args.size(), false);