qtdeclarative/src/qml/jsruntime/qv4stringobject.cpp

809 lines
28 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4stringobject_p.h"
#include "qv4regexpobject_p.h"
#include "qv4objectproto_p.h"
#include "qv4mm_p.h"
#include "qv4scopedvalue_p.h"
#include <QtCore/qnumeric.h>
#include <QtCore/qmath.h>
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
#include <QtCore/QDebug>
#include <cmath>
#include <qmath.h>
#include <qnumeric.h>
#include <cassert>
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
#include <qv4jsir_p.h>
#include <qv4codegen_p.h>
#ifndef Q_OS_WIN
# include <time.h>
# ifndef Q_OS_VXWORKS
# include <sys/time.h>
# else
# include "qplatformdefs.h"
# endif
#else
# include <windows.h>
#endif
using namespace QV4;
DEFINE_MANAGED_VTABLE(StringObject);
StringObject::StringObject(InternalClass *ic)
: Object(ic)
{
vtbl = &static_vtbl;
type = Type_StringObject;
value = ic->engine->newString("")->asReturnedValue();
tmpProperty.value = Primitive::undefinedValue();
defineReadonlyProperty(ic->engine->id_length, Primitive::fromInt32(0));
}
StringObject::StringObject(ExecutionEngine *engine, const ValueRef val)
: Object(engine->stringClass)
{
vtbl = &static_vtbl;
type = Type_StringObject;
value = *val;
tmpProperty.value = Primitive::undefinedValue();
assert(value.isString());
defineReadonlyProperty(engine->id_length, Primitive::fromUInt32(value.stringValue()->toQString().length()));
}
Property *StringObject::getIndex(uint index) const
{
QString str = value.stringValue()->toQString();
if (index >= (uint)str.length())
return 0;
tmpProperty.value = Encode(internalClass->engine->newString(str.mid(index, 1)));
return &tmpProperty;
}
bool StringObject::deleteIndexedProperty(Managed *m, uint index)
{
ExecutionEngine *v4 = m->engine();
Scope scope(v4);
Scoped<StringObject> o(scope, m->asStringObject());
if (!o)
v4->current->throwTypeError();
if (index < o->value.stringValue()->toQString().length()) {
if (v4->current->strictMode)
v4->current->throwTypeError();
return false;
}
return true;
}
Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attrs)
{
name = (String *)0;
StringObject *s = static_cast<StringObject *>(m);
uint slen = s->value.stringValue()->toQString().length();
if (it->arrayIndex < slen) {
while (it->arrayIndex < slen) {
*index = it->arrayIndex;
++it->arrayIndex;
if (attrs)
*attrs = s->arrayAttributes ? s->arrayAttributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable);
return s->__getOwnProperty__(*index);
}
it->arrayNode = s->sparseArrayBegin();
// iterate until we're past the end of the string
while (it->arrayNode && it->arrayNode->key() < slen)
it->arrayNode = it->arrayNode->nextNode();
}
return Object::advanceIterator(m, it, name, index, attrs);
}
void StringObject::markObjects(Managed *that)
{
StringObject *o = static_cast<StringObject *>(that);
o->value.stringValue()->mark();
o->tmpProperty.value.mark();
Object::markObjects(that);
}
DEFINE_MANAGED_VTABLE(StringCtor);
StringCtor::StringCtor(ExecutionContext *scope)
: FunctionObject(scope, QStringLiteral("String"))
{
vtbl = &static_vtbl;
}
ReturnedValue StringCtor::construct(Managed *m, CallData *callData)
{
ExecutionEngine *v4 = m->engine();
Scope scope(v4);
ScopedValue value(scope);
if (callData->argc)
value = callData->args[0].toString(v4->current);
else
value = v4->newString(QString());
return Encode(v4->newStringObject(value));
}
ReturnedValue StringCtor::call(Managed *m, CallData *callData)
{
ExecutionEngine *v4 = m->engine();
Scope scope(v4);
ScopedValue value(scope);
if (callData->argc)
value = callData->args[0].toString(v4->current);
else
value = v4->newString(QString());
return value.asReturnedValue();
}
void StringPrototype::init(ExecutionEngine *engine, ObjectRef ctor)
{
Scope scope(engine);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype, (o = this));
ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1));
ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString, method_toString);
defineDefaultProperty(engine->id_valueOf, method_toString); // valueOf and toString are identical
defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
defineDefaultProperty(QStringLiteral("match"), method_match, 1);
defineDefaultProperty(QStringLiteral("replace"), method_replace, 2);
defineDefaultProperty(QStringLiteral("search"), method_search, 1);
defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
defineDefaultProperty(QStringLiteral("split"), method_split, 2);
defineDefaultProperty(QStringLiteral("substr"), method_substr, 2);
defineDefaultProperty(QStringLiteral("substring"), method_substring, 2);
defineDefaultProperty(QStringLiteral("toLowerCase"), method_toLowerCase);
defineDefaultProperty(QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase);
defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase);
defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
defineDefaultProperty(QStringLiteral("trim"), method_trim);
}
static QString getThisString(ExecutionContext *ctx)
{
Scope scope(ctx);
ScopedValue t(scope, ctx->callData->thisObject);
if (t->isString())
return t->stringValue()->toQString();
if (StringObject *thisString = t->asStringObject())
return thisString->value.stringValue()->toQString();
if (t->isUndefined() || t->isNull())
ctx->throwTypeError();
return t->toQString();
}
ReturnedValue StringPrototype::method_toString(SimpleCallContext *context)
{
if (context->callData->thisObject.isString())
return context->callData->thisObject.asReturnedValue();
StringObject *o = context->callData->thisObject.asStringObject();
if (!o)
context->throwTypeError();
return o->value.asReturnedValue();
}
ReturnedValue StringPrototype::method_charAt(SimpleCallContext *context)
{
const QString str = getThisString(context);
int pos = 0;
if (context->callData->argc > 0)
pos = (int) context->callData->args[0].toInteger();
QString result;
if (pos >= 0 && pos < str.length())
result += str.at(pos);
return context->engine->newString(result)->asReturnedValue();
}
ReturnedValue StringPrototype::method_charCodeAt(SimpleCallContext *context)
{
const QString str = getThisString(context);
int pos = 0;
if (context->callData->argc > 0)
pos = (int) context->callData->args[0].toInteger();
if (pos >= 0 && pos < str.length())
return Encode(str.at(pos).unicode());
return Encode(qSNaN());
}
ReturnedValue StringPrototype::method_concat(SimpleCallContext *context)
{
Scope scope(context);
QString value = getThisString(context);
ScopedValue v(scope);
for (int i = 0; i < context->callData->argc; ++i) {
v = __qmljs_to_string(ValueRef(&context->callData->args[i]), context);
assert(v->isString());
value += v->stringValue()->toQString();
}
return context->engine->newString(value)->asReturnedValue();
}
ReturnedValue StringPrototype::method_indexOf(SimpleCallContext *context)
{
QString value = getThisString(context);
QString searchString;
if (context->callData->argc)
searchString = context->callData->args[0].toString(context)->toQString();
int pos = 0;
if (context->callData->argc > 1)
pos = (int) context->callData->args[1].toInteger();
int index = -1;
if (! value.isEmpty())
index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
return Encode(index);
}
ReturnedValue StringPrototype::method_lastIndexOf(SimpleCallContext *context)
{
Scope scope(context);
const QString value = getThisString(context);
QString searchString;
if (context->callData->argc)
searchString = context->callData->args[0].toQString();
ScopedValue posArg(scope, context->argument(1));
double position = __qmljs_to_number(posArg);
if (std::isnan(position))
position = +qInf();
else
position = trunc(position);
int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
if (!searchString.isEmpty() && pos == value.length())
--pos;
if (searchString.isNull() && pos == 0)
return Encode(-1);
int index = value.lastIndexOf(searchString, pos);
return Encode(index);
}
ReturnedValue StringPrototype::method_localeCompare(SimpleCallContext *context)
{
Scope scope(context);
const QString value = getThisString(context);
ScopedValue v(scope, context->callData->argument(0));
const QString that = v->toQString();
return Encode(QString::localeAwareCompare(value, that));
}
ReturnedValue StringPrototype::method_match(SimpleCallContext *context)
{
if (context->callData->thisObject.isUndefined() || context->callData->thisObject.isNull())
context->throwTypeError();
Scope scope(context);
ScopedString s(scope, context->callData->thisObject.toString(context));
ScopedValue regexp(scope, context->callData->argument(0));
Scoped<RegExpObject> rx(scope, regexp);
if (!rx) {
ScopedCallData callData(scope, 1);
callData->args[0] = regexp;
rx = context->engine->regExpCtor.asFunctionObject()->construct(callData);
}
if (!rx)
// ### CHECK
context->throwTypeError();
bool global = rx->global;
// ### use the standard builtin function, not the one that might be redefined in the proto
ScopedString execString(scope, context->engine->newString(QStringLiteral("exec")));
Scoped<FunctionObject> exec(scope, context->engine->regExpClass->prototype->get(execString));
ScopedCallData callData(scope, 1);
callData->thisObject = rx;
callData->args[0] = s;
if (!global)
return exec->call(callData);
ScopedString lastIndex(scope, context->engine->newString(QStringLiteral("lastIndex")));
rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0)));
Scoped<ArrayObject> a(scope, context->engine->newArrayObject());
double previousLastIndex = 0;
uint n = 0;
ScopedValue result(scope);
ScopedValue matchStr(scope);
ScopedValue index(scope);
while (1) {
result = exec->call(callData);
if (result->isNull())
break;
assert(result->isObject());
index = rx->get(lastIndex, 0);
double thisIndex = index->toInteger();
if (previousLastIndex == thisIndex) {
previousLastIndex = thisIndex + 1;
rx->put(lastIndex, ScopedValue(scope, Primitive::fromDouble(previousLastIndex)));
} else {
previousLastIndex = thisIndex;
}
matchStr = result->objectValue()->getIndexed(0);
a->arraySet(n, matchStr);
++n;
}
if (!n)
return Encode::null();
return a.asReturnedValue();
}
static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
{
result->reserve(result->length() + replaceValue.length());
for (int i = 0; i < replaceValue.length(); ++i) {
if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) {
ushort ch = replaceValue.at(++i).unicode();
uint substStart = JSC::Yarr::offsetNoMatch;
uint substEnd = JSC::Yarr::offsetNoMatch;
if (ch == '$') {
*result += ch;
continue;
} else if (ch == '&') {
substStart = matchOffsets[0];
substEnd = matchOffsets[1];
} else if (ch == '`') {
substStart = 0;
substEnd = matchOffsets[0];
} else if (ch == '\'') {
substStart = matchOffsets[1];
substEnd = input.length();
} else if (ch >= '1' && ch <= '9') {
uint capture = ch - '0';
if (capture > 0 && capture < captureCount) {
substStart = matchOffsets[capture * 2];
substEnd = matchOffsets[capture * 2 + 1];
}
} else if (ch == '0' && i < replaceValue.length() - 1) {
int capture = (ch - '0') * 10;
ch = replaceValue.at(++i).unicode();
if (ch >= '0' && ch <= '9') {
capture += ch - '0';
if (capture > 0 && capture < captureCount) {
substStart = matchOffsets[capture * 2];
substEnd = matchOffsets[capture * 2 + 1];
}
}
}
if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
*result += input.midRef(substStart, substEnd - substStart);
} else {
*result += replaceValue.at(i);
}
}
}
ReturnedValue StringPrototype::method_replace(SimpleCallContext *ctx)
{
Scope scope(ctx);
QString string;
if (StringObject *thisString = ctx->callData->thisObject.asStringObject())
string = thisString->value.stringValue()->toQString();
else
string = ctx->callData->thisObject.toString(ctx)->toQString();
int numCaptures = 0;
int numStringMatches = 0;
uint allocatedMatchOffsets = 32;
uint _matchOffsets[32];
uint *matchOffsets = _matchOffsets;
uint nMatchOffsets = 0;
ScopedValue searchValue(scope, ctx->argument(0));
Scoped<RegExpObject> regExp(scope, searchValue);
if (regExp) {
uint offset = 0;
while (true) {
int oldSize = nMatchOffsets;
if (allocatedMatchOffsets < nMatchOffsets + regExp->value->captureCount() * 2) {
allocatedMatchOffsets = qMax(allocatedMatchOffsets * 2, nMatchOffsets + regExp->value->captureCount() * 2);
uint *newOffsets = (uint *)malloc(allocatedMatchOffsets*sizeof(uint));
memcpy(newOffsets, matchOffsets, nMatchOffsets*sizeof(uint));
if (matchOffsets != _matchOffsets)
free(matchOffsets);
matchOffsets = newOffsets;
}
if (regExp->value->match(string, offset, matchOffsets + oldSize) == JSC::Yarr::offsetNoMatch) {
nMatchOffsets = oldSize;
break;
}
nMatchOffsets += regExp->value->captureCount() * 2;
if (!regExp->global)
break;
offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
}
if (regExp->global)
regExp->lastIndexProperty(ctx)->value = Primitive::fromUInt32(0);
numStringMatches = nMatchOffsets / (regExp->value->captureCount() * 2);
numCaptures = regExp->value->captureCount();
} else {
numCaptures = 1;
QString searchString = searchValue->toString(ctx)->toQString();
int idx = string.indexOf(searchString);
if (idx != -1) {
numStringMatches = 1;
nMatchOffsets = 2;
matchOffsets[0] = idx;
matchOffsets[1] = idx + searchString.length();
}
}
QString result;
ScopedValue replacement(scope);
ScopedValue replaceValue(scope, ctx->argument(1));
Scoped<FunctionObject> searchCallback(scope, replaceValue);
if (!!searchCallback) {
result.reserve(string.length() + 10*numStringMatches);
ScopedCallData callData(scope, numCaptures + 2);
callData->thisObject = Primitive::undefinedValue();
int lastEnd = 0;
ScopedValue entry(scope);
for (int i = 0; i < numStringMatches; ++i) {
for (int k = 0; k < numCaptures; ++k) {
int idx = (i * numCaptures + k) * 2;
uint start = matchOffsets[idx];
uint end = matchOffsets[idx + 1];
entry = Primitive::undefinedValue();
if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
entry = ctx->engine->newString(string.mid(start, end - start));
callData->args[k] = entry;
}
uint matchStart = matchOffsets[i * numCaptures * 2];
Q_ASSERT(matchStart >= lastEnd);
uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
callData->args[numCaptures] = Primitive::fromUInt32(matchStart);
callData->args[numCaptures + 1] = ctx->engine->newString(string);
replacement = searchCallback->call(callData);
result += string.midRef(lastEnd, matchStart - lastEnd);
result += replacement->toString(ctx)->toQString();
lastEnd = matchEnd;
}
result += string.midRef(lastEnd);
} else {
QString newString = replaceValue->toString(ctx)->toQString();
result.reserve(string.length() + numStringMatches*newString.size());
int lastEnd = 0;
for (int i = 0; i < numStringMatches; ++i) {
int baseIndex = i * numCaptures * 2;
uint matchStart = matchOffsets[baseIndex];
uint matchEnd = matchOffsets[baseIndex + 1];
if (matchStart == JSC::Yarr::offsetNoMatch)
continue;
result += string.midRef(lastEnd, matchStart - lastEnd);
appendReplacementString(&result, string, newString, matchOffsets + baseIndex, numCaptures);
lastEnd = matchEnd;
}
result += string.midRef(lastEnd);
}
if (matchOffsets != _matchOffsets)
free(matchOffsets);
return ctx->engine->newString(result)->asReturnedValue();
}
ReturnedValue StringPrototype::method_search(SimpleCallContext *ctx)
{
Scope scope(ctx);
QString string = getThisString(ctx);
ScopedValue regExpValue(scope, ctx->argument(0));
Scoped<RegExpObject> regExp(scope, regExpValue->as<RegExpObject>());
if (!regExp) {
ScopedCallData callData(scope, 1);
callData->args[0] = regExpValue;
regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(callData);
regExp = regExpValue->as<RegExpObject>();
Q_ASSERT(regExp);
}
uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint));
uint result = regExp->value->match(string, /*offset*/0, matchOffsets);
if (result == JSC::Yarr::offsetNoMatch)
return Encode(-1);
return Encode(result);
}
ReturnedValue StringPrototype::method_slice(SimpleCallContext *ctx)
{
const QString text = getThisString(ctx);
const double length = text.length();
double start = ctx->callData->argc ? ctx->callData->args[0].toInteger() : 0;
double end = (ctx->callData->argc < 2 || ctx->callData->args[1].isUndefined())
? length : ctx->callData->args[1].toInteger();
if (start < 0)
start = qMax(length + start, 0.);
else
start = qMin(start, length);
if (end < 0)
end = qMax(length + end, 0.);
else
end = qMin(end, length);
const int intStart = int(start);
const int intEnd = int(end);
int count = qMax(0, intEnd - intStart);
return ctx->engine->newString(text.mid(intStart, count))->asReturnedValue();
}
ReturnedValue StringPrototype::method_split(SimpleCallContext *ctx)
{
Scope scope(ctx);
QString text = getThisString(ctx);
ScopedValue separatorValue(scope, ctx->argument(0));
ScopedValue limitValue(scope, ctx->argument(1));
ScopedArrayObject array(scope, ctx->engine->newArrayObject());
if (separatorValue->isUndefined()) {
if (limitValue->isUndefined()) {
ScopedString s(scope, ctx->engine->newString(text));
array->push_back(s);
return array.asReturnedValue();
}
return ctx->engine->newString(text.left(limitValue->toInteger()))->asReturnedValue();
}
uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32();
if (limit == 0)
return array.asReturnedValue();
Scoped<RegExpObject> re(scope, separatorValue);
if (re) {
if (re->value->pattern().isEmpty()) {
re = (RegExpObject *)0;
separatorValue = ctx->engine->newString(QString());
}
}
ScopedString s(scope);
if (re) {
uint offset = 0;
uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint));
while (true) {
uint result = re->value->match(text, offset, matchOffsets);
if (result == JSC::Yarr::offsetNoMatch)
break;
array->push_back((s = ctx->engine->newString(text.mid(offset, matchOffsets[0] - offset))));
offset = qMax(offset + 1, matchOffsets[1]);
if (array->arrayLength() >= limit)
break;
for (int i = 1; i < re->value->captureCount(); ++i) {
uint start = matchOffsets[i * 2];
uint end = matchOffsets[i * 2 + 1];
array->push_back((s = ctx->engine->newString(text.mid(start, end - start))));
if (array->arrayLength() >= limit)
break;
}
}
if (array->arrayLength() < limit)
array->push_back((s = ctx->engine->newString(text.mid(offset))));
} else {
QString separator = separatorValue->toString(ctx)->toQString();
if (separator.isEmpty()) {
for (uint i = 0; i < qMin(limit, uint(text.length())); ++i)
array->push_back((s = ctx->engine->newString(text.mid(i, 1))));
return array.asReturnedValue();
}
int start = 0;
int end;
while ((end = text.indexOf(separator, start)) != -1) {
array->push_back((s = ctx->engine->newString(text.mid(start, end - start))));
start = end + separator.size();
if (array->arrayLength() >= limit)
break;
}
if (array->arrayLength() < limit && start != -1)
array->push_back((s = ctx->engine->newString(text.mid(start))));
}
return array.asReturnedValue();
}
ReturnedValue StringPrototype::method_substr(SimpleCallContext *context)
{
const QString value = getThisString(context);
double start = 0;
if (context->callData->argc > 0)
start = context->callData->args[0].toInteger();
double length = +qInf();
if (context->callData->argc > 1)
length = context->callData->args[1].toInteger();
double count = value.length();
if (start < 0)
start = qMax(count + start, 0.0);
length = qMin(qMax(length, 0.0), count - start);
qint32 x = Primitive::toInt32(start);
qint32 y = Primitive::toInt32(length);
return context->engine->newString(value.mid(x, y))->asReturnedValue();
}
ReturnedValue StringPrototype::method_substring(SimpleCallContext *context)
{
QString value = getThisString(context);
int length = value.length();
double start = 0;
double end = length;
if (context->callData->argc > 0)
start = context->callData->args[0].toInteger();
Scope scope(context);
ScopedValue endValue(scope, context->argument(1));
if (!endValue->isUndefined())
end = endValue->toInteger();
if (std::isnan(start) || start < 0)
start = 0;
if (std::isnan(end) || end < 0)
end = 0;
if (start > length)
start = length;
if (end > length)
end = length;
if (start > end) {
double was = start;
start = end;
end = was;
}
qint32 x = (int)start;
qint32 y = (int)(end - start);
return context->engine->newString(value.mid(x, y))->asReturnedValue();
}
ReturnedValue StringPrototype::method_toLowerCase(SimpleCallContext *ctx)
{
QString value = getThisString(ctx);
return ctx->engine->newString(value.toLower())->asReturnedValue();
}
ReturnedValue StringPrototype::method_toLocaleLowerCase(SimpleCallContext *ctx)
{
return method_toLowerCase(ctx);
}
ReturnedValue StringPrototype::method_toUpperCase(SimpleCallContext *ctx)
{
QString value = getThisString(ctx);
return ctx->engine->newString(value.toUpper())->asReturnedValue();
}
ReturnedValue StringPrototype::method_toLocaleUpperCase(SimpleCallContext *ctx)
{
return method_toUpperCase(ctx);
}
ReturnedValue StringPrototype::method_fromCharCode(SimpleCallContext *context)
{
QString str(context->callData->argc, Qt::Uninitialized);
QChar *ch = str.data();
for (int i = 0; i < context->callData->argc; ++i) {
*ch = QChar(context->callData->args[i].toUInt16());
++ch;
}
return context->engine->newString(str)->asReturnedValue();
}
ReturnedValue StringPrototype::method_trim(SimpleCallContext *ctx)
{
QString s = getThisString(ctx);
const QChar *chars = s.constData();
int start, end;
for (start = 0; start < s.length(); ++start) {
if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff)
break;
}
for (end = s.length() - 1; end >= start; --end) {
if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff)
break;
}
return ctx->engine->newString(QString(chars + start, end - start + 1))->asReturnedValue();
}