627 lines
21 KiB
C++
627 lines
21 KiB
C++
|
/****************************************************************************
|
||
|
**
|
||
|
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
||
|
** Contact: http://www.qt-project.org/legal
|
||
|
**
|
||
|
** This file is part of the V4VM 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.h"
|
||
|
#include "qv4ecmaobjects_p.h"
|
||
|
#include "qv4mm.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 <qv4ir_p.h>
|
||
|
#include <qv4codegen_p.h>
|
||
|
#include <qv4isel_masm_p.h>
|
||
|
|
||
|
#ifndef Q_WS_WIN
|
||
|
# include <time.h>
|
||
|
# ifndef Q_OS_VXWORKS
|
||
|
# include <sys/time.h>
|
||
|
# else
|
||
|
# include "qplatformdefs.h"
|
||
|
# endif
|
||
|
#else
|
||
|
# include <windows.h>
|
||
|
#endif
|
||
|
|
||
|
using namespace QQmlJS::VM;
|
||
|
|
||
|
StringObject::StringObject(ExecutionContext *ctx, const Value &value)
|
||
|
: value(value)
|
||
|
{
|
||
|
isString = true;
|
||
|
|
||
|
tmpProperty.type = PropertyDescriptor::Data;
|
||
|
tmpProperty.enumberable = PropertyDescriptor::Enabled;
|
||
|
tmpProperty.writable = PropertyDescriptor::Disabled;
|
||
|
tmpProperty.configurable = PropertyDescriptor::Disabled;
|
||
|
tmpProperty.value = Value::undefinedValue();
|
||
|
|
||
|
assert(value.isString());
|
||
|
defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length()));
|
||
|
}
|
||
|
|
||
|
PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index)
|
||
|
{
|
||
|
QString str = value.stringValue()->toQString();
|
||
|
if (index >= (uint)str.length())
|
||
|
return 0;
|
||
|
String *result = ctx->engine->newString(str.mid(index, 1));
|
||
|
tmpProperty.value = Value::fromString(result);
|
||
|
return &tmpProperty;
|
||
|
}
|
||
|
|
||
|
|
||
|
StringCtor::StringCtor(ExecutionContext *scope)
|
||
|
: FunctionObject(scope)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Value StringCtor::construct(ExecutionContext *ctx)
|
||
|
{
|
||
|
Value value;
|
||
|
if (ctx->argumentCount)
|
||
|
value = Value::fromString(ctx->argument(0).toString(ctx));
|
||
|
else
|
||
|
value = Value::fromString(ctx, QString());
|
||
|
return Value::fromObject(ctx->engine->newStringObject(ctx, value));
|
||
|
}
|
||
|
|
||
|
Value StringCtor::call(ExecutionContext *ctx)
|
||
|
{
|
||
|
Value value;
|
||
|
if (ctx->argumentCount)
|
||
|
value = Value::fromString(ctx->argument(0).toString(ctx));
|
||
|
else
|
||
|
value = Value::fromString(ctx, QString());
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
void StringPrototype::init(ExecutionContext *ctx, const Value &ctor)
|
||
|
{
|
||
|
ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
|
||
|
ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
|
||
|
ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1);
|
||
|
|
||
|
defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
|
||
|
defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim);
|
||
|
}
|
||
|
|
||
|
QString StringPrototype::getThisString(ExecutionContext *ctx)
|
||
|
{
|
||
|
String* str = 0;
|
||
|
Value thisObject = ctx->thisObject;
|
||
|
if (StringObject *thisString = thisObject.asStringObject())
|
||
|
str = thisString->value.stringValue();
|
||
|
else if (thisObject.isUndefined() || thisObject.isNull())
|
||
|
ctx->throwTypeError();
|
||
|
else
|
||
|
str = ctx->thisObject.toString(ctx);
|
||
|
return str->toQString();
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_toString(ExecutionContext *ctx)
|
||
|
{
|
||
|
StringObject *o = ctx->thisObject.asStringObject();
|
||
|
if (!o)
|
||
|
ctx->throwTypeError();
|
||
|
return o->value;
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_valueOf(ExecutionContext *ctx)
|
||
|
{
|
||
|
StringObject *o = ctx->thisObject.asStringObject();
|
||
|
if (!o)
|
||
|
ctx->throwTypeError();
|
||
|
return o->value;
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_charAt(ExecutionContext *ctx)
|
||
|
{
|
||
|
const QString str = getThisString(ctx);
|
||
|
|
||
|
int pos = 0;
|
||
|
if (ctx->argumentCount > 0)
|
||
|
pos = (int) ctx->argument(0).toInteger(ctx);
|
||
|
|
||
|
QString result;
|
||
|
if (pos >= 0 && pos < str.length())
|
||
|
result += str.at(pos);
|
||
|
|
||
|
return Value::fromString(ctx, result);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_charCodeAt(ExecutionContext *ctx)
|
||
|
{
|
||
|
const QString str = getThisString(ctx);
|
||
|
|
||
|
int pos = 0;
|
||
|
if (ctx->argumentCount > 0)
|
||
|
pos = (int) ctx->argument(0).toInteger(ctx);
|
||
|
|
||
|
double result = qSNaN();
|
||
|
|
||
|
if (pos >= 0 && pos < str.length())
|
||
|
result = str.at(pos).unicode();
|
||
|
|
||
|
return Value::fromDouble(result);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_concat(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString value = getThisString(ctx);
|
||
|
|
||
|
for (unsigned i = 0; i < ctx->argumentCount; ++i) {
|
||
|
Value v = __qmljs_to_string(ctx->argument(i), ctx);
|
||
|
assert(v.isString());
|
||
|
value += v.stringValue()->toQString();
|
||
|
}
|
||
|
|
||
|
return Value::fromString(ctx, value);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_indexOf(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString value = getThisString(ctx);
|
||
|
|
||
|
QString searchString;
|
||
|
if (ctx->argumentCount)
|
||
|
searchString = ctx->argument(0).toString(ctx)->toQString();
|
||
|
|
||
|
int pos = 0;
|
||
|
if (ctx->argumentCount > 1)
|
||
|
pos = (int) ctx->argument(1).toInteger(ctx);
|
||
|
|
||
|
int index = -1;
|
||
|
if (! value.isEmpty())
|
||
|
index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
|
||
|
|
||
|
return Value::fromDouble(index);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx)
|
||
|
{
|
||
|
const QString value = getThisString(ctx);
|
||
|
|
||
|
QString searchString;
|
||
|
if (ctx->argumentCount) {
|
||
|
Value v = __qmljs_to_string(ctx->argument(0), ctx);
|
||
|
searchString = v.stringValue()->toQString();
|
||
|
}
|
||
|
|
||
|
Value posArg = ctx->argument(1);
|
||
|
double position = __qmljs_to_number(posArg, ctx);
|
||
|
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;
|
||
|
int index = value.lastIndexOf(searchString, pos);
|
||
|
return Value::fromDouble(index);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_localeCompare(ExecutionContext *ctx)
|
||
|
{
|
||
|
const QString value = getThisString(ctx);
|
||
|
const QString that = ctx->argument(0).toString(ctx)->toQString();
|
||
|
return Value::fromDouble(QString::localeAwareCompare(value, that));
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_match(ExecutionContext *ctx)
|
||
|
{
|
||
|
if (ctx->thisObject.isUndefined() || ctx->thisObject.isNull())
|
||
|
__qmljs_throw_type_error(ctx);
|
||
|
|
||
|
String *s = ctx->thisObject.toString(ctx);
|
||
|
|
||
|
Value regexp = ctx->argument(0);
|
||
|
RegExpObject *rx = regexp.asRegExpObject();
|
||
|
if (!rx)
|
||
|
rx = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®exp, 1).asRegExpObject();
|
||
|
|
||
|
if (!rx)
|
||
|
// ### CHECK
|
||
|
__qmljs_throw_type_error(ctx);
|
||
|
|
||
|
bool global = rx->global;
|
||
|
|
||
|
FunctionObject *exec = ctx->engine->regExpPrototype->__get__(ctx, ctx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject();
|
||
|
|
||
|
Value arg = Value::fromString(s);
|
||
|
if (!global)
|
||
|
return exec->call(ctx, Value::fromObject(rx), &arg, 1);
|
||
|
|
||
|
String *lastIndex = ctx->engine->identifier(QStringLiteral("lastIndex"));
|
||
|
rx->__put__(ctx, lastIndex, Value::fromDouble(0.));
|
||
|
ArrayObject *a = ctx->engine->newArrayObject(ctx);
|
||
|
|
||
|
double previousLastIndex = 0;
|
||
|
uint n = 0;
|
||
|
while (1) {
|
||
|
Value result = exec->call(ctx, Value::fromObject(rx), &arg, 1);
|
||
|
if (result.isNull())
|
||
|
break;
|
||
|
assert(result.isObject());
|
||
|
double thisIndex = rx->__get__(ctx, lastIndex, 0).toInteger(ctx);
|
||
|
if (previousLastIndex == thisIndex) {
|
||
|
previousLastIndex = thisIndex + 1;
|
||
|
rx->__put__(ctx, lastIndex, Value::fromDouble(previousLastIndex));
|
||
|
} else {
|
||
|
previousLastIndex = thisIndex;
|
||
|
}
|
||
|
Value matchStr = result.objectValue()->__get__(ctx, (uint)0, (bool *)0);
|
||
|
a->array.set(n, matchStr);
|
||
|
++n;
|
||
|
}
|
||
|
if (!n)
|
||
|
return Value::nullValue();
|
||
|
|
||
|
return Value::fromObject(a);
|
||
|
|
||
|
}
|
||
|
|
||
|
static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
|
||
|
{
|
||
|
QString result;
|
||
|
result.reserve(replaceValue.length());
|
||
|
for (int i = 0; i < replaceValue.length(); ++i) {
|
||
|
if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) {
|
||
|
char ch = replaceValue.at(++i).toLatin1();
|
||
|
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') {
|
||
|
char 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).toLatin1();
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_replace(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString string;
|
||
|
if (StringObject *thisString = ctx->thisObject.asStringObject())
|
||
|
string = thisString->value.stringValue()->toQString();
|
||
|
else
|
||
|
string = ctx->thisObject.toString(ctx)->toQString();
|
||
|
|
||
|
int numCaptures = 0;
|
||
|
QVarLengthArray<uint, 16> matchOffsets;
|
||
|
int numStringMatches = 0;
|
||
|
|
||
|
Value searchValue = ctx->argument(0);
|
||
|
RegExpObject *regExp = searchValue.asRegExpObject();
|
||
|
if (regExp) {
|
||
|
uint offset = 0;
|
||
|
while (true) {
|
||
|
int oldSize = matchOffsets.size();
|
||
|
matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2);
|
||
|
if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) {
|
||
|
matchOffsets.resize(oldSize);
|
||
|
break;
|
||
|
}
|
||
|
if (!regExp->global)
|
||
|
break;
|
||
|
offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
|
||
|
}
|
||
|
if (regExp->global)
|
||
|
regExp->lastIndexProperty->value = Value::fromUInt32(0);
|
||
|
numStringMatches = matchOffsets.size() / (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;
|
||
|
matchOffsets.resize(2);
|
||
|
matchOffsets[0] = idx;
|
||
|
matchOffsets[1] = idx + searchString.length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QString result = string;
|
||
|
Value replaceValue = ctx->argument(1);
|
||
|
if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) {
|
||
|
int replacementDelta = 0;
|
||
|
int argc = numCaptures + 2;
|
||
|
Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value));
|
||
|
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];
|
||
|
Value entry = Value::undefinedValue();
|
||
|
if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
|
||
|
entry = Value::fromString(ctx, string.mid(start, end - start));
|
||
|
args[k] = entry;
|
||
|
}
|
||
|
uint matchStart = matchOffsets[i * numCaptures * 2];
|
||
|
uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
|
||
|
args[numCaptures] = Value::fromUInt32(matchStart);
|
||
|
args[numCaptures + 1] = Value::fromString(ctx, string);
|
||
|
Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc);
|
||
|
QString replacementString = replacement.toString(ctx)->toQString();
|
||
|
result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString);
|
||
|
replacementDelta += replacementString.length() - matchEnd + matchStart;
|
||
|
}
|
||
|
} else {
|
||
|
QString newString = replaceValue.toString(ctx)->toQString();
|
||
|
int replacementDelta = 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;
|
||
|
|
||
|
QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures);
|
||
|
result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement);
|
||
|
replacementDelta += replacement.length() - matchEnd + matchStart;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Value::fromString(ctx, result);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_search(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString string;
|
||
|
if (StringObject *thisString = ctx->thisObject.asStringObject())
|
||
|
string = thisString->value.stringValue()->toQString();
|
||
|
else
|
||
|
string = ctx->thisObject.toString(ctx)->toQString();
|
||
|
|
||
|
Value regExpValue = ctx->argument(0);
|
||
|
RegExpObject *regExp = regExpValue.asRegExpObject();
|
||
|
if (!regExp) {
|
||
|
regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1);
|
||
|
regExp = regExpValue.asRegExpObject();
|
||
|
}
|
||
|
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 Value::fromInt32(-1);
|
||
|
return Value::fromUInt32(result);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_slice(ExecutionContext *ctx)
|
||
|
{
|
||
|
const QString text = getThisString(ctx);
|
||
|
const int length = text.length();
|
||
|
|
||
|
int start = int (ctx->argument(0).toInteger(ctx));
|
||
|
int end = ctx->argument(1).isUndefined()
|
||
|
? length : int (ctx->argument(1).toInteger(ctx));
|
||
|
|
||
|
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);
|
||
|
|
||
|
int count = qMax(0, end - start);
|
||
|
return Value::fromString(ctx, text.mid(start, count));
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_split(ExecutionContext *ctx)
|
||
|
{
|
||
|
ctx->throwUnimplemented(QStringLiteral("String.prototype.splt"));
|
||
|
return Value::undefinedValue();
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_substr(ExecutionContext *ctx)
|
||
|
{
|
||
|
const QString value = getThisString(ctx);
|
||
|
|
||
|
double start = 0;
|
||
|
if (ctx->argumentCount > 0)
|
||
|
start = ctx->argument(0).toInteger(ctx);
|
||
|
|
||
|
double length = +qInf();
|
||
|
if (ctx->argumentCount > 1)
|
||
|
length = ctx->argument(1).toInteger(ctx);
|
||
|
|
||
|
double count = value.length();
|
||
|
if (start < 0)
|
||
|
start = qMax(count + start, 0.0);
|
||
|
|
||
|
length = qMin(qMax(length, 0.0), count - start);
|
||
|
|
||
|
qint32 x = Value::toInt32(start);
|
||
|
qint32 y = Value::toInt32(length);
|
||
|
return Value::fromString(ctx, value.mid(x, y));
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_substring(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString value = getThisString(ctx);
|
||
|
int length = value.length();
|
||
|
|
||
|
double start = 0;
|
||
|
double end = length;
|
||
|
|
||
|
if (ctx->argumentCount > 0)
|
||
|
start = ctx->argument(0).toInteger(ctx);
|
||
|
|
||
|
if (ctx->argumentCount > 1)
|
||
|
end = ctx->argument(1).toInteger(ctx);
|
||
|
|
||
|
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 = Value::toInt32(start);
|
||
|
qint32 y = Value::toInt32(end - start);
|
||
|
return Value::fromString(ctx, value.mid(x, y));
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_toLowerCase(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString value = getThisString(ctx);
|
||
|
return Value::fromString(ctx, value.toLower());
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx)
|
||
|
{
|
||
|
return method_toLowerCase(ctx);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_toUpperCase(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString value = getThisString(ctx);
|
||
|
return Value::fromString(ctx, value.toUpper());
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx)
|
||
|
{
|
||
|
return method_toUpperCase(ctx);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_fromCharCode(ExecutionContext *ctx)
|
||
|
{
|
||
|
QString str;
|
||
|
for (unsigned i = 0; i < ctx->argumentCount; ++i) {
|
||
|
QChar c(ctx->argument(i).toUInt16(ctx));
|
||
|
str += c;
|
||
|
}
|
||
|
return Value::fromString(ctx, str);
|
||
|
}
|
||
|
|
||
|
Value StringPrototype::method_trim(ExecutionContext *ctx)
|
||
|
{
|
||
|
if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined())
|
||
|
__qmljs_throw_type_error(ctx);
|
||
|
|
||
|
QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString();
|
||
|
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 Value::fromString(ctx, QString(chars + start, end - start + 1));
|
||
|
}
|