Port the rest of the sequence types to V4

Change-Id: Ia248f54b65b74d16e91e6ec72503eb967894586d
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Simon Hausmann 2013-05-14 18:58:34 +02:00 committed by Lars Knoll
parent cdc3433ebf
commit 32f7d078b6
11 changed files with 490 additions and 767 deletions

View File

@ -192,28 +192,29 @@ struct BoundFunction: FunctionObject {
static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value);
};
template <typename T, int ManagedType>
class MemberAccessorGetter : public FunctionObject
template <typename T>
class MemberAccessorGetterSetter : public FunctionObject
{
public:
typedef Value (T::*GetterFunction)(QV4::SimpleCallContext *ctx);
typedef Value (T::*GetterSetterFunction)(QV4::SimpleCallContext *ctx);
MemberAccessorGetter(ExecutionContext *scope, GetterFunction getter)
MemberAccessorGetterSetter(ExecutionContext *scope, GetterSetterFunction getterSetter, int managedType)
: FunctionObject(scope)
, managedType(managedType)
{
this->vtbl = &static_vtbl;
this->getter = getter;
this->getterSetter = getterSetter;
}
static QV4::Value call(Managed *that, ExecutionContext *context, const QV4::Value &thisObject, QV4::Value *args, int argc)
{
MemberAccessorGetter<T, ManagedType> *getter = static_cast<MemberAccessorGetter<T, ManagedType> *>(that);
MemberAccessorGetterSetter<T> *getterSetter = static_cast<MemberAccessorGetterSetter<T> *>(that);
Object *thisO = thisObject.asObject();
if (!thisO || thisO->internalType() != ManagedType)
if (!thisO || thisO->internalType() != getterSetter->managedType)
context->throwTypeError();
T *o = reinterpret_cast<T *>(thisO);
T *o = static_cast<T *>(thisO);
QV4::SimpleCallContext ctx;
ctx.initSimpleCallContext(context->engine);
@ -225,7 +226,7 @@ public:
QV4::Value result = QV4::Value::undefinedValue();
try {
result = (o->* getter->getter)(&ctx);
result = (o->* getterSetter->getterSetter)(&ctx);
} catch (QV4::Exception &ex) {
ex.partiallyUnwindContext(context);
throw;
@ -234,7 +235,8 @@ public:
return result;
}
protected:
GetterFunction getter;
GetterSetterFunction getterSetter;
const int managedType;
static const ManagedVTable static_vtbl;
};

View File

@ -50,11 +50,7 @@ QT_BEGIN_NAMESPACE
class QQmlLocaleData;
class QQuickJSContext2D;
class QQmlIntList;
class QQmlRealList;
class QQmlBoolList;
class QQmlStringList;
class QQmlUrlList;
template <typename, int> class QQmlSequence;
namespace QV4 {
@ -178,6 +174,7 @@ public:
Type_QmlRealList,
Type_QmlBoolList,
Type_QmlStringList,
Type_QmlQStringList,
Type_QmlUrlList,
// Wrapped QVariant
@ -206,11 +203,12 @@ public:
QQuickJSContext2D *asQQuickJSContext2D() { return type == Type_QQuickJSContext2D ? reinterpret_cast<QQuickJSContext2D *>(this) : 0; }
VariantObject *asVariantObject() { return type == Type_QVariant ? reinterpret_cast<VariantObject *>(this) : 0; }
QQmlIntList *asQmlIntList() { return type == Type_QmlIntList ? reinterpret_cast<QQmlIntList *>(this): 0; }
QQmlRealList *asQmlRealList() { return type == Type_QmlRealList ? reinterpret_cast<QQmlRealList *>(this): 0; }
QQmlBoolList *asQmlBoolList() { return type == Type_QmlBoolList ? reinterpret_cast<QQmlBoolList *>(this): 0; }
QQmlStringList *asQmlStringList() { return type == Type_QmlStringList ? reinterpret_cast<QQmlStringList *>(this): 0; }
QQmlUrlList *asQmlUrlList() { return type == Type_QmlUrlList ? reinterpret_cast<QQmlUrlList *>(this): 0; }
QQmlSequence<QList<int>, Type_QmlIntList> *asQmlIntList() { return type == Type_QmlIntList ? reinterpret_cast<QQmlSequence<QList<int>, Type_QmlIntList> *>(this): 0; }
QQmlSequence<QList<qreal>, Type_QmlRealList> *asQmlRealList() { return type == Type_QmlRealList ? reinterpret_cast<QQmlSequence<QList<qreal>, Type_QmlRealList> *>(this): 0; }
QQmlSequence<QList<bool>, Type_QmlBoolList> *asQmlBoolList() { return type == Type_QmlBoolList ? reinterpret_cast<QQmlSequence<QList<bool>, Type_QmlBoolList> *>(this): 0; }
QQmlSequence<QList<QString>, Type_QmlStringList> *asQmlStringList() { return type == Type_QmlStringList ? reinterpret_cast<QQmlSequence<QList<QString>, Type_QmlStringList> *>(this): 0; }
QQmlSequence<QStringList, Type_QmlQStringList> *asQmlQStringList() { return type == Type_QmlQStringList ? reinterpret_cast<QQmlSequence<QStringList, Type_QmlQStringList> *>(this): 0; }
QQmlSequence<QList<QUrl>, Type_QmlUrlList> *asQmlUrlList() { return type == Type_QmlUrlList ? reinterpret_cast<QQmlSequence<QList<QUrl>, Type_QmlUrlList> *>(this): 0; }
bool isListType() const { return type >= Type_QmlIntList && type <= Type_QmlUrlList; }

View File

@ -147,6 +147,7 @@ static inline void *popPtr(const char *&data)
#define ALIGN(size) (((size) + 3) & ~3)
void Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engine)
{
QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
if (v.isEmpty()) {
} else if (v.isUndefined()) {
push(data, valueheader(WorkerUndefined));
@ -234,31 +235,27 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engi
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
} else if (QV4::Object *o = v.asObject()) {
if (o->isListType()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint32_t seqLength = o->get(v4->id_length).toUInt32();
uint32_t length = seqLength + 1;
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
reserve(data, sizeof(quint32) + length * sizeof(quint32));
push(data, valueheader(WorkerSequence, length));
serialize(data, QV4::Value::fromInt32(engine->sequenceWrapper()->metaTypeForSequence(o)), engine); // sequence type
for (uint32_t ii = 0; ii < seqLength; ++ii)
serialize(data, o->getIndexed(ii), engine); // sequence elements
return;
}
v8::Handle<v8::Object> v8object(v);
QV8ObjectResource *r = static_cast<QV8ObjectResource *>(v8object->GetExternalResource());
if (r) {
if (r->resourceType() == QV8ObjectResource::SequenceType) {
// we can convert sequences, but not other types with external data.
QVariant sequenceVariant = engine->sequenceWrapper()->toVariant(r);
if (!sequenceVariant.isNull()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint32_t seqLength = engine->sequenceWrapper()->sequenceLength(r);
uint32_t length = seqLength + 1;
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
reserve(data, sizeof(quint32) + length * sizeof(quint32));
push(data, valueheader(WorkerSequence, length));
serialize(data, QV4::Value::fromInt32(sequenceVariant.userType()), engine); // sequence type
for (uint32_t ii = 0; ii < seqLength; ++ii) {
serialize(data, v8object->Get(ii)->v4Value(), engine); // sequence elements
}
return;
}
}
// not a sequence.
push(data, valueheader(WorkerUndefined));
return;
@ -377,11 +374,14 @@ QV4::Value Serialize::deserialize(const char *&data, QV8Engine *engine)
quint32 length = headersize(header);
quint32 seqLength = length - 1;
int sequenceType = deserialize(data, engine).integerValue();
v8::Handle<v8::Array> array = v8::Array::New(seqLength);
QV4::ArrayObject *array = v4->newArrayObject();
array->arrayReserve(seqLength);
for (quint32 ii = 0; ii < seqLength; ++ii)
array->Set(ii, deserialize(data, engine));
QVariant seqVariant = engine->sequenceWrapper()->toVariant(array, sequenceType, &succeeded);
return engine->sequenceWrapper()->fromVariant(seqVariant, &succeeded)->v4Value();
array->arrayData[ii].value = deserialize(data, engine);
array->arrayDataLen = seqLength;
array->setArrayLengthUnchecked(seqLength);
QVariant seqVariant = engine->sequenceWrapper()->toVariant(QV4::Value::fromObject(array), sequenceType, &succeeded);
return engine->sequenceWrapper()->fromVariant(seqVariant, &succeeded);
}
}
Q_ASSERT(!"Unreachable");

View File

@ -82,15 +82,27 @@ class Accessor():
self.setter = None
def generateBinding(self, out, parsedClass, obj):
managedType = "type"
if len(obj) > 0:
managedType = obj + "internalType()"
out.write(" {\n")
out.write(" QV4::FunctionObject *wrappedGetter = 0;\n")
out.write(" QV4::FunctionObject *wrappedSetter = 0;\n")
out.write("\n")
if self.getter:
out.write(" wrappedGetter = new (engine->memoryManager) %s_AccessorGetter(engine->rootContext, &%s::%s);\n" % (parsedClass.name, parsedClass.name, self.getter.fullMethodName()))
out.write(" wrappedGetter = new (engine->memoryManager) %s_AccessorGetterSetter(engine->rootContext, &%s::%s, %s);\n" % (parsedClass.name, parsedClass.name, self.getter.fullMethodName(), managedType))
if self.setter:
out.write(" wrappedSetter = new (engine->memoryManager) %s_AccessorSetter(engine->rootContext, &%s::%s);\n" % (parsedClass.name, parsedClass.name, self.setter.fullMethodName()))
out.write(" QV4::PropertyAttributes attrs = QV4::Attr_Accessor;\n")
out.write(" wrappedSetter = new (engine->memoryManager) %s_AccessorGetterSetter(engine->rootContext, &%s::%s, %s);\n" % (parsedClass.name, parsedClass.name, self.setter.fullMethodName(), managedType))
attributes = ["QV4::Attr_Accessor"];
if self.getter and "attributes" in self.getter.options:
attributes = attributes + [self.getter.options["attributes"]]
if self.setter and "attributes" in self.setter.options:
attributes = attributes + [self.setter.options["attributes"]]
out.write(" QV4::PropertyAttributes attrs = %s;\n" % "|".join(attributes))
out.write(" QV4::Property *pd = %sinsertMember(engine->newIdentifier(QStringLiteral(\"%s\")), attrs);\n" % (obj, self.name));
out.write(" pd->setGetter(wrappedGetter);\n");
out.write(" pd->setSetter(wrappedSetter);\n");
@ -124,10 +136,14 @@ def parse(lines):
currentClass = None
classPattern = re.compile(r".*QV4_JS_CLASS\((?P<ClassName>\w+)\).*")
annotatePattern = re.compile(r".*QV4_ANNOTATE\((?P<Options>.+)\).*")
methodPattern = re.compile(r".*\smethod_(?P<MethodName>\w+)\(.*\).*;")
methodPattern = re.compile(r".*\smethod_(?P<MethodName>\w+)\(.*\).*;?")
ctorMethodPattern = re.compile(r".*\sctor_method_(?P<MethodName>\w+)\(.*\).*;")
for line in lines:
if line == "};" and currentClass != None:
classes.append(currentClass)
currentClass = None
continue
line = line.strip()
classMatch = classPattern.match(line)
if classMatch:
@ -165,8 +181,6 @@ def parse(lines):
currentClass.options = parseOptions(annotateMatch.group("Options"))
continue
if currentClass != None:
classes.append(currentClass)
return classes
def generateBinding(out, parsedClass, vtableEntries):
@ -200,8 +214,8 @@ def generateBinding(out, parsedClass, vtableEntries):
out.write("}\n\n")
if len(parsedClass.accessors) > 0:
typename = "%s_AccessorGetter" % parsedClass.name
out.write("typedef QV4::MemberAccessorGetter<%s, QV4::Managed::Type_%s> %s;\n" % (parsedClass.name, parsedClass.managedTypeName(), typename))
typename = "%s_AccessorGetterSetter" % parsedClass.name
out.write("typedef QV4::MemberAccessorGetterSetter<%s> %s;\n" % (parsedClass.name, typename))
out.write("template<>\n")
out.write("DEFINE_MANAGED_VTABLE(%s);" % typename)
out.write("\n\n")

View File

@ -119,12 +119,6 @@ static bool ObjectComparisonCallback(v8::Handle<v8::Object> lhs, v8::Handle<v8::
return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, rv->data);
}
break;
case QV8ObjectResource::SequenceType:
// a sequence might be equal to itself.
if (rhst == QV8ObjectResource::SequenceType) {
return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr);
}
break;
default:
break;
}
@ -233,8 +227,6 @@ QVariant QV8Engine::toVariant(const QV4::Value &value, int typeHint)
return m_listWrapper.toVariant(r);
case QV8ObjectResource::ValueTypeType:
return m_valueTypeWrapper.toVariant(r);
case QV8ObjectResource::SequenceType:
return m_sequenceWrapper.toVariant(r);
}
} else if (typeHint == QMetaType::QJsonObject
&& !value.asArrayObject() && !value.asFunctionObject()) {

View File

@ -516,7 +516,7 @@ QVariant QV8Engine::toValueType(const QV4::Value &obj)
QV4::Value QV8Engine::newSequence(int sequenceType, QObject *object, int property, bool *succeeded)
{
return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded)->v4Value();
return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded);
}
QV4::Value QV8Engine::bindingFlagKey() const

View File

@ -73,7 +73,7 @@ public:
ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType,
ListModelType, Context2DStyleType, Context2DPixelArrayType,
ParticleDataType, SignalHandlerType, IncubatorType, VisualDataItemType,
SequenceType, ChangeSetArrayType };
ChangeSetArrayType };
virtual ResourceType resourceType() const = 0;
QV8Engine *engine;

View File

@ -63,42 +63,11 @@ void QV8SequenceWrapper::init(QV8Engine *engine)
{
FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE)
m_engine = engine;
m_toString = v8::FunctionTemplate::New(ToString)->GetFunction()->v4Value();
m_valueOf = v8::FunctionTemplate::New(ValueOf)->GetFunction()->v4Value();
m_engine = QV8Engine::getV4(engine);
QString defaultSortString = QLatin1String(
"(function compare(x,y) {"
" if (x === y) return 0;"
" x = x.toString();"
" y = y.toString();"
" if (x == y) return 0;"
" else return x < y ? -1 : 1;"
"})");
m_sort = v8::FunctionTemplate::New(Sort)->GetFunction()->v4Value();
m_arrayPrototype = v8::Array::New(1)->GetPrototype()->v4Value();
v8::Handle<v8::Script> defaultSortCompareScript = v8::Script::Compile(engine->toString(defaultSortString));
m_defaultSortComparer = defaultSortCompareScript->Run()->v4Value();
v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter);
ft->InstanceTemplate()->SetIndexedPropertyHandler(IndexedGetter, IndexedSetter, 0, IndexedDeleter, IndexedEnumerator);
ft->InstanceTemplate()->SetAccessor(v8::String::New("length"), LengthGetter, LengthSetter,
v8::Handle<v8::Value>(), v8::DEFAULT,
v8::PropertyAttribute(v8::DontDelete | v8::DontEnum));
ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0,
m_toString.value(), v8::DEFAULT,
v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum));
ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0,
m_valueOf.value(), v8::DEFAULT,
v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum));
ft->InstanceTemplate()->SetAccessor(v8::String::New("sort"), SortGetter, 0,
m_sort.value(), v8::DEFAULT,
v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum));
ft->InstanceTemplate()->SetHasExternalResource(true);
ft->InstanceTemplate()->MarkAsUseUserObjectComparison();
m_constructor = ft->GetFunction()->v4Value();
m_prototype = QV4::Value::fromObject(m_engine->newObject());
m_prototype.value().asObject()->prototype = m_engine->arrayPrototype;
QQmlSequencePrototype::initClass(m_engine, m_prototype.value());
}
#undef REGISTER_QML_SEQUENCE_METATYPE
@ -113,237 +82,91 @@ void QV8SequenceWrapper::destroy()
bool QV8SequenceWrapper::isSequenceType(int sequenceTypeId) const
{
FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE)
if (sequenceTypeId == qMetaTypeId<QStringList>()) {
return true;
} else
{ /* else */ return false; }
FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; }
}
#undef IS_SEQUENCE
bool QV8SequenceWrapper::isEqual(QV8ObjectResource *lhs, QV8ObjectResource *rhs)
{
Q_ASSERT(lhs && rhs && lhs->resourceType() == QV8ObjectResource::SequenceType && rhs->resourceType() == QV8ObjectResource::SequenceType);
QV8SequenceResource *lr = static_cast<QV8SequenceResource *>(lhs);
QV8SequenceResource *rr = static_cast<QV8SequenceResource *>(rhs);
return lr->isEqual(rr);
}
quint32 QV8SequenceWrapper::sequenceLength(QV8ObjectResource *r)
{
Q_ASSERT(r->resourceType() == QV8ObjectResource::SequenceType);
QV8SequenceResource *sr = static_cast<QV8SequenceResource *>(r);
Q_ASSERT(sr);
return sr->lengthGetter();
}
#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
r = new QV8##ElementTypeName##SequenceResource(m_engine, object, propertyIndex); \
QV4::Object *obj = new (m_engine->memoryManager) QQml##ElementTypeName##List(m_engine, object, propertyIndex); \
obj->prototype = m_prototype.value().asObject(); \
return QV4::Value::fromObject(obj); \
} else
v8::Handle<v8::Object> QV8SequenceWrapper::newSequence(int sequenceType, QObject *object, int propertyIndex, bool *succeeded)
QV4::Value QV8SequenceWrapper::newSequence(int sequenceType, QObject *object, int propertyIndex, bool *succeeded)
{
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_engine);
// This function is called when the property is a QObject Q_PROPERTY of
// the given sequence type. Internally we store a typed-sequence
// (as well as object ptr + property index for updated-read and write-back)
// and so access/mutate avoids variant conversion.
*succeeded = true;
QV8SequenceResource *r = 0;
FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE)
if (sequenceType == qMetaTypeId<QStringList>()) {
return QV4::Value::fromObject(new (v4->memoryManager) QQmlStringList(v4, object, propertyIndex));
} else { /* else */ *succeeded = false; return v8::Handle<v8::Object>(); }
v8::Handle<v8::Object> rv = m_constructor.value().asFunctionObject()->newInstance();
rv->SetExternalResource(r);
rv->SetPrototype(m_arrayPrototype.value());
return rv;
FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); }
}
#undef NEW_REFERENCE_SEQUENCE
#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
r = new QV8##ElementTypeName##SequenceResource(m_engine, v.value<SequenceType>()); \
QV4::Object *obj = new (m_engine->memoryManager) QQml##ElementTypeName##List(m_engine, v.value<SequenceType >()); \
obj->prototype = m_prototype.value().asObject(); \
return QV4::Value::fromObject(obj); \
} else
v8::Handle<v8::Object> QV8SequenceWrapper::fromVariant(const QVariant& v, bool *succeeded)
QV4::Value QV8SequenceWrapper::fromVariant(const QVariant& v, bool *succeeded)
{
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_engine);
// This function is called when assigning a sequence value to a normal JS var
// in a JS block. Internally, we store a sequence of the specified type.
// Access and mutation is extremely fast since it will not need to modify any
// QObject property.
int sequenceType = v.userType();
*succeeded = true;
QV8SequenceResource *r = 0;
FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE)
if (sequenceType == qMetaTypeId<QStringList>()) { \
return QV4::Value::fromObject(new (v4->memoryManager) QQmlStringList(v4, v.value<QStringList>()));
} else { /* else */ *succeeded = false; return v8::Handle<v8::Object>(); }
v8::Handle<v8::Object> rv = m_constructor.value().asFunctionObject()->newInstance();
rv->SetExternalResource(r);
rv->SetPrototype(m_arrayPrototype.value());
return rv;
FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); }
}
#undef NEW_COPY_SEQUENCE
QVariant QV8SequenceWrapper::toVariant(QV8ObjectResource *r)
{
Q_ASSERT(r->resourceType() == QV8ObjectResource::SequenceType);
QV8SequenceResource *resource = static_cast<QV8SequenceResource *>(r);
return resource->toVariant();
}
#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \
if (QQml##ElementTypeName##List *list = object->asQml##ElementTypeName##List()) \
return list->toVariant(); \
else
QVariant QV8SequenceWrapper::toVariant(QV4::Object *object)
{
Q_ASSERT(object->isListType());
if (QQmlStringList *list = object->asQmlStringList())
return list->toVariant();
return QVariant();
FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); }
}
#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \
if (typeHint == qMetaTypeId<SequenceType>()) { \
return QV8##ElementTypeName##SequenceResource::toVariant(m_engine, array, length, succeeded); \
return QQml##ElementTypeName##List::toVariant(a); \
} else
QVariant QV8SequenceWrapper::toVariant(v8::Handle<v8::Array> array, int typeHint, bool *succeeded)
QVariant QV8SequenceWrapper::toVariant(const QV4::Value &array, int typeHint, bool *succeeded)
{
*succeeded = true;
uint32_t length = array->Length();
FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT)
if (typeHint == qMetaTypeId<QStringList>()) { \
QV4::ArrayObject *a = array->v4Value().asArrayObject();
if (!a) {
*succeeded = false;
return QVariant();
}
return QVariant::fromValue(a->toQStringList());
} else { /* else */ *succeeded = false; return QVariant(); }
QV4::ArrayObject *a = array.asArrayObject();
if (!a) {
*succeeded = false;
return QVariant();
}
FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); }
}
#undef SEQUENCE_TO_VARIANT
v8::Handle<v8::Value> QV8SequenceWrapper::IndexedSetter(quint32 index, v8::Handle<v8::Value> value, const v8::AccessorInfo &info)
#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \
case QV4::Managed::Type_Qml##ElementTypeName##List: return qMetaTypeId<SequenceType>();
int QV8SequenceWrapper::metaTypeForSequence(QV4::Object *object)
{
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This());
Q_ASSERT(sr);
return sr->indexedSetter(index, value);
}
v8::Handle<v8::Value> QV8SequenceWrapper::IndexedGetter(quint32 index, const v8::AccessorInfo &info)
{
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This());
Q_ASSERT(sr);
return sr->indexedGetter(index);
}
v8::Handle<v8::Value> QV8SequenceWrapper::IndexedDeleter(quint32 index, const v8::AccessorInfo &info)
{
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This());
Q_ASSERT(sr);
return sr->indexedDeleter(index);
}
v8::Handle<v8::Array> QV8SequenceWrapper::IndexedEnumerator(const v8::AccessorInfo &info)
{
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This());
Q_ASSERT(sr);
return sr->indexedEnumerator();
}
v8::Handle<v8::Value> QV8SequenceWrapper::LengthGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info)
{
Q_UNUSED(property);
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This());
Q_ASSERT(sr);
return QV4::Value::fromUInt32(sr->lengthGetter());
}
void QV8SequenceWrapper::LengthSetter(v8::Handle<v8::String> property, v8::Handle<v8::Value> value, const v8::AccessorInfo &info)
{
Q_UNUSED(property);
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This());
Q_ASSERT(sr);
sr->lengthSetter(value);
}
v8::Handle<v8::Value> QV8SequenceWrapper::ToStringGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info)
{
Q_UNUSED(property);
return info.Data();
}
v8::Handle<v8::Value> QV8SequenceWrapper::ValueOfGetter(v8::Handle<v8::String> property,
const v8::AccessorInfo &info)
{
Q_UNUSED(property);
return info.Data();
}
v8::Handle<v8::Value> QV8SequenceWrapper::SortGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info)
{
Q_UNUSED(property);
return info.Data();
}
QV4::Value QV8SequenceWrapper::Sort(const v8::Arguments &args)
{
int argCount = args.Length();
if (argCount < 2) {
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This());
Q_ASSERT(sr);
qint32 length = sr->lengthGetter();
if (length > 1) {
v8::Handle<v8::Function> jsCompareFn = sr->engine->sequenceWrapper()->m_defaultSortComparer.value();
if (argCount == 1 && args[0]->IsFunction())
jsCompareFn = v8::Handle<v8::Function>(v8::Function::Cast(args[0].get()));
sr->sort(jsCompareFn);
}
switch (object->internalType()) {
FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE)
default:
return -1;
}
return args.This()->v4Value();
}
QV4::Value QV8SequenceWrapper::ToString(const v8::Arguments &args)
{
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This());
Q_ASSERT(sr);
return sr->toString();
}
#undef MAP_META_TYPE
QV4::Value QV8SequenceWrapper::ValueOf(const v8::Arguments &args)
{
QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This());
Q_ASSERT(sr);
QV4::Value tostringValue = sr->toString();
if (!tostringValue.isUndefined())
return tostringValue;
return QV4::Value::fromUInt32(sr->lengthGetter());
}
v8::Handle<v8::Value> QV8SequenceWrapper::Getter(v8::Handle<v8::String> property,
const v8::AccessorInfo &info)
{
Q_UNUSED(property);
Q_UNUSED(info);
return v8::Handle<v8::Value>();
}
v8::Handle<v8::Value> QV8SequenceWrapper::Setter(v8::Handle<v8::String> property,
v8::Handle<v8::Value> value,
const v8::AccessorInfo &info)
{
Q_UNUSED(property);
Q_UNUSED(info);
return value;
}
#include "qv8sequencewrapper_p_p_jsclass.cpp"
QT_END_NAMESPACE

View File

@ -55,7 +55,6 @@
#include <QtCore/qglobal.h>
#include <QtCore/qvariant.h>
#include <private/qv8_p.h>
#include <private/qv4value_p.h>
@ -75,40 +74,16 @@ public:
bool isSequenceType(int sequenceTypeId) const;
bool isEqual(QV8ObjectResource *lhs, const QVariant &rhs);
bool isEqual(QV8ObjectResource *lhs, QV8ObjectResource *rhs);
quint32 sequenceLength(QV8ObjectResource *);
v8::Handle<v8::Object> newSequence(int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
v8::Handle<v8::Object> fromVariant(const QVariant& v, bool *succeeded);
QVariant toVariant(QV8ObjectResource *);
QV4::Value newSequence(int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
QV4::Value fromVariant(const QVariant& v, bool *succeeded);
QVariant toVariant(QV4::Object *object);
QVariant toVariant(v8::Handle<v8::Array> array, int typeHint, bool *succeeded);
QVariant toVariant(const QV4::Value &array, int typeHint, bool *succeeded);
int metaTypeForSequence(QV4::Object *object);
private:
QV8Engine *m_engine;
QV4::ExecutionEngine *m_engine;
QV4::PersistentValue m_constructor;
QV4::PersistentValue m_toString;
QV4::PersistentValue m_valueOf;
QV4::PersistentValue m_sort;
QV4::PersistentValue m_arrayPrototype;
QV4::PersistentValue m_defaultSortComparer;
static v8::Handle<v8::Value> IndexedGetter(quint32 index, const v8::AccessorInfo &info);
static v8::Handle<v8::Value> IndexedSetter(quint32 index, v8::Handle<v8::Value> value, const v8::AccessorInfo &info);
static v8::Handle<v8::Value> IndexedDeleter(quint32 index, const v8::AccessorInfo &info);
static v8::Handle<v8::Array> IndexedEnumerator(const v8::AccessorInfo &info);
static v8::Handle<v8::Value> LengthGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info);
static void LengthSetter(v8::Handle<v8::String> property, v8::Handle<v8::Value> value, const v8::AccessorInfo &info);
static v8::Handle<v8::Value> ToStringGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info);
static QV4::Value ToString(const v8::Arguments &args);
static v8::Handle<v8::Value> ValueOfGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info);
static v8::Handle<v8::Value> SortGetter(v8::Handle<v8::String> property, const v8::AccessorInfo &info);
static QV4::Value ValueOf(const v8::Arguments &args);
static v8::Handle<v8::Value> Getter(v8::Handle<v8::String> property, const v8::AccessorInfo &info);
static v8::Handle<v8::Value> Setter(v8::Handle<v8::String> property, v8::Handle<v8::Value> value, const v8::AccessorInfo &info);
static QV4::Value Sort(const v8::Arguments &args);
QV4::PersistentValue m_prototype;
};

View File

@ -60,50 +60,6 @@
QT_BEGIN_NAMESPACE
/*!
\internal
\class QV8SequenceResource
\brief The abstract base class of the external resource used in sequence type objects
Every sequence type object returned by QV8SequenceWrapper::fromVariant() or
QV8SequenceWrapper::newSequence() has a type-specific QV8SequenceResource which
contains the type name, the meta type ids of the sequence and sequence element
types, as well as either the sequence data (copy) or object pointer and property
index (reference) data associated with the sequence.
*/
class QV8SequenceResource : public QV8ObjectResource
{
V8_RESOURCE_TYPE(SequenceType);
public:
virtual ~QV8SequenceResource() {}
enum ObjectType { Reference, Copy };
virtual QVariant toVariant() = 0;
virtual bool isEqual(const QV8SequenceResource *v) = 0;
virtual quint32 lengthGetter() = 0;
virtual void lengthSetter(v8::Handle<v8::Value> value) = 0;
virtual v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) = 0;
virtual v8::Handle<v8::Value> indexedGetter(quint32 index) = 0;
virtual v8::Handle<v8::Value> indexedDeleter(quint32 index) = 0;
virtual v8::Handle<v8::Array> indexedEnumerator() = 0;
virtual QV4::Value toString() = 0;
virtual void sort(v8::Handle<v8::Function> comparer) = 0;
ObjectType objectType;
QByteArray typeName;
int sequenceMetaTypeId;
int elementMetaTypeId;
protected:
QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId)
: QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId)
{
}
};
// helper function to generate valid warnings if errors occur during sequence operations.
static void generateWarning(QV8Engine *engine, const QString& description)
{
@ -123,415 +79,153 @@ static void generateWarning(QV8Engine *engine, const QString& description)
QQmlEnginePrivate::warning(engine->engine(), retn);
}
static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v)
{
return v->Int32Value();
}
static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v)
{
return QV4::Value::fromInt32(v);
}
static QString convertIntToString(QV8Engine *, int v)
{
return QString::number(v);
}
static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v)
{
return v->NumberValue();
}
static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v)
{
return QV4::Value::fromDouble(v);
}
static QString convertRealToString(QV8Engine *, qreal v)
{
return QString::number(v);
}
static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v)
{
return v->BooleanValue();
}
static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v)
{
return QV4::Value::fromBoolean(v);
}
static QString convertBoolToString(QV8Engine *, bool v)
{
if (v)
return QLatin1String("true");
return QLatin1String("false");
}
static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v)
{
return v->v4Value().toQString();
}
static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v)
{
return e->toString(v);
}
static QString convertStringToString(QV8Engine *, const QString &v)
{
return v;
}
static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v)
{
return v->v4Value().toQString();
}
static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v)
{
return e->toString(v);
}
static QString convertQStringToString(QV8Engine *, const QString &v)
{
return v;
}
static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v)
{
return QUrl(v->v4Value().toQString());
}
static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v)
{
return e->toString(QLatin1String(v.toEncoded().data()));
}
static QString convertUrlToString(QV8Engine *, const QUrl &v)
{
return v.toString();
}
/*
\internal
\class QV8<Type>SequenceResource
\brief The external resource used in sequence type objects
Every sequence type object returned by QV8SequenceWrapper::newSequence() has
a QV8<Type>SequenceResource which contains a property index and a pointer
to the object which contains the property.
Every sequence type object returned by QV8SequenceWrapper::fromVariant() has
a QV8<Type>SequenceResource which contains a copy of the sequence value.
Operations on the sequence are implemented directly in terms of that sequence data.
There exists one QV8<Type>SequenceResource instance for every JavaScript Object
(sequence) instance returned from QV8SequenceWrapper::newSequence() or
QV8SequenceWrapper::fromVariant().
*/
// F(elementType, elementTypeName, sequenceType, defaultValue)
#define FOREACH_QML_SEQUENCE_TYPE(F) \
F(int, Int, QList<int>, 0) \
F(qreal, Real, QList<qreal>, 0.0) \
F(bool, Bool, QList<bool>, false) \
F(QString, String, QList<QString>, QString()) \
F(QString, QString, QStringList, QString()) \
F(QUrl, Url, QList<QUrl>, QUrl())
#define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \
QT_END_NAMESPACE \
Q_DECLARE_METATYPE(SequenceType) \
QT_BEGIN_NAMESPACE \
class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \
public:\
QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, QObject *obj, int propIdx) \
: QV8SequenceResource(engine, QV8SequenceResource::Reference, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \
, object(obj), propertyIndex(propIdx) \
{ \
} \
QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, const SequenceType &value) \
: QV8SequenceResource(engine, QV8SequenceResource::Copy, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \
, object(0), propertyIndex(-1), c(value) \
{ \
} \
~QV8##SequenceElementTypeName##SequenceResource() \
{ \
} \
static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \
{ \
SequenceType list; \
list.reserve(length); \
for (uint32_t ii = 0; ii < length; ++ii) { \
list.append(ConversionFromV8fn(e, array->Get(ii))); \
} \
*succeeded = true; \
return QVariant::fromValue<SequenceType>(list); \
} \
QVariant toVariant() \
{ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return QVariant(); \
loadReference(); \
} \
return QVariant::fromValue<SequenceType>(c); \
} \
bool isEqual(const QV8SequenceResource *v) \
{ \
/* Note: two different sequences can never be equal (even if they */ \
/* contain the same elements in the same order) in order to */ \
/* maintain JavaScript semantics. However, if they both reference */ \
/* the same QObject+propertyIndex, they are equal. */ \
if (objectType == QV8SequenceResource::Reference && v->objectType == QV8SequenceResource::Reference) { \
if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \
const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \
return (object != 0 && object == rhs->object && propertyIndex == rhs->propertyIndex); \
} \
} else if (objectType == QV8SequenceResource::Copy && v->objectType == QV8SequenceResource::Copy) { \
if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \
const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \
return (this == rhs); \
} \
} \
return false; \
} \
quint32 lengthGetter() \
{ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return 0; \
loadReference(); \
} \
return static_cast<quint32>(c.count()); \
} \
void lengthSetter(v8::Handle<v8::Value> value) \
{ \
/* Get the new required length */ \
if (value.IsEmpty() || !value->IsUint32()) \
return; \
quint32 newLength = value->Uint32Value(); \
/* Qt containers have int (rather than uint) allowable indexes. */ \
if (newLength > INT_MAX) { \
generateWarning(engine, QLatin1String("Index out of range during length set")); \
return; \
} \
/* Read the sequence from the QObject property if we're a reference */ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return; \
loadReference(); \
} \
/* Determine whether we need to modify the sequence */ \
qint32 newCount = static_cast<qint32>(newLength); \
qint32 count = c.count(); \
if (newCount == count) { \
return; \
} else if (newCount > count) { \
/* according to ECMA262r3 we need to insert */ \
/* undefined values increasing length to newLength. */ \
/* We cannot, so we insert default-values instead. */ \
c.reserve(newCount); \
while (newCount > count++) { \
c.append(DefaultValue); \
} \
} else { \
/* according to ECMA262r3 we need to remove */ \
/* elements until the sequence is the required length. */ \
while (newCount < count) { \
count--; \
c.removeAt(count); \
} \
} \
/* write back if required. */ \
if (objectType == QV8SequenceResource::Reference) { \
/* write back. already checked that object is non-null, so skip that check here. */ \
storeReference(); \
} \
return; \
} \
v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \
{ \
/* Qt containers have int (rather than uint) allowable indexes. */ \
if (index > INT_MAX) { \
generateWarning(engine, QLatin1String("Index out of range during indexed set")); \
return QV4::Value::undefinedValue(); \
} \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return QV4::Value::undefinedValue(); \
loadReference(); \
} \
/* modify the sequence */ \
SequenceElementType elementValue = ConversionFromV8fn(engine, value); \
qint32 count = c.count(); \
qint32 signedIdx = static_cast<qint32>(index); \
if (signedIdx == count) { \
c.append(elementValue); \
} else if (signedIdx < count) { \
c[index] = elementValue; \
} else { \
/* according to ECMA262r3 we need to insert */ \
/* the value at the given index, increasing length to index+1. */ \
c.reserve(signedIdx + 1); \
while (signedIdx > count++) { \
c.append(DefaultValue); \
} \
c.append(elementValue); \
} \
/* write back. already checked that object is non-null, so skip that check here. */ \
if (objectType == QV8SequenceResource::Reference) \
storeReference(); \
return value; \
} \
v8::Handle<v8::Value> indexedGetter(quint32 index) \
{ \
/* Qt containers have int (rather than uint) allowable indexes. */ \
if (index > INT_MAX) { \
generateWarning(engine, QLatin1String("Index out of range during indexed get")); \
return QV4::Value::undefinedValue(); \
} \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return QV4::Value::undefinedValue(); \
loadReference(); \
} \
qint32 count = c.count(); \
qint32 signedIdx = static_cast<qint32>(index); \
if (signedIdx < count) \
return ConversionToV8fn(engine, c.at(signedIdx)); \
return QV4::Value::undefinedValue(); \
} \
v8::Handle<v8::Value> indexedDeleter(quint32 index) \
{ \
/* Qt containers have int (rather than uint) allowable indexes. */ \
if (index > INT_MAX) \
return QV4::Value::fromBoolean(false); \
/* Read in the sequence from the QObject */ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return QV4::Value::fromBoolean(false); \
loadReference(); \
} \
qint32 signedIdx = static_cast<qint32>(index); \
if (signedIdx < c.count()) { \
/* according to ECMA262r3 it should be Undefined, */ \
/* but we cannot, so we insert a default-value instead. */ \
c.replace(signedIdx, DefaultValue); \
if (objectType == QV8SequenceResource::Reference) { \
/* write back. already checked that object is non-null, so skip that check here. */ \
storeReference(); \
} \
return QV4::Value::fromBoolean(true); \
} \
return QV4::Value::fromBoolean(false); \
} \
v8::Handle<v8::Array> indexedEnumerator() \
{ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return v8::Handle<v8::Array>(); \
loadReference(); \
} \
qint32 count = c.count(); \
v8::Handle<v8::Array> retn = v8::Array::New(count); \
for (qint32 i = 0; i < count; ++i) { \
retn->Set(static_cast<quint32>(i), QV4::Value::fromUInt32(static_cast<quint32>(i))); \
} \
return retn; \
} \
QV4::Value toString() \
{ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return QV4::Value::undefinedValue(); \
loadReference(); \
} \
QString str; \
qint32 count = c.count(); \
for (qint32 i = 0; i < count; ++i) { \
str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \
} \
str.chop(1); \
return engine->toString(str); \
} \
void loadReference() \
{ \
Q_ASSERT(object); \
Q_ASSERT(objectType == QV8SequenceResource::Reference); \
void *a[] = { &c, 0 }; \
QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
} \
void storeReference() \
{ \
Q_ASSERT(object); \
Q_ASSERT(objectType == QV8SequenceResource::Reference); \
int status = -1; \
QQmlPropertyPrivate::WriteFlags flags = \
QQmlPropertyPrivate::DontRemoveBinding; \
void *a[] = { &c, 0, &status, &flags }; \
QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
} \
class CompareFunctor \
{ \
public: \
CompareFunctor(QV8Engine *engine, v8::Handle<v8::Function> f) : jsFn(f), eng(engine) {} \
bool operator()(SequenceElementType e0, SequenceElementType e1) \
{ \
v8::Handle<v8::Value> argv[2] = { eng->fromVariant(e0), eng->fromVariant(e1) }; \
v8::Handle<v8::Value> compareValue = jsFn->Call(v8::Value::fromV4Value(eng->global()), 2, argv); \
return compareValue->NumberValue() < 0; \
} \
private: \
v8::Handle<v8::Function> jsFn; \
QV8Engine *eng; \
}; \
void sort(v8::Handle<v8::Function> jsCompareFunction) \
{ \
CompareFunctor cf(engine, jsCompareFunction); \
qSort(c.begin(), c.end(), cf); \
} \
private: \
QQmlGuard<QObject> object; \
int propertyIndex; \
SequenceType c; \
};
#define GENERATE_QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue) \
QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue, convert##ElementTypeName##ToV8Value, convertV8ValueTo##ElementTypeName, convert##ElementTypeName##ToString)
FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE)
#undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE
#undef QML_SEQUENCE_TYPE_RESOURCE
class QQmlStringList : public QV4::Object
class QV4_JS_CLASS(QQmlSequenceBase) : public QV4::Object
{
public:
QQmlStringList(QV4::ExecutionEngine *engine, const QStringList &container)
QQmlSequenceBase(QV4::ExecutionEngine *engine)
: QV4::Object(engine)
, m_container(container)
{}
void initClass(QV4::ExecutionEngine *engine);
QV4::Value method_get_length(QV4::SimpleCallContext* ctx) QV4_ANNOTATE(attributes QV4::Attr_ReadOnly);
QV4::Value method_set_length(QV4::SimpleCallContext* ctx);
};
class QV4_JS_CLASS(QQmlSequencePrototype) : public QV4::Object
{
QV4_ANNOTATE(staticInitClass true)
public:
static void initClass(QV4::ExecutionEngine *engine, const QV4::Value &value);
static QV4::Value method_valueOf(QV4::SimpleCallContext *ctx)
{
type = Type_QmlStringList;
vtbl = &static_vtbl;
m_lengthProperty = insertMember(engine->id_length, QV4::Attr_ReadOnly);
prototype = engine->arrayPrototype;
updateLength();
return QV4::Value::fromString(ctx->thisObject.toString(ctx));
}
QQmlStringList(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex)
: QV4::Object(engine)
static QV4::Value method_sort(QV4::SimpleCallContext *ctx) QV4_ARGC(1);
};
static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, const QString &element)
{
return QV4::Value::fromString(ctx, element);
}
static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, int element)
{
return QV4::Value::fromInt32(element);
}
static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, const QUrl &element)
{
return QV4::Value::fromString(ctx, element.toString());
}
static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, qreal element)
{
return QV4::Value::fromDouble(element);
}
static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, bool element)
{
return QV4::Value::fromBoolean(element);
}
static QString convertElementToString(const QString &element)
{
return element;
}
static QString convertElementToString(int element)
{
return QString::number(element);
}
static QString convertElementToString(const QUrl &element)
{
return element.toString();
}
static QString convertElementToString(qreal element)
{
QString qstr;
__qmljs_numberToString(&qstr, element, 10);
return qstr;
}
static QString convertElementToString(bool element)
{
if (element)
return QStringLiteral("true");
else
return QStringLiteral("false");
}
template <typename ElementType> ElementType convertValueToElement(const QV4::Value &value);
template <> QString convertValueToElement(const QV4::Value &value)
{
return value.toQString();
}
template <> int convertValueToElement(const QV4::Value &value)
{
return value.toInt32();
}
template <> QUrl convertValueToElement(const QV4::Value &value)
{
return QUrl(value.toQString());
}
template <> qreal convertValueToElement(const QV4::Value &value)
{
return value.toNumber();
}
template <> bool convertValueToElement(const QV4::Value &value)
{
return value.toBoolean();
}
template <typename Container, int ManagedType>
class QQmlSequence : public QQmlSequenceBase
{
public:
QQmlSequence(QV4::ExecutionEngine *engine, const Container &container)
: QQmlSequenceBase(engine)
, m_container(container)
, m_object(0)
, m_propertyIndex(-1)
, m_isReference(false)
{
type = Type_QmlStringList;
type = ManagedType;
vtbl = &static_vtbl;
m_lengthProperty = insertMember(engine->id_length, QV4::Attr_ReadOnly);
prototype = engine->arrayPrototype;
void *a[] = { &m_container, 0 };
QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a);
updateLength();
initClass(engine);
}
QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex)
: QQmlSequenceBase(engine)
, m_object(object)
, m_propertyIndex(propertyIndex)
, m_isReference(true)
{
type = ManagedType;
vtbl = &static_vtbl;
prototype = engine->arrayPrototype;
loadReference();
initClass(engine);
}
QV4::Value containerGetIndexed(QV4::ExecutionContext *ctx, uint index, bool *hasProperty)
@ -543,11 +237,19 @@ public:
*hasProperty = false;
return QV4::Value::undefinedValue();
}
if (m_isReference) {
if (!m_object) {
if (hasProperty)
*hasProperty = false;
return QV4::Value::undefinedValue();
}
loadReference();
}
qint32 signedIdx = static_cast<qint32>(index);
if (signedIdx < m_container.count()) {
if (hasProperty)
*hasProperty = true;
return QV4::Value::fromString(ctx, m_container.at(signedIdx));
return convertElementToValue(ctx, m_container.at(signedIdx));
}
if (hasProperty)
*hasProperty = false;
@ -561,15 +263,21 @@ public:
generateWarning(QV8Engine::get(ctx->engine->publicEngine), QLatin1String("Index out of range during indexed put"));
return;
}
if (m_isReference) {
if (!m_object)
return;
loadReference();
}
qint32 signedIdx = static_cast<qint32>(index);
int count = m_container.count();
QString element = value.toQString();
typename Container::value_type element = convertValueToElement<typename Container::value_type>(value);
if (signedIdx == count) {
m_container.append(element);
updateLength();
} else if (signedIdx < count) {
m_container[signedIdx] = element;
} else {
@ -577,11 +285,13 @@ public:
/* the value at the given index, increasing length to index+1. */
m_container.reserve(signedIdx + 1);
while (signedIdx > count++) {
m_container.append(QString());
m_container.append(typename Container::value_type());
}
m_container.append(element);
updateLength();
}
if (m_isReference)
storeReference();
}
QV4::PropertyAttributes containerQueryIndexed(QV4::ExecutionContext *ctx, uint index)
@ -591,6 +301,11 @@ public:
generateWarning(QV8Engine::get(ctx->engine->publicEngine), QLatin1String("Index out of range during indexed query"));
return QV4::Attr_Invalid;
}
if (m_isReference) {
if (!m_object)
return QV4::Attr_Invalid;
loadReference();
}
qint32 signedIdx = static_cast<qint32>(index);
return (index < m_container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid;
}
@ -600,6 +315,11 @@ public:
/* Qt containers have int (rather than uint) allowable indexes. */
if (index > INT_MAX)
return false;
if (m_isReference) {
if (!m_object)
return false;
loadReference();
}
qint32 signedIdx = static_cast<qint32>(index);
if (signedIdx >= m_container.count())
@ -607,40 +327,235 @@ public:
/* according to ECMA262r3 it should be Undefined, */
/* but we cannot, so we insert a default-value instead. */
m_container.replace(signedIdx, QString());
m_container.replace(signedIdx, typename Container::value_type());
if (m_isReference)
storeReference();
return true;
}
QVariant toVariant() const
{ return QVariant::fromValue<QStringList>(m_container); }
private:
void updateLength()
void sort(QV4::SimpleCallContext *ctx)
{
m_lengthProperty->value = QV4::Value::fromInt32(m_container.length());
if (m_isReference) {
if (!m_object)
return;
loadReference();
}
struct DefaultCompareFunctor
{
bool operator()(typename Container::value_type lhs, typename Container::value_type rhs)
{
return convertElementToString(lhs) < convertElementToString(rhs);
}
};
struct CompareFunctor
{
CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn)
: m_ctx(ctx), m_compareFn(compareFn)
{}
bool operator()(typename Container::value_type lhs, typename Container::value_type rhs)
{
QV4::Managed *fun = m_compareFn.asManaged();
QV4::Value argv[2] = {
convertElementToValue(m_ctx, lhs),
convertElementToValue(m_ctx, rhs)
};
QV4::Value result = fun->call(m_ctx, QV4::Value::fromObject(m_ctx->engine->globalObject), argv, 2);
return result.toNumber() < 0;
}
private:
QV4::ExecutionContext *m_ctx;
QV4::Value m_compareFn;
};
if (ctx->argumentCount == 1 && ctx->arguments[0].asFunctionObject()) {
QV4::Value compareFn = ctx->arguments[0];
CompareFunctor cf(ctx, compareFn);
qSort(m_container.begin(), m_container.end(), cf);
} else {
DefaultCompareFunctor cf;
qSort(m_container.begin(), m_container.end(), cf);
}
if (m_isReference)
storeReference();
}
QStringList m_container;
QV4::Property *m_lengthProperty;
QV4::Value lengthGetter(QV4::SimpleCallContext*)
{
if (m_isReference) {
if (!m_object)
return QV4::Value::fromInt32(0);
loadReference();
}
return QV4::Value::fromInt32(m_container.count());
}
void lengthSetter(QV4::SimpleCallContext* ctx)
{
quint32 newLength = ctx->arguments[0].toUInt32();
/* Qt containers have int (rather than uint) allowable indexes. */
if (newLength > INT_MAX) {
generateWarning(QV8Engine::get(ctx->engine->publicEngine), QLatin1String("Index out of range during length set"));
return;
}
/* Read the sequence from the QObject property if we're a reference */
if (m_isReference) {
if (!m_object)
return;
loadReference();
}
/* Determine whether we need to modify the sequence */
qint32 newCount = static_cast<qint32>(newLength);
qint32 count = m_container.count();
if (newCount == count) {
return;
} else if (newCount > count) {
/* according to ECMA262r3 we need to insert */
/* undefined values increasing length to newLength. */
/* We cannot, so we insert default-values instead. */
m_container.reserve(newCount);
while (newCount > count++) {
m_container.append(typename Container::value_type());
}
} else {
/* according to ECMA262r3 we need to remove */
/* elements until the sequence is the required length. */
while (newCount < count) {
count--;
m_container.removeAt(count);
}
}
/* write back if required. */
if (m_isReference) {
/* write back. already checked that object is non-null, so skip that check here. */
storeReference();
}
}
QVariant toVariant() const
{ return QVariant::fromValue<Container>(m_container); }
static QVariant toVariant(QV4::ArrayObject *array)
{
Container result;
uint32_t length = array->arrayLength();
for (uint32_t i = 0; i < length; ++i)
result << convertValueToElement<typename Container::value_type>(array->getIndexed(i));
return QVariant::fromValue(result);
}
private:
void loadReference()
{
Q_ASSERT(m_object);
Q_ASSERT(m_isReference);
void *a[] = { &m_container, 0 };
QMetaObject::metacall(m_object, QMetaObject::ReadProperty, m_propertyIndex, a);
}
void storeReference()
{
Q_ASSERT(m_object);
Q_ASSERT(m_isReference);
int status = -1;
QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding;
void *a[] = { &m_container, 0, &status, &flags };
QMetaObject::metacall(m_object, QMetaObject::WriteProperty, m_propertyIndex, a);
}
Container m_container;
QQmlGuard<QObject> m_object;
int m_propertyIndex;
bool m_isReference;
static QV4::Value getIndexed(QV4::Managed *that, QV4::ExecutionContext *ctx, uint index, bool *hasProperty)
{ return static_cast<QQmlStringList *>(that)->containerGetIndexed(ctx, index, hasProperty); }
{ return static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerGetIndexed(ctx, index, hasProperty); }
static void putIndexed(Managed *that, QV4::ExecutionContext *ctx, uint index, const QV4::Value &value)
{ static_cast<QQmlStringList *>(that)->containerPutIndexed(ctx, index, value); }
{ static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerPutIndexed(ctx, index, value); }
static QV4::PropertyAttributes queryIndexed(QV4::Managed *that, QV4::ExecutionContext *ctx, uint index)
{ return static_cast<QQmlStringList *>(that)->containerQueryIndexed(ctx, index); }
{ return static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerQueryIndexed(ctx, index); }
static bool deleteIndexedProperty(QV4::Managed *that, QV4::ExecutionContext *ctx, uint index)
{ return static_cast<QQmlStringList *>(that)->containerDeleteIndexedProperty(ctx, index); }
{ return static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerDeleteIndexedProperty(ctx, index); }
static void destroy(Managed *that)
{
static_cast<QQmlStringList *>(that)->~QQmlStringList();
static_cast<QQmlSequence<Container, ManagedType> *>(that)->~QQmlSequence<Container, ManagedType>();
}
static const QV4::ManagedVTable static_vtbl;
};
typedef QQmlSequence<QStringList, QV4::Managed::Type_QmlQStringList> QQmlQStringList;
template<>
DEFINE_MANAGED_VTABLE(QQmlQStringList);
typedef QQmlSequence<QList<QString>, QV4::Managed::Type_QmlStringList> QQmlStringList;
template<>
DEFINE_MANAGED_VTABLE(QQmlStringList);
typedef QQmlSequence<QList<int>, QV4::Managed::Type_QmlIntList> QQmlIntList;
template<>
DEFINE_MANAGED_VTABLE(QQmlIntList);
typedef QQmlSequence<QList<QUrl>, QV4::Managed::Type_QmlUrlList> QQmlUrlList;
template<>
DEFINE_MANAGED_VTABLE(QQmlUrlList);
typedef QQmlSequence<QList<bool>, QV4::Managed::Type_QmlBoolList> QQmlBoolList;
template<>
DEFINE_MANAGED_VTABLE(QQmlBoolList);
typedef QQmlSequence<QList<qreal>, QV4::Managed::Type_QmlRealList> QQmlRealList;
template<>
DEFINE_MANAGED_VTABLE(QQmlRealList);
QV4::Value QQmlSequencePrototype::method_sort(QV4::SimpleCallContext *ctx)
{
QV4::Object *o = ctx->thisObject.asObject();
if (!o || !o->isListType())
ctx->throwTypeError();
if (ctx->argumentCount < 2) {
#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
case QV4::Managed::Type_Qml##SequenceElementTypeName##List: o->asQml##SequenceElementTypeName##List()->sort(ctx); break;
switch (o->internalType()) {
FOREACH_QML_SEQUENCE_TYPE(CALL_SORT)
default: break;
}
#undef CALL_SORT
}
return ctx->thisObject;
}
QV4::Value QQmlSequenceBase::method_get_length(QV4::SimpleCallContext* ctx) QV4_ANNOTATE(attributes QV4::Attr_ReadOnly)
{
#define CALL_LENGTH_GETTER(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
case QV4::Managed::Type_Qml##SequenceElementTypeName##List: return asQml##SequenceElementTypeName##List()->lengthGetter(ctx);
switch (internalType()) {
FOREACH_QML_SEQUENCE_TYPE(CALL_LENGTH_GETTER)
default: QV4::Value::undefinedValue();
}
#undef CALL_LENGTH_GETTER
}
QV4::Value QQmlSequenceBase::method_set_length(QV4::SimpleCallContext* ctx)
{
#define CALL_LENGTH_SETTER(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
case QV4::Managed::Type_Qml##SequenceElementTypeName##List: asQml##SequenceElementTypeName##List()->lengthSetter(ctx); break;
switch (internalType()) {
FOREACH_QML_SEQUENCE_TYPE(CALL_LENGTH_SETTER)
default: break;
}
#undef CALL_LENGTH_SETTER
return QV4::Value::undefinedValue();
}
QT_END_NAMESPACE

View File

@ -34,3 +34,7 @@ SOURCES += \
$$PWD/qv4domerrors.cpp \
$$PWD/qv4sqlerrors.cpp \
$$PWD/qqmlbuiltinfunctions.cpp
JS_CLASS_SOURCES += \
$$PWD/qv8sequencewrapper_p_p.h