Hi,

the index calculation is based on static code generation. I try to explain
the idea using an example:

Let's we have an QML object with 3 properties, each has their own expression:

anyObject {
  p1: exp1
  p2: exp2
  p3: exp3
}

We want to evaluate the expressions above using a scope chain, which
contains two objects: o1 and o2.

o1 have two members: x, y
o2 have one member: x
(thus both object have a member with the same name)

the scope chain would look like this: with (o1) with (o2) { expr }
The expr expression can access o2.x, and o1.y by the name of x and y.

// The following pseudo code generates the code sections for anyObject
string localVariables;
string setters;
set seenNames;
int index = 0;
for (obj IN each element of the scope chain from top-to-bottom)
    for (name IN attributes of obj)
        if (name NOT IN seenNames)
            // This way we could avoid name duplications
            localVariables += "var $name = $obj.$name;\n"
            setters += "function() { $name = $obj.$name; },\n"
            // Assign the index to the name (for future use)
            obj.name.index = index
            seenNames += name
            index++

string code;
// index contains the last member
for (property IN properties of anyObject)
    code += "function() {" + property.code "},\n"
    property.index = index;
    index++

Now we just use a plain JS concatenation;

"function generate()\n"
"{\n"
   localVariables
"    var array = [\n"
         setters
         code
"    ]\n"
"    return function() {\n"
"        for (var 0 = 1; var < arguments.length, ++i)\n"
"        array[arguments[i]]();\n"
"    }\n"
"}\n"

Calling this function would generate the necessary JS code for anyObject.

When a property is changed, we know its index from "obj.name.index"
generated during compilation. Thus we call the function by giving the
indicies as arguments. In this example, o2.x has index 0, o1.y has index
1, and if both are changed, we call f(0) and f(1) to update the local
variables. If o2.x affects exp2, which index is 3, we call f(3). Since we
can pass any number of arguments to the function, we could also call f(0,
1, 3). The for loop would process these arguments in order. This would
decrease the number of JS <-> C++ domain change overhead.

Does it make sense?

Regards,
Zoltan

> Probably we can :-)
>
> Why you iterate over the array? How you can get / compute indexes? I don't
> understand how "f(5, 6, 7, 1, 2, 3);" suppose to work, could you describe
> it
> more? Did you prototype it?
>
> Cheers,
>   Jędrek
>
> On Tuesday 24. May 2011 13.32.48 ext Zoltan Herczeg wrote:
>> Hi,
>>
>> as far as I remember the objects should be sealed, so no new properties
>> should be added / removed. Thus, we can generate their property list (by
>> iterating through the properties) during compile phase, can't we?
>>
>> Regards,
>> Zoltan
>>
>> > Hi,
>> >
>> >   In your solution you silently assume that 'b' has property 'y' and
>> 'a'
>> >
>> > has
>> > 'x', how you can now that?
>> > Cheers,
>> >
>> >   Jędrek
>> >
>> > On Monday 23. May 2011 09.04.40 ext Zoltan Herczeg wrote:
>> >> Hi,
>> >>
>> >> we could further improve this solution. As far as I remember, the QML
>> >> objects contain a list of attributes:
>> >>
>> >> object {
>> >>
>> >>    attrib 1: JS for attrib 1
>> >>    attrib 2: JS for attrib 2
>> >>    attrib 3: JS for attrib 3
>> >>    ....
>> >>
>> >> }
>> >>
>> >> We could generate something like the following for each object:
>> >>
>> >> function generate()
>> >> {
>> >>
>> >>      // listing the accessible members for the scope chain
>> >>      // and remember their indices for further use
>> >>      var x = a.x;
>> >>      var y = b.y;
>> >>      var array = [
>> >>
>> >>           // Funtion for calculating the attributes
>> >>           // we should also remember their indices
>> >>           function() { JS for attrib 1 }
>> >>           function() { JS for attrib 2 }
>> >>           function() { JS for attrib 3 }
>> >>           // Setters when something changed. We don't
>> >>           // actually need the new value, since we can simply read
>> it.
>> >>           function() { x = a.x }
>> >>           function() { y = b.y }
>> >>
>> >>      ];
>> >>      return function( /* list of which functions should be called. */
>> )
>> >>
>> >> {
>> >>
>> >>          // We could declare the array here, but this is faster
>> >>          // since we don't need to initialize it each time when
>> >>          // we call this function (local arrays are bad for JS perf).
>> >>          for (var 0 = 1; var < arguments.length, ++i)
>> >>
>> >>              array[i]();
>> >>
>> >>      }
>> >>
>> >> }
>> >>
>> >> var f = generate();
>> >> // We call the property setters first to update local variables
>> >> // Later we call the JS calculators for each attribute.
>> >> // We know their indices.
>> >> f(5, 6, 7, 1, 2, 3);
>> >>
>> >> As far as I know this soultion does not involve any lookups, easy to
>> >> generate, and engine independent. What do you think?
>> >>
>> >> Regards,
>> >> Zoltan
>> >>
>> >> > Hi,
>> >> >
>> >> > I played with the statement and turned out that it is really do a
>> full
>> >> > scope chain resolution which was unexpected. I thought this feature
>> is
>> >> > similar to joined objects:
>> >> >
>> >> > function f() {
>> >> >
>> >> >     var x = 3;
>> >> >     this.get = function() { return x; }
>> >> >     this.set = function(value) { x = value; }
>> >> >
>> >> > }
>> >> >
>> >> > o = new f();     // prints 3
>> >> > print(o.get());
>> >> > o.set(7);
>> >> > print(o.get());  // prints 7
>> >> >
>> >> > If we cannot find a better solution, we should "export" the whole
>> >>
>> >> scope
>> >>
>> >> > chain as local variables, and let the joined object mechanism
>> handle
>> >>
>> >> the
>> >>
>> >> > expression.
>> >> >
>> >> > var a = { x:5 }
>> >> > var b = { y:6 }
>> >> >
>> >> > // expression: return x + y
>> >> > // scope a, b
>> >> >
>> >> > var f = (function() {
>> >> >
>> >> >     // Generate the accessible member list
>> >> >     var x = a.x; // index 1
>> >> >     var y = b.y; // index 2
>> >> >     return [
>> >> >
>> >> >         // index 0 - the function
>> >> >         // the members are resolved
>> >> >         // by locals if possible
>> >> >         function() { return x + y; },
>> >> >         // same order as the vars above
>> >> >         function(value) { x = value; },
>> >> >         function(value) { y = value; },
>> >> >
>> >> >     ];
>> >> >
>> >> > })();
>> >> >
>> >> > // The following code does not involves lookups,
>> >> > // only joined objects
>> >> >
>> >> > print(f[0]()); // 5 + 6 = 11
>> >> > a.x = 8;
>> >> > print(f[0]()); // still 11, the change
>> >> >
>> >> >                // does not affects it anymore
>> >> >
>> >> > f[1](12);      // change 'x' to 12
>> >> > print(f[0]()); // 12 + 6 = 18
>> >> >
>> >> > This mechanism really does not involve any lookups, and we only
>> need
>> >>
>> >> to
>> >>
>> >> > statically record the property indicies for each function instance.
>> >>
>> >> When
>> >>
>> >> > the property is changed, just call the appropriate handler (by the
>> >>
>> >> index,
>> >>
>> >> > no lookup here either). We could wrap the array into another
>> function,
>> >>
>> >> so
>> >>
>> >> > f[1](12) could be turned to f(1, 12), which has a call API in v8.
>> '1'
>> >>
>> >> is
>> >>
>> >> > SMI, so changing the value does not involve memory allocation
>> (except
>> >>
>> >> for
>> >>
>> >> > the second argument sometimes).
>> >> >
>> >> > Regards,
>> >> > Zoltan
>> >> >
>> >> >> Hello guys,
>> >> >>
>> >> >> Zoltan, I think I'm misunderstanding something here. :-/  I have
>> some
>> >> >> questions that will help me understand better:
>> >> >>
>> >> >> 1) Thinking about the example that uses JS
>> >> >>
>> >> >>   globalObject->Set(String::New("context"), instance);
>> >> >>   globalObject->Set(String::New("newGlobal"), globalObject);
>> >> >>
>> >> >>   Local<Value> result = CompileRun(
>> >> >>
>> >> >>       "(function() {"
>> >> >>       "   with(context)"
>> >> >>       "     with(newGlobal)"
>> >> >>       "       return (function() { valueA + valueB + Math.sin(0)
>> });"
>> >> >>       "})();");
>> >> >>
>> >> >> imagine that during compilation 'newGlobal' has valueA. If I
>> >> >> understand correct, you are saying that when I execute the
>> function
>> >>
>> >> in
>> >>
>> >> >> result, it will not perform lookup?
>> >> >>
>> >> >> Imagine that we remove it from 'newGlobal' and add another into
>> >> >> 'context'. We need another lookup to know that valueA comes from
>> >> >> context and not newGlobal anymore, right? How does v8 knows that
>> it's
>> >> >> time to perform lookup again?
>> >> >>
>> >> >>
>> >> >> 2) The C++ API we have exists to create programatically the
>> construct
>> >> >> with with-statements in JavaScript. So I wasn't really expecting
>> that
>> >> >> we get too much difference from the both approaches. This is how
>> the
>> >> >> feature is implemented today.
>> >> >>
>> >> >>> As for cunclusion I would suggest the with statement, since if
>> you
>> >> >>> happens
>> >> >>> to change the JS engine again, it will still work.
>> >> >>
>> >> >> Yes, we get the semantics we want with this approach but the whole
>> >> >> point is that it is slow in current engines :-(  so we have two
>> ways:
>> >> >> (a) optimize the cases we use; (b) find another approach.
>> >> >>
>> >> >> For (a) there is the idea that engine could do better if knew that
>> >>
>> >> the
>> >>
>> >> >> objects in with() are "sealed". For (b) there's the approach of
>> >> >> changing the lookup code to match our semantics.
>> >> >>
>> >> >>
>> >> >> Cheers,
>> >> >>
>> >> >>
>> >> >> --
>> >> >> Caio Marcelo de Oliveira Filho
>> >> >> OpenBossa - INdT
>> >>
>> >> _______________________________________________
>> >> Qt-script mailing list
>> >> [email protected]
>> >> http://lists.qt.nokia.com/mailman/listinfo/qt-script
>> >
>> > _______________________________________________
>> > Qt-script mailing list
>> > [email protected]
>> > http://lists.qt.nokia.com/mailman/listinfo/qt-script
>


_______________________________________________
Qt-script mailing list
[email protected]
http://lists.qt.nokia.com/mailman/listinfo/qt-script

Reply via email to