Port the of qml locale getters to V4 accessors
authorSimon Hausmann <simon.hausmann@digia.com>
Thu, 9 May 2013 12:36:32 +0000 (14:36 +0200)
committerLars Knoll <lars.knoll@digia.com>
Thu, 9 May 2013 18:38:20 +0000 (20:38 +0200)
Added support for getters/setters to the class generator
as well as a simple help template for calling the member
function getters.

Change-Id: I3bb287a53d9f249837fa2c513d4886873476ad95
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/qml/qqmllocale.cpp
src/qml/qml/v4/qv4functionobject_p.h
src/qml/qml/v4/qv4object_p.h
src/qml/qml/v4/v4classgen

index 345a0d7..75c9784 100644 (file)
@@ -80,6 +80,25 @@ public:
     QV4::Value method_dayName(QV4::SimpleCallContext *ctx);
     QV4::Value method_standaloneDayName(QV4::SimpleCallContext *ctx);
 
+    QV4::Value method_get_firstDayOfWeek(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_measurementSystem(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_textDirection(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_weekDays(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_uiLanguages(QV4::SimpleCallContext *ctx);
+
+    QV4::Value method_get_name(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_nativeLanguageName(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_nativeCountryName(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_decimalPoint(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_groupSeparator(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_percent(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_zeroDigit(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_negativeSign(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_positiveSign(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_exponential(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_amText(QV4::SimpleCallContext *ctx);
+    QV4::Value method_get_pmText(QV4::SimpleCallContext *ctx);
+
 private:
     static void destroy(Managed *that)
     {
@@ -96,16 +115,13 @@ DEFINE_MANAGED_VTABLE(QQmlLocaleData);
     if (!r) \
         V4THROW_ERROR("Not a valid Locale object")
 
-#define V8_GET_LOCALE_DATA_RESOURCE(OBJECT) \
-    GET_LOCALE_DATA_RESOURCE((OBJECT).As<v8::Value>()->v4Value())
-
-static bool isLocaleObject(v8::Handle<v8::Value> val)
+static bool isLocaleObject(const QV4::Value &val)
 {
-    if (!val->IsObject())
+    QV4::Object *obj = val.asObject();
+    if (!obj)
         return false;
 
-    v8::Handle<v8::Object> localeObj = val->ToObject();
-    return localeObj->Has(v8::String::New("nativeLanguageName")); //XXX detect locale object properly
+    return obj->asQmlLocale();
 }
 
 //--------------
@@ -483,55 +499,51 @@ QV4::Value QQmlNumberExtension::fromLocaleString(QV4::SimpleCallContext *ctx)
 //--------------
 // Locale object
 
-static v8::Handle<v8::Value> locale_get_firstDayOfWeek(v8::Handle<v8::String>, const v8::AccessorInfo &info)
+QV4::Value QQmlLocaleData::method_get_firstDayOfWeek(QV4::SimpleCallContext*)
 {
-    V8_GET_LOCALE_DATA_RESOURCE(info.This());
-    int fdow = int(r->locale.firstDayOfWeek());
+    int fdow = int(locale.firstDayOfWeek());
     if (fdow == 7)
         fdow = 0; // Qt::Sunday = 7, but Sunday is 0 in JS Date
-    return v8::Integer::New(fdow);
+    return QV4::Value::fromInt32(fdow);
 }
 
-static v8::Handle<v8::Value> locale_get_measurementSystem(v8::Handle<v8::String>, const v8::AccessorInfo &info)
+QV4::Value QQmlLocaleData::method_get_measurementSystem(QV4::SimpleCallContext*)
 {
-    V8_GET_LOCALE_DATA_RESOURCE(info.This());
-    return v8::Integer::New(r->locale.measurementSystem());
+    return QV4::Value::fromInt32(locale.measurementSystem());
 }
 
-static v8::Handle<v8::Value> locale_get_textDirection(v8::Handle<v8::String>, const v8::AccessorInfo &info)
+QV4::Value QQmlLocaleData::method_get_textDirection(QV4::SimpleCallContext*)
 {
-    V8_GET_LOCALE_DATA_RESOURCE(info.This());
-    return v8::Integer::New(r->locale.textDirection());
+    return QV4::Value::fromInt32(locale.textDirection());
 }
 
-static v8::Handle<v8::Value> locale_get_weekDays(v8::Handle<v8::String>, const v8::AccessorInfo &info)
+QV4::Value QQmlLocaleData::method_get_weekDays(QV4::SimpleCallContext* ctx)
 {
-    V8_GET_LOCALE_DATA_RESOURCE(info.This());
-
-    QList<Qt::DayOfWeek> days = r->locale.weekdays();
+    QList<Qt::DayOfWeek> days = locale.weekdays();
 
-    v8::Handle<v8::Array> result = v8::Array::New(days.size());
+    QV4::ArrayObject *result = ctx->engine->newArrayObject();
+    result->arrayReserve(days.size());
     for (int i = 0; i < days.size(); ++i) {
         int day = days.at(i);
         if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday)
             day = 0;
-        result->Set(i, v8::Integer::New(day));
+        result->arrayData[i].value = QV4::Value::fromInt32(day);
     }
+    result->setArrayLengthUnchecked(days.size());
 
-    return result;
+    return QV4::Value::fromObject(result);
 }
 
-static v8::Handle<v8::Value> locale_get_uiLanguages(v8::Handle<v8::String>, const v8::AccessorInfo &info)
+QV4::Value QQmlLocaleData::method_get_uiLanguages(QV4::SimpleCallContext *ctx)
 {
-    V8_GET_LOCALE_DATA_RESOURCE(info.This());
-
-    QStringList langs = r->locale.uiLanguages();
-    v8::Handle<v8::Array> result = v8::Array::New(langs.size());
-    for (int i = 0; i < langs.size(); ++i) {
-        result->Set(i, r->engine->toString(langs.at(i)));
-    }
-
-    return result;
+    QStringList langs = locale.uiLanguages();
+    QV4::ArrayObject *result = ctx->engine->newArrayObject();
+    result->arrayReserve(langs.size());
+    for (int i = 0; i < langs.size(); ++i)
+        result->arrayData[i].value = QV4::Value::fromString(ctx, langs.at(i));
+    result->setArrayLengthUnchecked(langs.size());
+
+    return QV4::Value::fromObject(result);
 }
 
 QV4::Value QQmlLocaleData::method_currencySymbol(QV4::SimpleCallContext *ctx)
@@ -618,16 +630,11 @@ LOCALE_FORMATTED_MONTHNAME(standaloneMonthName)
 LOCALE_FORMATTED_DAYNAME(dayName)
 LOCALE_FORMATTED_DAYNAME(standaloneDayName)
 
-#define LOCALE_STRING_PROPERTY(VARIABLE) static v8::Handle<v8::Value> locale_get_ ## VARIABLE (v8::Handle<v8::String>, const v8::AccessorInfo &info) \
+#define LOCALE_STRING_PROPERTY(VARIABLE) QV4::Value QQmlLocaleData::method_get_ ## VARIABLE (QV4::SimpleCallContext* ctx) \
 { \
-    V8_GET_LOCALE_DATA_RESOURCE(info.This()); \
-    return r->engine->toString(r->locale. VARIABLE());\
+    return QV4::Value::fromString(ctx, locale. VARIABLE());\
 }
 
-#define LOCALE_REGISTER_STRING_ACCESSOR(FT, VARIABLE) \
-    FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #VARIABLE ), locale_get_ ## VARIABLE )
-
-
 LOCALE_STRING_PROPERTY(name)
 LOCALE_STRING_PROPERTY(nativeLanguageName)
 LOCALE_STRING_PROPERTY(nativeCountryName)
@@ -648,39 +655,13 @@ public:
     ~QV8LocaleDataDeletable();
 
     QV4::PersistentValue prototype;
-    QV4::PersistentValue v4Prototype;
 };
 
 QV8LocaleDataDeletable::QV8LocaleDataDeletable(QV8Engine *engine)
 {
-    v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
-    ft->InstanceTemplate()->SetHasExternalResource(true);
-
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, name);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, nativeLanguageName);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, nativeCountryName);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, decimalPoint);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, groupSeparator);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, percent);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, zeroDigit);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, negativeSign);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, positiveSign);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, exponential);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, amText);
-    LOCALE_REGISTER_STRING_ACCESSOR(ft, pmText);
-
-    ft->PrototypeTemplate()->SetAccessor(v8::String::New("firstDayOfWeek"), locale_get_firstDayOfWeek);
-    ft->PrototypeTemplate()->SetAccessor(v8::String::New("weekDays"), locale_get_weekDays);
-    ft->PrototypeTemplate()->SetAccessor(v8::String::New("measurementSystem"), locale_get_measurementSystem);
-    ft->PrototypeTemplate()->SetAccessor(v8::String::New("textDirection"), locale_get_textDirection);
-    ft->PrototypeTemplate()->SetAccessor(v8::String::New("uiLanguages"), locale_get_uiLanguages);
-
-    prototype = QV4::Value::fromObject(ft->GetFunction()->NewInstance()->v4Value().asObject()->prototype);
-
     QV4::ExecutionEngine *eng = QV8Engine::getV4(engine);
-    v4Prototype = QV4::Value::fromObject(eng->newObject());
-    QQmlLocaleData::initClass(eng, v4Prototype.value());
-    prototype.value().asObject()->prototype->prototype = v4Prototype.value().asObject();
+    prototype = QV4::Value::fromObject(eng->newObject());
+    QQmlLocaleData::initClass(eng, prototype.value());
 }
 
 QV8LocaleDataDeletable::~QV8LocaleDataDeletable()
index 940f9bc..5c62975 100644 (file)
@@ -192,6 +192,54 @@ struct BoundFunction: FunctionObject {
     static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value);
 };
 
+template <typename T, int ManagedType>
+class MemberAccessorGetter : public FunctionObject
+{
+public:
+    typedef Value (T::*GetterFunction)(QV4::SimpleCallContext *ctx);
+
+    MemberAccessorGetter(ExecutionContext *scope, GetterFunction getter)
+        : FunctionObject(scope)
+    {
+        this->vtbl = &static_vtbl;
+        this->getter = getter;
+    }
+
+    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);
+
+        Object *thisO = thisObject.asObject();
+        if (!thisO || thisO->internalType() != ManagedType)
+            context->throwTypeError();
+
+        T *o = reinterpret_cast<T *>(thisO);
+
+        QV4::SimpleCallContext ctx;
+        ctx.type = ExecutionContext::Type_SimpleCallContext;
+        ctx.strictMode = true;
+        ctx.marked = false;
+        ctx.thisObject = thisObject;
+        ctx.engine = context->engine;
+        ctx.arguments = args;
+        ctx.argumentCount = argc;
+        context->engine->pushContext(&ctx);
+
+        QV4::Value result = QV4::Value::undefinedValue();
+        try {
+            result = (o->* getter->getter)(&ctx);
+        } catch (QV4::Exception &ex) {
+            ex.partiallyUnwindContext(context);
+            throw;
+        }
+        context->engine->popContext();
+        return result;
+    }
+protected:
+    GetterFunction getter;
+    static const ManagedVTable static_vtbl;
+};
+
 }
 
 QT_END_NAMESPACE
index 17f187a..5b79a25 100644 (file)
@@ -427,14 +427,13 @@ inline void Object::setArrayLengthUnchecked(uint l)
     }
 }
 
-
-}
-
 // Macros used for JS class annotation
 #define QV4_JS_CLASS(ClassName) ClassName
 #define QV4_ANNOTATE(...)
 #define QV4_ARGC(...)
 
+}
+
 QT_END_NAMESPACE
 
 #endif // QMLJS_OBJECTS_H
index f725cfc..b67d3e7 100755 (executable)
@@ -13,6 +13,21 @@ class ParsedMethod():
     def isClassMethod(self):
         return self.static
 
+    def isGetter(self):
+        return self.name.startswith("get_")
+
+    def isSetter(self):
+        return self.name.startswith("set_")
+
+    def isAccessor(self):
+        return self.isGetter() or self.isSetter()
+
+    def nameWithoutGetterSetterPrefix(self):
+        return self.name[4:]
+
+    def fullMethodName(self):
+        return self.methodPrefix + "_" + self.name
+
     def generateBinding(self, out, objectPrefix = ""):
         length = 0
 
@@ -43,8 +58,9 @@ class ParsedClass():
     def __init__(self, name):
         self.name = name
         self.options = {}
-        self.methods = []
-        self.ctor_methods = []
+        self.methods = {}
+        self.ctor_methods = {}
+        self.accessors = {}
 
     def needsConstructor(self):
         return len(self.ctor_methods) > 0
@@ -59,6 +75,27 @@ class ParsedClass():
             return self.options[name]
         return None
 
+class Accessor():
+    def __init__(self, name):
+        self.name = name
+        self.getter = None
+        self.setter = None
+
+    def generateBinding(self, out, parsedClass, obj):
+        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()))
+        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("        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");
+        out.write("    }\n");
+
 def parseOptions(options):
     options = options.split(" ")
     result = {}
@@ -106,13 +143,21 @@ def parse(lines):
         methodMatch = methodPattern.match(line)
         if methodMatch:
             method = parseMethod(line, methodMatch, "method")
-            currentClass.methods.append(method)
+            if method.isAccessor():
+                name = method.nameWithoutGetterSetterPrefix()
+                acc = currentClass.accessors.setdefault(name, Accessor(name))
+                if method.isSetter():
+                    acc.setter = method
+                else:
+                    acc.getter = method
+            else:
+                currentClass.methods[method.name] = method
             continue
 
         ctorMethodMatch = ctorMethodPattern.match(line)
         if ctorMethodMatch:
             method = parseMethod(line, ctorMethodMatch, "ctor_method")
-            currentClass.ctor_methods.append(method)
+            currentClass.ctor_methods[method.name] = method
             continue
 
         annotateMatch = annotatePattern.match(line)
@@ -139,9 +184,7 @@ def generateBinding(out, parsedClass, vtableEntries):
         out.write("};\n\n");
         out.write("const QV4::ManagedVTable %s::static_vtbl = {\n" % ctorClass);
 
-        ctorOverrides = set()
-        for method in parsedClass.ctor_methods:
-            ctorOverrides.add(method.name)
+        ctorOverrides = set(parsedClass.ctor_methods.keys())
 
         for method in vtableEntries:
             entry = "FunctionObject::" + method
@@ -156,11 +199,18 @@ def generateBinding(out, parsedClass, vtableEntries):
         out.write("    return new (scope->engine->memoryManager) %s(scope);\n" % ctorClass)
         out.write("}\n\n")
 
-    for method in parsedClass.methods:
+    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))
+        out.write("template<>\n")
+        out.write("DEFINE_MANAGED_VTABLE(%s);" % typename)
+        out.write("\n\n")
+
+    for method in parsedClass.methods.values():
         if not method.isClassMethod():
             method.generateMemberFunctionWrapper(out, parsedClass)
 
-    for method in parsedClass.ctor_methods:
+    for method in parsedClass.ctor_methods.values():
         if not method.isClassMethod():
             method.generateMemberFunctionWrapper(out, parsedClass)
 
@@ -187,7 +237,7 @@ def generateBinding(out, parsedClass, vtableEntries):
         out.write("    %sdefineReadonlyProperty(engine->id_length, QV4::Value::fromInt32(%s));\n" % (ctor, ctorLength))
         out.write("\n")
 
-        for method in parsedClass.ctor_methods:
+        for method in parsedClass.ctor_methods.values():
             if method.name == "construct" or method.name == "call":
                 continue
             method.generateBinding(out, ctor);
@@ -197,9 +247,12 @@ def generateBinding(out, parsedClass, vtableEntries):
 
         out.write("\n")
 
-    for method in parsedClass.methods:
+    for method in parsedClass.methods.values():
         method.generateBinding(out, obj)
 
+    for accessor in parsedClass.accessors.values():
+        accessor.generateBinding(out, parsedClass, obj)
+
     out.write("}\n")
 
 def extractManagedVTableLayout(basePath):