From: Roberto Raggi Date: Mon, 14 May 2012 14:03:10 +0000 (+0200) Subject: Initial work on the String object. X-Git-Tag: upstream/5.2.1~669^2~659^2~1205 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3a7f8a31ed85b2643f2491ed600f29c3e61e2c78;p=platform%2Fupstream%2Fqtdeclarative.git Initial work on the String object. --- diff --git a/main.cpp b/main.cpp index 1bf71b4..a3c0b73 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "qv4codegen_p.h" #include "qv4isel_p.h" #include "qv4syntaxchecker_p.h" +#include "qv4ecmaobjects_p.h" #include #include @@ -37,72 +38,12 @@ struct Print: FunctionObject __qmljs_to_string(ctx, &v, &ctx->arguments[i]); if (i) std::cout << ' '; - std::cout << qPrintable(v.stringValue->text()); + std::cout << qPrintable(v.stringValue->toQString()); } std::cout << std::endl; } }; -struct ObjectCtor: FunctionObject -{ - ObjectCtor(Context *scope): FunctionObject(scope) {} - - virtual void construct(Context *ctx) - { - __qmljs_init_object(ctx, &ctx->thisObject, new Object()); - } - - virtual void call(Context *) - { - assert(!"not here"); - } -}; - -struct StringCtor: FunctionObject -{ - StringCtor(Context *scope): FunctionObject(scope) {} - - virtual void construct(Context *ctx) - { - Value arg = ctx->argument(0); - __qmljs_to_string(ctx, &arg, &arg); - __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(arg)); - } - - virtual void call(Context *ctx) - { - const Value arg = ctx->argument(0); - if (arg.is(UNDEFINED_TYPE)) - __qmljs_init_string(ctx, &ctx->result, String::get(ctx, QString())); - else - __qmljs_to_string(ctx, &ctx->result, &arg); - } -}; - -struct StringPrototype: Object -{ - StringPrototype(Context *ctx, FunctionObject *ctor) - { - setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); - setProperty(ctx, QLatin1String("toString"), toString); - } - - void setProperty(Context *ctx, const QString &name, const Value &value) - { - put(String::get(ctx, name), value); - } - - void setProperty(Context *ctx, const QString &name, void (*code)(Context *)) - { - setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); - } - - static void toString(Context *ctx) - { - __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject); - } -}; - } // builtins @@ -153,10 +94,10 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co __qmljs_init_object(ctx, &ctx->activation, globalObject); globalObject->put(VM::String::get(ctx, QLatin1String("print")), - VM::Value::object(ctx, new builtins::Print(ctx))); + VM::Value::object(ctx, new builtins::Print(ctx))); globalObject->put(VM::String::get(ctx, QLatin1String("Object")), - VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); + VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); VM::FunctionObject *stringCtor = new builtins::StringCtor(ctx); stringCtor->put(prototype, VM::Value::object(ctx, new builtins::StringPrototype(ctx, stringCtor))); @@ -165,7 +106,7 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { globalObject->put(VM::String::get(ctx, *function->name), - VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); + VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); } } codeByName.value(QLatin1String("%entry"))->code(ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a1a97e5..bcc08d3 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -11,6 +11,16 @@ Object::~Object() delete members; } +void Object::setProperty(Context *ctx, const QString &name, const Value &value) +{ + put(String::get(ctx, name), value); +} + +void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *)) +{ + setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); +} + bool Object::get(String *name, Value *result) { if (Value *prop = getProperty(name)) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 8658178..2dd4b09 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -28,7 +28,7 @@ struct String { String(const QString &text) : _text(text), _hashValue(0) {} - inline const QString &text() const { + inline const QString &toQString() const { return _text; } @@ -66,7 +66,7 @@ struct Property { inline bool hasName(String *n) const { if (name == n) { return true; - } else if (name->hashValue() == n->hashValue() && name->text() == n->text()) { + } else if (name->hashValue() == n->hashValue() && name->toQString() == n->toQString()) { return true; } return false; @@ -209,6 +209,12 @@ struct Object { virtual bool deleteProperty(String *name, bool flag); virtual void defaultValue(Value *result, int typeHint); // ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean + + // + // helpers + // + void setProperty(Context *ctx, const QString &name, const Value &value); + void setProperty(Context *ctx, const QString &name, void (*code)(Context *)); }; struct BooleanObject: Object { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3bacfa3..6fdf179 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -14,6 +14,62 @@ Value Value::string(Context *ctx, const QString &s) return string(ctx, String::get(ctx, s)); } +int Value::toInt32(double number) +{ + if (! number || isnan(number) || isinf(number)) + return +0; + return (int) trunc(number); // ### +} + +int Value::toInteger(double number) +{ + if (isnan(number)) + return +0; + else if (! number || isinf(number)) + return number; + const double v = floor(fabs(number)); + return signbit(number) ? -v : v; +} + +int Value::toUInt16(Context *ctx) +{ + return __qmljs_to_uint16(ctx, this); +} + +int Value::toInt32(Context *ctx) +{ + return __qmljs_to_int32(ctx, this); +} + +uint Value::toUInt32(Context *ctx) +{ + return __qmljs_to_int32(ctx, this); +} + +bool Value::toBoolean(Context *ctx) const +{ + return __qmljs_to_boolean(ctx, this); +} + +double Value::toInteger(Context *ctx) const +{ + return __qmljs_to_integer(ctx, this); +} + +double Value::toNumber(Context *ctx) const +{ + return __qmljs_to_number(ctx, this); +} + +String *Value::toString(Context *ctx) const +{ + Value v; + __qmljs_to_string(ctx, &v, this); + assert(v.is(STRING_TYPE)); + return v.stringValue; +} + + extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) @@ -102,13 +158,13 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig int __qmljs_string_length(Context *, String *string) { - return string->text().length(); + return string->toQString().length(); } double __qmljs_string_to_number(Context *, String *string) { bool ok; - return string->text().toDouble(&ok); // ### TODO + return string->toQString().toDouble(&ok); // ### TODO } void __qmljs_string_from_number(Context *ctx, Value *result, double number) @@ -119,19 +175,19 @@ void __qmljs_string_from_number(Context *ctx, Value *result, double number) bool __qmljs_string_compare(Context *, String *left, String *right) { - return left->text() < right->text(); + return left->toQString() < right->toQString(); } bool __qmljs_string_equal(Context *, String *left, String *right) { return left == right || (left->hashValue() == right->hashValue() && - left->text() == right->text()); + left->toQString() == right->toQString()); } String *__qmljs_string_concat(Context *ctx, String *first, String *second) { - return String::get(ctx, first->text() + second->text()); + return String::get(ctx, first->toQString() + second->toQString()); } bool __qmljs_is_function(Context *, const Value *value) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 143319e..2a0f42d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -199,6 +199,23 @@ struct Value { } static Value string(Context *ctx, const QString &string); + + static int toInteger(double number); + static int toInt32(double value); + int toUInt16(Context *ctx); + int toInt32(Context *ctx); + uint toUInt32(Context *ctx); + bool toBoolean(Context *ctx) const; + double toInteger(Context *ctx) const; + double toNumber(Context *ctx) const; + String *toString(Context *ctx) const; + + inline bool isUndefined() const { return is(UNDEFINED_TYPE); } + inline bool isNull() const { return is(NULL_TYPE); } + inline bool isString() const { return is(STRING_TYPE); } + inline bool isBoolean() const { return is(BOOLEAN_TYPE); } + inline bool isNumber() const { return is(NUMBER_TYPE); } + inline bool isObject() const { return is(OBJECT_TYPE); } }; extern "C" { @@ -311,7 +328,8 @@ inline double __qmljs_to_integer(Context *ctx, const Value *value) return +0; else if (! number || isinf(number)) return number; - return signbit(number) * floor(fabs(number)); + const double v = floor(fabs(number)); + return signbit(number) ? -v : v; } inline int __qmljs_to_int32(Context *ctx, const Value *value) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp new file mode 100644 index 0000000..455a454 --- /dev/null +++ b/qv4ecmaobjects.cpp @@ -0,0 +1,333 @@ + +#include "qv4ecmaobjects_p.h" +#include +#include +#include + +using namespace QQmlJS::VM; + +// +// Object +// +ObjectCtor::ObjectCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void ObjectCtor::construct(Context *ctx) +{ + __qmljs_init_object(ctx, &ctx->thisObject, new Object()); +} + +void ObjectCtor::call(Context *) +{ + assert(!"not here"); +} + + +ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) +{ + setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); +} + +// +// String +// +StringCtor::StringCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void StringCtor::construct(Context *ctx) +{ + Value arg = ctx->argument(0); + __qmljs_to_string(ctx, &arg, &arg); + __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(arg)); +} + +void StringCtor::call(Context *ctx) +{ + const Value arg = ctx->argument(0); + if (arg.is(UNDEFINED_TYPE)) + __qmljs_init_string(ctx, &ctx->result, String::get(ctx, QString())); + else + __qmljs_to_string(ctx, &ctx->result, &arg); +} + +StringPrototype::StringPrototype(Context *ctx, FunctionObject *ctor) +{ + setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString); + setProperty(ctx, QLatin1String("valueOf"), method_valueOf); + setProperty(ctx, QLatin1String("charAt"), method_charAt); + setProperty(ctx, QLatin1String("charCodeAt"), method_charCodeAt); + setProperty(ctx, QLatin1String("concat"), method_concat); + setProperty(ctx, QLatin1String("indexOf"), method_indexOf); + setProperty(ctx, QLatin1String("lastIndexOf"), method_lastIndexOf); + setProperty(ctx, QLatin1String("localeCompare"), method_localeCompare); + setProperty(ctx, QLatin1String("match"), method_match); + setProperty(ctx, QLatin1String("replace"), method_replace); + setProperty(ctx, QLatin1String("search"), method_search); + setProperty(ctx, QLatin1String("slice"), method_slice); + setProperty(ctx, QLatin1String("split"), method_split); + setProperty(ctx, QLatin1String("substr"), method_substr); + setProperty(ctx, QLatin1String("substring"), method_substring); + setProperty(ctx, QLatin1String("toLowerCase"), method_toLowerCase); + setProperty(ctx, QLatin1String("toLocaleLowerCase"), method_toLocaleLowerCase); + setProperty(ctx, QLatin1String("toUpperCase"), method_toUpperCase); + setProperty(ctx, QLatin1String("toLocaleUpperCase"), method_toLocaleUpperCase); + setProperty(ctx, QLatin1String("fromCharCode"), method_fromCharCode); +} + +QString StringPrototype::getThisString(Context *ctx) +{ + Value v; + __qmljs_to_string(ctx, &v, &ctx->thisObject); + assert(v.is(STRING_TYPE)); + return v.stringValue->toQString(); +} + +void StringPrototype::method_toString(Context *ctx) +{ + __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject); +} + +void StringPrototype::method_valueOf(Context *ctx) +{ + ctx->thisObject.objectValue->defaultValue(&ctx->result, STRING_HINT); +} + +void StringPrototype::method_charAt(Context *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); + + ctx->result = Value::string(ctx, result); +} + +void StringPrototype::method_charCodeAt(Context *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(); + + __qmljs_init_number(ctx, &ctx->result, result); +} + +void StringPrototype::method_concat(Context *ctx) +{ + QString value = getThisString(ctx); + + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + Value v; + __qmljs_to_string(ctx, &v, &ctx->arguments[i]); + assert(v.is(STRING_TYPE)); + value += v.stringValue->toQString(); + } + + ctx->result = Value::string(ctx, value); +} + +void StringPrototype::method_indexOf(Context *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())); + + __qmljs_init_number(ctx, &ctx->result, index); +} + +void StringPrototype::method_lastIndexOf(Context *ctx) +{ + const QString value = getThisString(ctx); + + QString searchString; + if (ctx->argumentCount) { + Value v; + __qmljs_to_string(ctx, &v, &ctx->arguments[0]); + searchString = v.stringValue->toQString(); + } + + Value posArg = ctx->argument(1); + double position = __qmljs_to_number(ctx, &posArg); + if (qIsNaN(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); + __qmljs_init_number(ctx, &ctx->result, index); +} + +void StringPrototype::method_localeCompare(Context *ctx) +{ + const QString value = getThisString(ctx); + const QString that = ctx->argument(0).toString(ctx)->toQString(); + __qmljs_init_number(ctx, &ctx->result, QString::localeAwareCompare(value, that)); +} + +void StringPrototype::method_match(Context *) +{ + // requires Regexp + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_replace(Context *) +{ + // requires Regexp + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_search(Context *) +{ + // requires Regexp + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_slice(Context *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); + ctx->result = Value::string(ctx, text.mid(start, count)); +} + +void StringPrototype::method_split(Context *) +{ + // requires Array + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_substr(Context *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); + ctx->result = Value::string(ctx, value.mid(x, y)); +} + +void StringPrototype::method_substring(Context *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 (qIsNaN(start) || start < 0) + start = 0; + + if (qIsNaN(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); + ctx->result = Value::string(ctx, value.mid(x, y)); +} + +void StringPrototype::method_toLowerCase(Context *ctx) +{ + QString value = getThisString(ctx); + ctx->result = Value::string(ctx, value.toLower()); +} + +void StringPrototype::method_toLocaleLowerCase(Context *ctx) +{ + method_toLowerCase(ctx); +} + +void StringPrototype::method_toUpperCase(Context *ctx) +{ + QString value = getThisString(ctx); + ctx->result = Value::string(ctx, value.toUpper()); +} + +void StringPrototype::method_toLocaleUpperCase(Context *ctx) +{ + method_toUpperCase(ctx); +} + +void StringPrototype::method_fromCharCode(Context *ctx) +{ + QString str; + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + QChar c(ctx->argument(i).toUInt16(ctx)); + str += c; + } + ctx->result = Value::string(ctx, str); +} diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h new file mode 100644 index 0000000..1b5d630 --- /dev/null +++ b/qv4ecmaobjects_p.h @@ -0,0 +1,62 @@ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qmljs_objects.h" + +namespace QQmlJS { +namespace VM { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *); +}; + +struct ObjectPrototype: Object +{ + ObjectPrototype(Context *ctx, FunctionObject *ctor); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct StringPrototype: Object +{ + StringPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static QString getThisString(Context *ctx); + + static void method_toString(Context *ctx); + static void method_valueOf(Context *ctx); + static void method_charAt(Context *ctx); + static void method_charCodeAt(Context *ctx); + static void method_concat(Context *ctx); + static void method_indexOf(Context *ctx); + static void method_lastIndexOf(Context *ctx); + static void method_localeCompare(Context *ctx); + static void method_match(Context *ctx); + static void method_replace(Context *ctx); + static void method_search(Context *ctx); + static void method_slice(Context *ctx); + static void method_split(Context *ctx); + static void method_substr(Context *ctx); + static void method_substring(Context *ctx); + static void method_toLowerCase(Context *ctx); + static void method_toLocaleLowerCase(Context *ctx); + static void method_toUpperCase(Context *ctx); + static void method_toLocaleUpperCase(Context *ctx); + static void method_fromCharCode(Context *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/tests/string.1.js b/tests/string.1.js index 5ae5535..8cc76bb 100644 --- a/tests/string.1.js +++ b/tests/string.1.js @@ -11,3 +11,10 @@ print(s3) var s4 = new String.prototype.constructor(321) print(s4) +print(s4.charAt(0), s4.charAt(1), s4.charCodeAt(0)) + +print(s4.concat("ciao", "cool")) +print(s4.indexOf('1')) +print(s4.lastIndexOf('1', 1)) + +print("ss", s4.slice(0, 2)) diff --git a/v4.pro b/v4.pro index c9d9b1a..fc26789 100644 --- a/v4.pro +++ b/v4.pro @@ -13,7 +13,8 @@ SOURCES += main.cpp \ qmljs_runtime.cpp \ qmljs_objects.cpp \ qv4isel.cpp \ - qv4syntaxchecker.cpp + qv4syntaxchecker.cpp \ + qv4ecmaobjects.cpp HEADERS += \ qv4codegen_p.h \ @@ -23,7 +24,9 @@ HEADERS += \ qv4isel_p.h \ x86-codegen.h \ amd64-codegen.h \ - qv4syntaxchecker_p.h + qv4syntaxchecker_p.h \ + qv4ecmaobjects_p.h +