1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
32 ****************************************************************************/
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>
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>
58 # include <sys/time.h>
60 # include "qplatformdefs.h"
68 DEFINE_OBJECT_VTABLE(StringObject);
70 Heap::StringObject::StringObject(InternalClass *ic, QV4::Object *prototype)
71 : Heap::Object(ic, prototype)
73 Q_ASSERT(vtable() == QV4::StringObject::staticVTable());
74 string = ic->engine->newString();
76 Scope scope(ic->engine);
77 ScopedObject s(scope, this);
78 s->defineReadonlyProperty(ic->engine->id_length(), Primitive::fromInt32(0));
81 Heap::StringObject::StringObject(ExecutionEngine *engine, const QV4::String *str)
82 : Heap::Object(engine->emptyClass, engine->stringPrototype())
87 ScopedObject s(scope, this);
88 s->defineReadonlyProperty(engine->id_length(), Primitive::fromUInt32(length()));
91 Heap::String *Heap::StringObject::getIndex(uint index) const
93 QString str = string->toQString();
94 if (index >= (uint)str.length())
96 return internalClass->engine->newString(str.mid(index, 1));
99 uint Heap::StringObject::length() const
101 return string->toQString().length();
104 bool StringObject::deleteIndexedProperty(Managed *m, uint index)
106 ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine();
108 Scoped<StringObject> o(scope, m->as<StringObject>());
111 if (index < static_cast<uint>(o->d()->string->toQString().length())) {
112 if (v4->currentContext()->strictMode)
113 v4->throwTypeError();
119 void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
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;
128 PropertyAttributes a;
130 s->getOwnProperty(*index, &a, &pd);
131 if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
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();
145 return Object::advanceIterator(m, it, name, index, p, attrs);
148 void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e)
150 StringObject::Data *o = static_cast<StringObject::Data *>(that);
152 Object::markObjects(that, e);
155 DEFINE_OBJECT_VTABLE(StringCtor);
157 Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope)
158 : Heap::FunctionObject(scope, QStringLiteral("String"))
162 ReturnedValue StringCtor::construct(const Managed *m, CallData *callData)
164 ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
166 ScopedString value(scope);
168 value = callData->args[0].toString(v4);
170 value = v4->newString();
171 return Encode(v4->newStringObject(value));
174 ReturnedValue StringCtor::call(const Managed *m, CallData *callData)
176 ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
178 ScopedValue value(scope);
180 value = callData->args[0].toString(v4);
182 value = v4->newString();
183 return value->asReturnedValue();
186 void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
189 ScopedObject o(scope);
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);
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);
218 static QString getThisString(ExecutionContext *ctx)
221 ScopedValue t(scope, ctx->thisObject());
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();
230 return t->toQString();
233 ReturnedValue StringPrototype::method_toString(CallContext *context)
235 if (context->thisObject().isString())
236 return context->thisObject().asReturnedValue();
238 StringObject *o = context->thisObject().as<StringObject>();
240 return context->engine()->throwTypeError();
241 return Encode(o->d()->string);
244 ReturnedValue StringPrototype::method_charAt(CallContext *context)
246 const QString str = getThisString(context);
247 if (context->d()->engine->hasException)
248 return Encode::undefined();
251 if (context->argc() > 0)
252 pos = (int) context->args()[0].toInteger();
255 if (pos >= 0 && pos < str.length())
256 result += str.at(pos);
258 return context->d()->engine->newString(result)->asReturnedValue();
261 ReturnedValue StringPrototype::method_charCodeAt(CallContext *context)
263 const QString str = getThisString(context);
264 if (context->d()->engine->hasException)
265 return Encode::undefined();
268 if (context->argc() > 0)
269 pos = (int) context->args()[0].toInteger();
272 if (pos >= 0 && pos < str.length())
273 return Encode(str.at(pos).unicode());
275 return Encode(qSNaN());
278 ReturnedValue StringPrototype::method_concat(CallContext *context)
280 Scope scope(context);
282 QString value = getThisString(context);
283 if (scope.engine->hasException)
284 return Encode::undefined();
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();
295 return context->d()->engine->newString(value)->asReturnedValue();
298 ReturnedValue StringPrototype::method_indexOf(CallContext *context)
300 QString value = getThisString(context);
301 if (context->d()->engine->hasException)
302 return Encode::undefined();
304 QString searchString;
306 searchString = context->args()[0].toQString();
309 if (context->argc() > 1)
310 pos = (int) context->args()[1].toInteger();
313 if (! value.isEmpty())
314 index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
316 return Encode(index);
319 ReturnedValue StringPrototype::method_lastIndexOf(CallContext *context)
321 Scope scope(context);
323 const QString value = getThisString(context);
324 if (scope.engine->hasException)
325 return Encode::undefined();
327 QString searchString;
329 searchString = context->args()[0].toQString();
331 ScopedValue posArg(scope, context->argument(1));
332 double position = RuntimeHelpers::toNumber(posArg);
333 if (std::isnan(position))
336 position = trunc(position);
338 int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
339 if (!searchString.isEmpty() && pos == value.length())
341 if (searchString.isNull() && pos == 0)
343 int index = value.lastIndexOf(searchString, pos);
344 return Encode(index);
347 ReturnedValue StringPrototype::method_localeCompare(CallContext *context)
349 Scope scope(context);
350 const QString value = getThisString(context);
351 if (scope.engine->hasException)
352 return Encode::undefined();
354 ScopedValue v(scope, context->argument(0));
355 const QString that = v->toQString();
356 return Encode(QString::localeAwareCompare(value, that));
359 ReturnedValue StringPrototype::method_match(CallContext *context)
361 if (context->thisObject().isUndefined() || context->thisObject().isNull())
362 return context->engine()->throwTypeError();
364 Scope scope(context);
365 ScopedString s(scope, context->thisObject().toString(scope.engine));
367 ScopedValue regexp(scope, context->argument(0));
368 Scoped<RegExpObject> rx(scope, regexp);
370 ScopedCallData callData(scope, 1);
371 callData->args[0] = regexp;
372 rx = context->d()->engine->regExpCtor()->construct(callData);
377 return context->engine()->throwTypeError();
379 bool global = rx->global();
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));
385 ScopedCallData callData(scope, 1);
386 callData->thisObject = rx;
387 callData->args[0] = s;
389 return exec->call(callData);
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());
395 double previousLastIndex = 0;
397 ScopedValue result(scope);
398 ScopedValue matchStr(scope);
399 ScopedValue index(scope);
401 result = exec->call(callData);
402 if (result->isNull())
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)));
411 previousLastIndex = thisIndex;
413 matchStr = result->objectValue()->getIndexed(0);
414 a->arraySet(n, matchStr);
418 return Encode::null();
420 return a.asReturnedValue();
424 static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
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;
433 *result += QChar(ch);
435 } else if (ch == '&') {
436 substStart = matchOffsets[0];
437 substEnd = matchOffsets[1];
438 } else if (ch == '`') {
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];
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') {
456 if (capture > 0 && capture < captureCount) {
457 substStart = matchOffsets[capture * 2];
458 substEnd = matchOffsets[capture * 2 + 1];
462 if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
463 *result += input.midRef(substStart, substEnd - substStart);
465 *result += replaceValue.at(i);
470 ReturnedValue StringPrototype::method_replace(CallContext *ctx)
474 if (StringObject *thisString = ctx->thisObject().as<StringObject>())
475 string = thisString->d()->string->toQString();
477 string = ctx->thisObject().toQString();
480 int numStringMatches = 0;
482 uint allocatedMatchOffsets = 64;
483 uint _matchOffsets[64];
484 uint *matchOffsets = _matchOffsets;
486 ScopedValue searchValue(scope, ctx->argument(0));
487 Scoped<RegExpObject> regExp(scope, searchValue);
490 uint nMatchOffsets = 0;
492 // We extract the pointer here to work around a compiler bug on Android.
493 Scoped<RegExp> re(scope, regExp->value());
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)
502 matchOffsets = newOffsets;
504 if (re->match(string, offset, matchOffsets + oldSize) == JSC::Yarr::offsetNoMatch) {
505 nMatchOffsets = oldSize;
508 nMatchOffsets += re->captureCount() * 2;
509 if (!regExp->d()->global)
511 offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
513 if (regExp->global())
514 regExp->lastIndexProperty()->value = Primitive::fromUInt32(0);
515 numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2);
516 numCaptures = regExp->value()->captureCount();
519 QString searchString = searchValue->toQString();
520 int idx = string.indexOf(searchString);
522 numStringMatches = 1;
523 matchOffsets[0] = idx;
524 matchOffsets[1] = idx + searchString.length();
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();
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;
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);
554 replacement = searchCallback->call(callData);
555 result += string.midRef(lastEnd, matchStart - lastEnd);
556 result += replacement->toQString();
559 result += string.midRef(lastEnd);
561 QString newString = replaceValue->toQString();
562 result.reserve(string.length() + numStringMatches*newString.size());
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)
572 result += string.midRef(lastEnd, matchStart - lastEnd);
573 appendReplacementString(&result, string, newString, matchOffsets + baseIndex, numCaptures);
576 result += string.midRef(lastEnd);
579 if (matchOffsets != _matchOffsets)
582 return ctx->d()->engine->newString(result)->asReturnedValue();
585 ReturnedValue StringPrototype::method_search(CallContext *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>());
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>();
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)
607 return Encode(result);
610 ReturnedValue StringPrototype::method_slice(CallContext *ctx)
612 const QString text = getThisString(ctx);
613 if (ctx->d()->engine->hasException)
614 return Encode::undefined();
616 const double length = text.length();
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();
623 start = qMax(length + start, 0.);
625 start = qMin(start, length);
628 end = qMax(length + end, 0.);
630 end = qMin(end, length);
632 const int intStart = int(start);
633 const int intEnd = int(end);
635 int count = qMax(0, intEnd - intStart);
636 return ctx->d()->engine->newString(text.mid(intStart, count))->asReturnedValue();
639 ReturnedValue StringPrototype::method_split(CallContext *ctx)
642 QString text = getThisString(ctx);
643 if (scope.engine->hasException)
644 return Encode::undefined();
646 ScopedValue separatorValue(scope, ctx->argument(0));
647 ScopedValue limitValue(scope, ctx->argument(1));
649 ScopedArrayObject array(scope, ctx->d()->engine->newArrayObject());
651 if (separatorValue->isUndefined()) {
652 if (limitValue->isUndefined()) {
653 ScopedString s(scope, ctx->d()->engine->newString(text));
655 return array.asReturnedValue();
657 return ctx->d()->engine->newString(text.left(limitValue->toInteger()))->asReturnedValue();
660 uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32();
663 return array.asReturnedValue();
665 Scoped<RegExpObject> re(scope, separatorValue);
667 if (re->value()->pattern.isEmpty()) {
668 re = (RegExpObject *)0;
669 separatorValue = ctx->d()->engine->newString();
673 ScopedString s(scope);
676 uint* matchOffsets = (uint*)alloca(re->value()->captureCount() * 2 * sizeof(uint));
678 Scoped<RegExp> regexp(scope, re->value());
679 uint result = regexp->match(text, offset, matchOffsets);
680 if (result == JSC::Yarr::offsetNoMatch)
683 array->push_back((s = ctx->d()->engine->newString(text.mid(offset, matchOffsets[0] - offset))));
684 offset = qMax(offset + 1, matchOffsets[1]);
686 if (array->getLength() >= limit)
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)
697 if (array->getLength() < limit)
698 array->push_back((s = ctx->d()->engine->newString(text.mid(offset))));
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();
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)
715 if (array->getLength() < limit && start != -1)
716 array->push_back((s = ctx->d()->engine->newString(text.mid(start))));
718 return array.asReturnedValue();
721 ReturnedValue StringPrototype::method_substr(CallContext *context)
723 const QString value = getThisString(context);
724 if (context->d()->engine->hasException)
725 return Encode::undefined();
728 if (context->argc() > 0)
729 start = context->args()[0].toInteger();
731 double length = +qInf();
732 if (context->argc() > 1)
733 length = context->args()[1].toInteger();
735 double count = value.length();
737 start = qMax(count + start, 0.0);
739 length = qMin(qMax(length, 0.0), count - start);
741 qint32 x = Primitive::toInt32(start);
742 qint32 y = Primitive::toInt32(length);
743 return context->d()->engine->newString(value.mid(x, y))->asReturnedValue();
746 ReturnedValue StringPrototype::method_substring(CallContext *context)
748 QString value = getThisString(context);
749 if (context->d()->engine->hasException)
750 return Encode::undefined();
751 int length = value.length();
756 if (context->argc() > 0)
757 start = context->args()[0].toInteger();
759 Scope scope(context);
760 ScopedValue endValue(scope, context->argument(1));
761 if (!endValue->isUndefined())
762 end = endValue->toInteger();
764 if (std::isnan(start) || start < 0)
767 if (std::isnan(end) || end < 0)
782 qint32 x = (int)start;
783 qint32 y = (int)(end - start);
784 return context->d()->engine->newString(value.mid(x, y))->asReturnedValue();
787 ReturnedValue StringPrototype::method_toLowerCase(CallContext *ctx)
789 QString value = getThisString(ctx);
790 if (ctx->d()->engine->hasException)
791 return Encode::undefined();
792 return ctx->d()->engine->newString(value.toLower())->asReturnedValue();
795 ReturnedValue StringPrototype::method_toLocaleLowerCase(CallContext *ctx)
797 return method_toLowerCase(ctx);
800 ReturnedValue StringPrototype::method_toUpperCase(CallContext *ctx)
802 QString value = getThisString(ctx);
803 if (ctx->d()->engine->hasException)
804 return Encode::undefined();
805 return ctx->d()->engine->newString(value.toUpper())->asReturnedValue();
808 ReturnedValue StringPrototype::method_toLocaleUpperCase(CallContext *ctx)
810 return method_toUpperCase(ctx);
813 ReturnedValue StringPrototype::method_fromCharCode(CallContext *context)
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());
821 return context->d()->engine->newString(str)->asReturnedValue();
824 ReturnedValue StringPrototype::method_trim(CallContext *ctx)
826 QString s = getThisString(ctx);
827 if (ctx->d()->engine->hasException)
828 return Encode::undefined();
830 const QChar *chars = s.constData();
832 for (start = 0; start < s.length(); ++start) {
833 if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff)
836 for (end = s.length() - 1; end >= start; --end) {
837 if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff)
841 return ctx->d()->engine->newString(QString(chars + start, end - start + 1))->asReturnedValue();