Move the String object into it's own file
authorLars Knoll <lars.knoll@digia.com>
Mon, 21 Jan 2013 13:20:19 +0000 (14:20 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Mon, 21 Jan 2013 13:26:07 +0000 (14:26 +0100)
Change-Id: I3efc9aeaaa7c851715a996e5cbc149fa87cf5503
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
qmljs_engine.cpp
qmljs_objects.cpp
qmljs_objects.h
qv4dateobject.cpp
qv4dateobject.h
qv4ecmaobjects.cpp
qv4ecmaobjects_p.h
qv4objectiterator.cpp
qv4stringobject.cpp [new file with mode: 0644]
qv4stringobject.h [new file with mode: 0644]
v4.pro

index a6ee9ab..136711a 100644 (file)
@@ -46,6 +46,7 @@
 #include <qv4argumentsobject.h>
 #include <qv4dateobject.h>
 #include <qv4jsonobject.h>
+#include <qv4stringobject.h>
 
 namespace QQmlJS {
 namespace VM {
index cf24c00..69c5d96 100644 (file)
@@ -43,6 +43,7 @@
 #include "qv4ir_p.h"
 #include "qv4isel_p.h"
 #include "qv4ecmaobjects_p.h"
+#include "qv4stringobject.h"
 #include "qv4mm.h"
 
 #include <private/qqmljsengine_p.h>
@@ -1227,31 +1228,6 @@ void BoundFunction::getCollectables(QVector<Object *> &objects)
 }
 
 
-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;
-}
-
 EvalErrorObject::EvalErrorObject(ExecutionContext *ctx)
     : ErrorObject(ctx->engine, ctx->argument(0))
 {
index 91b1241..429cc0a 100644 (file)
@@ -195,16 +195,6 @@ struct NumberObject: Object {
     virtual NumberObject *asNumberObject() { return this; }
 };
 
-struct StringObject: Object {
-    Value value;
-    PropertyDescriptor tmpProperty;
-    StringObject(ExecutionContext *ctx, const Value &value);
-    virtual QString className() { return QStringLiteral("String"); }
-    virtual StringObject *asStringObject() { return this; }
-
-    PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index);
-};
-
 struct ArrayObject: Object {
     ArrayObject(ExecutionContext *ctx) { init(ctx); }
     ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); }
index 96d7338..d9a40c9 100644 (file)
@@ -41,6 +41,7 @@
 
 
 #include "qv4dateobject.h"
+#include "qv4ecmaobjects_p.h"
 #include "qv4mm.h"
 #include <QtCore/qnumeric.h>
 #include <QtCore/qmath.h>
index 28b18b2..a0fba20 100644 (file)
@@ -42,7 +42,6 @@
 #define QV4DATEOBJECT_P_H
 
 #include "qmljs_objects.h"
-#include "qv4ecmaobjects_p.h"
 #include <QtCore/qnumeric.h>
 
 namespace QQmlJS {
index e330f7d..80f27b4 100644 (file)
@@ -561,533 +561,6 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope
     return Value::fromObject(o);
 }
 
-//
-// String
-//
-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, &regexp, 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, &regExpValue, 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));
-}
 
 //
 // Number object
index aaef393..46f6140 100644 (file)
@@ -87,44 +87,6 @@ struct ObjectPrototype: Object
     static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc);
 };
 
-struct StringCtor: FunctionObject
-{
-    StringCtor(ExecutionContext *scope);
-
-    virtual Value construct(ExecutionContext *ctx);
-    virtual Value call(ExecutionContext *ctx);
-};
-
-struct StringPrototype: StringObject
-{
-    StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {}
-    void init(ExecutionContext *ctx, const Value &ctor);
-
-    static QString getThisString(ExecutionContext *ctx);
-
-    static Value method_toString(ExecutionContext *ctx);
-    static Value method_valueOf(ExecutionContext *ctx);
-    static Value method_charAt(ExecutionContext *ctx);
-    static Value method_charCodeAt(ExecutionContext *ctx);
-    static Value method_concat(ExecutionContext *ctx);
-    static Value method_indexOf(ExecutionContext *ctx);
-    static Value method_lastIndexOf(ExecutionContext *ctx);
-    static Value method_localeCompare(ExecutionContext *ctx);
-    static Value method_match(ExecutionContext *ctx);
-    static Value method_replace(ExecutionContext *ctx);
-    static Value method_search(ExecutionContext *ctx);
-    static Value method_slice(ExecutionContext *ctx);
-    static Value method_split(ExecutionContext *ctx);
-    static Value method_substr(ExecutionContext *ctx);
-    static Value method_substring(ExecutionContext *ctx);
-    static Value method_toLowerCase(ExecutionContext *ctx);
-    static Value method_toLocaleLowerCase(ExecutionContext *ctx);
-    static Value method_toUpperCase(ExecutionContext *ctx);
-    static Value method_toLocaleUpperCase(ExecutionContext *ctx);
-    static Value method_fromCharCode(ExecutionContext *ctx);
-    static Value method_trim(ExecutionContext *ctx);
-};
-
 struct NumberCtor: FunctionObject
 {
     NumberCtor(ExecutionContext *scope);
index f324b1d..271a3c5 100644 (file)
@@ -40,6 +40,7 @@
 ****************************************************************************/
 #include "qv4objectiterator.h"
 #include "qmljs_objects.h"
+#include "qv4stringobject.h"
 
 namespace QQmlJS {
 namespace VM {
diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp
new file mode 100644 (file)
index 0000000..680a101
--- /dev/null
@@ -0,0 +1,626 @@
+/****************************************************************************
+**
+** 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, &regexp, 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, &regExpValue, 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));
+}
diff --git a/qv4stringobject.h b/qv4stringobject.h
new file mode 100644 (file)
index 0000000..3a9ee53
--- /dev/null
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QV4STRINGOBJECT_P_H
+#define QV4STRINGOBJECT_P_H
+
+#include "qmljs_objects.h"
+#include <QtCore/qnumeric.h>
+
+namespace QQmlJS {
+namespace VM {
+
+struct StringObject: Object {
+    Value value;
+    PropertyDescriptor tmpProperty;
+    StringObject(ExecutionContext *ctx, const Value &value);
+    virtual QString className() { return QStringLiteral("String"); }
+    virtual StringObject *asStringObject() { return this; }
+
+    PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index);
+};
+
+struct StringCtor: FunctionObject
+{
+    StringCtor(ExecutionContext *scope);
+
+    virtual Value construct(ExecutionContext *ctx);
+    virtual Value call(ExecutionContext *ctx);
+};
+
+struct StringPrototype: StringObject
+{
+    StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {}
+    void init(ExecutionContext *ctx, const Value &ctor);
+
+    static QString getThisString(ExecutionContext *ctx);
+
+    static Value method_toString(ExecutionContext *ctx);
+    static Value method_valueOf(ExecutionContext *ctx);
+    static Value method_charAt(ExecutionContext *ctx);
+    static Value method_charCodeAt(ExecutionContext *ctx);
+    static Value method_concat(ExecutionContext *ctx);
+    static Value method_indexOf(ExecutionContext *ctx);
+    static Value method_lastIndexOf(ExecutionContext *ctx);
+    static Value method_localeCompare(ExecutionContext *ctx);
+    static Value method_match(ExecutionContext *ctx);
+    static Value method_replace(ExecutionContext *ctx);
+    static Value method_search(ExecutionContext *ctx);
+    static Value method_slice(ExecutionContext *ctx);
+    static Value method_split(ExecutionContext *ctx);
+    static Value method_substr(ExecutionContext *ctx);
+    static Value method_substring(ExecutionContext *ctx);
+    static Value method_toLowerCase(ExecutionContext *ctx);
+    static Value method_toLocaleLowerCase(ExecutionContext *ctx);
+    static Value method_toUpperCase(ExecutionContext *ctx);
+    static Value method_toLocaleUpperCase(ExecutionContext *ctx);
+    static Value method_fromCharCode(ExecutionContext *ctx);
+    static Value method_trim(ExecutionContext *ctx);
+};
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/v4.pro b/v4.pro
index 723f331..d49bc62 100644 (file)
--- a/v4.pro
+++ b/v4.pro
@@ -30,6 +30,7 @@ SOURCES += main.cpp \
     qv4argumentsobject.cpp \
     qv4dateobject.cpp \
     qv4jsonobject.cpp \
+    qv4stringobject.cpp \
     qv4string.cpp \
     qv4objectiterator.cpp \
     qv4regexp.cpp
@@ -55,6 +56,7 @@ HEADERS += \
     qv4argumentsobject.h \
     qv4dateobject.h \
     qv4jsonobject.h \
+    qv4stringobject.h \
     qv4string.h \
     qv4propertydescriptor.h \
     qv4propertytable.h \