1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
|
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\title Moving from QSA to Qt Script
\page porting-qsa.html
\ingroup porting
The purpose of this document is to map the differences between Qt
Script for Applications (QSA) and Qt Script, the ECMAScript compatible
engine supplied with Qt 4.3. This document is not supposed to be a
complete function by function porting guide, but will cover the most
obvious aspects.
First of all it is important to realize that Qt Script is only an
interpreter, it does not provide an editor, completion or script project
management, like QSA does. Qt Script however does provides almost full
compliance with the ECMAScript standard and performs significantly
better than the script engine provided by QSA.
\tableofcontents
\section1 The Scripting Language
The scripting language used in QSA, from here on referred to as QSA,
was derived from ECMAScript 3.0 and 4.0 and is a hybrid of these
standards. Most of the run-time logic, such as classes and scoping
rules, is based on the ECMAScript 4.0 proposal, while the library
implementation is based on the ECMAScript 3.0 standard.
Qt Script on the other hand is solely based on the ECMAScript 3.0
standard. Though the languages look identical at first glance,
there are a few differences that we'll cover in the sections below.
\section2 Classes vs. Objects and Properties
QSA implements classes and inheritance much in a familiar way to users
of other object oriented languages, like C++ and Java. However, the
ECMAScript 3.0 standard defines that everything is an object, and objects
can have named properties. For instance to create an point object with
the properties x and y one would write the following Qt Script code:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 0
The object \c point in this case is constructed as a plain object and
we assign two properties, \c x and \c y, to it with the values 12 and
35. The \c point object is assigned to the "Global Object" as the
named property \c{point}. The global object can be considered the
global namespace of the script engine. Similarly, global functions are
named properties of the global object; for example:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 1
An equivalent construction that illustrates that the function is a
property of the global object is the following assignment:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 2
Since functions are objects, they can be assigned to objects as
properties, becoming member functions:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 3
In the code above, we see the first subtle difference between
QSA and Qt Script. In QSA one would write the point class like this:
\snippet doc/src/snippets/code/doc_src_porting-qsa.qdoc 4
where in the \c manhattanLength() function we access \c x and \c y
directly because, when the function is called, the \c this object is
implicitly part of the current scope, as in C++. In Qt Script,
however, this is not the case, and we need to explicitly access
the \c x and \c y values via \c{this}.
All the code above runs with QSA except the assignment of a function
to \c{point.manhattanLength}, which we repeat here for clarity:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 5
This is because, in QSA, the value of \c this is decided based on
the location of the declaration of the function it is used in. In the
code above, the function is assigned to an object, but it is declared
in the global scope, hence there will be no valid \c this value.
In Qt Script, the value of \c this is decided at run-time,
hence you could have assigned the \c manhattanLength() function to any
object that had \c x and \c y values.
\section2 Constructors
In the code above, we use a rather awkward method for constructing
the objects, by first instantiating them, then manually
assigning properties to them. In QSA, the proper way to solve this
is to implement a constructor in the class:
\snippet doc/src/snippets/code/doc_src_porting-qsa.qdoc 6
The equivalent in Qt Script is to create a constructor function:
\snippet doc/src/snippets/code/doc_src_porting-qsa.qdoc 7
As we can see, the constructor is just a normal function. What is
special with is how we call it, namely prefixed with the \c new
keyword. This will create a new object and call the \c Car()
function with the newly created object as the \c this pointer.
So, in a sense, it is equivalent to:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 8
This is similar to the manhattenLength() example above. Again, the
main difference between QSA and Qt Script is that one has to
explicitly use the keyword \c this to access the members and that
instead of declaring the variable, \c regNumber, we just extend the
\c this object with the property.
\section2 Member Functions and Prototypes
As we saw above, one way of creating member functions of a Qt Script
object is to assign the member function to the object as a property
and use the \c this object inside the functions. So, if we add a
\c toString function to the \c Car class
\snippet doc/src/snippets/code/doc_src_porting-qsa.qdoc 9
one could write this in Qt Script as:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 10
In QSA, the member functions were part of the class declaration,
and were therefore shared between all instances of a given class.
In Qt Script, each instance has a instance member for each function.
This means that more memory is used when multiple instances are used.
Qt Script uses prototypes to remedy this.
The basic prototype-based inheritance mechanism works as follows.
Each Qt Script object has an internal link to another object, its
prototype. When a property is looked up in an object, and the object
itself does not have the property, the interpreter searches for the
property in the prototype object instead; if the prototype has the
property then that property is returned. If the prototype object does
not have the property, the interpreter searches for the property in
the prototype of the prototype object, and so on.
This chain of objects constitutes a prototype chain. The chain of
prototype objects is followed until the property is found or the end
of the chain is reached.
To make the \c toString() function part of the prototype, we write
code like this:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 11
Here, we made the \c toString() function part of the prototype so
that, when we call \c{car.toString()} it will be resolved via the
internal prototype object of the car object. Note, however, that the
\c this object is still the original object that the function was
called on, namely \c{car}.
\section2 Inheritance
Now that we've seen how to use prototypes to create a "class" members
in Qt Script, let's see how we can use prototypes to create
polymorphism. In QSA you would write
\snippet doc/src/snippets/code/doc_src_porting-qsa.qdoc 12
With Qt Script, we acheive the same effect by creating a prototype
chain. The default prototype of an object is a plain \c Object
without any special members, but it is possible to replace this
object with another prototype object.
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 13
In the code above, we have a constructor, \c{GasolineCar}, which
calls the "base class" implementation of the constructor to
initialize the \c this object with the property \c{regNumber},
based on the values passed in the constructor. The interesting line
in this case is the line after the constructor where we change the
default prototype for \c GasolineCar to be an instance of type
\c{Car}. This means that all members available in a \c Car object
are now available in all \c GasolineCar objects. In the last line,
we replace the \c toString() function in the prototype with our own,
thus overriding the \c toString() for all instances of
\c{GasolineCar}.
\section2 Static Members
QSA allowed users to declare static members in classes, and these
could be accessed both through instances of the class and through
the class itself. For example, the following variable is accessed
through the \c Car class:
\snippet doc/src/snippets/code/doc_src_porting-qsa.qdoc 14
The equivalent in Qt Script is to assign variables that should appear
as static members as properties of the constructor function. For
example:
\snippet doc/src/snippets/code/doc_src_porting-qsa.js 15
Note that in QSA, static member variables were also accessible in
instances of the given class. In Qt Script, with the approach
illustrated above, the variable is a member of the constructor
object only, and thus only accessible through \c{Car.globalCount}.
\section1 The Built-in Functions and Library
The built-in functions in QSA are based on those defined in the
ECMAScript 3.0 standard, the same standard used for Qt Script, but
QSA adds some extensions to this, specifically for the \c String
and \c RegExp types. QSA also lacked some functions from the
standard, most notably the \c Date type. Below we list all the
differences. All changes made to Qt Script are to increase
compliance with ECMAScript 3.0.
\table
\header \o QSA Function \o Notes about Equivalent Qt Script Functions
\row \o eval()
\o The eval function in QSA opened a new scope for code being
executed in the eval function, so locally declared variables were not
accessible outside. In Qt Script, the eval() function shares the
current scope, making locally declared variables accessible outside
the eval() call.
\row \o debug()
\o This function is not available in Qt Script. Use print() instead.
\row \o connect()
\o QSA had closures, meaning that a member function
reference implicitly contained its \c this object. Qt Script does not
support this. See the Qt Script documentation for details on using the
connect function.
\row \o String.arg()
\o This function is not available in Qt Script. Use replace() or concat() instead.
\row \o String.argDec()
\o This function is not available in Qt Script. Use replace() or concat() instead.
\row \o String.argInt()
\o This function is not available in Qt Script. Use replace() or concat() instead.
\row \o String.argStr()
\o This function is not available in Qt Script. Use replace() or concat() instead.
\row \o String.endsWith()
\o This function is not available in Qt Script. Use lastIndexOf() instead.
\row \o String.find()
\o This function is not available in Qt Script. Use indexOf() instead.
\row \o String.findRev()
\o This function is not available in Qt Script. Use lastIndexOf() and length instead.
\row \o String.isEmpty()
\o This function is not available in Qt Script. Use length == 0 instead.
\row \o String.left()
\o This function is not available in Qt Script. Use substring() instead.
\row \o String.lower()
\o This function is not available in Qt Script. Use toLowerCase() instead.
\row \o String.mid()
\o This function is not available in Qt Script. Use substring() instead.
\row \o String.right()
\o This function is not available in Qt Script. Use substring() instead.
\row \o String.searchRev()
\o This function is not available in Qt Script. Use search() / match() instead.
\row \o String.startsWith()
\o This function is not available in Qt Script. Use indexOf() == 0 instead.
\row \o String.upper()
\o This function is not available in Qt Script. Use toUpperCase() instead.
\row \o RegExp.valid
\o This property is not available in Qt Script because it is not
required; a \c SyntaxError exception is thrown for bad \c RegExp objects.
\row \o RegExp.empty
\o This property is not available in Qt Script. Use \c{toString().length == 0} instead.
\row \o RegExp.matchedLength
\o This property is not available in Qt Script. RegExp.exec() returns an
array whose size is the matched length.
\row \o RegExp.capturedTexts
\o This property is not available in Qt Script. RegExp.exec() returns an
array of captured texts.
\row \o RegExp.search()
\o This function is not available in Qt Script. Use RegExp.exec() instead.
\row \o RegExp.searchRev()
\o This function is not available in Qt Script. Use RegExp.exec() or
String.search()/match() instead.
\row \o RegExp.exactMatch()
\o This function is not available in Qt Script. Use RegExp.exec() instead.
\row \o RegExp.pos()
\o This function is not available in Qt Script. Use String.match() instead.
\row \o RegExp.cap()
\o This function is not available in Qt Script. RegExp.exec() returns an
array of captured texts.
\endtable
QSA also defined some internal Qt API which is not present in Qt
Script. The types provided by QSA which are not provided by Qt Script are:
\list
\o Rect
\o Point
\o Size
\o Color
\o Palette
\o ColorGroup
\o Font
\o Pixmap
\o ByteArray
\endlist
\section1 The C++ API of QSA vs Qt Script
QSA is more than just a scripting engine. It provides project
management, an editor with completion and a minimalistic IDE to edit
scriptable projects. Qt Script on the other hand is just a scripting
engine. This means that equivalents to the classes \c QSEditor,
\c QSScript, \c QSProject and \c QSWorkbench do not exist in Qt Script.
QSA also provides some extension APIs through the \c QSUtilFactory and
\c QSInputDialogFactory. There is also no equivalent to these classes
in the Qt Script API.
\section2 Making QObjects Accessible from Scripts
There are two different ways of making \l{QObject}s accessible from
scripts in QSA. The first method is via the
\c QSInterpreter::addTransientObject() and \c QSProject::addObject()
functions. In this case objects are added to the global namespace of
the interpreter using their object names as the names of the
variables.
\snippet doc/src/snippets/code/doc_src_porting-qsa.cpp 16
The code above adds the button to the global namespace under the name
"button". One obvious limitation here is that there is potential for
either unnamed \l{QObject}s or objects whose names conflict. Qt Script
provides a more flexible way of adding QObjects to the scripting
environment.
\snippet doc/src/snippets/code/doc_src_porting-qsa.cpp 17
In the code above we create a QPushButton and wrap it in a script
value using the function, QScriptEngine::newQObject(). This gives us
a script value that we put into the global object using the name
"button". The concept of objects and properties discussed above is
quite visible here in the public C++ API as well. We have no
dependency on the object's name and we can also resolve name conflicts
more gracefully. Here, we operate directly on QScriptValue objects.
This is the actual object that is being passed around inside
the script engine, so we actually have low-level access to the
internal script data structures, far beyond that which is possible
in QSA. Properties, signals and slots of the QObject are accessible
to the scripter in Qt Script, just like in QSA.
The other way to expose \l{QObject}s in QSA was to create a
\c QSObjectFactory that made it possible to instantiate QObjects from
scripts.
Below is listed some code from the filter example in the QSA
package.
\snippet doc/src/snippets/code/doc_src_porting-qsa.cpp 18
The equivalent in Qt Script is written in much the same way as
constructors are written in scripts. We register a callback C++
function under the name "ImageSource" in the global namespace and
return the QObject from this function:
\snippet doc/src/snippets/code/doc_src_porting-qsa.cpp 19
In the Qt Script case we use the same approach that we use to expose
a QObject, namely via QScriptEngine::newQObject(). This function also
has the benefit that it is possible to specify if the QObject should
expose properties and slots of its base class. It is also possible to
specify custom ownership rules.
The reader might question why we don't add the constructor function
directly into the namespace, but create a meta-object script value for
it in addition. The plain function would certainly be good enough,
but by creating a QMetaObject based constructor we get the enums on
QPushButton for free in the QPushButton function object. Exposing
enums in QSA is rather painful in comparison.
If we want to add more "static" data to the QPushButton type in Qt
Script, we're free to add properties, similar to how we did for
the script. It is also possible to add custom functions to a Qt Script
QPushButton instance by setting more properties on it, such as making
the \l{QPushButton::}{setText()} C++ function available. It is also
possible to acheive this by installing a custom prototype, and be
memory efficient, as discussed in the script example above.
\section2 Accessing Non-QObjects
In QSA, it was possible to expose non-QObjects to QSA by wrapping them
in a QObject and using either \c QSWrapperFactory or \c QSObjectFactory
to expose them. Deciding when to use each of these classes could be
confusing, as one was used for script based construction and the other
for wrapping function parameters and return values, but in essence they
did exactly the same thing.
In Qt Script, providing access to QObjects and non-QObjects is done in
the same way as shown above, by creating a constructor function, and
by adding properties or a custom prototype to the constructed object.
\section2 Data Mapping
QSA supported a hardcoded set of type mappings which covered most
of the QVariant types, QObjects and primitives. For more complex type
signatures, such as the template-based tool classes, it had rather
limited support. Qt Script is significantly better at type mapping
and will convert lists of template types into arrays of the
appropriate types, given that all the types are declared to the
meta-type system.
*/
|