#include "value.h"
#include "object.h"
#include "types.h"
#include "interpreter.h"
#include "operations.h"
#include "regexp.h"
#include "regexp_object.h"
#include "string_object.h"
#include "error_object.h"
#include <stdio.h>
#include "string_object.lut.h"
using namespace KJS;
const ClassInfo StringInstanceImp::info = {"String", 0, 0, 0};
StringInstanceImp::StringInstanceImp(ObjectImp *proto)
: ObjectImp(proto)
{
setInternalValue(String(""));
}
StringInstanceImp::StringInstanceImp(ObjectImp *proto, const UString &string)
: ObjectImp(proto)
{
setInternalValue(String(string));
}
Value StringInstanceImp::get(ExecState *exec, const Identifier &propertyName) const
{
if (propertyName == lengthPropertyName)
return Number(internalValue().toString(exec).size());
bool ok;
const unsigned index = propertyName.toArrayIndex(&ok);
if (ok) {
const UString s = internalValue().toString(exec);
const unsigned length = s.size();
if (index >= length)
return Undefined();
const UChar c = s[index];
return String(UString(&c, 1));
}
return ObjectImp::get(exec, propertyName);
}
void StringInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
{
if (propertyName == lengthPropertyName)
return;
ObjectImp::put(exec, propertyName, value, attr);
}
bool StringInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
{
if (propertyName == lengthPropertyName)
return true;
bool ok;
const unsigned index = propertyName.toArrayIndex(&ok);
if (ok) {
const unsigned length = internalValue().toString(exec).size();
if (index < length)
return true;
}
return ObjectImp::hasProperty(exec, propertyName);
}
bool StringInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
{
if (propertyName == lengthPropertyName)
return false;
return ObjectImp::deleteProperty(exec, propertyName);
}
const ClassInfo StringPrototypeImp::info = {"String", &StringInstanceImp::info, &stringTable, 0};
StringPrototypeImp::StringPrototypeImp(ExecState *exec,
ObjectPrototypeImp *objProto)
: StringInstanceImp(objProto)
{
Value protect(this);
putDirect(lengthPropertyName, NumberImp::zero(), DontDelete|ReadOnly|DontEnum);
}
Value StringPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
{
return lookupGetFunction<StringProtoFuncImp, StringInstanceImp>( exec, propertyName, &stringTable, this );
}
StringProtoFuncImp::StringProtoFuncImp(ExecState *exec, int i, int len)
: InternalFunctionImp(
static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
), id(i)
{
Value protect(this);
putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
}
bool StringProtoFuncImp::implementsCall() const
{
return true;
}
Value StringProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
{
Value result;
if (id == ToString || id == ValueOf) {
if (thisObj.isNull() || !thisObj.inherits(&StringInstanceImp::info)) {
Object err = Error::create(exec,TypeError);
exec->setException(err);
return err;
}
return String(thisObj.internalValue().toString(exec));
}
int n, m;
UString u, u2, u3;
int pos, p0, i;
double d = 0.0;
UString s = thisObj.toString(exec);
int len = s.size();
Value a0 = args[0];
Value a1 = args[1];
switch (id) {
case ToString:
case ValueOf:
break;
case CharAt:
pos = a0.toInteger(exec);
if (pos < 0 || pos >= len)
u = "";
else
u = s.substr(pos, 1);
result = String(u);
break;
case CharCodeAt:
pos = a0.toInteger(exec);
if (pos < 0 || pos >= len)
d = NaN;
else {
UChar c = s[pos];
d = (c.high() << 8) + c.low();
}
result = Number(d);
break;
case Concat: {
ListIterator it = args.begin();
for ( ; it != args.end() ; ++it) {
s += it->dispatchToString(exec);
}
result = String(s);
break;
}
case IndexOf:
u2 = a0.toString(exec);
if (a1.type() == UndefinedType)
pos = 0;
else
pos = a1.toInteger(exec);
d = s.find(u2, pos);
result = Number(d);
break;
case LastIndexOf:
u2 = a0.toString(exec);
d = a1.toNumber(exec);
if (a1.type() == UndefinedType || KJS::isNaN(d) || KJS::isPosInf(d))
pos = len;
else
pos = a1.toInteger(exec);
if (pos < 0)
pos = 0;
d = s.rfind(u2, pos);
result = Number(d);
break;
case Match:
case Search: {
u = s;
RegExp *reg, *tmpReg = 0;
RegExpImp *imp = 0;
if (a0.isA(ObjectType) && a0.toObject(exec).inherits(&RegExpImp::info))
{
imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
reg = imp->regExp();
}
else
{
reg = tmpReg = new RegExp(a0.toString(exec), RegExp::None);
}
RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->interpreter()->builtinRegExp().imp());
int **ovector = regExpObj->registerRegexp(reg, u);
UString mstr = reg->match(u, -1, &pos, ovector);
if (id == Search) {
result = Number(pos);
} else {
if ((reg->flags() & RegExp::Global) == 0) {
if (mstr.isNull())
return Null(); regExpObj->setSubPatterns(reg->subPatterns());
result = regExpObj->arrayOfMatches(exec,mstr);
} else {
List list;
int lastIndex = 0;
while (pos >= 0) {
list.append(String(mstr));
lastIndex = pos;
pos += mstr.isEmpty() ? 1 : mstr.size();
delete [] *ovector;
mstr = reg->match(u, pos, &pos, ovector);
}
if (imp)
imp->put(exec, "lastIndex", Number(lastIndex), DontDelete|DontEnum);
if (list.isEmpty()) {
result = Null();
} else {
result = exec->interpreter()->builtinArray().construct(exec, list);
}
}
}
delete tmpReg;
break;
}
case Replace:
u = s;
if (a0.type() == ObjectType && a0.toObject(exec).inherits(&RegExpImp::info)) {
RegExpImp* imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
RegExp *reg = imp->regExp();
bool global = false;
Value tmp = imp->get(exec,"global");
if (tmp.type() != UndefinedType && tmp.toBoolean(exec) == true)
global = true;
RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->interpreter()->builtinRegExp().imp());
int lastIndex = 0;
u3 = a1.toString(exec); do {
int **ovector = regExpObj->registerRegexp( reg, u );
UString mstr = reg->match(u, lastIndex, &pos, ovector);
regExpObj->setSubPatterns(reg->subPatterns());
if (pos == -1)
break;
len = mstr.size();
UString rstr(u3);
bool ok;
for (int i = 0; (i = rstr.find(UString("$"), i)) != -1; i++) {
if (i+1<rstr.size() && rstr[i+1] == '$') { rstr = rstr.substr(0,i) + "$" + rstr.substr(i+2);
continue;
}
unsigned long pos = rstr.substr(i+1,1).toULong(&ok);
if (ok && pos <= (unsigned)reg->subPatterns()) {
rstr = rstr.substr(0,i)
+ u.substr((*ovector)[2*pos],
(*ovector)[2*pos+1]-(*ovector)[2*pos])
+ rstr.substr(i+2);
i += (*ovector)[2*pos+1]-(*ovector)[2*pos] - 1; }
}
lastIndex = pos + rstr.size();
u = u.substr(0, pos) + rstr + u.substr(pos + len);
if (len == 0) {
lastIndex = lastIndex + 1;
if (lastIndex > u.size())
break;
}
} while (global);
result = String(u);
} else { u2 = a0.toString(exec);
pos = u.find(u2);
len = u2.size();
if (pos == -1)
result = String(s);
else {
u3 = u.substr(0, pos) + a1.toString(exec) +
u.substr(pos + len);
result = String(u3);
}
}
break;
case Slice: {
result = exec->interpreter()->builtinArray().construct(exec,List::empty());
int begin = args[0].toUInt32(exec);
int end = len;
if (args[1].type() != UndefinedType)
{
end = args[1].toUInt32(exec);
if ( end < 0 )
end += len;
}
if ( begin < 0 || end < 0 || begin >= end ) {
result = String();
break;
}
result = String(s.substr(begin, end-begin));
break;
}
case Split: {
Object constructor = exec->interpreter()->builtinArray();
Object res = Object::dynamicCast(constructor.construct(exec,List::empty()));
result = res;
u = s;
i = p0 = 0;
d = (a1.type() != UndefinedType) ? a1.toInteger(exec) : -1; if (a0.type() == ObjectType && Object::dynamicCast(a0).inherits(&RegExpImp::info)) {
Object obj0 = Object::dynamicCast(a0);
RegExp reg(obj0.get(exec,"source").toString(exec));
if (u.isEmpty() && !reg.match(u, 0).isNull()) {
res.put(exec,lengthPropertyName, Number(0));
break;
}
pos = 0;
while (pos < u.size()) {
int mpos;
int *ovector = 0L;
UString mstr = reg.match(u, pos, &mpos, &ovector);
delete [] ovector; ovector = 0L;
if (mpos < 0)
break;
pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
if (mpos != p0 || !mstr.isEmpty()) {
res.put(exec,i, String(u.substr(p0, mpos-p0)));
p0 = mpos + mstr.size();
i++;
}
}
} else if (a0.type() != UndefinedType) {
u2 = a0.toString(exec);
if (u2.isEmpty()) {
if (u.isEmpty()) {
put(exec,lengthPropertyName, Number(0));
break;
} else {
while (i != d && i < u.size()-1)
res.put(exec, i++, String(u.substr(p0++, 1)));
}
} else {
while (i != d && (pos = u.find(u2, p0)) >= 0) {
res.put(exec, i, String(u.substr(p0, pos-p0)));
p0 = pos + u2.size();
i++;
}
}
}
if (i != d)
res.put(exec, i++, String(u.substr(p0)));
res.put(exec,lengthPropertyName, Number(i));
}
break;
case Substr: {
n = a0.toInteger(exec);
m = a1.toInteger(exec);
int d, d2;
if (n >= 0)
d = n;
else
d = maxInt(len + n, 0);
if (a1.type() == UndefinedType)
d2 = len - d;
else
d2 = minInt(maxInt(m, 0), len - d);
result = String(s.substr(d, d2));
break;
}
case Substring: {
double start = a0.toNumber(exec);
double end = a1.toNumber(exec);
if (KJS::isNaN(start))
start = 0;
if (KJS::isNaN(end))
end = 0;
if (start < 0)
start = 0;
if (end < 0)
end = 0;
if (start > len)
start = len;
if (end > len)
end = len;
if (a1.type() == UndefinedType)
end = len;
if (start > end) {
double temp = end;
end = start;
start = temp;
}
result = String(s.substr((int)start, (int)end-(int)start));
}
break;
case ToLowerCase:
u = s;
for (i = 0; i < len; i++)
u[i] = u[i].toLower();
result = String(u);
break;
case ToUpperCase:
u = s;
for (i = 0; i < len; i++)
u[i] = u[i].toUpper();
result = String(u);
break;
#ifndef KJS_PURE_ECMA
case Big:
result = String("<BIG>" + s + "</BIG>");
break;
case Small:
result = String("<SMALL>" + s + "</SMALL>");
break;
case Blink:
result = String("<BLINK>" + s + "</BLINK>");
break;
case Bold:
result = String("<B>" + s + "</B>");
break;
case Fixed:
result = String("<TT>" + s + "</TT>");
break;
case Italics:
result = String("<I>" + s + "</I>");
break;
case Strike:
result = String("<STRIKE>" + s + "</STRIKE>");
break;
case Sub:
result = String("<SUB>" + s + "</SUB>");
break;
case Sup:
result = String("<SUP>" + s + "</SUP>");
break;
case Fontcolor:
result = String("<FONT COLOR=" + a0.toString(exec) + ">"
+ s + "</FONT>");
break;
case Fontsize:
result = String("<FONT SIZE=" + a0.toString(exec) + ">"
+ s + "</FONT>");
break;
case Anchor:
result = String("<a name=" + a0.toString(exec) + ">"
+ s + "</a>");
break;
case Link:
result = String("<a href=" + a0.toString(exec) + ">"
+ s + "</a>");
break;
#endif
}
return result;
}
StringObjectImp::StringObjectImp(ExecState *exec,
FunctionPrototypeImp *funcProto,
StringPrototypeImp *stringProto)
: InternalFunctionImp(funcProto)
{
Value protect(this);
putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly);
static Identifier fromCharCode("fromCharCode");
putDirect(fromCharCode, new StringObjectFuncImp(exec,funcProto), DontEnum);
putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
}
bool StringObjectImp::implementsConstruct() const
{
return true;
}
Object StringObjectImp::construct(ExecState *exec, const List &args)
{
ObjectImp *proto = exec->interpreter()->builtinStringPrototype().imp();
if (args.size() == 0)
return Object(new StringInstanceImp(proto));
return Object(new StringInstanceImp(proto, args.begin()->dispatchToString(exec)));
}
bool StringObjectImp::implementsCall() const
{
return true;
}
Value StringObjectImp::call(ExecState *exec, Object &, const List &args)
{
if (args.isEmpty())
return String("");
else {
Value v = args[0];
return String(v.toString(exec));
}
}
StringObjectFuncImp::StringObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto)
: InternalFunctionImp(funcProto)
{
Value protect(this);
putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum);
}
bool StringObjectFuncImp::implementsCall() const
{
return true;
}
Value StringObjectFuncImp::call(ExecState *exec, Object &, const List &args)
{
UString s;
if (args.size()) {
UChar *buf = new UChar[args.size()];
UChar *p = buf;
ListIterator it = args.begin();
while (it != args.end()) {
unsigned short u = it->toUInt16(exec);
*p++ = UChar(u);
it++;
}
s = UString(buf, args.size(), false);
} else
s = "";
return String(s);
}