22 #include "string_object.h"
23 #include "string_object.lut.h"
25 #include "error_object.h"
26 #include "operations.h"
27 #include "PropertyNameArray.h"
28 #include "regexp_object.h"
29 #include "commonunicode.h"
30 #include <wtf/unicode/libc/UnicodeLibC.h>
43 const ClassInfo StringInstance::info = {
"String",
nullptr,
nullptr,
nullptr};
45 StringInstance::StringInstance(JSObject *proto)
46 : JSWrapperObject(proto), m_conversionsCustomized(false)
48 setInternalValue(jsString(
""));
51 StringInstance::StringInstance(JSObject *proto, StringImp *
string)
52 : JSWrapperObject(proto), m_conversionsCustomized(false)
54 setInternalValue(
string);
57 StringInstance::StringInstance(JSObject *proto,
const UString &
string)
58 : JSWrapperObject(proto), m_conversionsCustomized(false)
60 setInternalValue(jsString(
string));
63 JSValue *StringInstance::lengthGetter(ExecState *, JSObject *,
const Identifier &,
const PropertySlot &slot)
65 return jsNumber(
static_cast<StringInstance *
>(slot.slotBase())->internalValue()->value().size());
68 JSValue *StringInstance::indexGetter(ExecState *, JSObject *,
unsigned,
const PropertySlot &slot)
70 const UChar c =
static_cast<StringInstance *
>(slot.slotBase())->internalValue()->value()[slot.index()];
71 return jsString(UString(&c, 1));
74 bool StringInstance::getOwnPropertySlot(ExecState *exec,
const Identifier &propertyName, PropertySlot &slot)
76 if (propertyName == exec->propertyNames().length) {
77 slot.setCustom(
this, lengthGetter);
82 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
83 unsigned length = internalValue()->value().size();
84 if (isStrictUInt32 && i < length) {
85 slot.setCustomIndex(
this, i, indexGetter);
89 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
92 bool StringInstance::getOwnPropertySlot(ExecState *exec,
unsigned propertyName, PropertySlot &slot)
94 unsigned length = internalValue()->value().size();
95 if (propertyName < length) {
96 slot.setCustomIndex(
this, propertyName, indexGetter);
100 return JSObject::getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
103 bool StringInstance::getOwnPropertyDescriptor(ExecState *exec,
const Identifier &propertyName, PropertyDescriptor &desc)
105 if (propertyName == exec->propertyNames().length) {
106 desc.setPropertyDescriptorValues(exec, jsNumber(internalValue()->value().size()), ReadOnly | DontDelete | DontEnum);
110 return KJS::JSObject::getOwnPropertyDescriptor(exec, propertyName, desc);
113 void StringInstance::put(ExecState *exec,
const Identifier &propertyName, JSValue *value,
int attr)
115 if (propertyName == exec->propertyNames().length) {
119 if (propertyName == exec->propertyNames().valueOf || propertyName == exec->propertyNames().toString) {
120 m_conversionsCustomized =
true;
126 bool StringInstance::deleteProperty(ExecState *exec,
const Identifier &propertyName)
128 if (propertyName == exec->propertyNames().length) {
131 return JSObject::deleteProperty(exec, propertyName);
134 void StringInstance::getOwnPropertyNames(ExecState *exec, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
136 int size = internalValue()->getString().size();
137 for (
int i = 0; i < size; i++) {
141 if (mode == PropertyMap::IncludeDontEnumProperties) {
142 propertyNames.add(exec->propertyNames().length);
145 return JSObject::getOwnPropertyNames(exec, propertyNames, mode);
148 UString StringInstance::toString(ExecState *exec)
const
150 if (prototype() == originalProto() && !conversionsCustomized() &&
151 !
static_cast<StringPrototype *
>(prototype())->conversionsCustomized()) {
152 return internalValue()->value();
154 return JSObject::toString(exec);
158 JSObject *StringInstance::valueClone(Interpreter *targetCtx)
const
160 return new StringInstance(targetCtx->builtinStringPrototype(), internalValue());
164 const ClassInfo StringPrototype::info = {
"String", &StringInstance::info, &stringTable,
nullptr};
214 StringPrototype::StringPrototype(ExecState *exec, ObjectPrototype *objProto)
215 : StringInstance(objProto)
218 putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
221 bool StringPrototype::getOwnPropertySlot(ExecState *exec,
const Identifier &propertyName, PropertySlot &slot)
223 return getStaticFunctionSlot<StringProtoFunc, StringInstance>(exec, &stringTable,
this, propertyName, slot);
228 StringProtoFunc::StringProtoFunc(ExecState *exec,
int i,
int len,
const Identifier &name)
229 : InternalFunctionImp(static_cast<FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()),
name)
232 putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
235 static inline void expandSourceRanges(UString::Range *&array,
int &count,
int &capacity)
241 newCapacity = capacity * 2;
244 UString::Range *newArray =
new UString::Range[newCapacity];
245 for (
int i = 0; i < count; i++) {
246 newArray[i] = array[i];
251 capacity = newCapacity;
255 static void pushSourceRange(UString::Range *&array,
int &count,
int &capacity, UString::Range range)
257 if (count + 1 > capacity) {
258 expandSourceRanges(array, count, capacity);
261 array[count] = range;
265 static inline void expandReplacements(UString *&array,
int &count,
int &capacity)
271 newCapacity = capacity * 2;
274 UString *newArray =
new UString[newCapacity];
275 for (
int i = 0; i < count; i++) {
276 newArray[i] = array[i];
281 capacity = newCapacity;
285 static void pushReplacement(UString *&array,
int &count,
int &capacity, UString replacement)
287 if (count + 1 > capacity) {
288 expandReplacements(array, count, capacity);
291 array[count] = replacement;
295 static inline UString substituteBackreferences(
const UString &replacement,
const UString &source,
int *ovector, RegExp *reg)
297 UString substitutedReplacement = replacement;
300 while ((i = substitutedReplacement.find(UString(
"$"), i + 1)) != -1) {
301 if (i + 1 == substitutedReplacement.size()) {
305 unsigned short ref = substitutedReplacement[i + 1].unicode();
306 int backrefStart = 0;
307 int backrefLength = 0;
311 substitutedReplacement = substitutedReplacement.substr(0, i + 1) + substitutedReplacement.substr(i + 2);
313 }
else if (ref ==
'&') {
314 backrefStart = ovector[0];
315 backrefLength = ovector[1] - backrefStart;
316 }
else if (ref ==
'`') {
318 backrefLength = ovector[0];
319 }
else if (ref ==
'\'') {
320 backrefStart = ovector[1];
321 backrefLength = source.size() - backrefStart;
322 }
else if (ref >=
'0' && ref <=
'9') {
324 unsigned backrefIndex =
ref -
'0';
325 if (backrefIndex > (
unsigned)reg->subPatterns()) {
328 if (substitutedReplacement.size() > i + 2) {
329 ref = substitutedReplacement[i + 2].unicode();
330 if (ref >=
'0' && ref <=
'9') {
331 backrefIndex = 10 * backrefIndex +
ref -
'0';
332 if (backrefIndex > (
unsigned)reg->subPatterns()) {
333 backrefIndex = backrefIndex / 10;
339 backrefStart = ovector[2 * backrefIndex];
340 backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
345 substitutedReplacement = substitutedReplacement.substr(0, i) + source.substr(backrefStart, backrefLength) + substitutedReplacement.substr(i + 2 + advance);
346 i += backrefLength - 1;
349 return substitutedReplacement;
352 static inline int localeCompare(
const UString &a,
const UString &b)
355 int retval = CompareStringW(LOCALE_USER_DEFAULT, 0,
356 reinterpret_cast<LPCWSTR
>(a.data()), a.size(),
357 reinterpret_cast<LPCWSTR
>(b.data()), b.size());
358 return !retval ? retval : retval - 2;
360 CFStringRef sa = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
reinterpret_cast<const UniChar *
>(a.data()), a.size(), kCFAllocatorNull);
361 CFStringRef sb = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
reinterpret_cast<const UniChar *
>(b.data()), b.size(), kCFAllocatorNull);
363 int retval = CFStringCompare(sa, sb, kCFCompareLocalized);
372 return compare(a, b);
376 static JSValue *
replace(ExecState *exec,
const UString &source, JSValue *pattern, JSValue *replacement)
378 JSObject *replacementFunction =
nullptr;
379 UString replacementString;
381 if (JSValue::isObject(replacement) && JSValue::toObject(replacement, exec)->implementsCall()) {
382 replacementFunction = JSValue::toObject(replacement, exec);
384 replacementString = JSValue::toString(replacement, exec);
387 if (JSValue::isObject(pattern) &&
static_cast<JSObject *
>(pattern)->inherits(&RegExpImp::info)) {
388 RegExp *reg =
static_cast<RegExpImp *
>(
pattern)->regExp();
389 bool global = reg->flags() & RegExp::Global;
391 RegExpObjectImp *regExpObj =
static_cast<RegExpObjectImp *
>(exec->lexicalInterpreter()->builtinRegExp());
395 int startPosition = 0;
397 UString::Range *sourceRanges =
nullptr;
398 int sourceRangeCount = 0;
399 int sourceRangeCapacity = 0;
400 UString *replacements =
nullptr;
401 int replacementCount = 0;
402 int replacementCapacity = 0;
405 RegExpStringContext ctx(source);
408 UString matchString = regExpObj->performMatch(reg, exec, ctx, source, startPosition, &matchIndex, &ovector);
409 if (matchIndex == -1) {
412 int matchLen = matchString.size();
414 pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, matchIndex - lastIndex));
415 UString substitutedReplacement;
416 if (replacementFunction) {
417 int completeMatchStart = ovector[0];
420 args.append(jsString(matchString));
422 for (
unsigned i = 0; i < reg->subPatterns(); i++) {
423 int matchStart = ovector[(i + 1) * 2];
424 int matchLen = ovector[(i + 1) * 2 + 1] - matchStart;
426 args.append(jsString(source.substr(matchStart, matchLen)));
429 args.append(jsNumber(completeMatchStart));
430 args.append(jsString(source));
432 substitutedReplacement = JSValue::toString(replacementFunction->call(exec, exec->dynamicInterpreter()->globalObject(), args), exec);
434 substitutedReplacement = substituteBackreferences(replacementString, source, ovector, reg);
436 pushReplacement(replacements, replacementCount, replacementCapacity, substitutedReplacement);
438 lastIndex = matchIndex + matchLen;
439 startPosition = lastIndex;
444 if (startPosition > source.size()) {
450 if (lastIndex < source.size()) {
451 pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, source.size() - lastIndex));
456 result = source.spliceSubstringsWithSeparators(sourceRanges, sourceRangeCount, replacements, replacementCount);
459 delete [] sourceRanges;
460 delete [] replacements;
462 return jsString(result);
466 UString patternString = JSValue::toString(pattern, exec);
467 int matchPos = source.find(patternString);
468 int matchLen = patternString.size();
470 if (matchPos == -1) {
471 return jsString(source);
474 if (replacementFunction) {
477 args.append(jsString(source.substr(matchPos, matchLen)));
478 args.append(jsNumber(matchPos));
479 args.append(jsString(source));
481 replacementString = JSValue::toString(replacementFunction->call(exec, exec->dynamicInterpreter()->globalObject(), args), exec);
484 return jsString(source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen));
487 static UnicodeSupport::StringConversionFunction toUpperF = Unicode::toUpper;
488 static UnicodeSupport::StringConversionFunction toLowerF = Unicode::toLower;
490 void StringProtoFunc::setToLowerFunction(UnicodeSupport::StringConversionFunction f)
495 void StringProtoFunc::setToUpperFunction(UnicodeSupport::StringConversionFunction f)
501 JSValue *StringProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj,
const List &args)
503 JSValue *result =
nullptr;
506 if (
id == ToString ||
id == ValueOf) {
507 if (!thisObj || !thisObj->inherits(&StringInstance::info)) {
508 return throwError(exec, TypeError);
511 return jsString(
static_cast<StringInstance *
>(thisObj)->internalValue()->toString(exec));
519 UString s = thisObj->toString(exec);
522 JSValue *a0 = args[0];
523 JSValue *a1 = args[1];
533 dpos = JSValue::isUndefined(a0) ? 0 : JSValue::toInteger(a0, exec);
534 if (dpos >= 0 && dpos < len) {
535 u = s.substr(
static_cast<int>(dpos), 1);
539 result = jsString(u);
544 dpos = JSValue::isUndefined(a0) ? 0 : JSValue::toInteger(a0, exec);
545 if (dpos >= 0 && dpos < len) {
546 result = jsNumber(s[
static_cast<int>(dpos)].unicode());
552 ListIterator it = args.begin();
553 for (; it != args.end(); ++it) {
554 s += JSValue::toString(*it, exec);
556 result = jsString(s);
560 if (JSValue::isObject(a0) &&
static_cast<JSObject*
>(a0)->inherits(&RegExpImp::info)) {
561 return throwError(exec, TypeError,
"RegExp reserved for future");
563 u2 = JSValue::toString(a0, exec);
564 if (JSValue::isUndefined(a1))
567 i = minInt(s.size(), JSValue::toInteger(a1, exec));
569 result = jsBoolean(pos >= 0 && s.substr(pos, u2.size()) == u2);
572 if (JSValue::isObject(a0) &&
static_cast<JSObject*
>(a0)->inherits(&RegExpImp::info)) {
573 return throwError(exec, TypeError,
"RegExp reserved for future");
575 u2 = JSValue::toString(a0, exec);
576 pos = JSValue::toInteger(a1, exec);
578 result = jsBoolean(i >= 0);
581 u2 = JSValue::toString(a0, exec);
582 if (JSValue::isUndefined(a1)) {
585 dpos = JSValue::toInteger(a1, exec);
594 result = jsNumber(s.find(u2,
static_cast<int>(dpos)));
597 u2 = JSValue::toString(a0, exec);
598 d = JSValue::toNumber(a1, exec);
599 if (JSValue::isUndefined(a1) || KJS::isNaN(d)) {
602 dpos = JSValue::toInteger(a1, exec);
611 result = jsNumber(s.rfind(u2,
static_cast<int>(dpos)));
616 RegExp *reg, *tmpReg =
nullptr;
617 RegExpImp *imp =
nullptr;
618 if (JSValue::isObject(a0) &&
static_cast<JSObject *
>(a0)->inherits(&RegExpImp::info)) {
619 reg =
static_cast<RegExpImp *
>(a0)->regExp();
626 reg = tmpReg =
new RegExp(JSValue::toString(a0, exec), RegExp::None);
628 if (!reg->isValid()) {
630 return throwError(exec, SyntaxError,
"Invalid regular expression");
632 RegExpObjectImp *regExpObj =
static_cast<RegExpObjectImp *
>(exec->lexicalInterpreter()->builtinRegExp());
634 RegExpStringContext ctx(u);
635 UString mstr = regExpObj->performMatch(reg, exec, ctx, u, 0, &pos);
638 result = jsNumber(pos);
641 if ((reg->flags() & RegExp::Global) == 0) {
646 result = regExpObj->arrayOfMatches(exec, mstr);
659 pos += mstr.isEmpty() ? 1 : mstr.size();
660 mstr = regExpObj->performMatch(reg, exec, ctx, u, pos, &pos);
663 imp->put(exec,
"lastIndex", jsNumber(lastIndex), DontDelete | DontEnum);
671 result = exec->lexicalInterpreter()->builtinArray()->construct(exec, list);
680 result =
replace(exec, s, a0, a1);
684 double start = JSValue::toInteger(a0, exec);
685 double end = JSValue::isUndefined(a1) ? len : JSValue::toInteger(a1, exec);
688 if (to > from && to > 0 && from < len) {
695 result = jsString(s.substr(
static_cast<int>(from),
static_cast<int>(to - from)));
697 result = jsString(
"");
702 JSObject *constructor = exec->lexicalInterpreter()->builtinArray();
703 JSObject *res =
static_cast<JSObject *
>(constructor->construct(exec,
List::empty()));
707 uint32_t limit = JSValue::isUndefined(a1) ? 0xFFFFFFFFU : JSValue::toUInt32(a1, exec);
708 if (JSValue::isObject(a0) &&
static_cast<JSObject *
>(a0)->inherits(&RegExpImp::info)) {
709 RegExp *reg =
static_cast<RegExpImp *
>(a0)->regExp();
711 RegExpStringContext ctx(u);
713 if (u.isEmpty() && !reg->match(ctx, u, &error, 0).isNull()) {
716 res->put(exec, exec->propertyNames().length, jsNumber(0));
720 while (!error &&
static_cast<uint32_t
>(i) != limit && pos < u.size()) {
723 int *ovector =
nullptr;
724 UString mstr = reg->match(ctx, u, &error, pos, &mpos, &ovector);
725 delete [] ovector; ovector =
nullptr;
729 pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
730 if (mpos != p0 || !mstr.isEmpty()) {
731 res->put(exec, i, jsString(u.substr(p0, mpos - p0)));
732 p0 = mpos + mstr.size();
738 RegExpObjectImp::throwRegExpError(exec);
742 u2 = JSValue::toString(a0, exec);
746 put(exec, exec->propertyNames().length, jsNumber(0));
749 while (
static_cast<uint32_t
>(i) != limit && i < u.size() - 1) {
750 res->put(exec, i++, jsString(u.substr(p0++, 1)));
754 while (
static_cast<uint32_t
>(i) != limit && (pos = u.find(u2, p0)) >= 0) {
755 res->put(exec, i, jsString(u.substr(p0, pos - p0)));
756 p0 = pos + u2.size();
762 if (
static_cast<uint32_t
>(i) != limit) {
763 res->put(exec, i++, jsString(u.substr(p0)));
765 res->put(exec, exec->propertyNames().length, jsNumber(i));
769 if (JSValue::isObject(a0) &&
static_cast<JSObject*
>(a0)->inherits(&RegExpImp::info)) {
770 return throwError(exec, TypeError,
"RegExp reserved for future");
772 u2 = JSValue::toString(a0, exec);
773 pos = maxInt(0, JSValue::toInteger(a1, exec));
774 result = jsBoolean(s.substr(pos, u2.size()) == u2);
781 double start = JSValue::toInteger(a0, exec);
782 double length = JSValue::isUndefined(a1) ? len : JSValue::toInteger(a1, exec);
797 if (length > len -
start) {
798 length = len -
start;
801 result = jsString(s.substr(
static_cast<int>(
start),
static_cast<int>(length)));
805 double start = JSValue::toNumber(a0, exec);
806 double end = JSValue::toNumber(a1, exec);
825 if (JSValue::isUndefined(a1)) {
833 result = jsString(s.substr((
int)
start, (
int)end - (
int)
start));
837 case ToLocaleLowerCase: {
840 uint16_t *dataPtr =
reinterpret_cast<uint16_t *
>(u.rep()->data());
841 uint16_t *destIfNeeded;
843 int len = toLowerF(dataPtr, u.size(), destIfNeeded);
845 result = jsString(UString(
reinterpret_cast<UChar *
>(destIfNeeded ? destIfNeeded : dataPtr), len));
847 result = jsString(s);
854 case ToLocaleUpperCase: {
857 uint16_t *dataPtr =
reinterpret_cast<uint16_t *
>(u.rep()->data());
858 uint16_t *destIfNeeded;
860 int len = toUpperF(dataPtr, u.size(), destIfNeeded);
862 result = jsString(UString(
reinterpret_cast<UChar *
>(destIfNeeded ? destIfNeeded : dataPtr), len));
864 result = jsString(s);
871 if (args.size() < 1) {
874 return jsNumber(localeCompare(s, JSValue::toString(a0, exec)));
876 double n = JSValue::toInteger(args[0], exec);
877 if (exec->hadException())
878 return jsUndefined();
879 if (n < 0 || KJS::isPosInf(n))
880 return throwError(exec, RangeError,
"Invalid repeat count");
883 for (
int i = 0; i < n; ++i)
887 return jsString(ret);
892 const uint16_t *dataPtr =
reinterpret_cast<uint16_t *
>(s.rep()->data());
894 const int size = s.size();
897 while (left < size && CommonUnicode::isStrWhiteSpace(dataPtr[left])) {
903 while (right > left && CommonUnicode::isStrWhiteSpace(dataPtr[right - 1])) {
907 result = jsString(s.substr(left, right - left));
910 #ifndef KJS_PURE_ECMA
912 result = jsString(
"<big>" + s +
"</big>");
915 result = jsString(
"<small>" + s +
"</small>");
918 result = jsString(
"<blink>" + s +
"</blink>");
921 result = jsString(
"<b>" + s +
"</b>");
924 result = jsString(
"<tt>" + s +
"</tt>");
927 result = jsString(
"<i>" + s +
"</i>");
930 result = jsString(
"<strike>" + s +
"</strike>");
933 result = jsString(
"<sub>" + s +
"</sub>");
936 result = jsString(
"<sup>" + s +
"</sup>");
939 result = jsString(
"<font color=\"" + JSValue::toString(a0, exec) +
"\">" + s +
"</font>");
942 result = jsString(
"<font size=\"" + JSValue::toString(a0, exec) +
"\">" + s +
"</font>");
945 result = jsString(
"<a name=\"" + JSValue::toString(a0, exec) +
"\">" + s +
"</a>");
948 result = jsString(
"<a href=\"" + JSValue::toString(a0, exec) +
"\">" + s +
"</a>");
958 StringObjectImp::StringObjectImp(ExecState *exec,
959 FunctionPrototype *funcProto,
960 StringPrototype *stringProto)
961 : InternalFunctionImp(funcProto)
964 putDirect(exec->propertyNames().prototype, stringProto, DontEnum | DontDelete | ReadOnly);
966 putDirectFunction(
new StringObjectFuncImp(exec, funcProto, exec->propertyNames().fromCharCode), DontEnum);
969 putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum);
972 bool StringObjectImp::implementsConstruct()
const
978 JSObject *StringObjectImp::construct(ExecState *exec,
const List &args)
980 JSObject *proto = exec->lexicalInterpreter()->builtinStringPrototype();
981 if (args.size() == 0) {
982 return new StringInstance(proto);
984 return new StringInstance(proto, JSValue::toString(*args.begin(), exec));
988 JSValue *StringObjectImp::callAsFunction(ExecState *exec, JSObject * ,
const List &args)
990 if (args.isEmpty()) {
993 JSValue *v = args[0];
994 return jsString(JSValue::toString(v, exec));
1001 StringObjectFuncImp::StringObjectFuncImp(ExecState *exec, FunctionPrototype *funcProto,
const Identifier &name)
1002 : InternalFunctionImp(funcProto,
name)
1004 putDirect(exec->propertyNames().length, jsNumber(1), DontDelete | ReadOnly | DontEnum);
1007 JSValue *StringObjectFuncImp::callAsFunction(ExecState *exec, JSObject * ,
const List &args)
1011 UChar *buf =
static_cast<UChar *
>(fastMalloc(args.size() *
sizeof(UChar)));
1013 ListIterator it = args.begin();
1014 while (it != args.end()) {
1015 unsigned short u = JSValue::toUInt16(*it, exec);
1019 s = UString(buf, args.size(),
false);