1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 
41 #include "qv4stringobject_p.h"
42 #include "qv4regexp_p.h"
43 #include "qv4regexpobject_p.h"
44 #include "qv4objectproto_p.h"
45 #include <private/qv4mm_p.h>
46 #include "qv4scopedvalue_p.h"
47 #include "qv4symbol_p.h"
48 #include <private/qv4alloca_p.h>
49 #include "qv4jscall_p.h"
50 #include "qv4stringiterator_p.h"
51 #include <QtCore/QDateTime>
52 #include <QtCore/QDebug>
53 #include <QtCore/QStringList>
54 
55 #include <cassert>
56 
57 #ifndef Q_OS_WIN
58 #  include <time.h>
59 #  ifndef Q_OS_VXWORKS
60 #    include <sys/time.h>
61 #  else
62 #    include "qplatformdefs.h"
63 #  endif
64 #else
65 #  include <windows.h>
66 #endif
67 
68 using namespace QV4;
69 
70 DEFINE_OBJECT_VTABLE(StringObject);
71 
init()72 void Heap::StringObject::init()
73 {
74     Object::init();
75     Q_ASSERT(vtable() == QV4::StringObject::staticVTable());
76     string.set(internalClass->engine, internalClass->engine->id_empty()->d());
77     setProperty(internalClass->engine, LengthPropertyIndex, Value::fromInt32(0));
78 }
79 
init(const QV4::String * str)80 void Heap::StringObject::init(const QV4::String *str)
81 {
82     Object::init();
83     string.set(internalClass->engine, str->d());
84     setProperty(internalClass->engine, LengthPropertyIndex, Value::fromInt32(length()));
85 }
86 
getIndex(uint index) const87 Heap::String *Heap::StringObject::getIndex(uint index) const
88 {
89     QString str = string->toQString();
90     if (index >= (uint)str.length())
91         return nullptr;
92     return internalClass->engine->newString(str.mid(index, 1));
93 }
94 
length() const95 uint Heap::StringObject::length() const
96 {
97     return string->length();
98 }
99 
virtualDeleteProperty(Managed * m,PropertyKey id)100 bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id)
101 {
102     Q_ASSERT(m->as<StringObject>());
103     if (id.isArrayIndex()) {
104         StringObject *o = static_cast<StringObject *>(m);
105         uint index = id.asArrayIndex();
106         if (index < static_cast<uint>(o->d()->string->toQString().length()))
107             return false;
108     }
109     return Object::virtualDeleteProperty(m, id);
110 }
111 
112 struct StringObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
113 {
114     ~StringObjectOwnPropertyKeyIterator() override = default;
115     PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
116 
117 };
118 
next(const QV4::Object * o,Property * pd,PropertyAttributes * attrs)119 PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs)
120 {
121     const StringObject *s = static_cast<const StringObject *>(o);
122     uint slen = s->d()->string->toQString().length();
123     if (arrayIndex < slen) {
124         uint index = arrayIndex;
125         ++arrayIndex;
126         if (attrs)
127             *attrs = Attr_NotConfigurable|Attr_NotWritable;
128         if (pd)
129             pd->value = s->getIndex(index);
130         return PropertyKey::fromArrayIndex(index);
131     } else if (arrayIndex == slen) {
132         if (s->arrayData()) {
133             SparseArrayNode *arrayNode = s->sparseBegin();
134             // iterate until we're past the end of the string
135             while (arrayNode && arrayNode->key() < slen)
136                 arrayNode = arrayNode->nextNode();
137         }
138     }
139 
140     return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
141 }
142 
virtualOwnPropertyKeys(const Object * m,Value * target)143 OwnPropertyKeyIterator *StringObject::virtualOwnPropertyKeys(const Object *m, Value *target)
144 {
145     *target = *m;
146     return new StringObjectOwnPropertyKeyIterator;
147 }
148 
virtualGetOwnProperty(const Managed * m,PropertyKey id,Property * p)149 PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
150 {
151     PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
152     if (attributes != Attr_Invalid)
153         return attributes;
154 
155     if (id.isArrayIndex()) {
156         const uint index = id.asArrayIndex();
157         const auto s = static_cast<const StringObject *>(m);
158         if (index < uint(s->d()->string->toQString().length())) {
159             if (p)
160                 p->value = s->getIndex(index);
161             return Attr_NotConfigurable|Attr_NotWritable;
162         }
163     }
164     return Object::virtualGetOwnProperty(m, id, p);
165 }
166 
167 DEFINE_OBJECT_VTABLE(StringCtor);
168 
init(QV4::ExecutionContext * scope)169 void Heap::StringCtor::init(QV4::ExecutionContext *scope)
170 {
171     Heap::FunctionObject::init(scope, QStringLiteral("String"));
172 }
173 
virtualCallAsConstructor(const FunctionObject * f,const Value * argv,int argc,const Value * newTarget)174 ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
175 {
176     ExecutionEngine *v4 = static_cast<const Object *>(f)->engine();
177     Scope scope(v4);
178     ScopedString value(scope);
179     if (argc)
180         value = argv[0].toString(v4);
181     else
182         value = v4->newString();
183     CHECK_EXCEPTION();
184     ReturnedValue o = Encode(v4->newStringObject(value));
185 
186     if (!newTarget)
187         return o;
188     ScopedObject obj(scope, o);
189     obj->setProtoFromNewTarget(newTarget);
190     return obj->asReturnedValue();
191 }
192 
virtualCall(const FunctionObject * m,const Value *,const Value * argv,int argc)193 ReturnedValue StringCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc)
194 {
195     ExecutionEngine *v4 = m->engine();
196     if (!argc)
197         return v4->newString()->asReturnedValue();
198     if (argv[0].isSymbol())
199         return v4->newString(argv[0].symbolValue()->descriptiveString())->asReturnedValue();
200     return argv[0].toString(v4)->asReturnedValue();
201 }
202 
method_fromCharCode(const FunctionObject * b,const Value *,const Value * argv,int argc)203 ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
204 {
205     QString str(argc, Qt::Uninitialized);
206     QChar *ch = str.data();
207     for (int i = 0, ei = argc; i < ei; ++i) {
208         *ch = QChar(argv[i].toUInt16());
209         ++ch;
210     }
211     *ch = 0;
212     return Encode(b->engine()->newString(str));
213 }
214 
215 
216 
method_fromCodePoint(const FunctionObject * f,const Value *,const Value * argv,int argc)217 ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Value *, const Value *argv, int argc)
218 {
219     ExecutionEngine *e = f->engine();
220     QString result(argc*2, Qt::Uninitialized); // assume worst case
221     QChar *ch = result.data();
222     for (int i = 0; i < argc; ++i) {
223         double num = argv[i].toNumber();
224         if (e->hasException)
225             return Encode::undefined();
226         int cp = static_cast<int>(num);
227         if (cp != num || cp < 0 || cp > 0x10ffff)
228             return e->throwRangeError(QStringLiteral("String.fromCodePoint: argument out of range."));
229         if (cp > 0xffff) {
230             *ch = QChar::highSurrogate(cp);
231             ++ch;
232             *ch = QChar::lowSurrogate(cp);
233         } else {
234             *ch = cp;
235         }
236         ++ch;
237     }
238     *ch = 0;
239     result.truncate(ch - result.constData());
240     return e->newString(result)->asReturnedValue();
241 }
242 
method_raw(const FunctionObject * f,const Value *,const Value * argv,int argc)243 ReturnedValue StringCtor::method_raw(const FunctionObject *f, const Value *, const Value *argv, int argc)
244 {
245     Scope scope(f);
246     if (!argc)
247         return scope.engine->throwTypeError();
248 
249     ScopedObject cooked(scope, argv[0].toObject(scope.engine));
250     if (!cooked)
251         return scope.engine->throwTypeError();
252     ScopedString rawString(scope, scope.engine->newIdentifier(QStringLiteral("raw")));
253     ScopedValue rawValue(scope, cooked->get(rawString));
254     ScopedObject raw(scope, rawValue->toObject(scope.engine));
255     if (scope.hasException())
256         return Encode::undefined();
257 
258     ++argv;
259     --argc;
260 
261     QString result;
262     uint literalSegments = raw->getLength();
263     if (!literalSegments)
264         return scope.engine->id_empty()->asReturnedValue();
265 
266     uint nextIndex = 0;
267     ScopedValue val(scope);
268     while (1) {
269         val = raw->get(nextIndex);
270         result += val->toQString();
271         if (scope.engine->hasException)
272             return Encode::undefined();
273         if (nextIndex + 1 == literalSegments)
274             return scope.engine->newString(result)->asReturnedValue();
275 
276         if (nextIndex < static_cast<uint>(argc))
277             result += argv[nextIndex].toQString();
278         if (scope.engine->hasException)
279             return Encode::undefined();
280         ++nextIndex;
281     }
282 }
283 
init(ExecutionEngine * engine,Object * ctor)284 void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
285 {
286     Scope scope(engine);
287     ScopedObject o(scope);
288 
289     // need to set this once again, as these were not fully defined when creating the string proto
290     Heap::InternalClass *ic = scope.engine->classes[ExecutionEngine::Class_StringObject]->changePrototype(scope.engine->objectPrototype()->d());
291     d()->internalClass.set(scope.engine, ic);
292     d()->string.set(scope.engine, scope.engine->id_empty()->d());
293     setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Value::fromInt32(0));
294 
295     ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
296     ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
297     ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1);
298     ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1);
299     ctor->defineDefaultProperty(QStringLiteral("raw"), StringCtor::method_raw, 1);
300 
301     defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
302     defineDefaultProperty(engine->id_toString(), method_toString);
303     defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical
304     defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
305     defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
306     defineDefaultProperty(QStringLiteral("codePointAt"), method_codePointAt, 1);
307     defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
308     defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1);
309     defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
310     defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
311     defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
312     defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
313     defineDefaultProperty(QStringLiteral("match"), method_match, 1);
314     defineDefaultProperty(QStringLiteral("normalize"), method_normalize, 0);
315     defineDefaultProperty(QStringLiteral("padEnd"), method_padEnd, 1);
316     defineDefaultProperty(QStringLiteral("padStart"), method_padStart, 1);
317     defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1);
318     defineDefaultProperty(QStringLiteral("replace"), method_replace, 2);
319     defineDefaultProperty(QStringLiteral("search"), method_search, 1);
320     defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
321     defineDefaultProperty(QStringLiteral("split"), method_split, 2);
322     defineDefaultProperty(QStringLiteral("startsWith"), method_startsWith, 1);
323     defineDefaultProperty(QStringLiteral("substr"), method_substr, 2);
324     defineDefaultProperty(QStringLiteral("substring"), method_substring, 2);
325     defineDefaultProperty(QStringLiteral("toLowerCase"), method_toLowerCase);
326     defineDefaultProperty(QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase);
327     defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase);
328     defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
329     defineDefaultProperty(QStringLiteral("trim"), method_trim);
330     defineDefaultProperty(engine->symbol_iterator(), method_iterator);
331 }
332 
thisAsString(ExecutionEngine * v4,const QV4::Value * thisObject)333 static Heap::String *thisAsString(ExecutionEngine *v4, const QV4::Value *thisObject)
334 {
335     if (String *s = thisObject->stringValue())
336         return s->d();
337     if (const StringObject *thisString = thisObject->as<StringObject>())
338         return thisString->d()->string;
339     return thisObject->toString(v4);
340 }
341 
getThisString(ExecutionEngine * v4,const QV4::Value * thisObject)342 static QString getThisString(ExecutionEngine *v4, const QV4::Value *thisObject)
343 {
344     if (String *s = thisObject->stringValue())
345         return s->toQString();
346     if (const StringObject *thisString = thisObject->as<StringObject>())
347         return thisString->d()->string->toQString();
348     if (thisObject->isUndefined() || thisObject->isNull()) {
349         v4->throwTypeError();
350         return QString();
351     }
352     return thisObject->toQString();
353 }
354 
method_toString(const FunctionObject * b,const Value * thisObject,const Value *,int)355 ReturnedValue StringPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
356 {
357     if (thisObject->isString())
358         return thisObject->asReturnedValue();
359 
360     ExecutionEngine *v4 = b->engine();
361     const StringObject *o = thisObject->as<StringObject>();
362     if (!o)
363         return v4->throwTypeError();
364     return o->d()->string->asReturnedValue();
365 }
366 
method_charAt(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)367 ReturnedValue StringPrototype::method_charAt(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
368 {
369     ExecutionEngine *v4 = b->engine();
370     const QString str = getThisString(v4, thisObject);
371     if (v4->hasException)
372         return QV4::Encode::undefined();
373 
374     int pos = 0;
375     if (argc > 0)
376         pos = (int) argv[0].toInteger();
377 
378     QString result;
379     if (pos >= 0 && pos < str.length())
380         result += str.at(pos);
381 
382     return Encode(v4->newString(result));
383 }
384 
method_charCodeAt(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)385 ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
386 {
387     ExecutionEngine *v4 = b->engine();
388     const QString str = getThisString(v4, thisObject);
389     if (v4->hasException)
390         return QV4::Encode::undefined();
391 
392     int pos = 0;
393     if (argc > 0)
394         pos = (int) argv[0].toInteger();
395 
396 
397     if (pos >= 0 && pos < str.length())
398         RETURN_RESULT(Encode(str.at(pos).unicode()));
399 
400     return Encode(qt_qnan());
401 }
402 
method_codePointAt(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)403 ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
404 {
405     ExecutionEngine *v4 = f->engine();
406     QString value = getThisString(v4, thisObject);
407     if (v4->hasException)
408         return QV4::Encode::undefined();
409 
410     int index = argc ? argv[0].toInteger() : 0;
411     if (v4->hasException)
412         return QV4::Encode::undefined();
413 
414     if (index < 0 || index >= value.size())
415         return Encode::undefined();
416 
417     uint first = value.at(index).unicode();
418     if (QChar::isHighSurrogate(first) && index + 1 < value.size()) {
419         uint second = value.at(index + 1).unicode();
420         if (QChar::isLowSurrogate(second))
421             return Encode(QChar::surrogateToUcs4(first, second));
422     }
423     return Encode(first);
424 }
425 
method_concat(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)426 ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
427 {
428     ExecutionEngine *v4 = b->engine();
429     QString value = getThisString(v4, thisObject);
430     if (v4->hasException)
431         return QV4::Encode::undefined();
432 
433     Scope scope(v4);
434     ScopedString s(scope);
435     for (int i = 0; i < argc; ++i) {
436         s = argv[i].toString(scope.engine);
437         if (v4->hasException)
438             return QV4::Encode::undefined();
439 
440         Q_ASSERT(s->isString());
441         value += s->toQString();
442     }
443 
444     return Encode(v4->newString(value));
445 }
446 
method_endsWith(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)447 ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
448 {
449     ExecutionEngine *v4 = b->engine();
450     const QString value = getThisString(v4, thisObject);
451     if (v4->hasException)
452         return QV4::Encode::undefined();
453 
454     if (argc && argv[0].as<RegExpObject>())
455         return v4->throwTypeError();
456     QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
457     if (v4->hasException)
458         return Encode::undefined();
459 
460     int pos = value.length();
461     if (argc > 1)
462         pos = (int) argv[1].toInteger();
463 
464     if (pos == value.length())
465         RETURN_RESULT(Encode(value.endsWith(searchString)));
466 
467     QStringRef stringToSearch = value.leftRef(pos);
468     return Encode(stringToSearch.endsWith(searchString));
469 }
470 
method_indexOf(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)471 ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
472 {
473     ExecutionEngine *v4 = b->engine();
474     const QString value = getThisString(v4, thisObject);
475     if (v4->hasException)
476         return QV4::Encode::undefined();
477 
478     QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
479     if (v4->hasException)
480         return Encode::undefined();
481 
482     int pos = 0;
483     if (argc > 1)
484         pos = (int) argv[1].toInteger();
485 
486     int index = -1;
487     if (! value.isEmpty())
488         index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
489 
490     return Encode(index);
491 }
492 
method_includes(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)493 ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
494 {
495     ExecutionEngine *v4 = b->engine();
496     const QString value = getThisString(v4, thisObject);
497     if (v4->hasException)
498         return QV4::Encode::undefined();
499 
500     if (argc && argv[0].as<RegExpObject>())
501         return v4->throwTypeError();
502     QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
503     if (v4->hasException)
504         return Encode::undefined();
505 
506     int pos = 0;
507     if (argc > 1) {
508         const Value &posArg = argv[1];
509         pos = (int) posArg.toInteger();
510         if (!posArg.isInteger() && posArg.isNumber() && qIsInf(posArg.toNumber()))
511             pos = value.length();
512     }
513 
514     if (pos == 0)
515         RETURN_RESULT(Encode(value.contains(searchString)));
516 
517     QStringRef stringToSearch = value.midRef(pos);
518     return Encode(stringToSearch.contains(searchString));
519 }
520 
method_lastIndexOf(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)521 ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
522 {
523     ExecutionEngine *v4 = b->engine();
524     const QString value = getThisString(v4, thisObject);
525     if (v4->hasException)
526         return QV4::Encode::undefined();
527 
528     QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
529     if (v4->hasException)
530         return Encode::undefined();
531 
532     double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf();
533     if (std::isnan(position))
534         position = +qInf();
535     else
536         position = trunc(position);
537 
538     int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
539     if (!searchString.isEmpty() && pos == value.length())
540         --pos;
541     if (searchString.isNull() && pos == 0)
542         RETURN_RESULT(Encode(-1));
543     int index = value.lastIndexOf(searchString, pos);
544     return Encode(index);
545 }
546 
method_localeCompare(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)547 ReturnedValue StringPrototype::method_localeCompare(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
548 {
549     ExecutionEngine *v4 = b->engine();
550     const QString value = getThisString(v4, thisObject);
551     if (v4->hasException)
552         return QV4::Encode::undefined();
553 
554     const QString that = (argc ? argv[0] : Value::undefinedValue()).toQString();
555     return Encode(QString::localeAwareCompare(value, that));
556 }
557 
method_match(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)558 ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
559 {
560     ExecutionEngine *v4 = b->engine();
561     if (thisObject->isNullOrUndefined())
562         return v4->throwTypeError();
563 
564     Scope scope(v4);
565     if (argc && !argv[0].isNullOrUndefined()) {
566         ScopedObject r(scope, argv[0].toObject(scope.engine));
567         if (scope.hasException())
568             return Encode::undefined();
569         ScopedValue f(scope, r->get(scope.engine->symbol_match()));
570         if (!f->isNullOrUndefined()) {
571             ScopedFunctionObject fo(scope, f);
572             if (!fo)
573                 return scope.engine->throwTypeError();
574             return checkedResult(scope.engine, fo->call(r, thisObject, 1));
575         }
576     }
577 
578     ScopedString s(scope, thisObject->toString(v4));
579     if (v4->hasException)
580         return Encode::undefined();
581 
582     Scoped<RegExpObject> that(scope, argc ? argv[0] : Value::undefinedValue());
583     if (!that) {
584         // convert args[0] to a regexp
585         that = RegExpCtor::virtualCallAsConstructor(b, argv, argc, b);
586         if (v4->hasException)
587             return Encode::undefined();
588     }
589     Q_ASSERT(!!that);
590 
591     ScopedFunctionObject match(scope, that->get(scope.engine->symbol_match()));
592     if (!match)
593         return scope.engine->throwTypeError();
594     return checkedResult(scope.engine, match->call(that, s, 1));
595 }
596 
method_normalize(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)597 ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
598 {
599     ExecutionEngine *v4 = f->engine();
600     const QString value = getThisString(v4, thisObject);
601     if (v4->hasException)
602         return Encode::undefined();
603 
604     QString::NormalizationForm form = QString::NormalizationForm_C;
605     if (argc >= 1 && !argv[0].isUndefined()) {
606         QString f = argv[0].toQString();
607         if (v4->hasException)
608             return Encode::undefined();
609         if (f == QLatin1String("NFC"))
610             form = QString::NormalizationForm_C;
611         else if (f == QLatin1String("NFD"))
612             form = QString::NormalizationForm_D;
613         else if (f == QLatin1String("NFKC"))
614             form = QString::NormalizationForm_KC;
615         else if (f == QLatin1String("NFKD"))
616             form = QString::NormalizationForm_KD;
617         else
618             return v4->throwRangeError(QLatin1String("String.prototype.normalize: Invalid normalization form."));
619     }
620     QString normalized = value.normalized(form);
621     return v4->newString(normalized)->asReturnedValue();
622 }
623 
method_padEnd(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)624 ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
625 {
626     ExecutionEngine *v4 = f->engine();
627     if (thisObject->isNullOrUndefined())
628         return v4->throwTypeError();
629 
630     Scope scope(v4);
631     ScopedString s(scope, thisAsString(v4, thisObject));
632     if (v4->hasException)
633         return Encode::undefined();
634     if (!argc)
635         return s->asReturnedValue();
636 
637     int maxLen = argv[0].toInteger();
638     if (maxLen <= s->d()->length())
639         return s->asReturnedValue();
640     QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
641     if (v4->hasException)
642         return Encode::undefined();
643 
644     if (fillString.isEmpty())
645         return s->asReturnedValue();
646 
647     QString padded = s->toQString();
648     int oldLength = padded.length();
649     int toFill = maxLen - oldLength;
650     padded.resize(maxLen);
651     QChar *ch = padded.data() + oldLength;
652     while (toFill) {
653         int copy = qMin(fillString.length(), toFill);
654         memcpy(ch, fillString.constData(), copy*sizeof(QChar));
655         toFill -= copy;
656         ch += copy;
657     }
658     *ch = 0;
659 
660     return v4->newString(padded)->asReturnedValue();
661 }
662 
method_padStart(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)663 ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
664 {
665     ExecutionEngine *v4 = f->engine();
666     if (thisObject->isNullOrUndefined())
667         return v4->throwTypeError();
668 
669     Scope scope(v4);
670     ScopedString s(scope, thisAsString(v4, thisObject));
671     if (v4->hasException)
672         return Encode::undefined();
673     if (!argc)
674         return s->asReturnedValue();
675 
676     int maxLen = argv[0].toInteger();
677     if (maxLen <= s->d()->length())
678         return s->asReturnedValue();
679     QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
680     if (v4->hasException)
681         return Encode::undefined();
682 
683     if (fillString.isEmpty())
684         return s->asReturnedValue();
685 
686     QString original = s->toQString();
687     int oldLength = original.length();
688     int toFill = maxLen - oldLength;
689     QString padded;
690     padded.resize(maxLen);
691     QChar *ch = padded.data();
692     while (toFill) {
693         int copy = qMin(fillString.length(), toFill);
694         memcpy(ch, fillString.constData(), copy*sizeof(QChar));
695         toFill -= copy;
696         ch += copy;
697     }
698     memcpy(ch, original.constData(), oldLength*sizeof(QChar));
699     ch += oldLength;
700     *ch = 0;
701 
702     return v4->newString(padded)->asReturnedValue();
703 }
704 
705 
method_repeat(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)706 ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
707 {
708     ExecutionEngine *v4 = b->engine();
709     const QString value = getThisString(v4, thisObject);
710     if (v4->hasException)
711         return QV4::Encode::undefined();
712 
713     double repeats = (argc ? argv[0] : Value::undefinedValue()).toInteger();
714 
715     if (repeats < 0 || qIsInf(repeats))
716         return v4->throwRangeError(QLatin1String("Invalid count value"));
717 
718     return Encode(v4->newString(value.repeated(int(repeats))));
719 }
720 
appendReplacementString(QString * result,const QString & input,const QString & replaceValue,uint * matchOffsets,int captureCount)721 static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
722 {
723     result->reserve(result->length() + replaceValue.length());
724     for (int i = 0; i < replaceValue.length(); ++i) {
725         if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) {
726             ushort ch = replaceValue.at(i + 1).unicode();
727             uint substStart = JSC::Yarr::offsetNoMatch;
728             uint substEnd = JSC::Yarr::offsetNoMatch;
729             int skip = 0;
730             if (ch == '$') {
731                 *result += QChar(ch);
732                 ++i;
733                 continue;
734             } else if (ch == '&') {
735                 substStart = matchOffsets[0];
736                 substEnd = matchOffsets[1];
737                 skip = 1;
738             } else if (ch == '`') {
739                 substStart = 0;
740                 substEnd = matchOffsets[0];
741                 skip = 1;
742             } else if (ch == '\'') {
743                 substStart = matchOffsets[1];
744                 substEnd = input.length();
745                 skip = 1;
746             } else if (ch >= '0' && ch <= '9') {
747                 uint capture = ch - '0';
748                 skip = 1;
749                 if (i < replaceValue.length() - 2) {
750                     ch = replaceValue.at(i + 2).unicode();
751                     if (ch >= '0' && ch <= '9') {
752                         uint c = capture*10 + ch - '0';
753                         if (c < static_cast<uint>(captureCount)) {
754                             capture = c;
755                             skip = 2;
756                         }
757                     }
758                 }
759                 if (capture > 0 && capture < static_cast<uint>(captureCount)) {
760                     substStart = matchOffsets[capture * 2];
761                     substEnd = matchOffsets[capture * 2 + 1];
762                 } else {
763                     skip = 0;
764                 }
765             }
766             i += skip;
767             if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
768                 *result += input.midRef(substStart, substEnd - substStart);
769             else if (skip == 0) // invalid capture reference. Taken as literal value
770                 *result += replaceValue.at(i);
771         } else {
772             *result += replaceValue.at(i);
773         }
774     }
775 }
776 
method_replace(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)777 ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
778 {
779     QString string;
780     if (const StringObject *thisString = thisObject->as<StringObject>())
781         string = thisString->d()->string->toQString();
782     else
783         string = thisObject->toQString();
784 
785     int numCaptures = 0;
786     int numStringMatches = 0;
787 
788     uint allocatedMatchOffsets = 64;
789     uint _matchOffsets[64];
790     uint *matchOffsets = _matchOffsets;
791 
792     Scope scope(b);
793     ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
794     Scoped<RegExpObject> regExp(scope, searchValue);
795     if (regExp) {
796         uint offset = 0;
797         uint nMatchOffsets = 0;
798 
799         // We extract the pointer here to work around a compiler bug on Android.
800         Scoped<RegExp> re(scope, regExp->value());
801         while (true) {
802             int oldSize = nMatchOffsets;
803             if (allocatedMatchOffsets < nMatchOffsets + re->captureCount() * 2) {
804                 allocatedMatchOffsets = qMax(allocatedMatchOffsets * 2, nMatchOffsets + re->captureCount() * 2);
805                 uint *newOffsets = (uint *)malloc(allocatedMatchOffsets*sizeof(uint));
806                 memcpy(newOffsets, matchOffsets, nMatchOffsets*sizeof(uint));
807                 if (matchOffsets != _matchOffsets)
808                     free(matchOffsets);
809                 matchOffsets = newOffsets;
810             }
811             if (re->match(string, offset, matchOffsets + oldSize) == JSC::Yarr::offsetNoMatch) {
812                 nMatchOffsets = oldSize;
813                 break;
814             }
815             nMatchOffsets += re->captureCount() * 2;
816             if (!regExp->global())
817                 break;
818             offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
819         }
820         if (regExp->global()) {
821             regExp->setLastIndex(0);
822             if (scope.hasException())
823                 return Encode::undefined();
824         }
825         numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2);
826         numCaptures = regExp->value()->captureCount();
827     } else {
828         numCaptures = 1;
829         QString searchString = searchValue->toQString();
830         int idx = string.indexOf(searchString);
831         if (idx != -1) {
832             numStringMatches = 1;
833             matchOffsets[0] = idx;
834             matchOffsets[1] = idx + searchString.length();
835         }
836     }
837 
838     QString result;
839     ScopedValue replacement(scope);
840     ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Value::undefinedValue());
841     ScopedFunctionObject searchCallback(scope, replaceValue);
842     if (!!searchCallback) {
843         result.reserve(string.length() + 10*numStringMatches);
844         ScopedValue entry(scope);
845         Value *arguments = scope.alloc(numCaptures + 2);
846         int lastEnd = 0;
847         for (int i = 0; i < numStringMatches; ++i) {
848             for (int k = 0; k < numCaptures; ++k) {
849                 int idx = (i * numCaptures + k) * 2;
850                 uint start = matchOffsets[idx];
851                 uint end = matchOffsets[idx + 1];
852                 entry = Value::undefinedValue();
853                 if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
854                     entry = scope.engine->newString(string.mid(start, end - start));
855                 arguments[k] = entry;
856             }
857             uint matchStart = matchOffsets[i * numCaptures * 2];
858             Q_ASSERT(matchStart >= static_cast<uint>(lastEnd));
859             uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
860             arguments[numCaptures] = Value::fromUInt32(matchStart);
861             arguments[numCaptures + 1] = scope.engine->newString(string);
862 
863             Value that = Value::undefinedValue();
864             replacement = searchCallback->call(&that, arguments, numCaptures + 2);
865             CHECK_EXCEPTION();
866             result += string.midRef(lastEnd, matchStart - lastEnd);
867             result += replacement->toQString();
868             lastEnd = matchEnd;
869         }
870         result += string.midRef(lastEnd);
871     } else {
872         QString newString = replaceValue->toQString();
873         result.reserve(string.length() + numStringMatches*newString.size());
874 
875         int lastEnd = 0;
876         for (int i = 0; i < numStringMatches; ++i) {
877             int baseIndex = i * numCaptures * 2;
878             uint matchStart = matchOffsets[baseIndex];
879             uint matchEnd = matchOffsets[baseIndex + 1];
880             if (matchStart == JSC::Yarr::offsetNoMatch)
881                 continue;
882 
883             result += string.midRef(lastEnd, matchStart - lastEnd);
884             appendReplacementString(&result, string, newString, matchOffsets + baseIndex, numCaptures);
885             lastEnd = matchEnd;
886         }
887         result += string.midRef(lastEnd);
888     }
889 
890     if (matchOffsets != _matchOffsets)
891         free(matchOffsets);
892 
893     return Encode(scope.engine->newString(result));
894 }
895 
method_search(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)896 ReturnedValue StringPrototype::method_search(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
897 {
898     Scope scope(b);
899     QString string = getThisString(scope.engine, thisObject);
900     if (scope.engine->hasException)
901         return QV4::Encode::undefined();
902 
903     Scoped<RegExpObject> regExp(scope, argc ? argv[0] : Value::undefinedValue());
904     if (!regExp) {
905         regExp = scope.engine->regExpCtor()->callAsConstructor(argv, 1);
906         if (scope.engine->hasException)
907             return QV4::Encode::undefined();
908 
909         Q_ASSERT(regExp);
910     }
911     Scoped<RegExp> re(scope, regExp->value());
912     Q_ALLOCA_VAR(uint, matchOffsets, regExp->value()->captureCount() * 2 * sizeof(uint));
913     uint result = re->match(string, /*offset*/0, matchOffsets);
914     if (result == JSC::Yarr::offsetNoMatch)
915         return Encode(-1);
916     else
917         return Encode(result);
918 }
919 
method_slice(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)920 ReturnedValue StringPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
921 {
922     ExecutionEngine *v4 = b->engine();
923     Scope scope(v4);
924     ScopedString s(scope, thisAsString(v4, thisObject));
925     if (v4->hasException)
926         return QV4::Encode::undefined();
927     Q_ASSERT(s);
928 
929     const double length = s->d()->length();
930 
931     double start = argc ? argv[0].toInteger() : 0;
932     double end = (argc < 2 || argv[1].isUndefined())
933             ? length : argv[1].toInteger();
934 
935     if (start < 0)
936         start = qMax(length + start, 0.);
937     else
938         start = qMin(start, length);
939 
940     if (end < 0)
941         end = qMax(length + end, 0.);
942     else
943         end = qMin(end, length);
944 
945     const int intStart = int(start);
946     const int intEnd = int(end);
947 
948     int count = qMax(0, intEnd - intStart);
949     return Encode(v4->memoryManager->alloc<ComplexString>(s->d(), intStart, count));
950 }
951 
method_split(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)952 ReturnedValue StringPrototype::method_split(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
953 {
954     ExecutionEngine *v4 = b->engine();
955     QString text = getThisString(v4, thisObject);
956     if (v4->hasException)
957         return QV4::Encode::undefined();
958 
959     Scope scope(v4);
960     ScopedValue separatorValue(scope, argc ? argv[0] : Value::undefinedValue());
961     ScopedValue limitValue(scope, argc > 1 ? argv[1] : Value::undefinedValue());
962 
963     ScopedArrayObject array(scope, scope.engine->newArrayObject());
964 
965     if (separatorValue->isUndefined()) {
966         if (limitValue->isUndefined()) {
967             ScopedString s(scope, scope.engine->newString(text));
968             array->push_back(s);
969             return array.asReturnedValue();
970         }
971         RETURN_RESULT(scope.engine->newString(text.left(limitValue->toInteger())));
972     }
973 
974     uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32();
975 
976     if (limit == 0)
977         return array.asReturnedValue();
978 
979     Scoped<RegExpObject> re(scope, separatorValue);
980     if (re) {
981         if (re->value()->pattern->isEmpty()) {
982             re = (RegExpObject *)nullptr;
983             separatorValue = scope.engine->newString();
984         }
985     }
986 
987     ScopedString s(scope);
988     if (re) {
989         uint offset = 0;
990         Q_ALLOCA_VAR(uint, matchOffsets, re->value()->captureCount() * 2 * sizeof(uint));
991         while (true) {
992             Scoped<RegExp> regexp(scope, re->value());
993             uint result = regexp->match(text, offset, matchOffsets);
994             if (result == JSC::Yarr::offsetNoMatch)
995                 break;
996 
997             array->push_back((s = scope.engine->newString(text.mid(offset, matchOffsets[0] - offset))));
998             offset = qMax(offset + 1, matchOffsets[1]);
999 
1000             if (array->getLength() >= limit)
1001                 break;
1002 
1003             for (int i = 1; i < re->value()->captureCount(); ++i) {
1004                 uint start = matchOffsets[i * 2];
1005                 uint end = matchOffsets[i * 2 + 1];
1006                 array->push_back((s = scope.engine->newString(text.mid(start, end - start))));
1007                 if (array->getLength() >= limit)
1008                     break;
1009             }
1010         }
1011         if (array->getLength() < limit)
1012             array->push_back((s = scope.engine->newString(text.mid(offset))));
1013     } else {
1014         QString separator = separatorValue->toQString();
1015         if (separator.isEmpty()) {
1016             for (uint i = 0; i < qMin(limit, uint(text.length())); ++i)
1017                 array->push_back((s = scope.engine->newString(text.mid(i, 1))));
1018             return array.asReturnedValue();
1019         }
1020 
1021         int start = 0;
1022         int end;
1023         while ((end = text.indexOf(separator, start)) != -1) {
1024             array->push_back((s = scope.engine->newString(text.mid(start, end - start))));
1025             start = end + separator.size();
1026             if (array->getLength() >= limit)
1027                 break;
1028         }
1029         if (array->getLength() < limit && start != -1)
1030             array->push_back((s = scope.engine->newString(text.mid(start))));
1031     }
1032     return array.asReturnedValue();
1033 }
1034 
method_startsWith(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1035 ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1036 {
1037     ExecutionEngine *v4 = b->engine();
1038     const QString value = getThisString(v4, thisObject);
1039     if (v4->hasException)
1040         return QV4::Encode::undefined();
1041 
1042     if (argc && argv[0].as<RegExpObject>())
1043         return v4->throwTypeError();
1044     QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
1045     if (v4->hasException)
1046         return Encode::undefined();
1047 
1048     int pos = 0;
1049     if (argc > 1)
1050         pos = (int) argv[1].toInteger();
1051 
1052     if (pos == 0)
1053         return Encode(value.startsWith(searchString));
1054 
1055     QStringRef stringToSearch = value.midRef(pos);
1056     RETURN_RESULT(Encode(stringToSearch.startsWith(searchString)));
1057 }
1058 
method_substr(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1059 ReturnedValue StringPrototype::method_substr(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1060 {
1061     ExecutionEngine *v4 = b->engine();
1062     const QString value = getThisString(v4, thisObject);
1063     if (v4->hasException)
1064         return QV4::Encode::undefined();
1065 
1066     double start = 0;
1067     if (argc > 0)
1068         start = argv[0].toInteger();
1069 
1070     double length = +qInf();
1071     if (argc > 1)
1072         length = argv[1].toInteger();
1073 
1074     double count = value.length();
1075     if (start < 0)
1076         start = qMax(count + start, 0.0);
1077 
1078     length = qMin(qMax(length, 0.0), count - start);
1079 
1080     qint32 x = Value::toInt32(start);
1081     qint32 y = Value::toInt32(length);
1082     return Encode(v4->newString(value.mid(x, y)));
1083 }
1084 
method_substring(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1085 ReturnedValue StringPrototype::method_substring(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1086 {
1087     ExecutionEngine *v4 = b->engine();
1088     const QString value = getThisString(v4, thisObject);
1089     if (v4->hasException)
1090         return QV4::Encode::undefined();
1091 
1092     int length = value.length();
1093 
1094     double start = 0;
1095     double end = length;
1096 
1097     if (argc > 0)
1098         start = argv[0].toInteger();
1099 
1100     if (argc > 1 && !argv[1].isUndefined())
1101         end = argv[1].toInteger();
1102 
1103     if (std::isnan(start) || start < 0)
1104         start = 0;
1105 
1106     if (std::isnan(end) || end < 0)
1107         end = 0;
1108 
1109     if (start > length)
1110         start = length;
1111 
1112     if (end > length)
1113         end = length;
1114 
1115     if (start > end) {
1116         double was = start;
1117         start = end;
1118         end = was;
1119     }
1120 
1121     qint32 x = (int)start;
1122     qint32 y = (int)(end - start);
1123     return Encode(v4->newString(value.mid(x, y)));
1124 }
1125 
method_toLowerCase(const FunctionObject * b,const Value * thisObject,const Value *,int)1126 ReturnedValue StringPrototype::method_toLowerCase(const FunctionObject *b, const Value *thisObject, const Value *, int)
1127 {
1128     ExecutionEngine *v4 = b->engine();
1129     const QString value = getThisString(v4, thisObject);
1130     if (v4->hasException)
1131         return QV4::Encode::undefined();
1132 
1133     return Encode(v4->newString(value.toLower()));
1134 }
1135 
method_toLocaleLowerCase(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1136 ReturnedValue StringPrototype::method_toLocaleLowerCase(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1137 {
1138     return method_toLowerCase(b, thisObject, argv, argc);
1139 }
1140 
method_toUpperCase(const FunctionObject * b,const Value * thisObject,const Value *,int)1141 ReturnedValue StringPrototype::method_toUpperCase(const FunctionObject *b, const Value *thisObject, const Value *, int)
1142 {
1143     ExecutionEngine *v4 = b->engine();
1144     const QString value = getThisString(v4, thisObject);
1145     if (v4->hasException)
1146         return QV4::Encode::undefined();
1147 
1148     return Encode(v4->newString(value.toUpper()));
1149 }
1150 
method_toLocaleUpperCase(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1151 ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1152 {
1153     return method_toUpperCase(b, thisObject, argv, argc);
1154 }
1155 
method_trim(const FunctionObject * b,const Value * thisObject,const Value *,int)1156 ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int)
1157 {
1158     ExecutionEngine *v4 = b->engine();
1159     QString s = getThisString(v4, thisObject);
1160     if (v4->hasException)
1161         return QV4::Encode::undefined();
1162 
1163     const QChar *chars = s.constData();
1164     int start, end;
1165     for (start = 0; start < s.length(); ++start) {
1166         if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff)
1167             break;
1168     }
1169     for (end = s.length() - 1; end >= start; --end) {
1170         if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff)
1171             break;
1172     }
1173 
1174     return Encode(v4->newString(QString(chars + start, end - start + 1)));
1175 }
1176 
1177 
1178 
method_iterator(const FunctionObject * b,const Value * thisObject,const Value *,int)1179 ReturnedValue StringPrototype::method_iterator(const FunctionObject *b, const Value *thisObject, const Value *, int)
1180 {
1181     Scope scope(b);
1182     ScopedString s(scope, thisObject->toString(scope.engine));
1183     if (!s || thisObject->isNullOrUndefined())
1184         return scope.engine->throwTypeError();
1185 
1186     Scoped<StringIteratorObject> si(scope, scope.engine->memoryManager->allocate<StringIteratorObject>(s->d(), scope.engine));
1187     return si->asReturnedValue();
1188 }
1189