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:
parent
cdc3433ebf
commit
32f7d078b6
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -34,3 +34,7 @@ SOURCES += \
|
|||
$$PWD/qv4domerrors.cpp \
|
||||
$$PWD/qv4sqlerrors.cpp \
|
||||
$$PWD/qqmlbuiltinfunctions.cpp
|
||||
|
||||
JS_CLASS_SOURCES += \
|
||||
$$PWD/qv8sequencewrapper_p_p.h
|
||||
|
||||
|
|
Loading…
Reference in New Issue