return false;
}
+Value *ArrayObject::getOwnProperty(String *name, PropertyAttributes *attributes)
+{
+ if (name->toQString() == QLatin1String("length")) {
+ length.numberValue = value.size();
+ return &length;
+ }
+
+ return Object::getOwnProperty(name, attributes);
+}
+
bool FunctionObject::hasInstance(const Value &value) const
{
Q_UNUSED(value);
rootContext = newContext();
rootContext->init(this);
+ objectPrototype.type = NULL_TYPE;
+ stringPrototype.type = NULL_TYPE;
+ numberPrototype.type = NULL_TYPE;
+ booleanPrototype.type = NULL_TYPE;
+ arrayPrototype.type = NULL_TYPE;
+ datePrototype.type = NULL_TYPE;
+ //functionPrototype.type = NULL_TYPE;
+
//
// set up the global object
//
+ String *prototype = identifier(QLatin1String("prototype"));
+
VM::Object *glo = newArgumentsObject(rootContext);
__qmljs_init_object(&globalObject, glo);
__qmljs_init_object(&rootContext->activation, glo);
objectCtor = ObjectCtor::create(this);
+ objectCtor.objectValue->get(prototype, &objectPrototype);
+
stringCtor = StringCtor::create(this);
numberCtor = NumberCtor::create(this);
+ booleanCtor = BooleanCtor::create(this);
+ arrayCtor = ArrayCtor::create(this);
dateCtor = DateCtor::create(this);
- String *prototype = identifier(QLatin1String("prototype"));
-
- objectCtor.objectValue->get(prototype, &objectPrototype);
stringCtor.objectValue->get(prototype, &stringPrototype);
numberCtor.objectValue->get(prototype, &numberPrototype);
+ booleanCtor.objectValue->get(prototype, &booleanPrototype);
+ arrayCtor.objectValue->get(prototype, &arrayPrototype);
dateCtor.objectValue->get(prototype, &datePrototype);
glo->put(identifier(QLatin1String("Object")), objectCtor);
glo->put(identifier(QLatin1String("String")), stringCtor);
glo->put(identifier(QLatin1String("Number")), numberCtor);
+ glo->put(identifier(QLatin1String("Array")), arrayCtor);
glo->put(identifier(QLatin1String("Date")), dateCtor);
glo->put(identifier(QLatin1String("Math")), Value::fromObject(newMathObject(rootContext)));
}
return booleanProto;
}
+Object *ExecutionEngine::newArrayObject()
+{
+ ArrayObject *object = new ArrayObject();
+ object->prototype = arrayPrototype.objectValue;
+ return object;
+}
+
+Object *ExecutionEngine::newArrayObject(const Array &value)
+{
+ ArrayObject *object = new ArrayObject(value);
+ object->prototype = arrayPrototype.objectValue;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newArrayCtor(Context *ctx)
+{
+ return new ArrayCtor(ctx);
+}
+
+Object *ExecutionEngine::newArrayPrototype(Context *ctx, FunctionObject *proto)
+{
+ Object *arrayProto = new ArrayPrototype(ctx, proto);
+ arrayProto->prototype = objectPrototype.objectValue;
+ return arrayProto;
+}
+
Object *ExecutionEngine::newDateObject(const Value &value)
{
return new DateObject(value);
#define QMLJS_OBJECTS_H
#include "qmljs_runtime.h"
+#include "qv4array_p.h"
#include <QtCore/QString>
#include <QtCore/QHash>
};
struct ArrayObject: Object {
+ Array value;
+ Value length;
+ ArrayObject() { length.type = NUMBER_TYPE; }
+ ArrayObject(const Array &value): value(value) { length.type = NUMBER_TYPE; }
virtual ArrayObject *asArrayObject() { return this; }
+ virtual Value *getOwnProperty(String *name, PropertyAttributes *attributes);
};
struct FunctionObject: Object {
Value objectCtor;
Value stringCtor;
Value numberCtor;
+ Value booleanCtor;
+ Value arrayCtor;
Value dateCtor;
Value objectPrototype;
Value stringPrototype;
Value numberPrototype;
+ Value booleanPrototype;
+ Value arrayPrototype;
Value datePrototype;
QHash<QString, String *> identifiers;
FunctionObject *newBooleanCtor(Context *ctx);
Object *newBooleanPrototype(Context *ctx, FunctionObject *proto);
+ Object *newArrayObject();
+ Object *newArrayObject(const Array &value);
+ FunctionObject *newArrayCtor(Context *ctx);
+ Object *newArrayPrototype(Context *ctx, FunctionObject *proto);
+
Object *newDateObject(const Value &value);
FunctionObject *newDateCtor(Context *ctx);
Object *newDatePrototype(Context *ctx, FunctionObject *proto);
return (int) trunc(number); // ###
}
+uint Value::toUInt32(double number)
+{
+ if (! number || isnan(number) || isinf(number))
+ return +0;
+ return (uint) trunc(number); // ###
+}
+
int Value::toInteger(double number)
{
if (isnan(number))
if (typeHint == NUMBER_HINT)
qSwap(meth1, meth2);
- Value *conv = object->asObject()->getProperty(meth1);
+ Object *oo = object->asObject();
+ assert(oo != 0);
+ Value *conv = oo->getProperty(meth1);
if (conv && conv->isFunctionObject()) {
Value r;
__qmljs_call_value(ctx, &r, object, conv, 0, 0);
}
}
- assert(!"type error");
+ __qmljs_init_undefined(result);
}
void __qmljs_throw_type_error(Context *ctx, Value *result)
object->objectValue->put(name, value, /*flag*/ 0);
}
+void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index)
+{
+ if (object->isString() && index->isNumber()) {
+ const QString s = object->stringValue->toQString().mid(Value::toUInt32(index->numberValue), 1);
+ if (s.isNull())
+ __qmljs_init_undefined(result);
+ else
+ *result = Value::fromString(ctx, s);
+ } else if (object->isArrayObject() && index->isNumber()) {
+ *result = object->asArrayObject()->value.at(Value::toUInt32(index->numberValue));
+ } else {
+ String *name = index->toString(ctx);
+
+ if (! object->isObject())
+ __qmljs_to_object(ctx, object, object);
+
+ object->objectValue->get(name, result);
+ }
+}
+
+void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value)
+{
+ if (object->isArrayObject() && index->isNumber()) {
+ object->asArrayObject()->value.assign(Value::toUInt32(index->numberValue), *value);
+ } else {
+ String *name = index->toString(ctx);
+
+ if (! object->isObject())
+ __qmljs_to_object(ctx, object, object);
+
+ object->objectValue->put(name, *value, /*flags*/ 0);
+ }
+}
+
+void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number)
+{
+ Value v;
+ __qmljs_init_number(&v, number);
+ __qmljs_set_element(ctx, object, index, &v);
+}
+
+void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value)
+{
+ if (Value *base = ctx->lookup(name)) {
+ __qmljs_set_element(ctx, base, index, value);
+ } else {
+ assert(!"reference error");
+ }
+}
+
+void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *index, double number)
+{
+ Value v;
+ __qmljs_init_number(&v, number);
+ __qmljs_set_activation_element(ctx, name, index, &v);
+}
+
void __qmljs_set_activation_property(Context *ctx, String *name, Value *value)
{
if (Value *prop = ctx->lookup(name)) {
#include <QtCore/QString>
#include <QtCore/QDebug>
#include <math.h>
+#include <cassert>
namespace QQmlJS {
void __qmljs_copy_activation_property(Context *ctx, String *name, String *other);
void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other);
+void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index);
+
+void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value);
+void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number);
+
+void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value);
+void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *index, double number);
+
// context
void __qmljs_get_activation(Context *ctx, Value *result);
void __qmljs_get_thisObject(Context *ctx, Value *result);
static int toInteger(double fromNumber);
static int toInt32(double value);
+ static uint toUInt32(double value);
+
int toUInt16(Context *ctx);
int toInt32(Context *ctx);
uint toUInt32(Context *ctx);
case OBJECT_TYPE: {
Value prim;
__qmljs_to_primitive(ctx, &prim, value, STRING_HINT);
- __qmljs_to_string(ctx, result, &prim);
+ if (prim.isPrimitive())
+ __qmljs_to_string(ctx, result, &prim);
+ else
+ assert(!"type error");
break;
}
--- /dev/null
+
+#include "qv4array_p.h"
+#include "qmljs_objects.h"
+
+using namespace QQmlJS::VM;
+
+bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const
+{
+ if (v1.isUndefined())
+ return false;
+ if (v2.isUndefined())
+ return true;
+ if (!m_comparefn.isUndefined()) {
+ Value args[] = { v1, v2 };
+ Value result;
+ __qmljs_call_value(m_context, &result, 0, &m_comparefn, args, 2);
+ return result.toNumber(m_context) <= 0;
+ }
+ return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString();
+}
class Array
{
public:
- inline Array(ExecutionEngine *engine);
+ inline Array();
inline Array(const Array &other);
inline ~Array();
MapMode
};
- ExecutionEngine *m_engine;
Mode m_mode;
int m_instances;
inline ArrayElementLessThan(Context *context, const Value &comparefn)
: m_context(context), m_comparefn(comparefn) {}
- inline bool operator()(const Value &v1, const Value &v2) const
- {
- if (v1.isUndefined())
- return false;
- if (v2.isUndefined())
- return true;
- if (!m_comparefn.isUndefined()) {
- ArrayElementLessThan *that = const_cast<ArrayElementLessThan*>(this);
- Value args[] = { v1, v2 };
- Value result;
- __qmljs_call_value(m_context, &result, 0, &m_comparefn, args, 2);
- return result.toNumber(m_context) <= 0;
- }
- return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString();
- }
+ bool operator()(const Value &v1, const Value &v2) const;
private:
Context *m_context;
Value m_comparefn;
};
-inline Array::Array(ExecutionEngine *engine):
- m_engine(engine),
- m_mode(VectorMode),
- m_instances(0)
+inline Array::Array()
+ : m_mode(VectorMode)
+ , m_instances(0)
{
to_vector = new QVector<Value>();
}
inline Array::Array(const Array &other):
- m_engine(other.m_engine),
m_mode(other.m_mode),
m_instances(other.m_instances)
{
inline Array &Array::operator = (const Array &other)
{
- m_engine = other.m_engine;
m_instances = other.m_instances;
if (m_mode != other.m_mode) {
if (m_mode == VectorMode)
{
if (index >= size()) {
resize(index + 1);
- // if (m_engine)
- // m_engine->adjustBytesAllocated(sizeof(Value) * (size() - index));
}
const Value &oldv = at(index);
if (m_mode == VectorMode) {
if (s < N) {
- to_vector->resize (s);
+ to_vector->resize (s); // ### init
} else {
// switch to MapMode
QMap<uint, Value> *m = new QMap<uint, Value>();
else {
if (s < N) {
// switch to VectorMode
- QVector<Value> *v = new QVector<Value> (s);
+ QVector<Value> *v = new QVector<Value> (s, Value::undefinedValue());
QMap<uint, Value>::const_iterator it = to_map->constBegin();
for ( ; (it != to_map->constEnd()) && (it.key() < s); ++it)
(*v) [it.key()] = it.value();
for (uint i = 0; i < other.size(); ++i) {
Value v = other.at(i);
if (! v.isUndefined())
- continue;
-
- assign(k + i, v);
+ assign(k + i, v);
}
}
IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index)
{
- if (base->asTemp() || base->asName())
- return _block->SUBSCRIPT(base, index);
- else {
+ if (! base->asTemp()) {
const unsigned t = _block->newTemp();
- move(_block->TEMP(t), base);
- return _block->SUBSCRIPT(_block->TEMP(t), index);
+ _block->MOVE(_block->TEMP(t), base);
+ base = _block->TEMP(t);
+ }
+
+ if (! index->asTemp()) {
+ const unsigned t = _block->newTemp();
+ _block->MOVE(_block->TEMP(t), index);
+ index = _block->TEMP(t);
}
+
+ return _block->SUBSCRIPT(base, index);
}
IR::Expr *Codegen::argument(IR::Expr *expr)
bool Codegen::visit(IdentifierExpression *ast)
{
- if (! _function->needsActivation()) {
+ if (! _function->hasDirectEval) {
int index = _env->findMember(ast->name.toString());
if (index != -1) {
_expr.code = _block->TEMP(index);
ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor)
{
setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor));
+ setProperty(ctx, QLatin1String("toString"), method_toString, 0);
+}
+
+void ObjectPrototype::method_toString(Context *ctx)
+{
+ ctx->result = Value::fromString(ctx, "object");
}
//
void StringPrototype::method_toString(Context *ctx)
{
- __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject);
+ if (StringObject *o = ctx->thisObject.asStringObject()) {
+ ctx->result = o->value;
+ } else {
+ assert(!"type error");
+ }
}
void StringPrototype::method_valueOf(Context *ctx)
}
//
+// Array object
+//
+//
+// Number object
+//
+Value ArrayCtor::create(ExecutionEngine *engine)
+{
+ Context *ctx = engine->rootContext;
+ FunctionObject *ctor = ctx->engine->newArrayCtor(ctx);
+ ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newArrayPrototype(ctx, ctor)));
+ return Value::fromObject(ctor);
+}
+
+ArrayCtor::ArrayCtor(Context *scope)
+ : FunctionObject(scope)
+{
+}
+
+void ArrayCtor::construct(Context *ctx)
+{
+ ctx->thisObject = Value::fromObject(ctx->engine->newArrayObject());
+}
+
+void ArrayCtor::call(Context *ctx)
+{
+ ctx->result = Value::fromObject(ctx->engine->newArrayObject());
+}
+
+ArrayPrototype::ArrayPrototype(Context *ctx, FunctionObject *ctor)
+{
+ setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor));
+ setProperty(ctx, QLatin1String("toString"), method_toString, 0);
+ setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString, 0);
+ setProperty(ctx, QLatin1String("concat"), method_concat, 1);
+ setProperty(ctx, QLatin1String("join"), method_join, 1);
+ setProperty(ctx, QLatin1String("pop"), method_pop, 0);
+ setProperty(ctx, QLatin1String("push"), method_push, 1);
+ setProperty(ctx, QLatin1String("reverse"), method_reverse, 0);
+ setProperty(ctx, QLatin1String("shift"), method_shift, 0);
+ setProperty(ctx, QLatin1String("slice"), method_slice, 2);
+ setProperty(ctx, QLatin1String("sort"), method_sort, 1);
+ setProperty(ctx, QLatin1String("splice"), method_splice, 2);
+ setProperty(ctx, QLatin1String("unshift"), method_unshift, 1);
+}
+
+void ArrayPrototype::method_toString(Context *ctx)
+{
+ method_join(ctx);
+}
+
+void ArrayPrototype::method_toLocaleString(Context *ctx)
+{
+ method_toString(ctx);
+}
+
+void ArrayPrototype::method_concat(Context *ctx)
+{
+ Array result;
+
+ if (ArrayObject *instance = ctx->thisObject.asArrayObject())
+ result = instance->value;
+ else {
+ QString v = ctx->thisObject.toString(ctx)->toQString();
+ result.assign(0, Value::fromString(ctx, v));
+ }
+
+ for (uint i = 0; i < ctx->argumentCount; ++i) {
+ quint32 k = result.size();
+ Value arg = ctx->argument(i);
+
+ if (ArrayObject *elt = arg.asArrayObject())
+ result.concat(elt->value);
+
+ else
+ result.assign(k, Value::fromString(arg.toString(ctx)));
+ }
+
+ ctx->result = Value::fromObject(ctx->engine->newArrayObject(result));
+}
+
+void ArrayPrototype::method_join(Context *ctx)
+{
+ Value arg = ctx->argument(0);
+
+ QString r4;
+ if (arg.isUndefined())
+ r4 = QLatin1String(",");
+ else
+ r4 = arg.toString(ctx)->toQString();
+
+ Value self = ctx->thisObject;
+
+ Value *length = self.objectValue->getProperty(ctx->engine->identifier("length"));
+ double r1 = length ? length->toNumber(ctx) : 0;
+ quint32 r2 = Value::toUInt32(r1);
+
+ static QSet<Object *> visitedArrayElements;
+
+ if (! r2 || visitedArrayElements.contains(self.objectValue)) {
+ ctx->result = Value::fromString(ctx, QString());
+ return;
+ }
+
+ // avoid infinite recursion
+ visitedArrayElements.insert(self.objectValue);
+
+ QString R;
+
+ if (ArrayObject *a = self.objectValue->asArrayObject()) {
+ for (uint i = 0; i < a->value.size(); ++i) {
+ if (! R.isEmpty())
+ R += r4;
+
+ Value e = a->value.at(i);
+ if (! (e.isUndefined() || e.isNull()))
+ R += e.toString(ctx)->toQString();
+ }
+ } else {
+ //
+ // crazy!
+ //
+ Value *r6 = self.objectValue->getProperty(ctx->engine->identifier(QLatin1String("0")));
+ if (r6 && !(r6->isUndefined() || r6->isNull()))
+ R = r6->toString(ctx)->toQString();
+
+ for (quint32 k = 1; k < r2; ++k) {
+ R += r4;
+
+ String *name = Value::fromNumber(k).toString(ctx);
+ Value *r12 = self.objectValue->getProperty(name);
+
+ if (r12 && ! (r12->isUndefined() || r12->isNull()))
+ R += r12->toString(ctx)->toQString();
+ }
+ }
+
+ visitedArrayElements.remove(self.objectValue);
+ ctx->result = Value::fromString(ctx, R);
+}
+
+void ArrayPrototype::method_pop(Context *ctx)
+{
+ Value self = ctx->thisObject;
+ if (ArrayObject *instance = self.asArrayObject()) {
+ Value elt = instance->value.pop();
+ ctx->result = elt;
+ } else {
+ String *id_length = ctx->engine->identifier(QLatin1String("length"));
+ Value *r1 = self.objectValue->getProperty(id_length);
+ quint32 r2 = r1 ? r1->toUInt32(ctx) : 0;
+ if (! r2) {
+ self.objectValue->put(id_length, Value::fromNumber(0));
+ } else {
+ String *r6 = Value::fromNumber(r2 - 1).toString(ctx);
+ Value *r7 = self.objectValue->getProperty(r6);
+ self.objectValue->deleteProperty(r6, 0);
+ self.objectValue->put(id_length, Value::fromNumber(2 - 1));
+ if (r7)
+ ctx->result = *r7;
+ }
+ }
+}
+
+void ArrayPrototype::method_push(Context *ctx)
+{
+}
+
+void ArrayPrototype::method_reverse(Context *ctx)
+{
+}
+
+void ArrayPrototype::method_shift(Context *ctx)
+{
+}
+
+void ArrayPrototype::method_slice(Context *ctx)
+{
+}
+
+void ArrayPrototype::method_sort(Context *ctx)
+{
+}
+
+void ArrayPrototype::method_splice(Context *ctx)
+{
+}
+
+void ArrayPrototype::method_unshift(Context *ctx)
+{
+}
+
+//
// Date object
//
Value DateCtor::create(ExecutionEngine *engine)
struct ObjectPrototype: Object
{
ObjectPrototype(Context *ctx, FunctionObject *ctor);
+
+protected:
+ static void method_toString(Context *ctx);
};
struct StringCtor: FunctionObject
static void method_valueOf(Context *ctx);
};
+struct ArrayCtor: FunctionObject
+{
+ static Value create(ExecutionEngine *engine);
+
+ ArrayCtor(Context *scope);
+
+ virtual void construct(Context *ctx);
+ virtual void call(Context *ctx);
+};
+
+struct ArrayPrototype: Object
+{
+ ArrayPrototype(Context *ctx, FunctionObject *ctor);
+
+protected:
+ static void method_toString(Context *ctx);
+ static void method_toLocaleString(Context *ctx);
+ static void method_concat(Context *ctx);
+ static void method_join(Context *ctx);
+ static void method_pop(Context *ctx);
+ static void method_push(Context *ctx);
+ static void method_reverse(Context *ctx);
+ static void method_shift(Context *ctx);
+ static void method_slice(Context *ctx);
+ static void method_sort(Context *ctx);
+ static void method_splice(Context *ctx);
+ static void method_unshift(Context *ctx);
+};
+
struct DateCtor: FunctionObject
{
static Value create(ExecutionEngine *engine);
amd64_call_code(_codePtr, __qmljs_get_property);
return;
}
- assert(!"todo");
+ assert(!"wip");
+ return;
} else if (IR::Subscript *ss = s->source->asSubscript()) {
- qWarning() << "TODO load subscript";
+ amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
+ loadTempAddress(AMD64_RSI, t);
+ loadTempAddress(AMD64_RDX, ss->base->asTemp());
+ loadTempAddress(AMD64_RCX, ss->index->asTemp());
+ amd64_call_code(_codePtr, __qmljs_get_element);
return;
} else if (IR::Unop *u = s->source->asUnop()) {
if (IR::Temp *e = u->expr->asTemp()) {
amd64_call_code(_codePtr, op);
return;
} else if (IR::Const *c = u->expr->asConst()) {
+ assert(!"wip");
return;
}
} else if (IR::Binop *b = s->source->asBinop()) {
}
}
} else if (IR::Subscript *ss = s->target->asSubscript()) {
- if (IR::Temp *t = s->source->asTemp()) {
+ if (IR::Temp *t2 = s->source->asTemp()) {
+ loadTempAddress(AMD64_RSI, ss->base->asTemp());
+ loadTempAddress(AMD64_RDX, ss->index->asTemp());
+ loadTempAddress(AMD64_RCX, t2);
+ amd64_call_code(_codePtr, __qmljs_set_element);
return;
} else if (IR::Const *c = s->source->asConst()) {
+ amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
+ loadTempAddress(AMD64_RSI, ss->base->asTemp());
+ loadTempAddress(AMD64_RDX, ss->index->asTemp());
+ amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value);
+ amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX);
+ amd64_call_code(_codePtr, __qmljs_set_element_number);
return;
}
}
} else if (IR::Temp *t2 = s->source->asTemp()) {
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
loadTempAddress(AMD64_RSI, t);
- loadTempAddress(AMD64_RDX, t);
+ amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8);
+ // loadTempAddress(AMD64_RDX, t);
loadTempAddress(AMD64_RCX, t2);
void (*op)(Context *, Value *, const Value *, const Value *);
switch (s->op) {
qmljs_objects.cpp \
qv4isel.cpp \
qv4syntaxchecker.cpp \
- qv4ecmaobjects.cpp
+ qv4ecmaobjects.cpp \
+ qv4array.cpp
HEADERS += \
qv4codegen_p.h \