#include "qv4codegen_p.h"
#include "qv4isel_p.h"
#include "qv4syntaxchecker_p.h"
+#include "qv4ecmaobjects_p.h"
#include <QtCore>
#include <private/qqmljsengine_p.h>
__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
__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)));
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);
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)) {
String(const QString &text)
: _text(text), _hashValue(0) {}
- inline const QString &text() const {
+ inline const QString &toQString() const {
return _text;
}
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;
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 {
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)
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)
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)
}
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" {
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)
--- /dev/null
+
+#include "qv4ecmaobjects_p.h"
+#include <qmath.h>
+#include <qnumeric.h>
+#include <cassert>
+
+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);
+}
--- /dev/null
+#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
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))
qmljs_runtime.cpp \
qmljs_objects.cpp \
qv4isel.cpp \
- qv4syntaxchecker.cpp
+ qv4syntaxchecker.cpp \
+ qv4ecmaobjects.cpp
HEADERS += \
qv4codegen_p.h \
qv4isel_p.h \
x86-codegen.h \
amd64-codegen.h \
- qv4syntaxchecker_p.h
+ qv4syntaxchecker_p.h \
+ qv4ecmaobjects_p.h
+