Encapsulate and protect all accesses to the vtable of Heap objects
[platform/upstream/qtdeclarative.git] / src / qml / jsruntime / qv4stringobject.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33
34
35 #include "qv4stringobject_p.h"
36 #include "qv4regexp_p.h"
37 #include "qv4regexpobject_p.h"
38 #include "qv4objectproto_p.h"
39 #include <private/qv4mm_p.h>
40 #include "qv4scopedvalue_p.h"
41 #include "qv4alloca_p.h"
42 #include <QtCore/QDateTime>
43 #include <QtCore/QDebug>
44 #include <QtCore/QStringList>
45
46 #include <private/qqmljsengine_p.h>
47 #include <private/qqmljslexer_p.h>
48 #include <private/qqmljsparser_p.h>
49 #include <private/qqmljsast_p.h>
50 #include <qv4jsir_p.h>
51 #include <qv4codegen_p.h>
52
53 #include <cassert>
54
55 #ifndef Q_OS_WIN
56 #  include <time.h>
57 #  ifndef Q_OS_VXWORKS
58 #    include <sys/time.h>
59 #  else
60 #    include "qplatformdefs.h"
61 #  endif
62 #else
63 #  include <windows.h>
64 #endif
65
66 using namespace QV4;
67
68 DEFINE_OBJECT_VTABLE(StringObject);
69
70 Heap::StringObject::StringObject(InternalClass *ic, QV4::Object *prototype)
71     : Heap::Object(ic, prototype)
72 {
73     Q_ASSERT(vtable() == QV4::StringObject::staticVTable());
74     string = ic->engine->newString();
75
76     Scope scope(ic->engine);
77     ScopedObject s(scope, this);
78     s->defineReadonlyProperty(ic->engine->id_length(), Primitive::fromInt32(0));
79 }
80
81 Heap::StringObject::StringObject(ExecutionEngine *engine, const QV4::String *str)
82     : Heap::Object(engine->emptyClass, engine->stringPrototype())
83 {
84     string = str->d();
85
86     Scope scope(engine);
87     ScopedObject s(scope, this);
88     s->defineReadonlyProperty(engine->id_length(), Primitive::fromUInt32(length()));
89 }
90
91 Heap::String *Heap::StringObject::getIndex(uint index) const
92 {
93     QString str = string->toQString();
94     if (index >= (uint)str.length())
95         return 0;
96     return internalClass->engine->newString(str.mid(index, 1));
97 }
98
99 uint Heap::StringObject::length() const
100 {
101     return string->toQString().length();
102 }
103
104 bool StringObject::deleteIndexedProperty(Managed *m, uint index)
105 {
106     ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine();
107     Scope scope(v4);
108     Scoped<StringObject> o(scope, m->as<StringObject>());
109     Q_ASSERT(!!o);
110
111     if (index < static_cast<uint>(o->d()->string->toQString().length())) {
112         if (v4->currentContext()->strictMode)
113             v4->throwTypeError();
114         return false;
115     }
116     return true;
117 }
118
119 void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
120 {
121     name->setM(0);
122     StringObject *s = static_cast<StringObject *>(m);
123     uint slen = s->d()->string->toQString().length();
124     if (it->arrayIndex <= slen) {
125         while (it->arrayIndex < slen) {
126             *index = it->arrayIndex;
127             ++it->arrayIndex;
128             PropertyAttributes a;
129             Property pd;
130             s->getOwnProperty(*index, &a, &pd);
131             if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
132                 *attrs = a;
133                 p->copy(&pd, a);
134                 return;
135             }
136         }
137         if (s->arrayData()) {
138             it->arrayNode = s->sparseBegin();
139             // iterate until we're past the end of the string
140             while (it->arrayNode && it->arrayNode->key() < slen)
141                 it->arrayNode = it->arrayNode->nextNode();
142         }
143     }
144
145     return Object::advanceIterator(m, it, name, index, p, attrs);
146 }
147
148 void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e)
149 {
150     StringObject::Data *o = static_cast<StringObject::Data *>(that);
151     o->string->mark(e);
152     Object::markObjects(that, e);
153 }
154
155 DEFINE_OBJECT_VTABLE(StringCtor);
156
157 Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope)
158     : Heap::FunctionObject(scope, QStringLiteral("String"))
159 {
160 }
161
162 ReturnedValue StringCtor::construct(const Managed *m, CallData *callData)
163 {
164     ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
165     Scope scope(v4);
166     ScopedString value(scope);
167     if (callData->argc)
168         value = callData->args[0].toString(v4);
169     else
170         value = v4->newString();
171     return Encode(v4->newStringObject(value));
172 }
173
174 ReturnedValue StringCtor::call(const Managed *m, CallData *callData)
175 {
176     ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
177     Scope scope(v4);
178     ScopedValue value(scope);
179     if (callData->argc)
180         value = callData->args[0].toString(v4);
181     else
182         value = v4->newString();
183     return value->asReturnedValue();
184 }
185
186 void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
187 {
188     Scope scope(engine);
189     ScopedObject o(scope);
190
191     ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
192     ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
193     ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1);
194
195     defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
196     defineDefaultProperty(engine->id_toString(), method_toString);
197     defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical
198     defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
199     defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
200     defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
201     defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
202     defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
203     defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
204     defineDefaultProperty(QStringLiteral("match"), method_match, 1);
205     defineDefaultProperty(QStringLiteral("replace"), method_replace, 2);
206     defineDefaultProperty(QStringLiteral("search"), method_search, 1);
207     defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
208     defineDefaultProperty(QStringLiteral("split"), method_split, 2);
209     defineDefaultProperty(QStringLiteral("substr"), method_substr, 2);
210     defineDefaultProperty(QStringLiteral("substring"), method_substring, 2);
211     defineDefaultProperty(QStringLiteral("toLowerCase"), method_toLowerCase);
212     defineDefaultProperty(QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase);
213     defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase);
214     defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
215     defineDefaultProperty(QStringLiteral("trim"), method_trim);
216 }
217
218 static QString getThisString(ExecutionContext *ctx)
219 {
220     Scope scope(ctx);
221     ScopedValue t(scope, ctx->thisObject());
222     if (t->isString())
223         return t->stringValue()->toQString();
224     if (StringObject *thisString = t->as<StringObject>())
225         return thisString->d()->string->toQString();
226     if (t->isUndefined() || t->isNull()) {
227         scope.engine->throwTypeError();
228         return QString();
229     }
230     return t->toQString();
231 }
232
233 ReturnedValue StringPrototype::method_toString(CallContext *context)
234 {
235     if (context->thisObject().isString())
236         return context->thisObject().asReturnedValue();
237
238     StringObject *o = context->thisObject().as<StringObject>();
239     if (!o)
240         return context->engine()->throwTypeError();
241     return Encode(o->d()->string);
242 }
243
244 ReturnedValue StringPrototype::method_charAt(CallContext *context)
245 {
246     const QString str = getThisString(context);
247     if (context->d()->engine->hasException)
248         return Encode::undefined();
249
250     int pos = 0;
251     if (context->argc() > 0)
252         pos = (int) context->args()[0].toInteger();
253
254     QString result;
255     if (pos >= 0 && pos < str.length())
256         result += str.at(pos);
257
258     return context->d()->engine->newString(result)->asReturnedValue();
259 }
260
261 ReturnedValue StringPrototype::method_charCodeAt(CallContext *context)
262 {
263     const QString str = getThisString(context);
264     if (context->d()->engine->hasException)
265         return Encode::undefined();
266
267     int pos = 0;
268     if (context->argc() > 0)
269         pos = (int) context->args()[0].toInteger();
270
271
272     if (pos >= 0 && pos < str.length())
273         return Encode(str.at(pos).unicode());
274
275     return Encode(qSNaN());
276 }
277
278 ReturnedValue StringPrototype::method_concat(CallContext *context)
279 {
280     Scope scope(context);
281
282     QString value = getThisString(context);
283     if (scope.engine->hasException)
284         return Encode::undefined();
285
286     ScopedValue v(scope);
287     for (int i = 0; i < context->argc(); ++i) {
288         v = RuntimeHelpers::toString(scope.engine, context->args()[i]);
289         if (scope.hasException())
290             return Encode::undefined();
291         Q_ASSERT(v->isString());
292         value += v->stringValue()->toQString();
293     }
294
295     return context->d()->engine->newString(value)->asReturnedValue();
296 }
297
298 ReturnedValue StringPrototype::method_indexOf(CallContext *context)
299 {
300     QString value = getThisString(context);
301     if (context->d()->engine->hasException)
302         return Encode::undefined();
303
304     QString searchString;
305     if (context->argc())
306         searchString = context->args()[0].toQString();
307
308     int pos = 0;
309     if (context->argc() > 1)
310         pos = (int) context->args()[1].toInteger();
311
312     int index = -1;
313     if (! value.isEmpty())
314         index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
315
316     return Encode(index);
317 }
318
319 ReturnedValue StringPrototype::method_lastIndexOf(CallContext *context)
320 {
321     Scope scope(context);
322
323     const QString value = getThisString(context);
324     if (scope.engine->hasException)
325         return Encode::undefined();
326
327     QString searchString;
328     if (context->argc())
329         searchString = context->args()[0].toQString();
330
331     ScopedValue posArg(scope, context->argument(1));
332     double position = RuntimeHelpers::toNumber(posArg);
333     if (std::isnan(position))
334         position = +qInf();
335     else
336         position = trunc(position);
337
338     int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
339     if (!searchString.isEmpty() && pos == value.length())
340         --pos;
341     if (searchString.isNull() && pos == 0)
342         return Encode(-1);
343     int index = value.lastIndexOf(searchString, pos);
344     return Encode(index);
345 }
346
347 ReturnedValue StringPrototype::method_localeCompare(CallContext *context)
348 {
349     Scope scope(context);
350     const QString value = getThisString(context);
351     if (scope.engine->hasException)
352         return Encode::undefined();
353
354     ScopedValue v(scope, context->argument(0));
355     const QString that = v->toQString();
356     return Encode(QString::localeAwareCompare(value, that));
357 }
358
359 ReturnedValue StringPrototype::method_match(CallContext *context)
360 {
361     if (context->thisObject().isUndefined() || context->thisObject().isNull())
362         return context->engine()->throwTypeError();
363
364     Scope scope(context);
365     ScopedString s(scope, context->thisObject().toString(scope.engine));
366
367     ScopedValue regexp(scope, context->argument(0));
368     Scoped<RegExpObject> rx(scope, regexp);
369     if (!rx) {
370         ScopedCallData callData(scope, 1);
371         callData->args[0] = regexp;
372         rx = context->d()->engine->regExpCtor()->construct(callData);
373     }
374
375     if (!rx)
376         // ### CHECK
377         return context->engine()->throwTypeError();
378
379     bool global = rx->global();
380
381     // ### use the standard builtin function, not the one that might be redefined in the proto
382     ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec")));
383     ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString));
384
385     ScopedCallData callData(scope, 1);
386     callData->thisObject = rx;
387     callData->args[0] = s;
388     if (!global)
389         return exec->call(callData);
390
391     ScopedString lastIndex(scope, context->d()->engine->newString(QStringLiteral("lastIndex")));
392     rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0)));
393     ScopedArrayObject a(scope, context->d()->engine->newArrayObject());
394
395     double previousLastIndex = 0;
396     uint n = 0;
397     ScopedValue result(scope);
398     ScopedValue matchStr(scope);
399     ScopedValue index(scope);
400     while (1) {
401         result = exec->call(callData);
402         if (result->isNull())
403             break;
404         assert(result->isObject());
405         index = rx->get(lastIndex, 0);
406         double thisIndex = index->toInteger();
407         if (previousLastIndex == thisIndex) {
408             previousLastIndex = thisIndex + 1;
409             rx->put(lastIndex, ScopedValue(scope, Primitive::fromDouble(previousLastIndex)));
410         } else {
411             previousLastIndex = thisIndex;
412         }
413         matchStr = result->objectValue()->getIndexed(0);
414         a->arraySet(n, matchStr);
415         ++n;
416     }
417     if (!n)
418         return Encode::null();
419
420     return a.asReturnedValue();
421
422 }
423
424 static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
425 {
426     result->reserve(result->length() + replaceValue.length());
427     for (int i = 0; i < replaceValue.length(); ++i) {
428         if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) {
429             ushort ch = replaceValue.at(++i).unicode();
430             uint substStart = JSC::Yarr::offsetNoMatch;
431             uint substEnd = JSC::Yarr::offsetNoMatch;
432             if (ch == '$') {
433                 *result += QChar(ch);
434                 continue;
435             } else if (ch == '&') {
436                 substStart = matchOffsets[0];
437                 substEnd = matchOffsets[1];
438             } else if (ch == '`') {
439                 substStart = 0;
440                 substEnd = matchOffsets[0];
441             } else if (ch == '\'') {
442                 substStart = matchOffsets[1];
443                 substEnd = input.length();
444             } else if (ch >= '1' && ch <= '9') {
445                 uint capture = ch - '0';
446                 Q_ASSERT(capture > 0);
447                 if (capture < static_cast<uint>(captureCount)) {
448                     substStart = matchOffsets[capture * 2];
449                     substEnd = matchOffsets[capture * 2 + 1];
450                 }
451             } else if (ch == '0' && i < replaceValue.length() - 1) {
452                 int capture = (ch - '0') * 10;
453                 ch = replaceValue.at(++i).unicode();
454                 if (ch >= '0' && ch <= '9') {
455                     capture += ch - '0';
456                     if (capture > 0 && capture < captureCount) {
457                         substStart = matchOffsets[capture * 2];
458                         substEnd = matchOffsets[capture * 2 + 1];
459                     }
460                 }
461             }
462             if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
463                 *result += input.midRef(substStart, substEnd - substStart);
464         } else {
465             *result += replaceValue.at(i);
466         }
467     }
468 }
469
470 ReturnedValue StringPrototype::method_replace(CallContext *ctx)
471 {
472     Scope scope(ctx);
473     QString string;
474     if (StringObject *thisString = ctx->thisObject().as<StringObject>())
475         string = thisString->d()->string->toQString();
476     else
477         string = ctx->thisObject().toQString();
478
479     int numCaptures = 0;
480     int numStringMatches = 0;
481
482     uint allocatedMatchOffsets = 64;
483     uint _matchOffsets[64];
484     uint *matchOffsets = _matchOffsets;
485
486     ScopedValue searchValue(scope, ctx->argument(0));
487     Scoped<RegExpObject> regExp(scope, searchValue);
488     if (regExp) {
489         uint offset = 0;
490         uint nMatchOffsets = 0;
491
492         // We extract the pointer here to work around a compiler bug on Android.
493         Scoped<RegExp> re(scope, regExp->value());
494         while (true) {
495             int oldSize = nMatchOffsets;
496             if (allocatedMatchOffsets < nMatchOffsets + re->captureCount() * 2) {
497                 allocatedMatchOffsets = qMax(allocatedMatchOffsets * 2, nMatchOffsets + re->captureCount() * 2);
498                 uint *newOffsets = (uint *)malloc(allocatedMatchOffsets*sizeof(uint));
499                 memcpy(newOffsets, matchOffsets, nMatchOffsets*sizeof(uint));
500                 if (matchOffsets != _matchOffsets)
501                     free(matchOffsets);
502                 matchOffsets = newOffsets;
503             }
504             if (re->match(string, offset, matchOffsets + oldSize) == JSC::Yarr::offsetNoMatch) {
505                 nMatchOffsets = oldSize;
506                 break;
507             }
508             nMatchOffsets += re->captureCount() * 2;
509             if (!regExp->d()->global)
510                 break;
511             offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
512         }
513         if (regExp->global())
514             regExp->lastIndexProperty()->value = Primitive::fromUInt32(0);
515         numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2);
516         numCaptures = regExp->value()->captureCount();
517     } else {
518         numCaptures = 1;
519         QString searchString = searchValue->toQString();
520         int idx = string.indexOf(searchString);
521         if (idx != -1) {
522             numStringMatches = 1;
523             matchOffsets[0] = idx;
524             matchOffsets[1] = idx + searchString.length();
525         }
526     }
527
528     QString result;
529     ScopedValue replacement(scope);
530     ScopedValue replaceValue(scope, ctx->argument(1));
531     ScopedFunctionObject searchCallback(scope, replaceValue);
532     if (!!searchCallback) {
533         result.reserve(string.length() + 10*numStringMatches);
534         ScopedCallData callData(scope, numCaptures + 2);
535         callData->thisObject = Primitive::undefinedValue();
536         int lastEnd = 0;
537         ScopedValue entry(scope);
538         for (int i = 0; i < numStringMatches; ++i) {
539             for (int k = 0; k < numCaptures; ++k) {
540                 int idx = (i * numCaptures + k) * 2;
541                 uint start = matchOffsets[idx];
542                 uint end = matchOffsets[idx + 1];
543                 entry = Primitive::undefinedValue();
544                 if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
545                     entry = ctx->d()->engine->newString(string.mid(start, end - start));
546                 callData->args[k] = entry;
547             }
548             uint matchStart = matchOffsets[i * numCaptures * 2];
549             Q_ASSERT(matchStart >= static_cast<uint>(lastEnd));
550             uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
551             callData->args[numCaptures] = Primitive::fromUInt32(matchStart);
552             callData->args[numCaptures + 1] = ctx->d()->engine->newString(string);
553
554             replacement = searchCallback->call(callData);
555             result += string.midRef(lastEnd, matchStart - lastEnd);
556             result += replacement->toQString();
557             lastEnd = matchEnd;
558         }
559         result += string.midRef(lastEnd);
560     } else {
561         QString newString = replaceValue->toQString();
562         result.reserve(string.length() + numStringMatches*newString.size());
563
564         int lastEnd = 0;
565         for (int i = 0; i < numStringMatches; ++i) {
566             int baseIndex = i * numCaptures * 2;
567             uint matchStart = matchOffsets[baseIndex];
568             uint matchEnd = matchOffsets[baseIndex + 1];
569             if (matchStart == JSC::Yarr::offsetNoMatch)
570                 continue;
571
572             result += string.midRef(lastEnd, matchStart - lastEnd);
573             appendReplacementString(&result, string, newString, matchOffsets + baseIndex, numCaptures);
574             lastEnd = matchEnd;
575         }
576         result += string.midRef(lastEnd);
577     }
578
579     if (matchOffsets != _matchOffsets)
580         free(matchOffsets);
581
582     return ctx->d()->engine->newString(result)->asReturnedValue();
583 }
584
585 ReturnedValue StringPrototype::method_search(CallContext *ctx)
586 {
587     Scope scope(ctx);
588     QString string = getThisString(ctx);
589     ScopedValue regExpValue(scope, ctx->argument(0));
590     if (scope.engine->hasException)
591         return Encode::undefined();
592     Scoped<RegExpObject> regExp(scope, regExpValue->as<RegExpObject>());
593     if (!regExp) {
594         ScopedCallData callData(scope, 1);
595         callData->args[0] = regExpValue;
596         regExpValue = ctx->d()->engine->regExpCtor()->construct(callData);
597         if (scope.engine->hasException)
598             return Encode::undefined();
599         regExp = regExpValue->as<RegExpObject>();
600         Q_ASSERT(regExp);
601     }
602     Scoped<RegExp> re(scope, regExp->value());
603     uint* matchOffsets = (uint*)alloca(regExp->value()->captureCount() * 2 * sizeof(uint));
604     uint result = re->match(string, /*offset*/0, matchOffsets);
605     if (result == JSC::Yarr::offsetNoMatch)
606         return Encode(-1);
607     return Encode(result);
608 }
609
610 ReturnedValue StringPrototype::method_slice(CallContext *ctx)
611 {
612     const QString text = getThisString(ctx);
613     if (ctx->d()->engine->hasException)
614         return Encode::undefined();
615
616     const double length = text.length();
617
618     double start = ctx->argc() ? ctx->args()[0].toInteger() : 0;
619     double end = (ctx->argc() < 2 || ctx->args()[1].isUndefined())
620             ? length : ctx->args()[1].toInteger();
621
622     if (start < 0)
623         start = qMax(length + start, 0.);
624     else
625         start = qMin(start, length);
626
627     if (end < 0)
628         end = qMax(length + end, 0.);
629     else
630         end = qMin(end, length);
631
632     const int intStart = int(start);
633     const int intEnd = int(end);
634
635     int count = qMax(0, intEnd - intStart);
636     return ctx->d()->engine->newString(text.mid(intStart, count))->asReturnedValue();
637 }
638
639 ReturnedValue StringPrototype::method_split(CallContext *ctx)
640 {
641     Scope scope(ctx);
642     QString text = getThisString(ctx);
643     if (scope.engine->hasException)
644         return Encode::undefined();
645
646     ScopedValue separatorValue(scope, ctx->argument(0));
647     ScopedValue limitValue(scope, ctx->argument(1));
648
649     ScopedArrayObject array(scope, ctx->d()->engine->newArrayObject());
650
651     if (separatorValue->isUndefined()) {
652         if (limitValue->isUndefined()) {
653             ScopedString s(scope, ctx->d()->engine->newString(text));
654             array->push_back(s);
655             return array.asReturnedValue();
656         }
657         return ctx->d()->engine->newString(text.left(limitValue->toInteger()))->asReturnedValue();
658     }
659
660     uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32();
661
662     if (limit == 0)
663         return array.asReturnedValue();
664
665     Scoped<RegExpObject> re(scope, separatorValue);
666     if (re) {
667         if (re->value()->pattern.isEmpty()) {
668             re = (RegExpObject *)0;
669             separatorValue = ctx->d()->engine->newString();
670         }
671     }
672
673     ScopedString s(scope);
674     if (re) {
675         uint offset = 0;
676         uint* matchOffsets = (uint*)alloca(re->value()->captureCount() * 2 * sizeof(uint));
677         while (true) {
678             Scoped<RegExp> regexp(scope, re->value());
679             uint result = regexp->match(text, offset, matchOffsets);
680             if (result == JSC::Yarr::offsetNoMatch)
681                 break;
682
683             array->push_back((s = ctx->d()->engine->newString(text.mid(offset, matchOffsets[0] - offset))));
684             offset = qMax(offset + 1, matchOffsets[1]);
685
686             if (array->getLength() >= limit)
687                 break;
688
689             for (int i = 1; i < re->value()->captureCount(); ++i) {
690                 uint start = matchOffsets[i * 2];
691                 uint end = matchOffsets[i * 2 + 1];
692                 array->push_back((s = ctx->d()->engine->newString(text.mid(start, end - start))));
693                 if (array->getLength() >= limit)
694                     break;
695             }
696         }
697         if (array->getLength() < limit)
698             array->push_back((s = ctx->d()->engine->newString(text.mid(offset))));
699     } else {
700         QString separator = separatorValue->toQString();
701         if (separator.isEmpty()) {
702             for (uint i = 0; i < qMin(limit, uint(text.length())); ++i)
703                 array->push_back((s = ctx->d()->engine->newString(text.mid(i, 1))));
704             return array.asReturnedValue();
705         }
706
707         int start = 0;
708         int end;
709         while ((end = text.indexOf(separator, start)) != -1) {
710             array->push_back((s = ctx->d()->engine->newString(text.mid(start, end - start))));
711             start = end + separator.size();
712             if (array->getLength() >= limit)
713                 break;
714         }
715         if (array->getLength() < limit && start != -1)
716             array->push_back((s = ctx->d()->engine->newString(text.mid(start))));
717     }
718     return array.asReturnedValue();
719 }
720
721 ReturnedValue StringPrototype::method_substr(CallContext *context)
722 {
723     const QString value = getThisString(context);
724     if (context->d()->engine->hasException)
725         return Encode::undefined();
726
727     double start = 0;
728     if (context->argc() > 0)
729         start = context->args()[0].toInteger();
730
731     double length = +qInf();
732     if (context->argc() > 1)
733         length = context->args()[1].toInteger();
734
735     double count = value.length();
736     if (start < 0)
737         start = qMax(count + start, 0.0);
738
739     length = qMin(qMax(length, 0.0), count - start);
740
741     qint32 x = Primitive::toInt32(start);
742     qint32 y = Primitive::toInt32(length);
743     return context->d()->engine->newString(value.mid(x, y))->asReturnedValue();
744 }
745
746 ReturnedValue StringPrototype::method_substring(CallContext *context)
747 {
748     QString value = getThisString(context);
749     if (context->d()->engine->hasException)
750         return Encode::undefined();
751     int length = value.length();
752
753     double start = 0;
754     double end = length;
755
756     if (context->argc() > 0)
757         start = context->args()[0].toInteger();
758
759     Scope scope(context);
760     ScopedValue endValue(scope, context->argument(1));
761     if (!endValue->isUndefined())
762         end = endValue->toInteger();
763
764     if (std::isnan(start) || start < 0)
765         start = 0;
766
767     if (std::isnan(end) || end < 0)
768         end = 0;
769
770     if (start > length)
771         start = length;
772
773     if (end > length)
774         end = length;
775
776     if (start > end) {
777         double was = start;
778         start = end;
779         end = was;
780     }
781
782     qint32 x = (int)start;
783     qint32 y = (int)(end - start);
784     return context->d()->engine->newString(value.mid(x, y))->asReturnedValue();
785 }
786
787 ReturnedValue StringPrototype::method_toLowerCase(CallContext *ctx)
788 {
789     QString value = getThisString(ctx);
790     if (ctx->d()->engine->hasException)
791         return Encode::undefined();
792     return ctx->d()->engine->newString(value.toLower())->asReturnedValue();
793 }
794
795 ReturnedValue StringPrototype::method_toLocaleLowerCase(CallContext *ctx)
796 {
797     return method_toLowerCase(ctx);
798 }
799
800 ReturnedValue StringPrototype::method_toUpperCase(CallContext *ctx)
801 {
802     QString value = getThisString(ctx);
803     if (ctx->d()->engine->hasException)
804         return Encode::undefined();
805     return ctx->d()->engine->newString(value.toUpper())->asReturnedValue();
806 }
807
808 ReturnedValue StringPrototype::method_toLocaleUpperCase(CallContext *ctx)
809 {
810     return method_toUpperCase(ctx);
811 }
812
813 ReturnedValue StringPrototype::method_fromCharCode(CallContext *context)
814 {
815     QString str(context->argc(), Qt::Uninitialized);
816     QChar *ch = str.data();
817     for (int i = 0; i < context->argc(); ++i) {
818         *ch = QChar(context->args()[i].toUInt16());
819         ++ch;
820     }
821     return context->d()->engine->newString(str)->asReturnedValue();
822 }
823
824 ReturnedValue StringPrototype::method_trim(CallContext *ctx)
825 {
826     QString s = getThisString(ctx);
827     if (ctx->d()->engine->hasException)
828         return Encode::undefined();
829
830     const QChar *chars = s.constData();
831     int start, end;
832     for (start = 0; start < s.length(); ++start) {
833         if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff)
834             break;
835     }
836     for (end = s.length() - 1; end >= start; --end) {
837         if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff)
838             break;
839     }
840
841     return ctx->d()->engine->newString(QString(chars + start, end - start + 1))->asReturnedValue();
842 }