1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45 #include <qscriptengine.h>
46 #include <qscriptengineagent.h>
47 #include <qscriptprogram.h>
48 #include <qscriptvalueiterator.h>
49 #include <qgraphicsitem.h>
50 #include <qstandarditemmodel.h>
51 #include <QtCore/qnumeric.h>
52 #include <stdlib.h>
53
54 #include <QtScript/private/qscriptdeclarativeclass_p.h>
55
56 Q_DECLARE_METATYPE(QList<int>)
Q_DECLARE_METATYPE(QObjectList)57 Q_DECLARE_METATYPE(QObjectList)
58 Q_DECLARE_METATYPE(QScriptProgram)
59
60 //TESTED_CLASS=
61 //TESTED_FILES=
62
63 #if defined(Q_OS_SYMBIAN)
64 # define STRINGIFY(x) #x
65 # define TOSTRING(x) STRINGIFY(x)
66 # define SRCDIR "C:/Private/" TOSTRING(SYMBIAN_SRCDIR_UID)
67 #endif
68
69 // The JavaScriptCore GC marks the C stack. To try to ensure that there is
70 // no JSObject* left in stack memory by the compiler, we call this function
71 // to zap some bytes of memory before calling collectGarbage().
72 static void zapSomeStack()
73 {
74 char buf[4096];
75 memset(buf, 0, sizeof(buf));
76 }
77
collectGarbage_helper(QScriptEngine & eng)78 static void collectGarbage_helper(QScriptEngine &eng)
79 {
80 zapSomeStack();
81 eng.collectGarbage();
82 }
83
84 class tst_QScriptEngine : public QObject
85 {
86 Q_OBJECT
87
88 public:
89 tst_QScriptEngine();
90 virtual ~tst_QScriptEngine();
91
92 private slots:
93 void constructWithParent();
94 void currentContext();
95 void pushPopContext();
96 void getSetDefaultPrototype_int();
97 void getSetDefaultPrototype_customType();
98 void newFunction();
99 void newFunctionWithArg();
100 void newFunctionWithProto();
101 void newObject();
102 void newArray();
103 void newArray_HooliganTask218092();
104 void newArray_HooliganTask233836();
105 void newVariant();
106 void newVariant_defaultPrototype();
107 void newVariant_promoteObject();
108 void newVariant_replaceValue();
109 void newVariant_valueOfToString();
110 void newVariant_promoteNonObject();
111 void newVariant_promoteNonQScriptObject();
112 void newRegExp();
113 void jsRegExp();
114 void newDate();
115 void jsParseDate();
116 void newQObject();
117 void newQObject_ownership();
118 void newQObject_promoteObject();
119 void newQObject_sameQObject();
120 void newQObject_defaultPrototype();
121 void newQObject_promoteNonObject();
122 void newQObject_promoteNonQScriptObject();
123 void newQMetaObject();
124 void newActivationObject();
125 void getSetGlobalObject();
126 void globalObjectProperties();
127 void globalObjectProperties_enumerate();
128 void createGlobalObjectProperty();
129 void globalObjectGetterSetterProperty();
130 void customGlobalObjectWithPrototype();
131 void globalObjectWithCustomPrototype();
132 void builtinFunctionNames_data();
133 void builtinFunctionNames();
134 void checkSyntax_data();
135 void checkSyntax();
136 void canEvaluate_data();
137 void canEvaluate();
138 void evaluate_data();
139 void evaluate();
140 void nestedEvaluate();
141 void uncaughtException();
142 void errorMessage_QT679();
143 void valueConversion_basic();
144 void valueConversion_customType();
145 void valueConversion_sequence();
146 void valueConversion_QVariant();
147 void valueConversion_hooliganTask248802();
148 void valueConversion_basic2();
149 void valueConversion_dateTime();
150 void valueConversion_regExp();
151 void qScriptValueFromValue_noEngine();
152 void importExtension();
153 void infiniteRecursion();
154 void castWithPrototypeChain();
155 void castWithMultipleInheritance();
156 void collectGarbage();
157 void reportAdditionalMemoryCost();
158 void gcWithNestedDataStructure();
159 void processEventsWhileRunning();
160 void throwErrorFromProcessEvents_data();
161 void throwErrorFromProcessEvents();
162 void disableProcessEventsInterval();
163 void stacktrace();
164 void numberParsing_data();
165 void numberParsing();
166 void automaticSemicolonInsertion();
167 void abortEvaluation_notEvaluating();
168 void abortEvaluation_data();
169 void abortEvaluation();
170 void abortEvaluation_tryCatch();
171 void abortEvaluation_fromNative();
172 void abortEvaluation_QTBUG9433();
173 void isEvaluating_notEvaluating();
174 void isEvaluating_fromNative();
175 void isEvaluating_fromEvent();
176 void printFunctionWithCustomHandler();
177 void printThrowsException();
178 void errorConstructors();
179 void argumentsProperty_globalContext();
180 void argumentsProperty_JS();
181 void argumentsProperty_evaluateInNativeFunction();
182 void jsNumberClass();
183 void jsForInStatement_simple();
184 void jsForInStatement_prototypeProperties();
185 void jsForInStatement_mutateWhileIterating();
186 void jsForInStatement_arrays();
187 void jsForInStatement_nullAndUndefined();
188 void jsFunctionDeclarationAsStatement();
189 void stringObjects();
190 void jsStringPrototypeReplaceBugs();
191 void getterSetterThisObject_global();
192 void getterSetterThisObject_plain();
193 void getterSetterThisObject_prototypeChain();
194 void getterSetterThisObject_activation();
195 void jsContinueInSwitch();
196 void jsShadowReadOnlyPrototypeProperty();
197 void toObject();
198 void jsReservedWords_data();
199 void jsReservedWords();
200 void jsFutureReservedWords_data();
201 void jsFutureReservedWords();
202 void jsThrowInsideWithStatement();
203 void getSetAgent_ownership();
204 void getSetAgent_deleteAgent();
205 void getSetAgent_differentEngine();
206 void reentrancy_stringHandles();
207 void reentrancy_processEventsInterval();
208 void reentrancy_typeConversion();
209 void reentrancy_globalObjectProperties();
210 void reentrancy_Array();
211 void reentrancy_objectCreation();
212 void jsIncDecNonObjectProperty();
213 void installTranslatorFunctions_data();
214 void installTranslatorFunctions();
215 void translateScript_data();
216 void translateScript();
217 void translateScript_crossScript();
218 void translateScript_callQsTrFromNative();
219 void translateScript_trNoOp();
220 void translateScript_callQsTrFromCpp();
221 void translateWithInvalidArgs_data();
222 void translateWithInvalidArgs();
223 void translationContext_data();
224 void translationContext();
225 void translateScriptIdBased();
226 void translateScriptUnicode_data();
227 void translateScriptUnicode();
228 void translateScriptUnicodeIdBased_data();
229 void translateScriptUnicodeIdBased();
230 void translateFromBuiltinCallback();
231 void functionScopes();
232 void nativeFunctionScopes();
233 void evaluateProgram();
234 void evaluateProgram_customScope();
235 void evaluateProgram_closure();
236 void evaluateProgram_executeLater();
237 void evaluateProgram_multipleEngines();
238 void evaluateProgram_empty();
239 void collectGarbageAfterConnect();
240 void collectGarbageAfterNativeArguments();
241 void promoteThisObjectToQObjectInConstructor();
242 void scriptValueFromQMetaObject();
243
244 void qRegExpInport_data();
245 void qRegExpInport();
246 void reentrency();
247 void newFixedStaticScopeObject();
248 void newGrowingStaticScopeObject();
249 void dateRoundtripJSQtJS();
250 void dateRoundtripQtJSQt();
251 void dateConversionJSQt();
252 void dateConversionQtJS();
253 void stringListFromArrayWithEmptyElement();
254 };
255
tst_QScriptEngine()256 tst_QScriptEngine::tst_QScriptEngine()
257 {
258 }
259
~tst_QScriptEngine()260 tst_QScriptEngine::~tst_QScriptEngine()
261 {
262 }
263
constructWithParent()264 void tst_QScriptEngine::constructWithParent()
265 {
266 QPointer<QScriptEngine> ptr;
267 {
268 QObject obj;
269 QScriptEngine *engine = new QScriptEngine(&obj);
270 ptr = engine;
271 }
272 QVERIFY(ptr == 0);
273 }
274
currentContext()275 void tst_QScriptEngine::currentContext()
276 {
277 QScriptEngine eng;
278 QScriptContext *globalCtx = eng.currentContext();
279 QVERIFY(globalCtx != 0);
280 QVERIFY(globalCtx->parentContext() == 0);
281 QCOMPARE(globalCtx->engine(), &eng);
282 QCOMPARE(globalCtx->argumentCount(), 0);
283 QCOMPARE(globalCtx->backtrace().size(), 1);
284 QVERIFY(!globalCtx->isCalledAsConstructor());
285 QVERIFY(!globalCtx->callee().isValid());
286 QCOMPARE(globalCtx->state(), QScriptContext::NormalState);
287 QVERIFY(globalCtx->thisObject().strictlyEquals(eng.globalObject()));
288 QVERIFY(globalCtx->activationObject().strictlyEquals(eng.globalObject()));
289 QVERIFY(globalCtx->argumentsObject().isObject());
290 }
291
pushPopContext()292 void tst_QScriptEngine::pushPopContext()
293 {
294 QScriptEngine eng;
295 QScriptContext *globalCtx = eng.currentContext();
296 QScriptContext *ctx = eng.pushContext();
297 QVERIFY(ctx != 0);
298 QCOMPARE(ctx->parentContext(), globalCtx);
299 QVERIFY(!ctx->isCalledAsConstructor());
300 QVERIFY(!ctx->callee().isValid());
301 QVERIFY(ctx->thisObject().strictlyEquals(eng.globalObject()));
302 QCOMPARE(ctx->argumentCount(), 0);
303 QCOMPARE(ctx->backtrace().size(), 2);
304 QCOMPARE(ctx->engine(), &eng);
305 QCOMPARE(ctx->state(), QScriptContext::NormalState);
306 QVERIFY(ctx->activationObject().isObject());
307 QVERIFY(ctx->argumentsObject().isObject());
308
309 QScriptContext *ctx2 = eng.pushContext();
310 QVERIFY(ctx2 != 0);
311 QCOMPARE(ctx2->parentContext(), ctx);
312 QVERIFY(!ctx2->activationObject().strictlyEquals(ctx->activationObject()));
313 QVERIFY(!ctx2->argumentsObject().strictlyEquals(ctx->argumentsObject()));
314
315 eng.popContext();
316 eng.popContext();
317 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
318 eng.popContext(); // ignored
319 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
320 eng.popContext(); // ignored
321 }
322
myFunction(QScriptContext *,QScriptEngine * eng)323 static QScriptValue myFunction(QScriptContext *, QScriptEngine *eng)
324 {
325 return eng->nullValue();
326 }
327
myFunctionWithVoidArg(QScriptContext *,QScriptEngine * eng,void *)328 static QScriptValue myFunctionWithVoidArg(QScriptContext *, QScriptEngine *eng, void *)
329 {
330 return eng->nullValue();
331 }
332
myThrowingFunction(QScriptContext * ctx,QScriptEngine *)333 static QScriptValue myThrowingFunction(QScriptContext *ctx, QScriptEngine *)
334 {
335 return ctx->throwError("foo");
336 }
337
newFunction()338 void tst_QScriptEngine::newFunction()
339 {
340 QScriptEngine eng;
341 {
342 QScriptValue fun = eng.newFunction(myFunction);
343 QCOMPARE(fun.isValid(), true);
344 QCOMPARE(fun.isFunction(), true);
345 QCOMPARE(fun.isObject(), true);
346 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
347 // a prototype property is automatically constructed
348 {
349 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
350 QVERIFY(prot.isObject());
351 QVERIFY(prot.property("constructor").strictlyEquals(fun));
352 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
353 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
354 }
355 // prototype should be Function.prototype
356 QCOMPARE(fun.prototype().isValid(), true);
357 QCOMPARE(fun.prototype().isFunction(), true);
358 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
359
360 QCOMPARE(fun.call().isNull(), true);
361 QCOMPARE(fun.construct().isObject(), true);
362 }
363 }
364
newFunctionWithArg()365 void tst_QScriptEngine::newFunctionWithArg()
366 {
367 QScriptEngine eng;
368 {
369 QScriptValue fun = eng.newFunction(myFunctionWithVoidArg, (void*)this);
370 QVERIFY(fun.isFunction());
371 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
372 // a prototype property is automatically constructed
373 {
374 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
375 QVERIFY(prot.isObject());
376 QVERIFY(prot.property("constructor").strictlyEquals(fun));
377 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
378 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
379 }
380 // prototype should be Function.prototype
381 QCOMPARE(fun.prototype().isValid(), true);
382 QCOMPARE(fun.prototype().isFunction(), true);
383 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
384
385 QCOMPARE(fun.call().isNull(), true);
386 QCOMPARE(fun.construct().isObject(), true);
387 }
388 }
389
newFunctionWithProto()390 void tst_QScriptEngine::newFunctionWithProto()
391 {
392 QScriptEngine eng;
393 {
394 QScriptValue proto = eng.newObject();
395 QScriptValue fun = eng.newFunction(myFunction, proto);
396 QCOMPARE(fun.isValid(), true);
397 QCOMPARE(fun.isFunction(), true);
398 QCOMPARE(fun.isObject(), true);
399 // internal prototype should be Function.prototype
400 QCOMPARE(fun.prototype().isValid(), true);
401 QCOMPARE(fun.prototype().isFunction(), true);
402 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
403 // public prototype should be the one we passed
404 QCOMPARE(fun.property("prototype").strictlyEquals(proto), true);
405 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
406 QCOMPARE(proto.property("constructor").strictlyEquals(fun), true);
407 QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
408
409 QCOMPARE(fun.call().isNull(), true);
410 QCOMPARE(fun.construct().isObject(), true);
411 }
412 }
413
newObject()414 void tst_QScriptEngine::newObject()
415 {
416 QScriptEngine eng;
417 QScriptValue object = eng.newObject();
418 QCOMPARE(object.isValid(), true);
419 QCOMPARE(object.isObject(), true);
420 QCOMPARE(object.isFunction(), false);
421 QCOMPARE(object.scriptClass(), (QScriptClass*)0);
422 // prototype should be Object.prototype
423 QCOMPARE(object.prototype().isValid(), true);
424 QCOMPARE(object.prototype().isObject(), true);
425 QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
426 }
427
newArray()428 void tst_QScriptEngine::newArray()
429 {
430 QScriptEngine eng;
431 QScriptValue array = eng.newArray();
432 QCOMPARE(array.isValid(), true);
433 QCOMPARE(array.isArray(), true);
434 QCOMPARE(array.isObject(), true);
435 QVERIFY(!array.isFunction());
436 QCOMPARE(array.scriptClass(), (QScriptClass*)0);
437 // prototype should be Array.prototype
438 QCOMPARE(array.prototype().isValid(), true);
439 QCOMPARE(array.prototype().isArray(), true);
440 QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
441 }
442
newArray_HooliganTask218092()443 void tst_QScriptEngine::newArray_HooliganTask218092()
444 {
445 QScriptEngine eng;
446 {
447 QScriptValue ret = eng.evaluate("[].splice(0, 0, 'a')");
448 QVERIFY(ret.isArray());
449 QCOMPARE(ret.property("length").toInt32(), 0);
450 }
451 {
452 QScriptValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
453 QVERIFY(ret.isArray());
454 QCOMPARE(ret.property("length").toInt32(), 1);
455 }
456 {
457 QScriptValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
458 QVERIFY(ret.isArray());
459 QCOMPARE(ret.property("length").toInt32(), 1);
460 }
461 {
462 QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
463 QVERIFY(ret.isArray());
464 QCOMPARE(ret.property("length").toInt32(), 2);
465 }
466 {
467 QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
468 QVERIFY(ret.isArray());
469 QCOMPARE(ret.property("length").toInt32(), 2);
470 }
471 }
472
newArray_HooliganTask233836()473 void tst_QScriptEngine::newArray_HooliganTask233836()
474 {
475 QScriptEngine eng;
476 {
477 // According to ECMA-262, this should cause a RangeError.
478 QScriptValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')");
479 QEXPECT_FAIL("", "ECMA compliance bug in Array.prototype.push: https://bugs.webkit.org/show_bug.cgi?id=55033", Continue);
480 QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError")));
481 }
482 {
483 QScriptValue ret = eng.newArray(0xFFFFFFFF);
484 QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF));
485 ret.setProperty(0xFFFFFFFF, 123);
486 QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF));
487 QVERIFY(ret.property(0xFFFFFFFF).isNumber());
488 QCOMPARE(ret.property(0xFFFFFFFF).toInt32(), 123);
489 ret.setProperty(123, 456);
490 QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF));
491 QVERIFY(ret.property(123).isNumber());
492 QCOMPARE(ret.property(123).toInt32(), 456);
493 }
494 }
495
newVariant()496 void tst_QScriptEngine::newVariant()
497 {
498 QScriptEngine eng;
499 {
500 QScriptValue opaque = eng.newVariant(QVariant());
501 QCOMPARE(opaque.isValid(), true);
502 QCOMPARE(opaque.isVariant(), true);
503 QVERIFY(!opaque.isFunction());
504 QCOMPARE(opaque.isObject(), true);
505 QCOMPARE(opaque.prototype().isValid(), true);
506 QCOMPARE(opaque.prototype().isVariant(), true);
507 QVERIFY(opaque.property("valueOf").call(opaque).isUndefined());
508 }
509 }
510
newVariant_defaultPrototype()511 void tst_QScriptEngine::newVariant_defaultPrototype()
512 {
513 // default prototype should be set automatically
514 QScriptEngine eng;
515 {
516 QScriptValue proto = eng.newObject();
517 eng.setDefaultPrototype(qMetaTypeId<QString>(), proto);
518 QScriptValue ret = eng.newVariant(QVariant(QString::fromLatin1("hello")));
519 QVERIFY(ret.isVariant());
520 QCOMPARE(ret.scriptClass(), (QScriptClass*)0);
521 QVERIFY(ret.prototype().strictlyEquals(proto));
522 eng.setDefaultPrototype(qMetaTypeId<QString>(), QScriptValue());
523 QScriptValue ret2 = eng.newVariant(QVariant(QString::fromLatin1("hello")));
524 QVERIFY(ret2.isVariant());
525 QVERIFY(!ret2.prototype().strictlyEquals(proto));
526 }
527 }
528
newVariant_promoteObject()529 void tst_QScriptEngine::newVariant_promoteObject()
530 {
531 // "promote" plain object to variant
532 QScriptEngine eng;
533 {
534 QScriptValue object = eng.newObject();
535 object.setProperty("foo", eng.newObject());
536 object.setProperty("bar", object.property("foo"));
537 QVERIFY(object.property("foo").isObject());
538 QVERIFY(!object.property("foo").isVariant());
539 QScriptValue originalProto = object.property("foo").prototype();
540 QScriptValue ret = eng.newVariant(object.property("foo"), QVariant(123));
541 QVERIFY(ret.isValid());
542 QVERIFY(ret.strictlyEquals(object.property("foo")));
543 QVERIFY(ret.isVariant());
544 QVERIFY(object.property("foo").isVariant());
545 QVERIFY(object.property("bar").isVariant());
546 QCOMPARE(ret.toVariant(), QVariant(123));
547 QVERIFY(ret.prototype().strictlyEquals(originalProto));
548 }
549 }
550
newVariant_replaceValue()551 void tst_QScriptEngine::newVariant_replaceValue()
552 {
553 // replace value of existing object
554 QScriptEngine eng;
555 {
556 QScriptValue object = eng.newVariant(QVariant(123));
557 for (int x = 0; x < 2; ++x) {
558 QScriptValue ret = eng.newVariant(object, QVariant(456));
559 QVERIFY(ret.isValid());
560 QVERIFY(ret.strictlyEquals(object));
561 QVERIFY(ret.isVariant());
562 QCOMPARE(ret.toVariant(), QVariant(456));
563 }
564 }
565 }
566
newVariant_valueOfToString()567 void tst_QScriptEngine::newVariant_valueOfToString()
568 {
569 // valueOf() and toString()
570 QScriptEngine eng;
571 {
572 QScriptValue object = eng.newVariant(QVariant(123));
573 QScriptValue value = object.property("valueOf").call(object);
574 QVERIFY(value.isNumber());
575 QCOMPARE(value.toInt32(), 123);
576 QCOMPARE(object.toString(), QString::fromLatin1("123"));
577 QCOMPARE(object.toVariant().toString(), object.toString());
578 }
579 {
580 QScriptValue object = eng.newVariant(QVariant(QString::fromLatin1("hello")));
581 QScriptValue value = object.property("valueOf").call(object);
582 QVERIFY(value.isString());
583 QCOMPARE(value.toString(), QString::fromLatin1("hello"));
584 QCOMPARE(object.toString(), QString::fromLatin1("hello"));
585 QCOMPARE(object.toVariant().toString(), object.toString());
586 }
587 {
588 QScriptValue object = eng.newVariant(QVariant(false));
589 QScriptValue value = object.property("valueOf").call(object);
590 QVERIFY(value.isBoolean());
591 QCOMPARE(value.toBoolean(), false);
592 QCOMPARE(object.toString(), QString::fromLatin1("false"));
593 QCOMPARE(object.toVariant().toString(), object.toString());
594 }
595 {
596 QScriptValue object = eng.newVariant(QVariant(QPoint(10, 20)));
597 QScriptValue value = object.property("valueOf").call(object);
598 QVERIFY(value.isObject());
599 QVERIFY(value.strictlyEquals(object));
600 QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)"));
601 }
602 }
603
newVariant_promoteNonObject()604 void tst_QScriptEngine::newVariant_promoteNonObject()
605 {
606 QScriptEngine eng;
607 {
608 QVariant var(456);
609 QScriptValue ret = eng.newVariant(123, var);
610 QVERIFY(ret.isVariant());
611 QCOMPARE(ret.toVariant(), var);
612 }
613 }
614
newVariant_promoteNonQScriptObject()615 void tst_QScriptEngine::newVariant_promoteNonQScriptObject()
616 {
617 QScriptEngine eng;
618 {
619 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newVariant(): changing class of non-QScriptObject not supported");
620 QScriptValue ret = eng.newVariant(eng.newArray(), 123);
621 QVERIFY(!ret.isValid());
622 }
623 }
624
newRegExp()625 void tst_QScriptEngine::newRegExp()
626 {
627 QScriptEngine eng;
628 for (int x = 0; x < 2; ++x) {
629 QScriptValue rexp;
630 if (x == 0)
631 rexp = eng.newRegExp("foo", "bar");
632 else
633 rexp = eng.newRegExp(QRegExp("foo"));
634 QCOMPARE(rexp.isValid(), true);
635 QCOMPARE(rexp.isRegExp(), true);
636 QCOMPARE(rexp.isObject(), true);
637 QVERIFY(rexp.isFunction()); // in JSC, RegExp objects are callable
638 // prototype should be RegExp.prototype
639 QCOMPARE(rexp.prototype().isValid(), true);
640 QCOMPARE(rexp.prototype().isObject(), true);
641 QCOMPARE(rexp.prototype().isRegExp(), false);
642 QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
643
644 QCOMPARE(rexp.toRegExp().pattern(), QRegExp("foo").pattern());
645 }
646 }
647
jsRegExp()648 void tst_QScriptEngine::jsRegExp()
649 {
650 // See ECMA-262 Section 15.10, "RegExp Objects".
651 // These should really be JS-only tests, as they test the implementation's
652 // ECMA-compliance, not the C++ API. Compliance should already be covered
653 // by the Mozilla tests (qscriptjstestsuite).
654 // We can consider updating the expected results of this test if the
655 // RegExp implementation changes.
656
657 QScriptEngine eng;
658 QScriptValue r = eng.evaluate("/foo/gim");
659 QVERIFY(r.isRegExp());
660 QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim"));
661
662 QScriptValue rxCtor = eng.globalObject().property("RegExp");
663 QScriptValue r2 = rxCtor.call(QScriptValue(), QScriptValueList() << r);
664 QVERIFY(r2.isRegExp());
665 QVERIFY(r2.strictlyEquals(r));
666
667 QScriptValue r3 = rxCtor.call(QScriptValue(), QScriptValueList() << r << "gim");
668 QVERIFY(r3.isError());
669 QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another
670
671 QScriptValue r4 = rxCtor.call(QScriptValue(), QScriptValueList() << "foo" << "gim");
672 QVERIFY(r4.isRegExp());
673
674 QScriptValue r5 = rxCtor.construct(QScriptValueList() << r);
675 QVERIFY(r5.isRegExp());
676 QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
677 // In JSC, constructing a RegExp from another produces the same identical object.
678 // This is different from SpiderMonkey and old back-end.
679 QEXPECT_FAIL("", "RegExp copy-constructor should return a new object: https://bugs.webkit.org/show_bug.cgi?id=55040", Continue);
680 QVERIFY(!r5.strictlyEquals(r));
681
682 QScriptValue r6 = rxCtor.construct(QScriptValueList() << "foo" << "bar");
683 QVERIFY(r6.isError());
684 QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
685
686 QScriptValue r7 = eng.evaluate("/foo/gimp");
687 QVERIFY(r7.isError());
688 QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
689
690 // JSC doesn't complain about duplicate flags.
691 QScriptValue r8 = eng.evaluate("/foo/migmigmig");
692 QVERIFY(r8.isRegExp());
693 QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim"));
694
695 QScriptValue r9 = rxCtor.construct();
696 QVERIFY(r9.isRegExp());
697 QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));
698
699 QScriptValue r10 = rxCtor.construct(QScriptValueList() << "" << "gim");
700 QVERIFY(r10.isRegExp());
701 QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));
702
703 QScriptValue r11 = rxCtor.construct(QScriptValueList() << "{1.*}" << "g");
704 QVERIFY(r11.isRegExp());
705 QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
706 }
707
newDate()708 void tst_QScriptEngine::newDate()
709 {
710 QScriptEngine eng;
711
712 {
713 QScriptValue date = eng.newDate(0);
714 QCOMPARE(date.isValid(), true);
715 QCOMPARE(date.isDate(), true);
716 QCOMPARE(date.isObject(), true);
717 QVERIFY(!date.isFunction());
718 // prototype should be Date.prototype
719 QCOMPARE(date.prototype().isValid(), true);
720 QCOMPARE(date.prototype().isDate(), true);
721 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
722 }
723
724 {
725 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
726 QScriptValue date = eng.newDate(dt);
727 QCOMPARE(date.isValid(), true);
728 QCOMPARE(date.isDate(), true);
729 QCOMPARE(date.isObject(), true);
730 // prototype should be Date.prototype
731 QCOMPARE(date.prototype().isValid(), true);
732 QCOMPARE(date.prototype().isDate(), true);
733 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
734
735 QCOMPARE(date.toDateTime(), dt);
736 }
737
738 {
739 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
740 QScriptValue date = eng.newDate(dt);
741 // toDateTime() result should be in local time
742 QCOMPARE(date.toDateTime(), dt.toLocalTime());
743 }
744 }
745
jsParseDate()746 void tst_QScriptEngine::jsParseDate()
747 {
748 QScriptEngine eng;
749 // Date.parse() should return NaN when it fails
750 {
751 QScriptValue ret = eng.evaluate("Date.parse()");
752 QVERIFY(ret.isNumber());
753 QVERIFY(qIsNaN(ret.toNumber()));
754 }
755
756 // Date.parse() should be able to parse the output of Date().toString()
757 #ifndef Q_WS_WIN // TODO: Test and remove this since 169701 has been fixed
758 {
759 QScriptValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
760 QVERIFY(ret.isBoolean());
761 QCOMPARE(ret.toBoolean(), true);
762 }
763 #endif
764 }
765
newQObject()766 void tst_QScriptEngine::newQObject()
767 {
768 QScriptEngine eng;
769
770 {
771 QScriptValue qobject = eng.newQObject(0);
772 QCOMPARE(qobject.isValid(), true);
773 QCOMPARE(qobject.isNull(), true);
774 QCOMPARE(qobject.isObject(), false);
775 QCOMPARE(qobject.toQObject(), (QObject *)0);
776 }
777 {
778 QScriptValue qobject = eng.newQObject(this);
779 QCOMPARE(qobject.isValid(), true);
780 QCOMPARE(qobject.isQObject(), true);
781 QCOMPARE(qobject.isObject(), true);
782 QCOMPARE(qobject.toQObject(), (QObject *)this);
783 QVERIFY(!qobject.isFunction());
784 // prototype should be QObject.prototype
785 QCOMPARE(qobject.prototype().isValid(), true);
786 QCOMPARE(qobject.prototype().isQObject(), true);
787 QCOMPARE(qobject.scriptClass(), (QScriptClass*)0);
788 }
789 }
790
newQObject_ownership()791 void tst_QScriptEngine::newQObject_ownership()
792 {
793 QScriptEngine eng;
794 {
795 QPointer<QObject> ptr = new QObject();
796 QVERIFY(ptr != 0);
797 {
798 QScriptValue v = eng.newQObject(ptr, QScriptEngine::ScriptOwnership);
799 }
800 eng.evaluate("gc()");
801 if (ptr)
802 QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue);
803 QVERIFY(ptr == 0);
804 }
805 {
806 QPointer<QObject> ptr = new QObject();
807 QVERIFY(ptr != 0);
808 {
809 QScriptValue v = eng.newQObject(ptr, QScriptEngine::QtOwnership);
810 }
811 QObject *before = ptr;
812 eng.evaluate("gc()");
813 QVERIFY(ptr == before);
814 delete ptr;
815 }
816 {
817 QObject *parent = new QObject();
818 QObject *child = new QObject(parent);
819 QScriptValue v = eng.newQObject(child, QScriptEngine::QtOwnership);
820 QCOMPARE(v.toQObject(), child);
821 delete parent;
822 QCOMPARE(v.toQObject(), (QObject *)0);
823 }
824 {
825 QPointer<QObject> ptr = new QObject();
826 QVERIFY(ptr != 0);
827 {
828 QScriptValue v = eng.newQObject(ptr, QScriptEngine::AutoOwnership);
829 }
830 eng.evaluate("gc()");
831 // no parent, so it should be like ScriptOwnership
832 if (ptr)
833 QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue);
834 QVERIFY(ptr == 0);
835 }
836 {
837 QObject *parent = new QObject();
838 QPointer<QObject> child = new QObject(parent);
839 QVERIFY(child != 0);
840 {
841 QScriptValue v = eng.newQObject(child, QScriptEngine::AutoOwnership);
842 }
843 eng.evaluate("gc()");
844 // has parent, so it should be like QtOwnership
845 QVERIFY(child != 0);
846 delete parent;
847 }
848 }
849
newQObject_promoteObject()850 void tst_QScriptEngine::newQObject_promoteObject()
851 {
852 QScriptEngine eng;
853 // "promote" plain object to QObject
854 {
855 QScriptValue obj = eng.newObject();
856 QScriptValue originalProto = obj.prototype();
857 QScriptValue ret = eng.newQObject(obj, this);
858 QVERIFY(ret.isValid());
859 QVERIFY(ret.isQObject());
860 QVERIFY(ret.strictlyEquals(obj));
861 QVERIFY(obj.isQObject());
862 QCOMPARE(ret.toQObject(), (QObject *)this);
863 QVERIFY(ret.prototype().strictlyEquals(originalProto));
864 QScriptValue val = ret.property("objectName");
865 QVERIFY(val.isString());
866 }
867 // "promote" variant object to QObject
868 {
869 QScriptValue obj = eng.newVariant(123);
870 QVERIFY(obj.isVariant());
871 QScriptValue originalProto = obj.prototype();
872 QScriptValue ret = eng.newQObject(obj, this);
873 QVERIFY(ret.isQObject());
874 QVERIFY(ret.strictlyEquals(obj));
875 QVERIFY(obj.isQObject());
876 QCOMPARE(ret.toQObject(), (QObject *)this);
877 QVERIFY(ret.prototype().strictlyEquals(originalProto));
878 }
879 // replace QObject* of existing object
880 {
881 QScriptValue object = eng.newVariant(123);
882 QScriptValue originalProto = object.prototype();
883 QObject otherQObject;
884 for (int x = 0; x < 2; ++x) {
885 QScriptValue ret = eng.newQObject(object, &otherQObject);
886 QVERIFY(ret.isValid());
887 QVERIFY(ret.isQObject());
888 QVERIFY(ret.strictlyEquals(object));
889 QCOMPARE(ret.toQObject(), (QObject *)&otherQObject);
890 QVERIFY(ret.prototype().strictlyEquals(originalProto));
891 }
892 }
893 }
894
newQObject_sameQObject()895 void tst_QScriptEngine::newQObject_sameQObject()
896 {
897 QScriptEngine eng;
898 // calling newQObject() several times with same object
899 for (int x = 0; x < 2; ++x) {
900 QObject qobj;
901 // the default is to create a new wrapper object
902 QScriptValue obj1 = eng.newQObject(&qobj);
903 QScriptValue obj2 = eng.newQObject(&qobj);
904 QVERIFY(!obj2.strictlyEquals(obj1));
905
906 QScriptEngine::QObjectWrapOptions opt = 0;
907 bool preferExisting = (x != 0);
908 if (preferExisting)
909 opt |= QScriptEngine::PreferExistingWrapperObject;
910
911 QScriptValue obj3 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
912 QVERIFY(!obj3.strictlyEquals(obj2));
913 QScriptValue obj4 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
914 QCOMPARE(obj4.strictlyEquals(obj3), preferExisting);
915
916 QScriptValue obj5 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
917 QVERIFY(!obj5.strictlyEquals(obj4));
918 QScriptValue obj6 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
919 QCOMPARE(obj6.strictlyEquals(obj5), preferExisting);
920
921 QScriptValue obj7 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
922 QScriptEngine::ExcludeSuperClassMethods | opt);
923 QVERIFY(!obj7.strictlyEquals(obj6));
924 QScriptValue obj8 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
925 QScriptEngine::ExcludeSuperClassMethods | opt);
926 QCOMPARE(obj8.strictlyEquals(obj7), preferExisting);
927 }
928 }
929
newQObject_defaultPrototype()930 void tst_QScriptEngine::newQObject_defaultPrototype()
931 {
932 QScriptEngine eng;
933 // newQObject() should set the default prototype, if one has been registered
934 {
935 QScriptValue oldQObjectProto = eng.defaultPrototype(qMetaTypeId<QObject*>());
936
937 QScriptValue qobjectProto = eng.newObject();
938 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), qobjectProto);
939 {
940 QScriptValue ret = eng.newQObject(this);
941 QVERIFY(ret.prototype().equals(qobjectProto));
942 }
943 QScriptValue tstProto = eng.newObject();
944 int typeId = qRegisterMetaType<tst_QScriptEngine*>("tst_QScriptEngine*");
945 eng.setDefaultPrototype(typeId, tstProto);
946 {
947 QScriptValue ret = eng.newQObject(this);
948 QVERIFY(ret.prototype().equals(tstProto));
949 }
950
951 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), oldQObjectProto);
952 eng.setDefaultPrototype(typeId, QScriptValue());
953 }
954 }
955
newQObject_promoteNonObject()956 void tst_QScriptEngine::newQObject_promoteNonObject()
957 {
958 QScriptEngine eng;
959 {
960 QScriptValue ret = eng.newQObject(123, this);
961 QVERIFY(ret.isQObject());
962 QCOMPARE(ret.toQObject(), this);
963 }
964 }
965
newQObject_promoteNonQScriptObject()966 void tst_QScriptEngine::newQObject_promoteNonQScriptObject()
967 {
968 QScriptEngine eng;
969 {
970 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newQObject(): changing class of non-QScriptObject not supported");
971 QScriptValue ret = eng.newQObject(eng.newArray(), this);
972 QVERIFY(!ret.isValid());
973 }
974 }
975
976 QT_BEGIN_NAMESPACE
Q_SCRIPT_DECLARE_QMETAOBJECT(QObject,QObject *)977 Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*)
978 Q_SCRIPT_DECLARE_QMETAOBJECT(QWidget, QWidget*)
979 QT_END_NAMESPACE
980
981 static QScriptValue myConstructor(QScriptContext *ctx, QScriptEngine *eng)
982 {
983 QScriptValue obj;
984 if (ctx->isCalledAsConstructor()) {
985 obj = ctx->thisObject();
986 } else {
987 obj = eng->newObject();
988 obj.setPrototype(ctx->callee().property("prototype"));
989 }
990 obj.setProperty("isCalledAsConstructor", QScriptValue(eng, ctx->isCalledAsConstructor()));
991 return obj;
992 }
993
instanceofJS(const QScriptValue & inst,const QScriptValue & ctor)994 static QScriptValue instanceofJS(const QScriptValue &inst, const QScriptValue &ctor)
995 {
996 return inst.engine()->evaluate("(function(inst, ctor) { return inst instanceof ctor; })")
997 .call(QScriptValue(), QScriptValueList() << inst << ctor);
998 }
999
newQMetaObject()1000 void tst_QScriptEngine::newQMetaObject()
1001 {
1002 QScriptEngine eng;
1003 #if 0
1004 QScriptValue qclass = eng.newQMetaObject<QObject>();
1005 QScriptValue qclass2 = eng.newQMetaObject<QWidget>();
1006 #else
1007 QScriptValue qclass = qScriptValueFromQMetaObject<QObject>(&eng);
1008 QScriptValue qclass2 = qScriptValueFromQMetaObject<QWidget>(&eng);
1009 #endif
1010 QCOMPARE(qclass.isValid(), true);
1011 QCOMPARE(qclass.isQMetaObject(), true);
1012 QCOMPARE(qclass.toQMetaObject(), &QObject::staticMetaObject);
1013 QCOMPARE(qclass.isFunction(), true);
1014 QVERIFY(qclass.property("prototype").isObject());
1015
1016 QCOMPARE(qclass2.isValid(), true);
1017 QCOMPARE(qclass2.isQMetaObject(), true);
1018 QCOMPARE(qclass2.toQMetaObject(), &QWidget::staticMetaObject);
1019 QCOMPARE(qclass2.isFunction(), true);
1020 QVERIFY(qclass2.property("prototype").isObject());
1021
1022 // prototype should be QMetaObject.prototype
1023 QCOMPARE(qclass.prototype().isObject(), true);
1024 QCOMPARE(qclass2.prototype().isObject(), true);
1025
1026 QScriptValue instance = qclass.construct();
1027 QCOMPARE(instance.isQObject(), true);
1028 QCOMPARE(instance.toQObject()->metaObject(), qclass.toQMetaObject());
1029 QVERIFY(instance.instanceOf(qclass));
1030 QVERIFY(instanceofJS(instance, qclass).strictlyEquals(true));
1031
1032 QScriptValue instance2 = qclass2.construct();
1033 QCOMPARE(instance2.isQObject(), true);
1034 QCOMPARE(instance2.toQObject()->metaObject(), qclass2.toQMetaObject());
1035 QVERIFY(instance2.instanceOf(qclass2));
1036 QVERIFY(instanceofJS(instance2, qclass2).strictlyEquals(true));
1037 QVERIFY(!instance2.instanceOf(qclass));
1038 QVERIFY(instanceofJS(instance2, qclass).strictlyEquals(false));
1039
1040 QScriptValueList args;
1041 args << instance;
1042 QScriptValue instance3 = qclass.construct(args);
1043 QCOMPARE(instance3.isQObject(), true);
1044 QCOMPARE(instance3.toQObject()->parent(), instance.toQObject());
1045 QVERIFY(instance3.instanceOf(qclass));
1046 QVERIFY(instanceofJS(instance3, qclass).strictlyEquals(true));
1047 QVERIFY(!instance3.instanceOf(qclass2));
1048 QVERIFY(instanceofJS(instance3, qclass2).strictlyEquals(false));
1049 args.clear();
1050
1051 QPointer<QObject> qpointer1 = instance.toQObject();
1052 QPointer<QObject> qpointer2 = instance2.toQObject();
1053 QPointer<QObject> qpointer3 = instance3.toQObject();
1054
1055 QVERIFY(qpointer1);
1056 QVERIFY(qpointer2);
1057 QVERIFY(qpointer3);
1058
1059 // verify that AutoOwnership is in effect
1060 instance = QScriptValue();
1061 collectGarbage_helper(eng);
1062
1063 QVERIFY(!qpointer1);
1064 QVERIFY(qpointer2);
1065 QVERIFY(!qpointer3); // was child of instance
1066
1067 QVERIFY(instance.toQObject() == 0);
1068 QVERIFY(instance3.toQObject() == 0); // was child of instance
1069 QVERIFY(instance2.toQObject() != 0);
1070 instance2 = QScriptValue();
1071 collectGarbage_helper(eng);
1072 QVERIFY(instance2.toQObject() == 0);
1073
1074 // with custom constructor
1075 QScriptValue ctor = eng.newFunction(myConstructor);
1076 QScriptValue qclass3 = eng.newQMetaObject(&QObject::staticMetaObject, ctor);
1077 QVERIFY(qclass3.property("prototype").equals(ctor.property("prototype")));
1078 {
1079 QScriptValue ret = qclass3.call();
1080 QVERIFY(ret.isObject());
1081 QVERIFY(ret.property("isCalledAsConstructor").isBoolean());
1082 QVERIFY(!ret.property("isCalledAsConstructor").toBoolean());
1083 QVERIFY(ret.instanceOf(qclass3));
1084 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1085 QVERIFY(!ret.instanceOf(qclass));
1086 QVERIFY(instanceofJS(ret, qclass).strictlyEquals(false));
1087 }
1088 {
1089 QScriptValue ret = qclass3.construct();
1090 QVERIFY(ret.isObject());
1091 QVERIFY(ret.property("isCalledAsConstructor").isBoolean());
1092 QVERIFY(ret.property("isCalledAsConstructor").toBoolean());
1093 QVERIFY(ret.instanceOf(qclass3));
1094 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1095 QVERIFY(!ret.instanceOf(qclass2));
1096 QVERIFY(instanceofJS(ret, qclass2).strictlyEquals(false));
1097 }
1098
1099 // subclassing
1100 qclass2.setProperty("prototype", qclass.construct());
1101 QVERIFY(qclass2.construct().instanceOf(qclass));
1102 QVERIFY(instanceofJS(qclass2.construct(), qclass).strictlyEquals(true));
1103
1104 // with meta-constructor
1105 QScriptValue qclass4 = eng.newQMetaObject(&QObject::staticMetaObject);
1106 {
1107 QScriptValue inst = qclass4.construct();
1108 QVERIFY(inst.isQObject());
1109 QVERIFY(inst.toQObject() != 0);
1110 QCOMPARE(inst.toQObject()->parent(), (QObject*)0);
1111 QVERIFY(inst.instanceOf(qclass4));
1112 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1113 QVERIFY(!inst.instanceOf(qclass3));
1114 QVERIFY(instanceofJS(inst, qclass3).strictlyEquals(false));
1115 }
1116 {
1117 QScriptValue inst = qclass4.construct(QScriptValueList() << eng.newQObject(this));
1118 QVERIFY(inst.isQObject());
1119 QVERIFY(inst.toQObject() != 0);
1120 QCOMPARE(inst.toQObject()->parent(), (QObject*)this);
1121 QVERIFY(inst.instanceOf(qclass4));
1122 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1123 QVERIFY(!inst.instanceOf(qclass2));
1124 QVERIFY(instanceofJS(inst, qclass2).strictlyEquals(false));
1125 }
1126 }
1127
newActivationObject()1128 void tst_QScriptEngine::newActivationObject()
1129 {
1130 QSKIP("internal function not implemented in JSC-based back-end", SkipAll);
1131 QScriptEngine eng;
1132 QScriptValue act = eng.newActivationObject();
1133 QEXPECT_FAIL("", "", Continue);
1134 QCOMPARE(act.isValid(), true);
1135 QEXPECT_FAIL("", "", Continue);
1136 QCOMPARE(act.isObject(), true);
1137 QVERIFY(!act.isFunction());
1138 QScriptValue v(&eng, 123);
1139 act.setProperty("prop", v);
1140 QEXPECT_FAIL("", "", Continue);
1141 QCOMPARE(act.property("prop").strictlyEquals(v), true);
1142 QCOMPARE(act.scope().isValid(), false);
1143 QEXPECT_FAIL("", "", Continue);
1144 QVERIFY(act.prototype().isNull());
1145 }
1146
getSetGlobalObject()1147 void tst_QScriptEngine::getSetGlobalObject()
1148 {
1149 QScriptEngine eng;
1150 QScriptValue glob = eng.globalObject();
1151 QCOMPARE(glob.isValid(), true);
1152 QCOMPARE(glob.isObject(), true);
1153 QVERIFY(!glob.isFunction());
1154 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(glob));
1155 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(glob));
1156 QCOMPARE(glob.toString(), QString::fromLatin1("[object global]"));
1157 // prototype should be Object.prototype
1158 QCOMPARE(glob.prototype().isValid(), true);
1159 QCOMPARE(glob.prototype().isObject(), true);
1160 QCOMPARE(glob.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
1161
1162 eng.setGlobalObject(glob);
1163 QVERIFY(eng.globalObject().equals(glob));
1164 eng.setGlobalObject(123);
1165 QVERIFY(eng.globalObject().equals(glob));
1166
1167 QScriptValue obj = eng.newObject();
1168 eng.setGlobalObject(obj);
1169 QVERIFY(eng.globalObject().strictlyEquals(obj));
1170 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1171 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1172 QVERIFY(eng.evaluate("this").strictlyEquals(obj));
1173 QCOMPARE(eng.globalObject().toString(), QString::fromLatin1("[object Object]"));
1174
1175 glob = QScriptValue(); // kill reference to old global object
1176 collectGarbage_helper(eng);
1177 obj = eng.newObject();
1178 eng.setGlobalObject(obj);
1179 QVERIFY(eng.globalObject().strictlyEquals(obj));
1180 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1181 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1182
1183 collectGarbage_helper(eng);
1184 QVERIFY(eng.globalObject().strictlyEquals(obj));
1185 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1186 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1187
1188 QVERIFY(!obj.property("foo").isValid());
1189 eng.evaluate("var foo = 123");
1190 {
1191 QScriptValue ret = obj.property("foo");
1192 QVERIFY(ret.isNumber());
1193 QCOMPARE(ret.toInt32(), 123);
1194 }
1195
1196 QVERIFY(!obj.property("bar").isValid());
1197 eng.evaluate("bar = 456");
1198 {
1199 QScriptValue ret = obj.property("bar");
1200 QVERIFY(ret.isNumber());
1201 QCOMPARE(ret.toInt32(), 456);
1202 }
1203
1204 QVERIFY(!obj.property("baz").isValid());
1205 eng.evaluate("this['baz'] = 789");
1206 {
1207 QScriptValue ret = obj.property("baz");
1208 QVERIFY(ret.isNumber());
1209 QCOMPARE(ret.toInt32(), 789);
1210 }
1211
1212 {
1213 QScriptValue ret = eng.evaluate("(function() { return this; })()");
1214 QVERIFY(ret.strictlyEquals(obj));
1215 }
1216
1217 // Delete property.
1218 {
1219 QScriptValue ret = eng.evaluate("delete foo");
1220 QVERIFY(ret.isBool());
1221 QVERIFY(ret.toBool());
1222 QVERIFY(!obj.property("foo").isValid());
1223 }
1224
1225 // Getter/setter property.
1226 QVERIFY(eng.evaluate("this.__defineGetter__('oof', function() { return this.bar; })").isUndefined());
1227 QVERIFY(eng.evaluate("this.__defineSetter__('oof', function(v) { this.bar = v; })").isUndefined());
1228 QVERIFY(eng.evaluate("this.__lookupGetter__('oof')").isFunction());
1229 QVERIFY(eng.evaluate("this.__lookupSetter__('oof')").isFunction());
1230 eng.evaluate("oof = 123");
1231 QVERIFY(eng.evaluate("oof").equals(obj.property("bar")));
1232
1233 // Enumeration.
1234 {
1235 QScriptValue ret = eng.evaluate("a = []; for (var p in this) a.push(p); a");
1236 QCOMPARE(ret.toString(), QString::fromLatin1("bar,baz,oof,p,a"));
1237 }
1238 }
1239
getSetFoo(QScriptContext * ctx,QScriptEngine *)1240 static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *)
1241 {
1242 if (ctx->argumentCount() > 0)
1243 ctx->thisObject().setProperty("foo", ctx->argument(0));
1244 return ctx->thisObject().property("foo");
1245 }
1246
globalObjectProperties()1247 void tst_QScriptEngine::globalObjectProperties()
1248 {
1249 // See ECMA-262 Section 15.1, "The Global Object".
1250
1251 QScriptEngine eng;
1252 QScriptValue global = eng.globalObject();
1253
1254 QVERIFY(global.property("NaN").isNumber());
1255 QVERIFY(qIsNaN(global.property("NaN").toNumber()));
1256 QCOMPARE(global.propertyFlags("NaN"), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable);
1257
1258 QVERIFY(global.property("Infinity").isNumber());
1259 QVERIFY(qIsInf(global.property("Infinity").toNumber()));
1260 QCOMPARE(global.propertyFlags("NaN"), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable);
1261
1262 QVERIFY(global.property("undefined").isUndefined());
1263 QCOMPARE(global.propertyFlags("undefined"), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable);
1264
1265 QVERIFY(global.property("eval").isFunction());
1266 QCOMPARE(global.propertyFlags("eval"), QScriptValue::SkipInEnumeration);
1267
1268 QVERIFY(global.property("parseInt").isFunction());
1269 QCOMPARE(global.propertyFlags("parseInt"), QScriptValue::SkipInEnumeration);
1270
1271 QVERIFY(global.property("parseFloat").isFunction());
1272 QCOMPARE(global.propertyFlags("parseFloat"), QScriptValue::SkipInEnumeration);
1273
1274 QVERIFY(global.property("isNaN").isFunction());
1275 QCOMPARE(global.propertyFlags("isNaN"), QScriptValue::SkipInEnumeration);
1276
1277 QVERIFY(global.property("isFinite").isFunction());
1278 QCOMPARE(global.propertyFlags("isFinite"), QScriptValue::SkipInEnumeration);
1279
1280 QVERIFY(global.property("decodeURI").isFunction());
1281 QCOMPARE(global.propertyFlags("decodeURI"), QScriptValue::SkipInEnumeration);
1282
1283 QVERIFY(global.property("decodeURIComponent").isFunction());
1284 QCOMPARE(global.propertyFlags("decodeURIComponent"), QScriptValue::SkipInEnumeration);
1285
1286 QVERIFY(global.property("encodeURI").isFunction());
1287 QCOMPARE(global.propertyFlags("encodeURI"), QScriptValue::SkipInEnumeration);
1288
1289 QVERIFY(global.property("encodeURIComponent").isFunction());
1290 QCOMPARE(global.propertyFlags("encodeURIComponent"), QScriptValue::SkipInEnumeration);
1291
1292 QVERIFY(global.property("Object").isFunction());
1293 QCOMPARE(global.propertyFlags("Object"), QScriptValue::SkipInEnumeration);
1294 QVERIFY(global.property("Function").isFunction());
1295 QCOMPARE(global.propertyFlags("Function"), QScriptValue::SkipInEnumeration);
1296 QVERIFY(global.property("Array").isFunction());
1297 QCOMPARE(global.propertyFlags("Array"), QScriptValue::SkipInEnumeration);
1298 QVERIFY(global.property("String").isFunction());
1299 QCOMPARE(global.propertyFlags("String"), QScriptValue::SkipInEnumeration);
1300 QVERIFY(global.property("Boolean").isFunction());
1301 QCOMPARE(global.propertyFlags("Boolean"), QScriptValue::SkipInEnumeration);
1302 QVERIFY(global.property("Number").isFunction());
1303 QCOMPARE(global.propertyFlags("Number"), QScriptValue::SkipInEnumeration);
1304 QVERIFY(global.property("Date").isFunction());
1305 QCOMPARE(global.propertyFlags("Date"), QScriptValue::SkipInEnumeration);
1306 QVERIFY(global.property("RegExp").isFunction());
1307 QCOMPARE(global.propertyFlags("RegExp"), QScriptValue::SkipInEnumeration);
1308 QVERIFY(global.property("Error").isFunction());
1309 QCOMPARE(global.propertyFlags("Error"), QScriptValue::SkipInEnumeration);
1310 QVERIFY(global.property("EvalError").isFunction());
1311 QCOMPARE(global.propertyFlags("EvalError"), QScriptValue::SkipInEnumeration);
1312 QVERIFY(global.property("RangeError").isFunction());
1313 QCOMPARE(global.propertyFlags("RangeError"), QScriptValue::SkipInEnumeration);
1314 QVERIFY(global.property("ReferenceError").isFunction());
1315 QCOMPARE(global.propertyFlags("ReferenceError"), QScriptValue::SkipInEnumeration);
1316 QVERIFY(global.property("SyntaxError").isFunction());
1317 QCOMPARE(global.propertyFlags("SyntaxError"), QScriptValue::SkipInEnumeration);
1318 QVERIFY(global.property("TypeError").isFunction());
1319 QCOMPARE(global.propertyFlags("TypeError"), QScriptValue::SkipInEnumeration);
1320 QVERIFY(global.property("URIError").isFunction());
1321 QCOMPARE(global.propertyFlags("URIError"), QScriptValue::SkipInEnumeration);
1322 QVERIFY(global.property("Math").isObject());
1323 QVERIFY(!global.property("Math").isFunction());
1324 QEXPECT_FAIL("", "[ECMA compliance] JSC sets DontDelete flag for Math object: https://bugs.webkit.org/show_bug.cgi?id=55034", Continue);
1325 QCOMPARE(global.propertyFlags("Math"), QScriptValue::SkipInEnumeration);
1326 }
1327
globalObjectProperties_enumerate()1328 void tst_QScriptEngine::globalObjectProperties_enumerate()
1329 {
1330 QScriptEngine eng;
1331 QScriptValue global = eng.globalObject();
1332
1333 QSet<QString> expectedNames;
1334 expectedNames
1335 << "isNaN"
1336 << "parseFloat"
1337 << "String"
1338 << "EvalError"
1339 << "URIError"
1340 << "Math"
1341 << "encodeURIComponent"
1342 << "RangeError"
1343 << "eval"
1344 << "isFinite"
1345 << "ReferenceError"
1346 << "Infinity"
1347 << "Function"
1348 << "RegExp"
1349 << "Number"
1350 << "parseInt"
1351 << "Object"
1352 << "decodeURI"
1353 << "TypeError"
1354 << "Boolean"
1355 << "encodeURI"
1356 << "NaN"
1357 << "Error"
1358 << "decodeURIComponent"
1359 << "Date"
1360 << "Array"
1361 << "escape"
1362 << "unescape"
1363 << "SyntaxError"
1364 << "undefined"
1365 // non-standard
1366 << "gc"
1367 << "version"
1368 << "print"
1369 // JavaScriptCore
1370 << "JSON"
1371 ;
1372 QSet<QString> actualNames;
1373 {
1374 QScriptValueIterator it(global);
1375 while (it.hasNext()) {
1376 it.next();
1377 actualNames.insert(it.name());
1378 }
1379 }
1380
1381 QSet<QString> remainingNames = actualNames;
1382 {
1383 QSet<QString>::const_iterator it;
1384 for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) {
1385 QString name = *it;
1386 QVERIFY(actualNames.contains(name));
1387 remainingNames.remove(name);
1388 }
1389 }
1390 QVERIFY(remainingNames.isEmpty());
1391 }
1392
createGlobalObjectProperty()1393 void tst_QScriptEngine::createGlobalObjectProperty()
1394 {
1395 QScriptEngine eng;
1396 QScriptValue global = eng.globalObject();
1397 // create property with no attributes
1398 {
1399 QString name = QString::fromLatin1("foo");
1400 QVERIFY(!global.property(name).isValid());
1401 QScriptValue val(123);
1402 global.setProperty(name, val);
1403 QVERIFY(global.property(name).equals(val));
1404 QVERIFY(global.propertyFlags(name) == 0);
1405 global.setProperty(name, QScriptValue());
1406 QVERIFY(!global.property(name).isValid());
1407 }
1408 // create property with attributes
1409 {
1410 QString name = QString::fromLatin1("bar");
1411 QVERIFY(!global.property(name).isValid());
1412 QScriptValue val(QString::fromLatin1("ciao"));
1413 QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration;
1414 global.setProperty(name, val, flags);
1415 QVERIFY(global.property(name).equals(val));
1416 QEXPECT_FAIL("", "QTBUG-6134: custom Global Object properties don't retain attributes", Continue);
1417 QCOMPARE(global.propertyFlags(name), flags);
1418 global.setProperty(name, QScriptValue());
1419 QVERIFY(!global.property(name).isValid());
1420 }
1421 }
1422
globalObjectGetterSetterProperty()1423 void tst_QScriptEngine::globalObjectGetterSetterProperty()
1424 {
1425 QScriptEngine engine;
1426 QScriptValue global = engine.globalObject();
1427 global.setProperty("bar", engine.newFunction(getSetFoo),
1428 QScriptValue::PropertySetter | QScriptValue::PropertyGetter);
1429 global.setProperty("foo", 123);
1430 QVERIFY(global.property("bar").equals(global.property("foo")));
1431 QVERIFY(engine.evaluate("bar").equals(global.property("foo")));
1432 global.setProperty("bar", 456);
1433 QVERIFY(global.property("bar").equals(global.property("foo")));
1434
1435 engine.evaluate("__defineGetter__('baz', function() { return 789; })");
1436 QVERIFY(engine.evaluate("baz").equals(789));
1437 QVERIFY(global.property("baz").equals(789));
1438 }
1439
customGlobalObjectWithPrototype()1440 void tst_QScriptEngine::customGlobalObjectWithPrototype()
1441 {
1442 for (int x = 0; x < 2; ++x) {
1443 QScriptEngine engine;
1444 QScriptValue wrap = engine.newObject();
1445 QScriptValue global = engine.globalObject();
1446 QScriptValue originalGlobalProto = global.prototype();
1447 if (!x) {
1448 // Set prototype before setting global object
1449 wrap.setPrototype(global);
1450 QVERIFY(wrap.prototype().strictlyEquals(global));
1451 engine.setGlobalObject(wrap);
1452 } else {
1453 // Set prototype after setting global object
1454 engine.setGlobalObject(wrap);
1455 wrap.setPrototype(global);
1456 QVERIFY(wrap.prototype().strictlyEquals(global));
1457 }
1458 {
1459 QScriptValue ret = engine.evaluate("print");
1460 QVERIFY(ret.isFunction());
1461 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1462 }
1463 {
1464 QScriptValue ret = engine.evaluate("this.print");
1465 QVERIFY(ret.isFunction());
1466 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1467 }
1468 {
1469 QScriptValue ret = engine.evaluate("hasOwnProperty('print')");
1470 QVERIFY(ret.isBool());
1471 QVERIFY(!ret.toBool());
1472 }
1473 {
1474 QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')");
1475 QVERIFY(ret.isBool());
1476 QVERIFY(!ret.toBool());
1477 }
1478
1479 QScriptValue anotherProto = engine.newObject();
1480 anotherProto.setProperty("anotherProtoProperty", 123);
1481 global.setPrototype(anotherProto);
1482 {
1483 QScriptValue ret = engine.evaluate("print");
1484 QVERIFY(ret.isFunction());
1485 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1486 }
1487 {
1488 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1489 QVERIFY(ret.isNumber());
1490 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1491 }
1492 {
1493 QScriptValue ret = engine.evaluate("this.anotherProtoProperty");
1494 QVERIFY(ret.isNumber());
1495 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1496 }
1497
1498 wrap.setPrototype(anotherProto);
1499 {
1500 QScriptValue ret = engine.evaluate("print");
1501 QVERIFY(ret.isError());
1502 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: print"));
1503 }
1504 {
1505 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1506 QVERIFY(ret.isNumber());
1507 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1508 }
1509 QVERIFY(global.prototype().strictlyEquals(anotherProto));
1510
1511 global.setPrototype(originalGlobalProto);
1512 engine.setGlobalObject(global);
1513 {
1514 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1515 QVERIFY(ret.isError());
1516 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: anotherProtoProperty"));
1517 }
1518 {
1519 QScriptValue ret = engine.evaluate("print");
1520 QVERIFY(ret.isFunction());
1521 QVERIFY(ret.strictlyEquals(global.property("print")));
1522 }
1523 QVERIFY(!anotherProto.property("print").isValid());
1524 }
1525 }
1526
globalObjectWithCustomPrototype()1527 void tst_QScriptEngine::globalObjectWithCustomPrototype()
1528 {
1529 QScriptEngine engine;
1530 QScriptValue proto = engine.newObject();
1531 proto.setProperty("protoProperty", 123);
1532 QScriptValue global = engine.globalObject();
1533 QScriptValue originalProto = global.prototype();
1534 global.setPrototype(proto);
1535 {
1536 QScriptValue ret = engine.evaluate("protoProperty");
1537 QVERIFY(ret.isNumber());
1538 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1539 }
1540 {
1541 QScriptValue ret = engine.evaluate("this.protoProperty");
1542 QVERIFY(ret.isNumber());
1543 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1544 }
1545 {
1546 QScriptValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
1547 QVERIFY(ret.isBool());
1548 QVERIFY(!ret.toBool());
1549 }
1550 {
1551 QScriptValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
1552 QVERIFY(ret.isBool());
1553 QVERIFY(!ret.toBool());
1554 }
1555
1556 // Custom prototype set from JS
1557 {
1558 QScriptValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
1559 QVERIFY(ret.isNumber());
1560 QEXPECT_FAIL("", "QTBUG-9737: Prototype change in JS not reflected on C++ side", Continue);
1561 QVERIFY(ret.strictlyEquals(global.property("a")));
1562 }
1563 }
1564
builtinFunctionNames_data()1565 void tst_QScriptEngine::builtinFunctionNames_data()
1566 {
1567 QTest::addColumn<QString>("expression");
1568 QTest::addColumn<QString>("expectedName");
1569
1570 // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects".
1571
1572 QTest::newRow("print") << QString("print") << QString("print"); // QtScript extension.
1573 QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt");
1574 QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat");
1575 QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN");
1576 QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite");
1577 QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI");
1578 QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent");
1579 QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI");
1580 QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent");
1581 QTest::newRow("escape") << QString("escape") << QString("escape");
1582 QTest::newRow("unescape") << QString("unescape") << QString("unescape");
1583 QTest::newRow("version") << QString("version") << QString("version"); // QtScript extension.
1584 QTest::newRow("gc") << QString("gc") << QString("gc"); // QtScript extension.
1585
1586 QTest::newRow("Array") << QString("Array") << QString("Array");
1587 QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString");
1588 QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString");
1589 QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat");
1590 QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join");
1591 QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop");
1592 QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push");
1593 QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse");
1594 QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift");
1595 QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice");
1596 QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort");
1597 QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice");
1598 QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift");
1599
1600 QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean");
1601 QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString");
1602
1603 QTest::newRow("Date") << QString("Date") << QString("Date");
1604 QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString");
1605 QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString");
1606 QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString");
1607 QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString");
1608 QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString");
1609 QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString");
1610 QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf");
1611 QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime");
1612 QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear");
1613 QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear");
1614 QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear");
1615 QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth");
1616 QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth");
1617 QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate");
1618 QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate");
1619 QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay");
1620 QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay");
1621 QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours");
1622 QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours");
1623 QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes");
1624 QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes");
1625 QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds");
1626 QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds");
1627 QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds");
1628 QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds");
1629 QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset");
1630 QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime");
1631 QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds");
1632 QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds");
1633 QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds");
1634 QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds");
1635 QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes");
1636 QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes");
1637 QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours");
1638 QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours");
1639 QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate");
1640 QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate");
1641 QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth");
1642 QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth");
1643 QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear");
1644 QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear");
1645 QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear");
1646 QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString");
1647 QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString");
1648
1649 QTest::newRow("Error") << QString("Error") << QString("Error");
1650 // QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace");
1651 QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString");
1652
1653 QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError");
1654 QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError");
1655 QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError");
1656 QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError");
1657 QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError");
1658 QTest::newRow("URIError") << QString("URIError") << QString("URIError");
1659
1660 QTest::newRow("Function") << QString("Function") << QString("Function");
1661 QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString");
1662 QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply");
1663 QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call");
1664 QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect");
1665 QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");
1666
1667 QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs");
1668 QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos");
1669 QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin");
1670 QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan");
1671 QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2");
1672 QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil");
1673 QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos");
1674 QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp");
1675 QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor");
1676 QTest::newRow("Math.log") << QString("Math.log") << QString("log");
1677 QTest::newRow("Math.max") << QString("Math.max") << QString("max");
1678 QTest::newRow("Math.min") << QString("Math.min") << QString("min");
1679 QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
1680 QTest::newRow("Math.random") << QString("Math.random") << QString("random");
1681 QTest::newRow("Math.round") << QString("Math.round") << QString("round");
1682 QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
1683 QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
1684 QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
1685
1686 QTest::newRow("Number") << QString("Number") << QString("Number");
1687 QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
1688 QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
1689 QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
1690 QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed");
1691 QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential");
1692 QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision");
1693
1694 QTest::newRow("Object") << QString("Object") << QString("Object");
1695 QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString");
1696 QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString");
1697 QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf");
1698 QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty");
1699 QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf");
1700 QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable");
1701 QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__");
1702 QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__");
1703
1704 QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp");
1705 QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec");
1706 QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test");
1707 QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString");
1708
1709 QTest::newRow("String") << QString("String") << QString("String");
1710 QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString");
1711 QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf");
1712 QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
1713 QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
1714 QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
1715 QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
1716 QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
1717 QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
1718 QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match");
1719 QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace");
1720 QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
1721 QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
1722 QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
1723 QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
1724 QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
1725 QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
1726 QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase");
1727 QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase");
1728 }
1729
builtinFunctionNames()1730 void tst_QScriptEngine::builtinFunctionNames()
1731 {
1732 QFETCH(QString, expression);
1733 QFETCH(QString, expectedName);
1734 QScriptEngine eng;
1735 // The "name" property is actually non-standard, but JSC supports it.
1736 QScriptValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression));
1737 QVERIFY(ret.isString());
1738 QCOMPARE(ret.toString(), expectedName);
1739 }
1740
checkSyntax_data()1741 void tst_QScriptEngine::checkSyntax_data()
1742 {
1743 QTest::addColumn<QString>("code");
1744 QTest::addColumn<int>("expectedState");
1745 QTest::addColumn<int>("errorLineNumber");
1746 QTest::addColumn<int>("errorColumnNumber");
1747 QTest::addColumn<QString>("errorMessage");
1748
1749 QTest::newRow("0")
1750 << QString("0") << int(QScriptSyntaxCheckResult::Valid)
1751 << -1 << -1 << "";
1752 QTest::newRow("if (")
1753 << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate)
1754 << 1 << 4 << "";
1755 QTest::newRow("if else")
1756 << QString("\nif else") << int(QScriptSyntaxCheckResult::Error)
1757 << 2 << 4 << "Expected `('";
1758 QTest::newRow("foo[")
1759 << QString("foo[") << int(QScriptSyntaxCheckResult::Error)
1760 << 1 << 4 << "";
1761 QTest::newRow("foo['bar']")
1762 << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid)
1763 << -1 << -1 << "";
1764
1765 QTest::newRow("/*")
1766 << QString("/*") << int(QScriptSyntaxCheckResult::Intermediate)
1767 << 1 << 1 << "Unclosed comment at end of file";
1768 QTest::newRow("/*\nMy comment")
1769 << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Intermediate)
1770 << 1 << 1 << "Unclosed comment at end of file";
1771 QTest::newRow("/*\nMy comment */\nfoo = 10")
1772 << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid)
1773 << -1 << -1 << "";
1774 QTest::newRow("foo = 10 /*")
1775 << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Intermediate)
1776 << -1 << -1 << "";
1777 QTest::newRow("foo = 10; /*")
1778 << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Intermediate)
1779 << 1 << 11 << "Expected `end of file'";
1780 QTest::newRow("foo = 10 /* My comment */")
1781 << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid)
1782 << -1 << -1 << "";
1783
1784 QTest::newRow("/=/")
1785 << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1786 QTest::newRow("/=/g")
1787 << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1788 QTest::newRow("/a/")
1789 << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1790 QTest::newRow("/a/g")
1791 << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1792 }
1793
checkSyntax()1794 void tst_QScriptEngine::checkSyntax()
1795 {
1796 QFETCH(QString, code);
1797 QFETCH(int, expectedState);
1798 QFETCH(int, errorLineNumber);
1799 QFETCH(int, errorColumnNumber);
1800 QFETCH(QString, errorMessage);
1801
1802 QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code);
1803 QCOMPARE(result.state(), QScriptSyntaxCheckResult::State(expectedState));
1804 QCOMPARE(result.errorLineNumber(), errorLineNumber);
1805 QCOMPARE(result.errorColumnNumber(), errorColumnNumber);
1806 QCOMPARE(result.errorMessage(), errorMessage);
1807
1808 // assignment
1809 {
1810 QScriptSyntaxCheckResult copy = result;
1811 QCOMPARE(copy.state(), result.state());
1812 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
1813 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
1814 QCOMPARE(copy.errorMessage(), result.errorMessage());
1815 }
1816 {
1817 QScriptSyntaxCheckResult copy(result);
1818 QCOMPARE(copy.state(), result.state());
1819 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
1820 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
1821 QCOMPARE(copy.errorMessage(), result.errorMessage());
1822 }
1823 }
1824
canEvaluate_data()1825 void tst_QScriptEngine::canEvaluate_data()
1826 {
1827 QTest::addColumn<QString>("code");
1828 QTest::addColumn<bool>("expectSuccess");
1829
1830 QTest::newRow("") << QString("") << true;
1831 QTest::newRow("0") << QString("0") << true;
1832 QTest::newRow("!") << QString("!\n") << false;
1833 QTest::newRow("if (") << QString("if (\n") << false;
1834 QTest::newRow("if (10) //") << QString("if (10) //\n") << false;
1835 QTest::newRow("a = 1; if (") << QString("a = 1;\nif (\n") << false;
1836 QTest::newRow("./test.js") << QString("./test.js\n") << true;
1837 QTest::newRow("if (0) print(1)") << QString("if (0)\nprint(1)\n") << true;
1838 QTest::newRow("0 = ") << QString("0 = \n") << false;
1839 QTest::newRow("0 = 0") << QString("0 = 0\n") << true;
1840 QTest::newRow("foo[") << QString("foo[") << true; // automatic semicolon will be inserted
1841 QTest::newRow("foo[") << QString("foo[\n") << false;
1842 QTest::newRow("foo['bar']") << QString("foo['bar']") << true;
1843
1844 QTest::newRow("/*") << QString("/*") << false;
1845 QTest::newRow("/*\nMy comment") << QString("/*\nMy comment") << false;
1846 QTest::newRow("/*\nMy comment */\nfoo = 10") << QString("/*\nMy comment */\nfoo = 10") << true;
1847 QTest::newRow("foo = 10 /*") << QString("foo = 10 /*") << false;
1848 QTest::newRow("foo = 10; /*") << QString("foo = 10; /*") << false;
1849 QTest::newRow("foo = 10 /* My comment */") << QString("foo = 10 /* My comment */") << true;
1850
1851 QTest::newRow("/=/") << QString("/=/") << true;
1852 QTest::newRow("/=/g") << QString("/=/g") << true;
1853 QTest::newRow("/a/") << QString("/a/") << true;
1854 QTest::newRow("/a/g") << QString("/a/g") << true;
1855 }
1856
canEvaluate()1857 void tst_QScriptEngine::canEvaluate()
1858 {
1859 QFETCH(QString, code);
1860 QFETCH(bool, expectSuccess);
1861
1862 QScriptEngine eng;
1863 QCOMPARE(eng.canEvaluate(code), expectSuccess);
1864 }
1865
evaluate_data()1866 void tst_QScriptEngine::evaluate_data()
1867 {
1868 QTest::addColumn<QString>("code");
1869 QTest::addColumn<int>("lineNumber");
1870 QTest::addColumn<bool>("expectHadError");
1871 QTest::addColumn<int>("expectErrorLineNumber");
1872
1873 QTest::newRow("(newline)") << QString("\n") << -1 << false << -1;
1874 QTest::newRow("0 //") << QString("0 //") << -1 << false << -1;
1875 QTest::newRow("/* */") << QString("/* */") << -1 << false << -1;
1876 QTest::newRow("//") << QString("//") << -1 << false << -1;
1877 QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1;
1878 QTest::newRow("(empty)") << QString("") << -1 << false << -1;
1879 QTest::newRow("0") << QString("0") << -1 << false << -1;
1880 QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2;
1881 QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1;
1882 QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2;
1883
1884 QTest::newRow("f()") << QString("function f()\n"
1885 "{\n"
1886 " var a;\n"
1887 " var b=\";\n" // here's the error
1888 "}\n"
1889 "f();\n")
1890 << -1 << true << 4;
1891
1892 QTest::newRow("0") << QString("0") << 10 << false << -1;
1893 QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 13;
1894 QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1;
1895 QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12;
1896
1897 QTest::newRow("f()") << QString("function f()\n"
1898 "{\n"
1899 " var a;\n"
1900 "\n\n"
1901 " var b=\";\n" // here's the error
1902 "}\n"
1903 "f();\n")
1904 << 10 << true << 15;
1905 QTest::newRow("functionThatDoesntExist()")
1906 << QString(";\n;\n;\nfunctionThatDoesntExist()")
1907 << -1 << true << 4;
1908 QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }")
1909 << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }")
1910 << 4 << true << 5;
1911 QTest::newRow("duplicateLabel: { duplicateLabel: ; }")
1912 << QString("duplicateLabel: { duplicateLabel: ; }")
1913 << 12 << true << 12;
1914
1915 QTest::newRow("/=/") << QString("/=/") << -1 << false << -1;
1916 QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1;
1917 QTest::newRow("/a/") << QString("/a/") << -1 << false << -1;
1918 QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1;
1919 QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1;
1920 QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1;
1921 }
1922
evaluate()1923 void tst_QScriptEngine::evaluate()
1924 {
1925 QFETCH(QString, code);
1926 QFETCH(int, lineNumber);
1927 QFETCH(bool, expectHadError);
1928 QFETCH(int, expectErrorLineNumber);
1929
1930 QScriptEngine eng;
1931 QScriptValue ret;
1932 if (lineNumber != -1)
1933 ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber);
1934 else
1935 ret = eng.evaluate(code);
1936 QCOMPARE(eng.hasUncaughtException(), expectHadError);
1937 QCOMPARE(eng.uncaughtExceptionLineNumber(), expectErrorLineNumber);
1938 if (eng.hasUncaughtException() && ret.isError())
1939 QVERIFY(ret.property("lineNumber").strictlyEquals(QScriptValue(&eng, expectErrorLineNumber)));
1940 else
1941 QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
1942 }
1943
eval_nested(QScriptContext * ctx,QScriptEngine * eng)1944 static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng)
1945 {
1946 QScriptValue result = eng->newObject();
1947 eng->evaluate("var bar = 'local';");
1948 result.setProperty("thisObjectIdBefore", ctx->thisObject().property("id"));
1949 QScriptValue evaluatedThisObject = eng->evaluate("this");
1950 result.setProperty("thisObjectIdAfter", ctx->thisObject().property("id"));
1951 result.setProperty("evaluatedThisObjectId", evaluatedThisObject.property("id"));
1952 result.setProperty("local_bar", eng->evaluate("bar"));
1953
1954 return result;
1955 }
1956
1957 // Tests that nested evaluate uses the "this" that was passed.
nestedEvaluate()1958 void tst_QScriptEngine::nestedEvaluate()
1959 {
1960 QScriptEngine eng;
1961 QScriptValue fun = eng.newFunction(eval_nested);
1962 eng.globalObject().setProperty("fun", fun);
1963 // From JS function call
1964 {
1965 QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()");
1966 QCOMPARE(result.property("local_bar").toString(), QString("local"));
1967 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
1968 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
1969 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
1970 QScriptValue bar = eng.evaluate("bar"); // Was introduced in local scope.
1971 QVERIFY(bar.isError());
1972 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
1973 }
1974 // From QScriptValue::call()
1975 {
1976 QScriptValue result = fun.call(eng.evaluate("p = { id:'foo' }") , QScriptValueList() );
1977 QCOMPARE(result.property("local_bar").toString(), QString("local"));
1978 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
1979 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
1980 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
1981 QScriptValue bar = eng.evaluate("bar");
1982 QVERIFY(bar.isError());
1983 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
1984 }
1985 }
1986
uncaughtException()1987 void tst_QScriptEngine::uncaughtException()
1988 {
1989 QScriptEngine eng;
1990 QScriptValue fun = eng.newFunction(myFunction);
1991 QScriptValue throwFun = eng.newFunction(myThrowingFunction);
1992 for (int x = -1; x < 2; ++x) {
1993 {
1994 QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", /*fileName=*/QString(), /*lineNumber=*/x);
1995 QVERIFY(eng.hasUncaughtException());
1996 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
1997 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
1998 (void)ret.toString();
1999 QVERIFY(eng.hasUncaughtException());
2000 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2001 QVERIFY(fun.call().isNull());
2002 QVERIFY(eng.hasUncaughtException());
2003 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2004 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2005 eng.clearExceptions();
2006 QVERIFY(!eng.hasUncaughtException());
2007 QCOMPARE(eng.uncaughtExceptionLineNumber(), -1);
2008 QVERIFY(!eng.uncaughtException().isValid());
2009
2010 eng.evaluate("2 = 3");
2011 QVERIFY(eng.hasUncaughtException());
2012 QScriptValue ret2 = throwFun.call();
2013 QVERIFY(ret2.isError());
2014 QVERIFY(eng.hasUncaughtException());
2015 QVERIFY(eng.uncaughtException().strictlyEquals(ret2));
2016 QCOMPARE(eng.uncaughtExceptionLineNumber(), 0);
2017 eng.clearExceptions();
2018 QVERIFY(!eng.hasUncaughtException());
2019 eng.evaluate("1 + 2");
2020 QVERIFY(!eng.hasUncaughtException());
2021 }
2022 {
2023 QScriptValue ret = eng.evaluate("a = 10");
2024 QVERIFY(!eng.hasUncaughtException());
2025 QVERIFY(!eng.uncaughtException().isValid());
2026 }
2027 {
2028 QScriptValue ret = eng.evaluate("1 = 2");
2029 QVERIFY(eng.hasUncaughtException());
2030 eng.clearExceptions();
2031 QVERIFY(!eng.hasUncaughtException());
2032 }
2033 {
2034 eng.globalObject().setProperty("throwFun", throwFun);
2035 eng.evaluate("1;\nthrowFun();");
2036 QVERIFY(eng.hasUncaughtException());
2037 QCOMPARE(eng.uncaughtExceptionLineNumber(), 2);
2038 eng.clearExceptions();
2039 QVERIFY(!eng.hasUncaughtException());
2040 }
2041 }
2042 }
2043
errorMessage_QT679()2044 void tst_QScriptEngine::errorMessage_QT679()
2045 {
2046 QScriptEngine engine;
2047 engine.globalObject().setProperty("foo", 15);
2048 QScriptValue error = engine.evaluate("'hello world';\nfoo.bar.blah");
2049 QVERIFY(error.isError());
2050 // The exact message is back-end specific and subject to change.
2051 QCOMPARE(error.toString(), QString::fromLatin1("TypeError: Result of expression 'foo.bar' [undefined] is not an object."));
2052 }
2053
2054 struct Foo {
2055 public:
2056 int x, y;
FooFoo2057 Foo() : x(-1), y(-1) { }
2058 };
2059
2060 Q_DECLARE_METATYPE(Foo)
Q_DECLARE_METATYPE(Foo *)2061 Q_DECLARE_METATYPE(Foo*)
2062
2063 void tst_QScriptEngine::getSetDefaultPrototype_int()
2064 {
2065 QScriptEngine eng;
2066
2067 QScriptValue object = eng.newObject();
2068 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).isValid(), false);
2069 eng.setDefaultPrototype(qMetaTypeId<int>(), object);
2070 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).strictlyEquals(object), true);
2071 QScriptValue value = eng.newVariant(int(123));
2072 QCOMPARE(value.prototype().isObject(), true);
2073 QCOMPARE(value.prototype().strictlyEquals(object), true);
2074
2075 eng.setDefaultPrototype(qMetaTypeId<int>(), QScriptValue());
2076 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).isValid(), false);
2077 QScriptValue value2 = eng.newVariant(int(123));
2078 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2079 }
2080
getSetDefaultPrototype_customType()2081 void tst_QScriptEngine::getSetDefaultPrototype_customType()
2082 {
2083 QScriptEngine eng;
2084
2085 QScriptValue object = eng.newObject();
2086 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).isValid(), false);
2087 eng.setDefaultPrototype(qMetaTypeId<Foo>(), object);
2088 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(object), true);
2089 QScriptValue value = eng.newVariant(qVariantFromValue(Foo()));
2090 QCOMPARE(value.prototype().isObject(), true);
2091 QCOMPARE(value.prototype().strictlyEquals(object), true);
2092
2093 eng.setDefaultPrototype(qMetaTypeId<Foo>(), QScriptValue());
2094 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).isValid(), false);
2095 QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo()));
2096 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2097 }
2098
fooToScriptValue(QScriptEngine * eng,const Foo & foo)2099 static QScriptValue fooToScriptValue(QScriptEngine *eng, const Foo &foo)
2100 {
2101 QScriptValue obj = eng->newObject();
2102 obj.setProperty("x", QScriptValue(eng, foo.x));
2103 obj.setProperty("y", QScriptValue(eng, foo.y));
2104 return obj;
2105 }
2106
fooFromScriptValue(const QScriptValue & value,Foo & foo)2107 static void fooFromScriptValue(const QScriptValue &value, Foo &foo)
2108 {
2109 foo.x = value.property("x").toInt32();
2110 foo.y = value.property("y").toInt32();
2111 }
2112
fooToScriptValueV2(QScriptEngine * eng,const Foo & foo)2113 static QScriptValue fooToScriptValueV2(QScriptEngine *eng, const Foo &foo)
2114 {
2115 return QScriptValue(eng, foo.x);
2116 }
2117
fooFromScriptValueV2(const QScriptValue & value,Foo & foo)2118 static void fooFromScriptValueV2(const QScriptValue &value, Foo &foo)
2119 {
2120 foo.x = value.toInt32();
2121 }
2122
2123 Q_DECLARE_METATYPE(QLinkedList<QString>)
Q_DECLARE_METATYPE(QList<Foo>)2124 Q_DECLARE_METATYPE(QList<Foo>)
2125 Q_DECLARE_METATYPE(QVector<QChar>)
2126 Q_DECLARE_METATYPE(QStack<int>)
2127 Q_DECLARE_METATYPE(QQueue<char>)
2128 Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
2129
2130 void tst_QScriptEngine::valueConversion_basic()
2131 {
2132 QScriptEngine eng;
2133 {
2134 QScriptValue num = qScriptValueFromValue(&eng, 123);
2135 QCOMPARE(num.isNumber(), true);
2136 QCOMPARE(num.strictlyEquals(QScriptValue(&eng, 123)), true);
2137
2138 int inum = qScriptValueToValue<int>(num);
2139 QCOMPARE(inum, 123);
2140
2141 QString snum = qScriptValueToValue<QString>(num);
2142 QCOMPARE(snum, QLatin1String("123"));
2143 }
2144 {
2145 QScriptValue num = eng.toScriptValue(123);
2146 QCOMPARE(num.isNumber(), true);
2147 QCOMPARE(num.strictlyEquals(QScriptValue(&eng, 123)), true);
2148
2149 int inum = eng.fromScriptValue<int>(num);
2150 QCOMPARE(inum, 123);
2151
2152 QString snum = eng.fromScriptValue<QString>(num);
2153 QCOMPARE(snum, QLatin1String("123"));
2154 }
2155 {
2156 QScriptValue num(&eng, 123);
2157 QCOMPARE(qScriptValueToValue<char>(num), char(123));
2158 QCOMPARE(qScriptValueToValue<unsigned char>(num), (unsigned char)(123));
2159 QCOMPARE(qScriptValueToValue<short>(num), short(123));
2160 QCOMPARE(qScriptValueToValue<unsigned short>(num), (unsigned short)(123));
2161 QCOMPARE(qScriptValueToValue<float>(num), float(123));
2162 QCOMPARE(qScriptValueToValue<double>(num), double(123));
2163 QCOMPARE(qScriptValueToValue<qlonglong>(num), qlonglong(123));
2164 QCOMPARE(qScriptValueToValue<qulonglong>(num), qulonglong(123));
2165 }
2166 {
2167 QScriptValue num(123);
2168 QCOMPARE(qScriptValueToValue<char>(num), char(123));
2169 QCOMPARE(qScriptValueToValue<unsigned char>(num), (unsigned char)(123));
2170 QCOMPARE(qScriptValueToValue<short>(num), short(123));
2171 QCOMPARE(qScriptValueToValue<unsigned short>(num), (unsigned short)(123));
2172 QCOMPARE(qScriptValueToValue<float>(num), float(123));
2173 QCOMPARE(qScriptValueToValue<double>(num), double(123));
2174 QCOMPARE(qScriptValueToValue<qlonglong>(num), qlonglong(123));
2175 QCOMPARE(qScriptValueToValue<qulonglong>(num), qulonglong(123));
2176 }
2177
2178 {
2179 QScriptValue num = qScriptValueFromValue(&eng, Q_INT64_C(0x100000000));
2180 QCOMPARE(qScriptValueToValue<qlonglong>(num), Q_INT64_C(0x100000000));
2181 QCOMPARE(qScriptValueToValue<qulonglong>(num), Q_UINT64_C(0x100000000));
2182 }
2183
2184 {
2185 QChar c = QLatin1Char('c');
2186 QScriptValue str = QScriptValue(&eng, "ciao");
2187 QCOMPARE(qScriptValueToValue<QChar>(str), c);
2188 QScriptValue code = QScriptValue(&eng, c.unicode());
2189 QCOMPARE(qScriptValueToValue<QChar>(code), c);
2190 QCOMPARE(qScriptValueToValue<QChar>(qScriptValueFromValue(&eng, c)), c);
2191 }
2192 }
2193
valueConversion_customType()2194 void tst_QScriptEngine::valueConversion_customType()
2195 {
2196 QScriptEngine eng;
2197 {
2198 // a type that we don't have built-in conversion of
2199 // (it's stored as a variant)
2200 QTime tm(1, 2, 3, 4);
2201 QScriptValue val = qScriptValueFromValue(&eng, tm);
2202 QCOMPARE(qScriptValueToValue<QTime>(val), tm);
2203 }
2204
2205 {
2206 Foo foo;
2207 foo.x = 12;
2208 foo.y = 34;
2209 QScriptValue fooVal = qScriptValueFromValue(&eng, foo);
2210 QCOMPARE(fooVal.isVariant(), true);
2211
2212 Foo foo2 = qScriptValueToValue<Foo>(fooVal);
2213 QCOMPARE(foo2.x, foo.x);
2214 QCOMPARE(foo2.y, foo.y);
2215 }
2216
2217 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValue, fooFromScriptValue);
2218
2219 {
2220 Foo foo;
2221 foo.x = 12;
2222 foo.y = 34;
2223 QScriptValue fooVal = qScriptValueFromValue(&eng, foo);
2224 QCOMPARE(fooVal.isObject(), true);
2225 QVERIFY(fooVal.prototype().strictlyEquals(eng.evaluate("Object.prototype")));
2226 QCOMPARE(fooVal.property("x").strictlyEquals(QScriptValue(&eng, 12)), true);
2227 QCOMPARE(fooVal.property("y").strictlyEquals(QScriptValue(&eng, 34)), true);
2228 fooVal.setProperty("x", QScriptValue(&eng, 56));
2229 fooVal.setProperty("y", QScriptValue(&eng, 78));
2230
2231 Foo foo2 = qScriptValueToValue<Foo>(fooVal);
2232 QCOMPARE(foo2.x, 56);
2233 QCOMPARE(foo2.y, 78);
2234
2235 QScriptValue fooProto = eng.newObject();
2236 eng.setDefaultPrototype(qMetaTypeId<Foo>(), fooProto);
2237 QScriptValue fooVal2 = qScriptValueFromValue(&eng, foo2);
2238 QVERIFY(fooVal2.prototype().strictlyEquals(fooProto));
2239 QVERIFY(fooVal2.property("x").strictlyEquals(QScriptValue(&eng, 56)));
2240 QVERIFY(fooVal2.property("y").strictlyEquals(QScriptValue(&eng, 78)));
2241 }
2242 }
2243
valueConversion_sequence()2244 void tst_QScriptEngine::valueConversion_sequence()
2245 {
2246 QScriptEngine eng;
2247 qScriptRegisterSequenceMetaType<QLinkedList<QString> >(&eng);
2248
2249 {
2250 QLinkedList<QString> lst;
2251 lst << QLatin1String("foo") << QLatin1String("bar");
2252 QScriptValue lstVal = qScriptValueFromValue(&eng, lst);
2253 QCOMPARE(lstVal.isArray(), true);
2254 QCOMPARE(lstVal.property("length").toInt32(), 2);
2255 QCOMPARE(lstVal.property("0").isString(), true);
2256 QCOMPARE(lstVal.property("0").toString(), QLatin1String("foo"));
2257 QCOMPARE(lstVal.property("1").isString(), true);
2258 QCOMPARE(lstVal.property("1").toString(), QLatin1String("bar"));
2259 }
2260
2261 qScriptRegisterSequenceMetaType<QList<Foo> >(&eng);
2262 qScriptRegisterSequenceMetaType<QStack<int> >(&eng);
2263 qScriptRegisterSequenceMetaType<QVector<QChar> >(&eng);
2264 qScriptRegisterSequenceMetaType<QQueue<char> >(&eng);
2265 qScriptRegisterSequenceMetaType<QLinkedList<QStack<int> > >(&eng);
2266
2267 {
2268 QLinkedList<QStack<int> > lst;
2269 QStack<int> first; first << 13 << 49; lst << first;
2270 QStack<int> second; second << 99999;lst << second;
2271 QScriptValue lstVal = qScriptValueFromValue(&eng, lst);
2272 QCOMPARE(lstVal.isArray(), true);
2273 QCOMPARE(lstVal.property("length").toInt32(), 2);
2274 QCOMPARE(lstVal.property("0").isArray(), true);
2275 QCOMPARE(lstVal.property("0").property("length").toInt32(), 2);
2276 QCOMPARE(lstVal.property("0").property("0").toInt32(), first.at(0));
2277 QCOMPARE(lstVal.property("0").property("1").toInt32(), first.at(1));
2278 QCOMPARE(lstVal.property("1").isArray(), true);
2279 QCOMPARE(lstVal.property("1").property("length").toInt32(), 1);
2280 QCOMPARE(lstVal.property("1").property("0").toInt32(), second.at(0));
2281 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("0")), first);
2282 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("1")), second);
2283 QCOMPARE(qscriptvalue_cast<QLinkedList<QStack<int> > >(lstVal), lst);
2284 }
2285
2286 // pointers
2287 {
2288 Foo foo;
2289 {
2290 QScriptValue v = qScriptValueFromValue(&eng, &foo);
2291 Foo *pfoo = qscriptvalue_cast<Foo*>(v);
2292 QCOMPARE(pfoo, &foo);
2293 }
2294 {
2295 Foo *pfoo = 0;
2296 QScriptValue v = qScriptValueFromValue(&eng, pfoo);
2297 QCOMPARE(v.isNull(), true);
2298 QVERIFY(qscriptvalue_cast<Foo*>(v) == 0);
2299 }
2300 }
2301
2302 // QList<int> and QObjectList should be converted from/to arrays by default
2303 {
2304 QList<int> lst;
2305 lst << 1 << 2 << 3;
2306 QScriptValue val = qScriptValueFromValue(&eng, lst);
2307 QVERIFY(val.isArray());
2308 QCOMPARE(val.property("length").toInt32(), lst.size());
2309 QCOMPARE(val.property(0).toInt32(), lst.at(0));
2310 QCOMPARE(val.property(1).toInt32(), lst.at(1));
2311 QCOMPARE(val.property(2).toInt32(), lst.at(2));
2312
2313 QCOMPARE(qscriptvalue_cast<QList<int> >(val), lst);
2314 }
2315 {
2316 QObjectList lst;
2317 lst << this;
2318 QScriptValue val = qScriptValueFromValue(&eng, lst);
2319 QVERIFY(val.isArray());
2320 QCOMPARE(val.property("length").toInt32(), lst.size());
2321 QCOMPARE(val.property(0).toQObject(), (QObject *)this);
2322
2323 QCOMPARE(qscriptvalue_cast<QObjectList>(val), lst);
2324 }
2325 }
2326
valueConversion_QVariant()2327 void tst_QScriptEngine::valueConversion_QVariant()
2328 {
2329 QScriptEngine eng;
2330 // qScriptValueFromValue() should be "smart" when the argument is a QVariant
2331 {
2332 QScriptValue val = qScriptValueFromValue(&eng, QVariant());
2333 QVERIFY(!val.isVariant());
2334 QVERIFY(val.isUndefined());
2335 }
2336 // Checking nested QVariants
2337 {
2338 QVariant tmp1;
2339 QVariant tmp2(QMetaType::QVariant, &tmp1);
2340 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2341
2342 QScriptValue val1 = qScriptValueFromValue(&eng, tmp1);
2343 QScriptValue val2 = qScriptValueFromValue(&eng, tmp2);
2344 QVERIFY(val1.isValid());
2345 QVERIFY(val2.isValid());
2346 QVERIFY(val1.isUndefined());
2347 QVERIFY(!val2.isUndefined());
2348 QVERIFY(!val1.isVariant());
2349 QVERIFY(val2.isVariant());
2350 }
2351 {
2352 QVariant tmp1(123);
2353 QVariant tmp2(QMetaType::QVariant, &tmp1);
2354 QVariant tmp3(QMetaType::QVariant, &tmp2);
2355 QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int);
2356 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2357 QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant);
2358
2359 QScriptValue val1 = qScriptValueFromValue(&eng, tmp2);
2360 QScriptValue val2 = qScriptValueFromValue(&eng, tmp3);
2361 QVERIFY(val1.isValid());
2362 QVERIFY(val2.isValid());
2363 QVERIFY(val1.isVariant());
2364 QVERIFY(val2.isVariant());
2365 QVERIFY(val1.toVariant().toInt() == 123);
2366 QVERIFY(qScriptValueFromValue(&eng, val2.toVariant()).toVariant().toInt() == 123);
2367 }
2368 {
2369 QScriptValue val = qScriptValueFromValue(&eng, QVariant(true));
2370 QVERIFY(!val.isVariant());
2371 QVERIFY(val.isBoolean());
2372 QCOMPARE(val.toBoolean(), true);
2373 }
2374 {
2375 QScriptValue val = qScriptValueFromValue(&eng, QVariant(int(123)));
2376 QVERIFY(!val.isVariant());
2377 QVERIFY(val.isNumber());
2378 QCOMPARE(val.toNumber(), qsreal(123));
2379 }
2380 {
2381 QScriptValue val = qScriptValueFromValue(&eng, QVariant(qsreal(1.25)));
2382 QVERIFY(!val.isVariant());
2383 QVERIFY(val.isNumber());
2384 QCOMPARE(val.toNumber(), qsreal(1.25));
2385 }
2386 {
2387 QString str = QString::fromLatin1("ciao");
2388 QScriptValue val = qScriptValueFromValue(&eng, QVariant(str));
2389 QVERIFY(!val.isVariant());
2390 QVERIFY(val.isString());
2391 QCOMPARE(val.toString(), str);
2392 }
2393 {
2394 QScriptValue val = qScriptValueFromValue(&eng, qVariantFromValue((QObject*)this));
2395 QVERIFY(!val.isVariant());
2396 QVERIFY(val.isQObject());
2397 QCOMPARE(val.toQObject(), (QObject*)this);
2398 }
2399 {
2400 QVariant var = qVariantFromValue(QPoint(123, 456));
2401 QScriptValue val = qScriptValueFromValue(&eng, var);
2402 QVERIFY(val.isVariant());
2403 QCOMPARE(val.toVariant(), var);
2404 }
2405
2406 QCOMPARE(qscriptvalue_cast<QVariant>(QScriptValue(123)), QVariant(123));
2407 }
2408
valueConversion_hooliganTask248802()2409 void tst_QScriptEngine::valueConversion_hooliganTask248802()
2410 {
2411 QScriptEngine eng;
2412 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValueV2, fooFromScriptValueV2);
2413 {
2414 QScriptValue num(&eng, 123);
2415 Foo foo = qScriptValueToValue<Foo>(num);
2416 QCOMPARE(foo.x, 123);
2417 }
2418 {
2419 QScriptValue num(123);
2420 Foo foo = qScriptValueToValue<Foo>(num);
2421 QCOMPARE(foo.x, -1);
2422 }
2423 {
2424 QScriptValue str(&eng, "123");
2425 Foo foo = qScriptValueToValue<Foo>(str);
2426 QCOMPARE(foo.x, 123);
2427 }
2428
2429 }
2430
valueConversion_basic2()2431 void tst_QScriptEngine::valueConversion_basic2()
2432 {
2433 QScriptEngine eng;
2434 // more built-in types
2435 {
2436 QScriptValue val = qScriptValueFromValue(&eng, uint(123));
2437 QVERIFY(val.isNumber());
2438 QCOMPARE(val.toInt32(), 123);
2439 }
2440 {
2441 QScriptValue val = qScriptValueFromValue(&eng, qulonglong(123));
2442 QVERIFY(val.isNumber());
2443 QCOMPARE(val.toInt32(), 123);
2444 }
2445 {
2446 QScriptValue val = qScriptValueFromValue(&eng, float(123));
2447 QVERIFY(val.isNumber());
2448 QCOMPARE(val.toInt32(), 123);
2449 }
2450 {
2451 QScriptValue val = qScriptValueFromValue(&eng, short(123));
2452 QVERIFY(val.isNumber());
2453 QCOMPARE(val.toInt32(), 123);
2454 }
2455 {
2456 QScriptValue val = qScriptValueFromValue(&eng, ushort(123));
2457 QVERIFY(val.isNumber());
2458 QCOMPARE(val.toInt32(), 123);
2459 }
2460 {
2461 QScriptValue val = qScriptValueFromValue(&eng, char(123));
2462 QVERIFY(val.isNumber());
2463 QCOMPARE(val.toInt32(), 123);
2464 }
2465 {
2466 QScriptValue val = qScriptValueFromValue(&eng, uchar(123));
2467 QVERIFY(val.isNumber());
2468 QCOMPARE(val.toInt32(), 123);
2469 }
2470 }
2471
valueConversion_dateTime()2472 void tst_QScriptEngine::valueConversion_dateTime()
2473 {
2474 QScriptEngine eng;
2475 {
2476 QDateTime in = QDateTime::currentDateTime();
2477 QScriptValue val = qScriptValueFromValue(&eng, in);
2478 QVERIFY(val.isDate());
2479 QCOMPARE(val.toDateTime(), in);
2480 }
2481 {
2482 QDate in = QDate::currentDate();
2483 QScriptValue val = qScriptValueFromValue(&eng, in);
2484 QVERIFY(val.isDate());
2485 QCOMPARE(val.toDateTime().date(), in);
2486 }
2487 }
2488
valueConversion_regExp()2489 void tst_QScriptEngine::valueConversion_regExp()
2490 {
2491 QScriptEngine eng;
2492 {
2493 QRegExp in = QRegExp("foo");
2494 QScriptValue val = qScriptValueFromValue(&eng, in);
2495 QVERIFY(val.isRegExp());
2496 QRegExp out = val.toRegExp();
2497 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue);
2498 QCOMPARE(out.patternSyntax(), in.patternSyntax());
2499 QCOMPARE(out.pattern(), in.pattern());
2500 QCOMPARE(out.caseSensitivity(), in.caseSensitivity());
2501 QCOMPARE(out.isMinimal(), in.isMinimal());
2502 }
2503 {
2504 QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2);
2505 QScriptValue val = qScriptValueFromValue(&eng, in);
2506 QVERIFY(val.isRegExp());
2507 QCOMPARE(val.toRegExp(), in);
2508 }
2509 {
2510 QRegExp in = QRegExp("foo");
2511 in.setMinimal(true);
2512 QScriptValue val = qScriptValueFromValue(&eng, in);
2513 QVERIFY(val.isRegExp());
2514 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue);
2515 QCOMPARE(val.toRegExp().isMinimal(), in.isMinimal());
2516 }
2517 }
2518
qScriptValueFromValue_noEngine()2519 void tst_QScriptEngine::qScriptValueFromValue_noEngine()
2520 {
2521 QVERIFY(!qScriptValueFromValue(0, 123).isValid());
2522 QVERIFY(!qScriptValueFromValue(0, QVariant(123)).isValid());
2523 }
2524
__import__(QScriptContext * ctx,QScriptEngine * eng)2525 static QScriptValue __import__(QScriptContext *ctx, QScriptEngine *eng)
2526 {
2527 return eng->importExtension(ctx->argument(0).toString());
2528 }
2529
importExtension()2530 void tst_QScriptEngine::importExtension()
2531 {
2532 QStringList libPaths = QCoreApplication::instance()->libraryPaths();
2533 QCoreApplication::instance()->setLibraryPaths(QStringList() << SRCDIR);
2534
2535 QStringList availableExtensions;
2536 {
2537 QScriptEngine eng;
2538 QVERIFY(eng.importedExtensions().isEmpty());
2539 QStringList ret = eng.availableExtensions();
2540 QCOMPARE(ret.size(), 4);
2541 QCOMPARE(ret.at(0), QString::fromLatin1("com"));
2542 QCOMPARE(ret.at(1), QString::fromLatin1("com.trolltech"));
2543 QCOMPARE(ret.at(2), QString::fromLatin1("com.trolltech.recursive"));
2544 QCOMPARE(ret.at(3), QString::fromLatin1("com.trolltech.syntaxerror"));
2545 availableExtensions = ret;
2546 }
2547
2548 // try to import something that doesn't exist
2549 {
2550 QScriptEngine eng;
2551 QScriptValue ret = eng.importExtension("this.extension.does.not.exist");
2552 QCOMPARE(eng.hasUncaughtException(), true);
2553 QCOMPARE(ret.isError(), true);
2554 QCOMPARE(ret.toString(), QString::fromLatin1("Error: Unable to import this.extension.does.not.exist: no such extension"));
2555 }
2556
2557 {
2558 QScriptEngine eng;
2559 for (int x = 0; x < 2; ++x) {
2560 QCOMPARE(eng.globalObject().property("com").isValid(), x == 1);
2561 QScriptValue ret = eng.importExtension("com.trolltech");
2562 QCOMPARE(eng.hasUncaughtException(), false);
2563 QCOMPARE(ret.isUndefined(), true);
2564
2565 QScriptValue com = eng.globalObject().property("com");
2566 QCOMPARE(com.isObject(), true);
2567 QCOMPARE(com.property("wasDefinedAlready")
2568 .strictlyEquals(QScriptValue(&eng, false)), true);
2569 QCOMPARE(com.property("name")
2570 .strictlyEquals(QScriptValue(&eng, "com")), true);
2571 QCOMPARE(com.property("level")
2572 .strictlyEquals(QScriptValue(&eng, 1)), true);
2573 QVERIFY(com.property("originalPostInit").isUndefined());
2574 QVERIFY(com.property("postInitCallCount").strictlyEquals(1));
2575
2576 QScriptValue trolltech = com.property("trolltech");
2577 QCOMPARE(trolltech.isObject(), true);
2578 QCOMPARE(trolltech.property("wasDefinedAlready")
2579 .strictlyEquals(QScriptValue(&eng, false)), true);
2580 QCOMPARE(trolltech.property("name")
2581 .strictlyEquals(QScriptValue(&eng, "com.trolltech")), true);
2582 QCOMPARE(trolltech.property("level")
2583 .strictlyEquals(QScriptValue(&eng, 2)), true);
2584 QVERIFY(trolltech.property("originalPostInit").isUndefined());
2585 QVERIFY(trolltech.property("postInitCallCount").strictlyEquals(1));
2586 }
2587 QStringList imp = eng.importedExtensions();
2588 QCOMPARE(imp.size(), 2);
2589 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2590 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2591 QCOMPARE(eng.availableExtensions(), availableExtensions);
2592 }
2593
2594 // recursive import should throw an error
2595 {
2596 QScriptEngine eng;
2597 QVERIFY(eng.importedExtensions().isEmpty());
2598 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2599 QScriptValue ret = eng.importExtension("com.trolltech.recursive");
2600 QCOMPARE(eng.hasUncaughtException(), true);
2601 QVERIFY(ret.isError());
2602 QCOMPARE(ret.toString(), QString::fromLatin1("Error: recursive import of com.trolltech.recursive"));
2603 QStringList imp = eng.importedExtensions();
2604 QCOMPARE(imp.size(), 2);
2605 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2606 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2607 QCOMPARE(eng.availableExtensions(), availableExtensions);
2608 }
2609
2610 {
2611 QScriptEngine eng;
2612 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2613 for (int x = 0; x < 2; ++x) {
2614 if (x == 0)
2615 QVERIFY(eng.importedExtensions().isEmpty());
2616 QScriptValue ret = eng.importExtension("com.trolltech.syntaxerror");
2617 QVERIFY(eng.hasUncaughtException());
2618 QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue);
2619 QCOMPARE(eng.uncaughtExceptionLineNumber(), 4);
2620 QVERIFY(ret.isError());
2621 QVERIFY(ret.toString().contains(QLatin1String("SyntaxError")));
2622 }
2623 QStringList imp = eng.importedExtensions();
2624 QCOMPARE(imp.size(), 2);
2625 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2626 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2627 QCOMPARE(eng.availableExtensions(), availableExtensions);
2628 }
2629
2630 QCoreApplication::instance()->setLibraryPaths(libPaths);
2631 }
2632
recurse(QScriptContext * ctx,QScriptEngine * eng)2633 static QScriptValue recurse(QScriptContext *ctx, QScriptEngine *eng)
2634 {
2635 Q_UNUSED(eng);
2636 return ctx->callee().call();
2637 }
2638
recurse2(QScriptContext * ctx,QScriptEngine * eng)2639 static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng)
2640 {
2641 Q_UNUSED(eng);
2642 return ctx->callee().construct();
2643 }
2644
infiniteRecursion()2645 void tst_QScriptEngine::infiniteRecursion()
2646 {
2647 // Infinite recursion in JS should cause the VM to throw an error
2648 // when the JS stack is exhausted.
2649 // The exact error is back-end specific and subject to change.
2650 const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded.");
2651 QScriptEngine eng;
2652 {
2653 QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();");
2654 QCOMPARE(ret.isError(), true);
2655 QCOMPARE(ret.toString(), stackOverflowError);
2656 }
2657 #if 0 //The native C++ stack overflow before the JS stack
2658 {
2659 QScriptValue fun = eng.newFunction(recurse);
2660 QScriptValue ret = fun.call();
2661 QCOMPARE(ret.isError(), true);
2662 QCOMPARE(ret.toString(), stackOverflowError);
2663 }
2664 {
2665 QScriptValue fun = eng.newFunction(recurse2);
2666 QScriptValue ret = fun.construct();
2667 QCOMPARE(ret.isError(), true);
2668 QCOMPARE(ret.toString(), stackOverflowError);
2669 }
2670 #endif
2671 }
2672
2673 struct Bar {
2674 int a;
2675 };
2676
2677 struct Baz : public Bar {
2678 int b;
2679 };
2680
2681 Q_DECLARE_METATYPE(Bar*)
2682 Q_DECLARE_METATYPE(Baz*)
2683
2684 Q_DECLARE_METATYPE(QGradient)
2685 Q_DECLARE_METATYPE(QGradient*)
2686 Q_DECLARE_METATYPE(QLinearGradient)
2687
2688 class Zoo : public QObject
2689 {
2690 Q_OBJECT
2691 public:
Zoo()2692 Zoo() { }
2693 public slots:
toBaz(Bar * b)2694 Baz *toBaz(Bar *b) { return reinterpret_cast<Baz*>(b); }
2695 };
2696
castWithPrototypeChain()2697 void tst_QScriptEngine::castWithPrototypeChain()
2698 {
2699 QScriptEngine eng;
2700 Bar bar;
2701 Baz baz;
2702 QScriptValue barProto = qScriptValueFromValue(&eng, &bar);
2703 QScriptValue bazProto = qScriptValueFromValue(&eng, &baz);
2704 eng.setDefaultPrototype(qMetaTypeId<Bar*>(), barProto);
2705 eng.setDefaultPrototype(qMetaTypeId<Baz*>(), bazProto);
2706
2707 Baz baz2;
2708 baz2.a = 123;
2709 baz2.b = 456;
2710 QScriptValue baz2Value = qScriptValueFromValue(&eng, &baz2);
2711 {
2712 // qscriptvalue_cast() does magic; if the QScriptValue contains
2713 // t of type T, and the target type is T*, &t is returned.
2714 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2715 QVERIFY(pbaz != 0);
2716 QCOMPARE(pbaz->b, baz2.b);
2717
2718 Zoo zoo;
2719 QScriptValue scriptZoo = eng.newQObject(&zoo);
2720 QScriptValue toBaz = scriptZoo.property("toBaz");
2721 QVERIFY(toBaz.isFunction());
2722
2723 // no relation between Bar and Baz's proto --> casting fails
2724 {
2725 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2726 QVERIFY(pbar == 0);
2727 }
2728
2729 {
2730 QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value);
2731 QVERIFY(ret.isError());
2732 QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to toBaz(); candidates were\n toBaz(Bar*)"));
2733 }
2734
2735 // establish chain -- now casting should work
2736 // Why? because qscriptvalue_cast() does magic again.
2737 // It the instance itself is not of type T, qscriptvalue_cast()
2738 // searches the prototype chain for T, and if it finds one, it infers
2739 // that the instance can also be casted to that type. This cast is
2740 // _not_ safe and thus relies on the developer doing the right thing.
2741 // This is an undocumented feature to enable qscriptvalue_cast() to
2742 // be used by prototype functions to cast the JS this-object to C++.
2743 bazProto.setPrototype(barProto);
2744
2745 {
2746 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2747 QVERIFY(pbar != 0);
2748 QCOMPARE(pbar->a, baz2.a);
2749 }
2750
2751 {
2752 QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value);
2753 QVERIFY(!ret.isError());
2754 QCOMPARE(qscriptvalue_cast<Baz*>(ret), pbaz);
2755 }
2756 }
2757
2758 bazProto.setPrototype(barProto.prototype()); // kill chain
2759 {
2760 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2761 QVERIFY(pbaz != 0);
2762 // should not work anymore
2763 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2764 QVERIFY(pbar == 0);
2765 }
2766
2767 bazProto.setPrototype(eng.newQObject(this));
2768 {
2769 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2770 QVERIFY(pbaz != 0);
2771 // should not work now either
2772 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2773 QVERIFY(pbar == 0);
2774 }
2775
2776 {
2777 QScriptValue b = qScriptValueFromValue(&eng, QBrush());
2778 b.setPrototype(barProto);
2779 // this shows that a "wrong" cast is possible, if you
2780 // don't play by the rules (the pointer is actually a QBrush*)...
2781 Bar *pbar = qscriptvalue_cast<Bar*>(b);
2782 QVERIFY(pbar != 0);
2783 }
2784
2785 {
2786 QScriptValue gradientProto = qScriptValueFromValue(&eng, QGradient());
2787 QScriptValue linearGradientProto = qScriptValueFromValue(&eng, QLinearGradient());
2788 linearGradientProto.setPrototype(gradientProto);
2789 QLinearGradient lg(10, 20, 30, 40);
2790 QScriptValue linearGradient = qScriptValueFromValue(&eng, lg);
2791 {
2792 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
2793 QVERIFY(pgrad == 0);
2794 }
2795 linearGradient.setPrototype(linearGradientProto);
2796 {
2797 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
2798 QVERIFY(pgrad != 0);
2799 QCOMPARE(pgrad->type(), QGradient::LinearGradient);
2800 QLinearGradient *plingrad = static_cast<QLinearGradient*>(pgrad);
2801 QCOMPARE(plingrad->start(), lg.start());
2802 QCOMPARE(plingrad->finalStop(), lg.finalStop());
2803 }
2804 }
2805 }
2806
2807 class Klazz : public QWidget,
2808 public QStandardItem,
2809 public QGraphicsItem
2810 {
2811 Q_OBJECT
2812 public:
Klazz(QWidget * parent=0)2813 Klazz(QWidget *parent = 0) : QWidget(parent) { }
boundingRect() const2814 virtual QRectF boundingRect() const { return QRectF(); }
paint(QPainter *,const QStyleOptionGraphicsItem *,QWidget *)2815 virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { }
2816 };
2817
2818 Q_DECLARE_METATYPE(Klazz*)
Q_DECLARE_METATYPE(QStandardItem *)2819 Q_DECLARE_METATYPE(QStandardItem*)
2820
2821 void tst_QScriptEngine::castWithMultipleInheritance()
2822 {
2823 QScriptEngine eng;
2824 Klazz klz;
2825 QScriptValue v = eng.newQObject(&klz);
2826
2827 QCOMPARE(qscriptvalue_cast<Klazz*>(v), &klz);
2828 QCOMPARE(qscriptvalue_cast<QWidget*>(v), (QWidget *)&klz);
2829 QCOMPARE(qscriptvalue_cast<QObject*>(v), (QObject *)&klz);
2830 QCOMPARE(qscriptvalue_cast<QStandardItem*>(v), (QStandardItem *)&klz);
2831 QCOMPARE(qscriptvalue_cast<QGraphicsItem*>(v), (QGraphicsItem *)&klz);
2832 }
2833
collectGarbage()2834 void tst_QScriptEngine::collectGarbage()
2835 {
2836 QScriptEngine eng;
2837 eng.evaluate("a = new Object(); a = new Object(); a = new Object()");
2838 QScriptValue a = eng.newObject();
2839 a = eng.newObject();
2840 a = eng.newObject();
2841 QPointer<QObject> ptr = new QObject();
2842 QVERIFY(ptr != 0);
2843 (void)eng.newQObject(ptr, QScriptEngine::ScriptOwnership);
2844 collectGarbage_helper(eng);
2845 QVERIFY(ptr == 0);
2846 }
2847
reportAdditionalMemoryCost()2848 void tst_QScriptEngine::reportAdditionalMemoryCost()
2849 {
2850 QScriptEngine eng;
2851 // There isn't any reliable way to test whether calling
2852 // this function affects garbage collection responsiveness;
2853 // the best we can do is call it with a few different values.
2854 for (int x = 0; x < 1000; ++x) {
2855 eng.reportAdditionalMemoryCost(0);
2856 eng.reportAdditionalMemoryCost(10);
2857 eng.reportAdditionalMemoryCost(1000);
2858 eng.reportAdditionalMemoryCost(10000);
2859 eng.reportAdditionalMemoryCost(100000);
2860 eng.reportAdditionalMemoryCost(1000000);
2861 eng.reportAdditionalMemoryCost(10000000);
2862 eng.reportAdditionalMemoryCost(-1);
2863 eng.reportAdditionalMemoryCost(-1000);
2864 QScriptValue obj = eng.newObject();
2865 eng.collectGarbage();
2866 }
2867 }
2868
gcWithNestedDataStructure()2869 void tst_QScriptEngine::gcWithNestedDataStructure()
2870 {
2871 // The GC must be able to traverse deeply nested objects, otherwise this
2872 // test would crash.
2873 QScriptEngine eng;
2874 eng.evaluate(
2875 "function makeList(size)"
2876 "{"
2877 " var head = { };"
2878 " var l = head;"
2879 " for (var i = 0; i < size; ++i) {"
2880 " l.data = i + \"\";"
2881 " l.next = { }; l = l.next;"
2882 " }"
2883 " l.next = null;"
2884 " return head;"
2885 "}");
2886 QCOMPARE(eng.hasUncaughtException(), false);
2887 const int size = 200;
2888 QScriptValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size));
2889 QCOMPARE(eng.hasUncaughtException(), false);
2890 for (int x = 0; x < 2; ++x) {
2891 if (x == 1)
2892 eng.evaluate("gc()");
2893 QScriptValue l = head;
2894 // Make sure all the nodes are still alive.
2895 for (int i = 0; i < 200; ++i) {
2896 QCOMPARE(l.property("data").toString(), QString::number(i));
2897 l = l.property("next");
2898 }
2899 }
2900 }
2901
2902 class EventReceiver : public QObject
2903 {
2904 public:
EventReceiver()2905 EventReceiver() {
2906 received = false;
2907 }
2908
event(QEvent * e)2909 bool event(QEvent *e) {
2910 received |= (e->type() == QEvent::User + 1);
2911 return QObject::event(e);
2912 }
2913
2914 bool received;
2915 };
2916
processEventsWhileRunning()2917 void tst_QScriptEngine::processEventsWhileRunning()
2918 {
2919 for (int x = 0; x < 2; ++x) {
2920 QScriptEngine eng;
2921 if (x == 0)
2922 eng.pushContext();
2923
2924 // This is running for a silly amount of time just to make sure
2925 // the script doesn't finish before event processing is triggered.
2926 QString script = QString::fromLatin1(
2927 "var end = Number(new Date()) + 2000;"
2928 "var x = 0;"
2929 "while (Number(new Date()) < end) {"
2930 " ++x;"
2931 "}");
2932
2933 EventReceiver receiver;
2934 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
2935
2936 eng.evaluate(script);
2937 QVERIFY(!eng.hasUncaughtException());
2938 QVERIFY(!receiver.received);
2939
2940 QCOMPARE(eng.processEventsInterval(), -1);
2941 eng.setProcessEventsInterval(100);
2942 eng.evaluate(script);
2943 QVERIFY(!eng.hasUncaughtException());
2944 QVERIFY(receiver.received);
2945
2946 if (x == 0)
2947 eng.popContext();
2948 }
2949 }
2950
2951 class EventReceiver2 : public QObject
2952 {
2953 public:
EventReceiver2(QScriptEngine * eng)2954 EventReceiver2(QScriptEngine *eng) {
2955 engine = eng;
2956 }
2957
event(QEvent * e)2958 bool event(QEvent *e) {
2959 if (e->type() == QEvent::User + 1) {
2960 engine->currentContext()->throwError("Killed");
2961 }
2962 return QObject::event(e);
2963 }
2964
2965 QScriptEngine *engine;
2966 };
2967
throwErrorFromProcessEvents_data()2968 void tst_QScriptEngine::throwErrorFromProcessEvents_data()
2969 {
2970 QTest::addColumn<QString>("script");
2971 QTest::addColumn<QString>("error");
2972
2973 QTest::newRow("while (1)")
2974 << QString::fromLatin1("while (1) { }")
2975 << QString::fromLatin1("Error: Killed");
2976 QTest::newRow("while (1) i++")
2977 << QString::fromLatin1("i = 0; while (1) { i++; }")
2978 << QString::fromLatin1("Error: Killed");
2979 // Unlike abortEvaluation(), scripts should be able to catch the
2980 // exception.
2981 QTest::newRow("try catch")
2982 << QString::fromLatin1("try {"
2983 " while (1) { }"
2984 "} catch(e) {"
2985 " throw new Error('Caught');"
2986 "}")
2987 << QString::fromLatin1("Error: Caught");
2988 }
2989
throwErrorFromProcessEvents()2990 void tst_QScriptEngine::throwErrorFromProcessEvents()
2991 {
2992 QFETCH(QString, script);
2993 QFETCH(QString, error);
2994
2995 QScriptEngine eng;
2996
2997 EventReceiver2 receiver(&eng);
2998 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
2999
3000 eng.setProcessEventsInterval(100);
3001 QScriptValue ret = eng.evaluate(script);
3002 QVERIFY(ret.isError());
3003 QCOMPARE(ret.toString(), error);
3004 }
3005
disableProcessEventsInterval()3006 void tst_QScriptEngine::disableProcessEventsInterval()
3007 {
3008 QScriptEngine eng;
3009 eng.setProcessEventsInterval(100);
3010 QCOMPARE(eng.processEventsInterval(), 100);
3011 eng.setProcessEventsInterval(0);
3012 QCOMPARE(eng.processEventsInterval(), 0);
3013 eng.setProcessEventsInterval(-1);
3014 QCOMPARE(eng.processEventsInterval(), -1);
3015 eng.setProcessEventsInterval(-100);
3016 QCOMPARE(eng.processEventsInterval(), -100);
3017 }
3018
stacktrace()3019 void tst_QScriptEngine::stacktrace()
3020 {
3021 QString script = QString::fromLatin1(
3022 "function foo(counter) {\n"
3023 " switch (counter) {\n"
3024 " case 0: foo(counter+1); break;\n"
3025 " case 1: foo(counter+1); break;\n"
3026 " case 2: foo(counter+1); break;\n"
3027 " case 3: foo(counter+1); break;\n"
3028 " case 4: foo(counter+1); break;\n"
3029 " default:\n"
3030 " throw new Error('blah');\n"
3031 " }\n"
3032 "}\n"
3033 "foo(0);");
3034
3035 const QString fileName("testfile");
3036
3037 QStringList backtrace;
3038 backtrace << "foo(5)@testfile:9"
3039 << "foo(4)@testfile:7"
3040 << "foo(3)@testfile:6"
3041 << "foo(2)@testfile:5"
3042 << "foo(1)@testfile:4"
3043 << "foo(0)@testfile:3"
3044 << "<global>()@testfile:12";
3045
3046 QScriptEngine eng;
3047 QScriptValue result = eng.evaluate(script, fileName);
3048 QVERIFY(eng.hasUncaughtException());
3049 QVERIFY(result.isError());
3050
3051 QEXPECT_FAIL("", "QTBUG-6139: uncaughtExceptionBacktrace() doesn't give the full backtrace", Abort);
3052 QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace);
3053 QVERIFY(eng.hasUncaughtException());
3054 QVERIFY(result.strictlyEquals(eng.uncaughtException()));
3055
3056 QCOMPARE(result.property("fileName").toString(), fileName);
3057 QCOMPARE(result.property("lineNumber").toInt32(), 9);
3058
3059 QScriptValue stack = result.property("stack");
3060 QVERIFY(stack.isArray());
3061
3062 QCOMPARE(stack.property("length").toInt32(), 7);
3063
3064 QScriptValueIterator it(stack);
3065 int counter = 5;
3066 while (it.hasNext()) {
3067 it.next();
3068 QScriptValue obj = it.value();
3069 QScriptValue frame = obj.property("frame");
3070
3071 QCOMPARE(obj.property("fileName").toString(), fileName);
3072 if (counter >= 0) {
3073 QScriptValue callee = frame.property("arguments").property("callee");
3074 QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo")));
3075 QCOMPARE(obj.property("functionName").toString(), QString("foo"));
3076 int line = obj.property("lineNumber").toInt32();
3077 if (counter == 5)
3078 QCOMPARE(line, 9);
3079 else
3080 QCOMPARE(line, 3 + counter);
3081 } else {
3082 QVERIFY(frame.strictlyEquals(eng.globalObject()));
3083 QVERIFY(obj.property("functionName").toString().isEmpty());
3084 }
3085
3086 --counter;
3087 }
3088
3089 {
3090 QScriptValue bt = result.property("backtrace").call(result);
3091 QCOMPARE(qscriptvalue_cast<QStringList>(bt), backtrace);
3092 }
3093
3094 // throw something that isn't an Error object
3095 eng.clearExceptions();
3096 QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3097 QString script2 = QString::fromLatin1(
3098 "function foo(counter) {\n"
3099 " switch (counter) {\n"
3100 " case 0: foo(counter+1); break;\n"
3101 " case 1: foo(counter+1); break;\n"
3102 " case 2: foo(counter+1); break;\n"
3103 " case 3: foo(counter+1); break;\n"
3104 " case 4: foo(counter+1); break;\n"
3105 " default:\n"
3106 " throw 'just a string';\n"
3107 " }\n"
3108 "}\n"
3109 "foo(0);");
3110
3111 QScriptValue result2 = eng.evaluate(script2, fileName);
3112 QVERIFY(eng.hasUncaughtException());
3113 QVERIFY(!result2.isError());
3114 QVERIFY(result2.isString());
3115
3116 QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace);
3117 QVERIFY(eng.hasUncaughtException());
3118
3119 eng.clearExceptions();
3120 QVERIFY(!eng.hasUncaughtException());
3121 QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3122 }
3123
numberParsing_data()3124 void tst_QScriptEngine::numberParsing_data()
3125 {
3126 QTest::addColumn<QString>("string");
3127 QTest::addColumn<qsreal>("expect");
3128
3129 QTest::newRow("decimal 0") << QString("0") << qsreal(0);
3130 QTest::newRow("octal 0") << QString("00") << qsreal(00);
3131 QTest::newRow("hex 0") << QString("0x0") << qsreal(0x0);
3132 QTest::newRow("decimal 100") << QString("100") << qsreal(100);
3133 QTest::newRow("hex 100") << QString("0x100") << qsreal(0x100);
3134 QTest::newRow("octal 100") << QString("0100") << qsreal(0100);
3135 QTest::newRow("decimal 4G") << QString("4294967296") << qsreal(Q_UINT64_C(4294967296));
3136 QTest::newRow("hex 4G") << QString("0x100000000") << qsreal(Q_UINT64_C(0x100000000));
3137 QTest::newRow("octal 4G") << QString("040000000000") << qsreal(Q_UINT64_C(040000000000));
3138 QTest::newRow("0.5") << QString("0.5") << qsreal(0.5);
3139 QTest::newRow("1.5") << QString("1.5") << qsreal(1.5);
3140 QTest::newRow("1e2") << QString("1e2") << qsreal(100);
3141 }
3142
numberParsing()3143 void tst_QScriptEngine::numberParsing()
3144 {
3145 QFETCH(QString, string);
3146 QFETCH(qsreal, expect);
3147
3148 QScriptEngine eng;
3149 QScriptValue ret = eng.evaluate(string);
3150 QVERIFY(ret.isNumber());
3151 qsreal actual = ret.toNumber();
3152 QCOMPARE(actual, expect);
3153 }
3154
3155 // see ECMA-262, section 7.9
3156 // This is testing ECMA compliance, not our C++ API, but it's important that
3157 // the back-end is conformant in this regard.
automaticSemicolonInsertion()3158 void tst_QScriptEngine::automaticSemicolonInsertion()
3159 {
3160 QScriptEngine eng;
3161 {
3162 QScriptValue ret = eng.evaluate("{ 1 2 } 3");
3163 QVERIFY(ret.isError());
3164 QVERIFY(ret.toString().contains("SyntaxError"));
3165 }
3166 {
3167 QScriptValue ret = eng.evaluate("{ 1\n2 } 3");
3168 QVERIFY(ret.isNumber());
3169 QCOMPARE(ret.toInt32(), 3);
3170 }
3171 {
3172 QScriptValue ret = eng.evaluate("for (a; b\n)");
3173 QVERIFY(ret.isError());
3174 QVERIFY(ret.toString().contains("SyntaxError"));
3175 }
3176 {
3177 QScriptValue ret = eng.evaluate("(function() { return\n1 + 2 })()");
3178 QVERIFY(ret.isUndefined());
3179 }
3180 {
3181 eng.evaluate("c = 2; b = 1");
3182 QScriptValue ret = eng.evaluate("a = b\n++c");
3183 QVERIFY(ret.isNumber());
3184 QCOMPARE(ret.toInt32(), 3);
3185 }
3186 {
3187 QScriptValue ret = eng.evaluate("if (a > b)\nelse c = d");
3188 QVERIFY(ret.isError());
3189 QVERIFY(ret.toString().contains("SyntaxError"));
3190 }
3191 {
3192 eng.evaluate("function c() { return { foo: function() { return 5; } } }");
3193 eng.evaluate("b = 1; d = 2; e = 3");
3194 QScriptValue ret = eng.evaluate("a = b + c\n(d + e).foo()");
3195 QVERIFY(ret.isNumber());
3196 QCOMPARE(ret.toInt32(), 6);
3197 }
3198 {
3199 QScriptValue ret = eng.evaluate("throw\n1");
3200 QVERIFY(ret.isError());
3201 QVERIFY(ret.toString().contains("SyntaxError"));
3202 }
3203 {
3204 QScriptValue ret = eng.evaluate("a = Number(1)\n++a");
3205 QVERIFY(ret.isNumber());
3206 QCOMPARE(ret.toInt32(), 2);
3207 }
3208
3209 // "a semicolon is never inserted automatically if the semicolon
3210 // would then be parsed as an empty statement"
3211 {
3212 eng.evaluate("a = 123");
3213 QScriptValue ret = eng.evaluate("if (0)\n ++a; a");
3214 QVERIFY(ret.isNumber());
3215 QCOMPARE(ret.toInt32(), 123);
3216 }
3217 {
3218 eng.evaluate("a = 123");
3219 QScriptValue ret = eng.evaluate("if (0)\n --a; a");
3220 QVERIFY(ret.isNumber());
3221 QCOMPARE(ret.toInt32(), 123);
3222 }
3223 {
3224 eng.evaluate("a = 123");
3225 QScriptValue ret = eng.evaluate("if ((0))\n ++a; a");
3226 QVERIFY(ret.isNumber());
3227 QCOMPARE(ret.toInt32(), 123);
3228 }
3229 {
3230 eng.evaluate("a = 123");
3231 QScriptValue ret = eng.evaluate("if ((0))\n --a; a");
3232 QVERIFY(ret.isNumber());
3233 QCOMPARE(ret.toInt32(), 123);
3234 }
3235 {
3236 eng.evaluate("a = 123");
3237 QScriptValue ret = eng.evaluate("if (0\n)\n ++a; a");
3238 QVERIFY(ret.isNumber());
3239 QCOMPARE(ret.toInt32(), 123);
3240 }
3241 {
3242 eng.evaluate("a = 123");
3243 QScriptValue ret = eng.evaluate("if (0\n ++a; a");
3244 QVERIFY(ret.isError());
3245 }
3246 {
3247 eng.evaluate("a = 123");
3248 QScriptValue ret = eng.evaluate("if (0))\n ++a; a");
3249 QVERIFY(ret.isError());
3250 }
3251 {
3252 QScriptValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n");
3253 QVERIFY(ret.isNumber());
3254 QCOMPARE(ret.toInt32(), 10);
3255 }
3256 {
3257 QScriptValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n");
3258 QVERIFY(ret.isNumber());
3259 QCOMPARE(ret.toInt32(), 20);
3260 }
3261 {
3262 QScriptValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n");
3263 QVERIFY(ret.isNumber());
3264 QCOMPARE(ret.toInt32(), 10);
3265 }
3266 {
3267 QScriptValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n");
3268 QVERIFY(ret.isNumber());
3269 QCOMPARE(ret.toInt32(), 20);
3270 }
3271 {
3272 QScriptValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n");
3273 QVERIFY(ret.isNumber());
3274 QCOMPARE(ret.toInt32(), 10);
3275 }
3276 {
3277 QScriptValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n");
3278 QVERIFY(ret.isNumber());
3279 QCOMPARE(ret.toInt32(), 20);
3280 }
3281 {
3282 QScriptValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n");
3283 QVERIFY(ret.isNumber());
3284 QCOMPARE(ret.toInt32(), 3);
3285 }
3286 {
3287 QScriptValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n");
3288 QVERIFY(ret.isNumber());
3289 QCOMPARE(ret.toInt32(), 6);
3290 }
3291 {
3292 QScriptValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n");
3293 QVERIFY(ret.isNumber());
3294 QCOMPARE(ret.toInt32(), 3);
3295 }
3296 {
3297 QScriptValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n");
3298 QVERIFY(ret.isNumber());
3299 QCOMPARE(ret.toInt32(), 6);
3300 }
3301 {
3302 QScriptValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n");
3303 QVERIFY(ret.isNumber());
3304 QCOMPARE(ret.toInt32(), 5);
3305 }
3306 {
3307 QScriptValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n");
3308 QVERIFY(ret.isNumber());
3309 QCOMPARE(ret.toInt32(), 10);
3310 }
3311 {
3312 QScriptValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n");
3313 QVERIFY(ret.isNumber());
3314 QCOMPARE(ret.toInt32(), 15);
3315 }
3316 {
3317 QScriptValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n");
3318 QVERIFY(ret.isNumber());
3319 QCOMPARE(ret.toInt32(), 10);
3320 }
3321
3322 {
3323 QScriptValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n");
3324 QVERIFY(ret.isNumber());
3325 QCOMPARE(ret.toInt32(), 2);
3326 }
3327 {
3328 QScriptValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n");
3329 QVERIFY(ret.isNumber());
3330 QCOMPARE(ret.toInt32(), 0);
3331 }
3332
3333 {
3334 QScriptValue ret = eng.evaluate("if (0)");
3335 QVERIFY(ret.isError());
3336 }
3337 {
3338 QScriptValue ret = eng.evaluate("while (0)");
3339 QVERIFY(ret.isError());
3340 }
3341 {
3342 QScriptValue ret = eng.evaluate("for (;;)");
3343 QVERIFY(ret.isError());
3344 }
3345 {
3346 QScriptValue ret = eng.evaluate("for (p in this)");
3347 QVERIFY(ret.isError());
3348 }
3349 {
3350 QScriptValue ret = eng.evaluate("with (this)");
3351 QVERIFY(ret.isError());
3352 }
3353 {
3354 QScriptValue ret = eng.evaluate("do");
3355 QVERIFY(ret.isError());
3356 }
3357 }
3358
3359 class EventReceiver3 : public QObject
3360 {
3361 public:
3362 enum AbortionResult {
3363 None = 0,
3364 String = 1,
3365 Error = 2,
3366 Number = 3
3367 };
3368
EventReceiver3(QScriptEngine * eng)3369 EventReceiver3(QScriptEngine *eng) {
3370 engine = eng;
3371 resultType = None;
3372 }
3373
event(QEvent * e)3374 bool event(QEvent *e) {
3375 if (e->type() == QEvent::User + 1) {
3376 switch (resultType) {
3377 case None:
3378 engine->abortEvaluation();
3379 break;
3380 case String:
3381 engine->abortEvaluation(QScriptValue(engine, QString::fromLatin1("Aborted")));
3382 break;
3383 case Error:
3384 engine->abortEvaluation(engine->currentContext()->throwError("AbortedWithError"));
3385 break;
3386 case Number:
3387 engine->abortEvaluation(QScriptValue(1234));
3388 }
3389 }
3390 return QObject::event(e);
3391 }
3392
3393 AbortionResult resultType;
3394 QScriptEngine *engine;
3395 };
3396
myFunctionAbortingEvaluation(QScriptContext *,QScriptEngine * eng)3397 static QScriptValue myFunctionAbortingEvaluation(QScriptContext *, QScriptEngine *eng)
3398 {
3399 eng->abortEvaluation();
3400 return eng->nullValue(); // should be ignored
3401 }
3402
abortEvaluation_notEvaluating()3403 void tst_QScriptEngine::abortEvaluation_notEvaluating()
3404 {
3405 QScriptEngine eng;
3406
3407 eng.abortEvaluation();
3408 QVERIFY(!eng.hasUncaughtException());
3409
3410 eng.abortEvaluation(123);
3411 {
3412 QScriptValue ret = eng.evaluate("'ciao'");
3413 QVERIFY(ret.isString());
3414 QCOMPARE(ret.toString(), QString::fromLatin1("ciao"));
3415 }
3416 }
3417
abortEvaluation_data()3418 void tst_QScriptEngine::abortEvaluation_data()
3419 {
3420 QTest::addColumn<QString>("script");
3421
3422 QTest::newRow("while (1)")
3423 << QString::fromLatin1("while (1) { }");
3424 QTest::newRow("while (1) i++")
3425 << QString::fromLatin1("i = 0; while (1) { i++; }");
3426 QTest::newRow("try catch")
3427 << QString::fromLatin1("try {"
3428 " while (1) { }"
3429 "} catch(e) {"
3430 " throw new Error('Caught');"
3431 "}");
3432 }
3433
abortEvaluation()3434 void tst_QScriptEngine::abortEvaluation()
3435 {
3436 QFETCH(QString, script);
3437
3438 QScriptEngine eng;
3439 EventReceiver3 receiver(&eng);
3440
3441 eng.setProcessEventsInterval(100);
3442 for (int x = 0; x < 4; ++x) {
3443 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3444 receiver.resultType = EventReceiver3::AbortionResult(x);
3445 QScriptValue ret = eng.evaluate(script);
3446 switch (receiver.resultType) {
3447 case EventReceiver3::None:
3448 QVERIFY(!eng.hasUncaughtException());
3449 QVERIFY(!ret.isValid());
3450 break;
3451 case EventReceiver3::Number:
3452 QVERIFY(!eng.hasUncaughtException());
3453 QVERIFY(ret.isNumber());
3454 QCOMPARE(ret.toInt32(), 1234);
3455 break;
3456 case EventReceiver3::String:
3457 QVERIFY(!eng.hasUncaughtException());
3458 QVERIFY(ret.isString());
3459 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3460 break;
3461 case EventReceiver3::Error:
3462 QVERIFY(eng.hasUncaughtException());
3463 QVERIFY(ret.isError());
3464 QCOMPARE(ret.toString(), QString::fromLatin1("Error: AbortedWithError"));
3465 break;
3466 }
3467 }
3468
3469 }
3470
abortEvaluation_tryCatch()3471 void tst_QScriptEngine::abortEvaluation_tryCatch()
3472 {
3473 QScriptEngine eng;
3474 EventReceiver3 receiver(&eng);
3475 eng.setProcessEventsInterval(100);
3476 // scripts cannot intercept the abortion with try/catch
3477 for (int y = 0; y < 4; ++y) {
3478 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3479 receiver.resultType = EventReceiver3::AbortionResult(y);
3480 QScriptValue ret = eng.evaluate(QString::fromLatin1(
3481 "while (1) {\n"
3482 " try {\n"
3483 " (function() { while (1) { } })();\n"
3484 " } catch (e) {\n"
3485 " ;\n"
3486 " }\n"
3487 "}"));
3488 switch (receiver.resultType) {
3489 case EventReceiver3::None:
3490 QVERIFY(!eng.hasUncaughtException());
3491 QVERIFY(!ret.isValid());
3492 break;
3493 case EventReceiver3::Number:
3494 QVERIFY(!eng.hasUncaughtException());
3495 QVERIFY(ret.isNumber());
3496 QCOMPARE(ret.toInt32(), 1234);
3497 break;
3498 case EventReceiver3::String:
3499 QVERIFY(!eng.hasUncaughtException());
3500 QVERIFY(ret.isString());
3501 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3502 break;
3503 case EventReceiver3::Error:
3504 QVERIFY(eng.hasUncaughtException());
3505 QVERIFY(ret.isError());
3506 break;
3507 }
3508 }
3509 }
3510
abortEvaluation_fromNative()3511 void tst_QScriptEngine::abortEvaluation_fromNative()
3512 {
3513 QScriptEngine eng;
3514 QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation);
3515 eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun);
3516 QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()");
3517 QVERIFY(!ret.isValid());
3518 }
3519
3520 class ThreadedEngine : public QThread {
3521 Q_OBJECT;
3522
3523 private:
3524 QScriptEngine* m_engine;
3525 protected:
run()3526 void run() {
3527 m_engine = new QScriptEngine();
3528 m_engine->setGlobalObject(m_engine->newQObject(this));
3529 m_engine->evaluate("while(1) { sleep(); }");
3530 delete m_engine;
3531 }
3532
3533 public slots:
sleep()3534 void sleep()
3535 {
3536 QTest::qSleep(25);
3537 m_engine->abortEvaluation();
3538 }
3539 };
3540
abortEvaluation_QTBUG9433()3541 void tst_QScriptEngine::abortEvaluation_QTBUG9433()
3542 {
3543 ThreadedEngine engine;
3544 engine.start();
3545 QVERIFY(engine.isRunning());
3546 QTest::qSleep(50);
3547 for (uint i = 0; i < 50; ++i) { // up to ~2500 ms
3548 if (engine.isFinished())
3549 return;
3550 QTest::qSleep(50);
3551 }
3552 if (!engine.isFinished()) {
3553 engine.terminate();
3554 engine.wait(7000);
3555 QFAIL("abortEvaluation doesn't work");
3556 }
3557
3558 }
3559
myFunctionReturningIsEvaluating(QScriptContext *,QScriptEngine * eng)3560 static QScriptValue myFunctionReturningIsEvaluating(QScriptContext *, QScriptEngine *eng)
3561 {
3562 return QScriptValue(eng, eng->isEvaluating());
3563 }
3564
3565 class EventReceiver4 : public QObject
3566 {
3567 public:
EventReceiver4(QScriptEngine * eng)3568 EventReceiver4(QScriptEngine *eng) {
3569 engine = eng;
3570 wasEvaluating = false;
3571 }
3572
event(QEvent * e)3573 bool event(QEvent *e) {
3574 if (e->type() == QEvent::User + 1) {
3575 wasEvaluating = engine->isEvaluating();
3576 }
3577 return QObject::event(e);
3578 }
3579
3580 QScriptEngine *engine;
3581 bool wasEvaluating;
3582 };
3583
isEvaluating_notEvaluating()3584 void tst_QScriptEngine::isEvaluating_notEvaluating()
3585 {
3586 QScriptEngine eng;
3587
3588 QVERIFY(!eng.isEvaluating());
3589
3590 eng.evaluate("");
3591 QVERIFY(!eng.isEvaluating());
3592 eng.evaluate("123");
3593 QVERIFY(!eng.isEvaluating());
3594 eng.evaluate("0 = 0");
3595 QVERIFY(!eng.isEvaluating());
3596 }
3597
isEvaluating_fromNative()3598 void tst_QScriptEngine::isEvaluating_fromNative()
3599 {
3600 QScriptEngine eng;
3601 QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating);
3602 eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun);
3603 QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()");
3604 QVERIFY(ret.isBoolean());
3605 QVERIFY(ret.toBoolean());
3606 }
3607
isEvaluating_fromEvent()3608 void tst_QScriptEngine::isEvaluating_fromEvent()
3609 {
3610 QScriptEngine eng;
3611 EventReceiver4 receiver(&eng);
3612 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3613
3614 QString script = QString::fromLatin1(
3615 "var end = Number(new Date()) + 1000;"
3616 "var x = 0;"
3617 "while (Number(new Date()) < end) {"
3618 " ++x;"
3619 "}");
3620
3621 eng.setProcessEventsInterval(100);
3622 eng.evaluate(script);
3623 QVERIFY(receiver.wasEvaluating);
3624 }
3625
3626 static QtMsgType theMessageType;
3627 static QString theMessage;
3628
myMsgHandler(QtMsgType type,const char * msg)3629 static void myMsgHandler(QtMsgType type, const char *msg)
3630 {
3631 theMessageType = type;
3632 theMessage = QString::fromLatin1(msg);
3633 }
3634
printFunctionWithCustomHandler()3635 void tst_QScriptEngine::printFunctionWithCustomHandler()
3636 {
3637 // The built-in print() function passes the output to Qt's message
3638 // handler. By installing a custom message handler, the output can be
3639 // redirected without changing the print() function itself.
3640 // This behavior is not documented.
3641 QScriptEngine eng;
3642 QtMsgHandler oldHandler = qInstallMsgHandler(myMsgHandler);
3643 QVERIFY(eng.globalObject().property("print").isFunction());
3644
3645 theMessageType = QtSystemMsg;
3646 QVERIFY(theMessage.isEmpty());
3647 QVERIFY(eng.evaluate("print('test')").isUndefined());
3648 QCOMPARE(theMessageType, QtDebugMsg);
3649 QCOMPARE(theMessage, QString::fromLatin1("test"));
3650
3651 theMessageType = QtSystemMsg;
3652 theMessage.clear();
3653 QVERIFY(eng.evaluate("print(3, true, 'little pigs')").isUndefined());
3654 QCOMPARE(theMessageType, QtDebugMsg);
3655 QCOMPARE(theMessage, QString::fromLatin1("3 true little pigs"));
3656
3657 qInstallMsgHandler(oldHandler);
3658 }
3659
printThrowsException()3660 void tst_QScriptEngine::printThrowsException()
3661 {
3662 // If an argument to print() causes an exception to be thrown when
3663 // it's converted to a string, print() should propagate the exception.
3664 QScriptEngine eng;
3665 QScriptValue ret = eng.evaluate("print({ toString: function() { throw 'foo'; } });");
3666 QVERIFY(eng.hasUncaughtException());
3667 QVERIFY(ret.strictlyEquals(QScriptValue(&eng, QLatin1String("foo"))));
3668 }
3669
errorConstructors()3670 void tst_QScriptEngine::errorConstructors()
3671 {
3672 QScriptEngine eng;
3673 QStringList prefixes;
3674 prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI";
3675 for (int x = 0; x < 3; ++x) {
3676 for (int i = 0; i < prefixes.size(); ++i) {
3677 QString name = prefixes.at(i) + QLatin1String("Error");
3678 QString code = QString(i+1, QLatin1Char('\n'));
3679 if (x == 0)
3680 code += QLatin1String("throw ");
3681 else if (x == 1)
3682 code += QLatin1String("new ");
3683 code += name + QLatin1String("()");
3684 QScriptValue ret = eng.evaluate(code);
3685 QVERIFY(ret.isError());
3686 QCOMPARE(eng.hasUncaughtException(), x == 0);
3687 eng.clearExceptions();
3688 QVERIFY(ret.toString().startsWith(name));
3689 if (x != 0)
3690 QEXPECT_FAIL("", "QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown", Continue);
3691 QCOMPARE(ret.property("lineNumber").toInt32(), i+2);
3692 }
3693 }
3694 }
3695
argumentsProperty_globalContext()3696 void tst_QScriptEngine::argumentsProperty_globalContext()
3697 {
3698 QScriptEngine eng;
3699 {
3700 // Unlike function calls, the global context doesn't have an
3701 // arguments property.
3702 QScriptValue ret = eng.evaluate("arguments");
3703 QVERIFY(ret.isError());
3704 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
3705 }
3706 eng.evaluate("arguments = 10");
3707 {
3708 QScriptValue ret = eng.evaluate("arguments");
3709 QVERIFY(ret.isNumber());
3710 QCOMPARE(ret.toInt32(), 10);
3711 }
3712 QVERIFY(eng.evaluate("delete arguments").toBoolean());
3713 QVERIFY(!eng.globalObject().property("arguments").isValid());
3714 }
3715
argumentsProperty_JS()3716 void tst_QScriptEngine::argumentsProperty_JS()
3717 {
3718 {
3719 QScriptEngine eng;
3720 eng.evaluate("o = { arguments: 123 }");
3721 QScriptValue ret = eng.evaluate("with (o) { arguments; }");
3722 QVERIFY(ret.isNumber());
3723 QCOMPARE(ret.toInt32(), 123);
3724 }
3725 {
3726 QScriptEngine eng;
3727 QVERIFY(!eng.globalObject().property("arguments").isValid());
3728 // This is testing ECMA-262 compliance. In function calls, "arguments"
3729 // appears like a local variable, and it can be replaced.
3730 QScriptValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()");
3731 QVERIFY(ret.isNumber());
3732 QCOMPARE(ret.toInt32(), 456);
3733 QVERIFY(!eng.globalObject().property("arguments").isValid());
3734 }
3735 }
3736
argumentsProperty_fun(QScriptContext *,QScriptEngine * eng)3737 static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng)
3738 {
3739 // Since evaluate() is done in the current context, "arguments" should
3740 // refer to currentContext()->argumentsObject().
3741 // This is for consistency with the built-in JS eval() function.
3742 eng->evaluate("var a = arguments[0];");
3743 eng->evaluate("arguments[0] = 200;");
3744 return eng->evaluate("a + arguments[0]");
3745 }
3746
argumentsProperty_evaluateInNativeFunction()3747 void tst_QScriptEngine::argumentsProperty_evaluateInNativeFunction()
3748 {
3749 QScriptEngine eng;
3750 QScriptValue fun = eng.newFunction(argumentsProperty_fun);
3751 eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun));
3752 QScriptValue result = eng.evaluate("fun(18)");
3753 QVERIFY(result.isNumber());
3754 QCOMPARE(result.toInt32(), 200+18);
3755 }
3756
jsNumberClass()3757 void tst_QScriptEngine::jsNumberClass()
3758 {
3759 // See ECMA-262 Section 15.7, "Number Objects".
3760
3761 QScriptEngine eng;
3762
3763 QScriptValue ctor = eng.globalObject().property("Number");
3764 QVERIFY(ctor.property("length").isNumber());
3765 QCOMPARE(ctor.property("length").toNumber(), qsreal(1));
3766 QScriptValue proto = ctor.property("prototype");
3767 QVERIFY(proto.isObject());
3768 {
3769 QScriptValue::PropertyFlags flags = QScriptValue::SkipInEnumeration
3770 | QScriptValue::Undeletable
3771 | QScriptValue::ReadOnly;
3772 QCOMPARE(ctor.propertyFlags("prototype"), flags);
3773 QVERIFY(ctor.property("MAX_VALUE").isNumber());
3774 QCOMPARE(ctor.propertyFlags("MAX_VALUE"), flags);
3775 QVERIFY(ctor.property("MIN_VALUE").isNumber());
3776 QCOMPARE(ctor.propertyFlags("MIN_VALUE"), flags);
3777 QVERIFY(ctor.property("NaN").isNumber());
3778 QCOMPARE(ctor.propertyFlags("NaN"), flags);
3779 QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
3780 QCOMPARE(ctor.propertyFlags("NEGATIVE_INFINITY"), flags);
3781 QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
3782 QCOMPARE(ctor.propertyFlags("POSITIVE_INFINITY"), flags);
3783 }
3784 QVERIFY(proto.instanceOf(eng.globalObject().property("Object")));
3785 QCOMPARE(proto.toNumber(), qsreal(0));
3786 QVERIFY(proto.property("constructor").strictlyEquals(ctor));
3787
3788 {
3789 QScriptValue ret = eng.evaluate("Number()");
3790 QVERIFY(ret.isNumber());
3791 QCOMPARE(ret.toNumber(), qsreal(0));
3792 }
3793 {
3794 QScriptValue ret = eng.evaluate("Number(123)");
3795 QVERIFY(ret.isNumber());
3796 QCOMPARE(ret.toNumber(), qsreal(123));
3797 }
3798 {
3799 QScriptValue ret = eng.evaluate("Number('456')");
3800 QVERIFY(ret.isNumber());
3801 QCOMPARE(ret.toNumber(), qsreal(456));
3802 }
3803 {
3804 QScriptValue ret = eng.evaluate("new Number()");
3805 QVERIFY(!ret.isNumber());
3806 QVERIFY(ret.isObject());
3807 QCOMPARE(ret.toNumber(), qsreal(0));
3808 }
3809 {
3810 QScriptValue ret = eng.evaluate("new Number(123)");
3811 QVERIFY(!ret.isNumber());
3812 QVERIFY(ret.isObject());
3813 QCOMPARE(ret.toNumber(), qsreal(123));
3814 }
3815 {
3816 QScriptValue ret = eng.evaluate("new Number('456')");
3817 QVERIFY(!ret.isNumber());
3818 QVERIFY(ret.isObject());
3819 QCOMPARE(ret.toNumber(), qsreal(456));
3820 }
3821
3822 QVERIFY(proto.property("toString").isFunction());
3823 {
3824 QScriptValue ret = eng.evaluate("new Number(123).toString()");
3825 QVERIFY(ret.isString());
3826 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
3827 }
3828 {
3829 QScriptValue ret = eng.evaluate("new Number(123).toString(8)");
3830 QVERIFY(ret.isString());
3831 QCOMPARE(ret.toString(), QString::fromLatin1("173"));
3832 }
3833 {
3834 QScriptValue ret = eng.evaluate("new Number(123).toString(16)");
3835 QVERIFY(ret.isString());
3836 QCOMPARE(ret.toString(), QString::fromLatin1("7b"));
3837 }
3838 QVERIFY(proto.property("toLocaleString").isFunction());
3839 {
3840 QScriptValue ret = eng.evaluate("new Number(123).toLocaleString()");
3841 QVERIFY(ret.isString());
3842 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
3843 }
3844 QVERIFY(proto.property("valueOf").isFunction());
3845 {
3846 QScriptValue ret = eng.evaluate("new Number(123).valueOf()");
3847 QVERIFY(ret.isNumber());
3848 QCOMPARE(ret.toNumber(), qsreal(123));
3849 }
3850 QVERIFY(proto.property("toExponential").isFunction());
3851 {
3852 QScriptValue ret = eng.evaluate("new Number(123).toExponential()");
3853 QVERIFY(ret.isString());
3854 QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2"));
3855 }
3856 QVERIFY(proto.property("toFixed").isFunction());
3857 {
3858 QScriptValue ret = eng.evaluate("new Number(123).toFixed()");
3859 QVERIFY(ret.isString());
3860 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
3861 }
3862 QVERIFY(proto.property("toPrecision").isFunction());
3863 {
3864 QScriptValue ret = eng.evaluate("new Number(123).toPrecision()");
3865 QVERIFY(ret.isString());
3866 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
3867 }
3868 }
3869
jsForInStatement_simple()3870 void tst_QScriptEngine::jsForInStatement_simple()
3871 {
3872 QScriptEngine eng;
3873 {
3874 QScriptValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r");
3875 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3876 QVERIFY(lst.isEmpty());
3877 }
3878 {
3879 QScriptValue ret = eng.evaluate("o = { p: 123 }; r = [];"
3880 "for (var p in o) r[r.length] = p; r");
3881 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3882 QCOMPARE(lst.size(), 1);
3883 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3884 }
3885 {
3886 QScriptValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
3887 "for (var p in o) r[r.length] = p; r");
3888 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3889 QCOMPARE(lst.size(), 2);
3890 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3891 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
3892 }
3893 }
3894
jsForInStatement_prototypeProperties()3895 void tst_QScriptEngine::jsForInStatement_prototypeProperties()
3896 {
3897 QScriptEngine eng;
3898 {
3899 QScriptValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];"
3900 "for (var p in o) r[r.length] = p; r");
3901 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3902 QCOMPARE(lst.size(), 1);
3903 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3904 }
3905 {
3906 QScriptValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];"
3907 "for (var p in o) r[r.length] = p; r");
3908 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3909 QCOMPARE(lst.size(), 2);
3910 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3911 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
3912 }
3913 {
3914 // shadowed property
3915 QScriptValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];"
3916 "for (var p in o) r[r.length] = p; r");
3917 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3918 QCOMPARE(lst.size(), 1);
3919 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3920 }
3921
3922 }
3923
jsForInStatement_mutateWhileIterating()3924 void tst_QScriptEngine::jsForInStatement_mutateWhileIterating()
3925 {
3926 QScriptEngine eng;
3927 // deleting property during enumeration
3928 {
3929 QScriptValue ret = eng.evaluate("o = { p: 123 }; r = [];"
3930 "for (var p in o) { r[r.length] = p; delete r[p]; } r");
3931 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3932 QCOMPARE(lst.size(), 1);
3933 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3934 }
3935 {
3936 QScriptValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
3937 "for (var p in o) { r[r.length] = p; delete o.q; } r");
3938 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3939 QCOMPARE(lst.size(), 1);
3940 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3941 }
3942
3943 // adding property during enumeration
3944 {
3945 QScriptValue ret = eng.evaluate("o = { p: 123 }; r = [];"
3946 "for (var p in o) { r[r.length] = p; o.q = 456; } r");
3947 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3948 QCOMPARE(lst.size(), 1);
3949 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
3950 }
3951
3952 }
3953
jsForInStatement_arrays()3954 void tst_QScriptEngine::jsForInStatement_arrays()
3955 {
3956 QScriptEngine eng;
3957 {
3958 QScriptValue ret = eng.evaluate("a = [123, 456]; r = [];"
3959 "for (var p in a) r[r.length] = p; r");
3960 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3961 QCOMPARE(lst.size(), 2);
3962 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
3963 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
3964 }
3965 {
3966 QScriptValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];"
3967 "for (var p in a) r[r.length] = p; r");
3968 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3969 QCOMPARE(lst.size(), 3);
3970 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
3971 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
3972 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
3973 }
3974 {
3975 QScriptValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';"
3976 "b = [111, 222, 333]; b.bar = 'baz';"
3977 "a.__proto__ = b; r = [];"
3978 "for (var p in a) r[r.length] = p; r");
3979 QStringList lst = qscriptvalue_cast<QStringList>(ret);
3980 QCOMPARE(lst.size(), 5);
3981 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
3982 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
3983 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
3984 QCOMPARE(lst.at(3), QString::fromLatin1("2"));
3985 QCOMPARE(lst.at(4), QString::fromLatin1("bar"));
3986 }
3987 }
3988
jsForInStatement_nullAndUndefined()3989 void tst_QScriptEngine::jsForInStatement_nullAndUndefined()
3990 {
3991 QScriptEngine eng;
3992 {
3993 QScriptValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r");
3994 QVERIFY(ret.isBool());
3995 QVERIFY(ret.toBool());
3996 }
3997 {
3998 QScriptValue ret = eng.evaluate("r = true; for (var p in null) r = false; r");
3999 QVERIFY(ret.isBool());
4000 QVERIFY(ret.toBool());
4001 }
4002 }
4003
jsFunctionDeclarationAsStatement()4004 void tst_QScriptEngine::jsFunctionDeclarationAsStatement()
4005 {
4006 // ECMA-262 does not allow function declarations to be used as statements,
4007 // but several popular implementations (including JSC) do. See the NOTE
4008 // at the beginning of chapter 12 in ECMA-262 5th edition, where it's
4009 // recommended that implementations either disallow this usage or issue
4010 // a warning.
4011 // Since we had a bug report long ago about QtScript not supporting this
4012 // "feature" (and thus deviating from other implementations), we still
4013 // check this behavior.
4014
4015 QScriptEngine eng;
4016 QVERIFY(!eng.globalObject().property("bar").isValid());
4017 eng.evaluate("function foo(arg) {\n"
4018 " if (arg == 'bar')\n"
4019 " function bar() { return 'bar'; }\n"
4020 " else\n"
4021 " function baz() { return 'baz'; }\n"
4022 " return (arg == 'bar') ? bar : baz;\n"
4023 "}");
4024 QVERIFY(!eng.globalObject().property("bar").isValid());
4025 QVERIFY(!eng.globalObject().property("baz").isValid());
4026 QVERIFY(eng.evaluate("foo").isFunction());
4027 {
4028 QScriptValue ret = eng.evaluate("foo('bar')");
4029 QVERIFY(ret.isFunction());
4030 QScriptValue ret2 = ret.call(QScriptValue());
4031 QCOMPARE(ret2.toString(), QString::fromLatin1("bar"));
4032 QVERIFY(!eng.globalObject().property("bar").isValid());
4033 QVERIFY(!eng.globalObject().property("baz").isValid());
4034 }
4035 {
4036 QScriptValue ret = eng.evaluate("foo('baz')");
4037 QVERIFY(ret.isFunction());
4038 QScriptValue ret2 = ret.call(QScriptValue());
4039 QCOMPARE(ret2.toString(), QString::fromLatin1("baz"));
4040 QVERIFY(!eng.globalObject().property("bar").isValid());
4041 QVERIFY(!eng.globalObject().property("baz").isValid());
4042 }
4043 }
4044
stringObjects()4045 void tst_QScriptEngine::stringObjects()
4046 {
4047 // See ECMA-262 Section 15.5, "String Objects".
4048
4049 QScriptEngine eng;
4050 QString str("ciao");
4051 // in C++
4052 {
4053 QScriptValue obj = QScriptValue(&eng, str).toObject();
4054 QCOMPARE(obj.property("length").toInt32(), str.length());
4055 QCOMPARE(obj.propertyFlags("length"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration | QScriptValue::ReadOnly));
4056 for (int i = 0; i < str.length(); ++i) {
4057 QString pname = QString::number(i);
4058 QVERIFY(obj.property(pname).isString());
4059 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4060 QCOMPARE(obj.propertyFlags(pname), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::ReadOnly));
4061 obj.setProperty(pname, QScriptValue());
4062 obj.setProperty(pname, QScriptValue(&eng, 123));
4063 QVERIFY(obj.property(pname).isString());
4064 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4065 }
4066 QVERIFY(!obj.property("-1").isValid());
4067 QVERIFY(!obj.property(QString::number(str.length())).isValid());
4068
4069 QScriptValue val(&eng, 123);
4070 obj.setProperty("-1", val);
4071 QVERIFY(obj.property("-1").strictlyEquals(val));
4072 obj.setProperty("100", val);
4073 QVERIFY(obj.property("100").strictlyEquals(val));
4074 }
4075
4076 // in script
4077 {
4078 QScriptValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r");
4079 QVERIFY(ret.isArray());
4080 QStringList lst = qscriptvalue_cast<QStringList>(ret);
4081 QCOMPARE(lst.size(), str.length());
4082 for (int i = 0; i < str.length(); ++i)
4083 QCOMPARE(lst.at(i), QString::number(i));
4084
4085 QScriptValue ret2 = eng.evaluate("s[0] = 123; s[0]");
4086 QVERIFY(ret2.isString());
4087 QCOMPARE(ret2.toString().length(), 1);
4088 QCOMPARE(ret2.toString().at(0), str.at(0));
4089
4090 QScriptValue ret3 = eng.evaluate("s[-1] = 123; s[-1]");
4091 QVERIFY(ret3.isNumber());
4092 QCOMPARE(ret3.toInt32(), 123);
4093
4094 QScriptValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]");
4095 QVERIFY(ret4.isNumber());
4096 QCOMPARE(ret4.toInt32(), 456);
4097
4098 QScriptValue ret5 = eng.evaluate("delete s[0]");
4099 QVERIFY(ret5.isBoolean());
4100 QVERIFY(!ret5.toBoolean());
4101
4102 QScriptValue ret6 = eng.evaluate("delete s[-1]");
4103 QVERIFY(ret6.isBoolean());
4104 QVERIFY(ret6.toBoolean());
4105
4106 QScriptValue ret7 = eng.evaluate("delete s[s.length]");
4107 QVERIFY(ret7.isBoolean());
4108 QVERIFY(ret7.toBoolean());
4109 }
4110 }
4111
jsStringPrototypeReplaceBugs()4112 void tst_QScriptEngine::jsStringPrototypeReplaceBugs()
4113 {
4114 QScriptEngine eng;
4115 // task 212440
4116 {
4117 QScriptValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args");
4118 QVERIFY(ret.isArray());
4119 int len = ret.property("length").toInt32();
4120 QCOMPARE(len, 3);
4121 for (int i = 0; i < len; ++i) {
4122 QScriptValue args = ret.property(i);
4123 QCOMPARE(args.property("length").toInt32(), 4);
4124 QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string
4125 QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture
4126 QVERIFY(args.property(2).isNumber());
4127 QCOMPARE(args.property(2).toInt32(), i*2); // index of match
4128 QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a"));
4129 }
4130 }
4131 // task 212501
4132 {
4133 QScriptValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})");
4134 QVERIFY(ret.isString());
4135 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4136 }
4137 }
4138
getterSetterThisObject_global()4139 void tst_QScriptEngine::getterSetterThisObject_global()
4140 {
4141 {
4142 QScriptEngine eng;
4143 // read
4144 eng.evaluate("__defineGetter__('x', function() { return this; });");
4145 {
4146 QScriptValue ret = eng.evaluate("x");
4147 QVERIFY(ret.equals(eng.globalObject()));
4148 }
4149 {
4150 QScriptValue ret = eng.evaluate("(function() { return x; })()");
4151 QVERIFY(ret.equals(eng.globalObject()));
4152 }
4153 {
4154 QScriptValue ret = eng.evaluate("with (this) x");
4155 QVERIFY(ret.equals(eng.globalObject()));
4156 }
4157 {
4158 QScriptValue ret = eng.evaluate("with ({}) x");
4159 QVERIFY(ret.equals(eng.globalObject()));
4160 }
4161 {
4162 QScriptValue ret = eng.evaluate("(function() { with ({}) return x; })()");
4163 QVERIFY(ret.equals(eng.globalObject()));
4164 }
4165 // write
4166 eng.evaluate("__defineSetter__('x', function() { return this; });");
4167 {
4168 QScriptValue ret = eng.evaluate("x = 'foo'");
4169 // SpiderMonkey says setter return value, JSC says RHS.
4170 QVERIFY(ret.isString());
4171 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4172 }
4173 {
4174 QScriptValue ret = eng.evaluate("(function() { return x = 'foo'; })()");
4175 // SpiderMonkey says setter return value, JSC says RHS.
4176 QVERIFY(ret.isString());
4177 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4178 }
4179 {
4180 QScriptValue ret = eng.evaluate("with (this) x = 'foo'");
4181 // SpiderMonkey says setter return value, JSC says RHS.
4182 QVERIFY(ret.isString());
4183 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4184 }
4185 {
4186 QScriptValue ret = eng.evaluate("with ({}) x = 'foo'");
4187 // SpiderMonkey says setter return value, JSC says RHS.
4188 QVERIFY(ret.isString());
4189 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4190 }
4191 {
4192 QScriptValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()");
4193 // SpiderMonkey says setter return value, JSC says RHS.
4194 QVERIFY(ret.isString());
4195 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4196 }
4197 }
4198 }
4199
getterSetterThisObject_plain()4200 void tst_QScriptEngine::getterSetterThisObject_plain()
4201 {
4202 {
4203 QScriptEngine eng;
4204 eng.evaluate("o = {}");
4205 // read
4206 eng.evaluate("o.__defineGetter__('x', function() { return this; })");
4207 QVERIFY(eng.evaluate("o.x === o").toBoolean());
4208 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4209 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBoolean());
4210 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4211 // write
4212 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4213 // SpiderMonkey says setter return value, JSC says RHS.
4214 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean());
4215 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4216 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4217 }
4218 }
4219
getterSetterThisObject_prototypeChain()4220 void tst_QScriptEngine::getterSetterThisObject_prototypeChain()
4221 {
4222 {
4223 QScriptEngine eng;
4224 eng.evaluate("o = {}; p = {}; o.__proto__ = p");
4225 // read
4226 eng.evaluate("p.__defineGetter__('x', function() { return this; })");
4227 QVERIFY(eng.evaluate("o.x === o").toBoolean());
4228 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4229 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBoolean());
4230 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4231 eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o"));
4232 // write
4233 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4234 // SpiderMonkey says setter return value, JSC says RHS.
4235 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean());
4236 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4237 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4238 }
4239 }
4240
getterSetterThisObject_activation()4241 void tst_QScriptEngine::getterSetterThisObject_activation()
4242 {
4243 {
4244 QScriptEngine eng;
4245 QScriptContext *ctx = eng.pushContext();
4246 QVERIFY(ctx != 0);
4247 QScriptValue act = ctx->activationObject();
4248 act.setProperty("act", act);
4249 // read
4250 eng.evaluate("act.__defineGetter__('x', function() { return this; })");
4251 QVERIFY(eng.evaluate("x === act").toBoolean());
4252 QEXPECT_FAIL("", "QTBUG-17605: Not possible to implement local variables as getter/setter properties", Abort);
4253 QVERIFY(!eng.hasUncaughtException());
4254 QVERIFY(eng.evaluate("with (act) x").equals("foo"));
4255 QVERIFY(eng.evaluate("(function() { with (act) return x; })() === act").toBoolean());
4256 eng.evaluate("q = {}; with (act) with (q) x").equals(eng.evaluate("act"));
4257 eng.evaluate("with (q) with (act) x").equals(eng.evaluate("act"));
4258 // write
4259 eng.evaluate("act.__defineSetter__('x', function() { return this; });");
4260 QVERIFY(eng.evaluate("(x = 'foo') === 'foo'").toBoolean());
4261 QVERIFY(eng.evaluate("with (act) x = 'foo'").equals("foo"));
4262 QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals("foo"));
4263 eng.popContext();
4264 }
4265 }
4266
jsContinueInSwitch()4267 void tst_QScriptEngine::jsContinueInSwitch()
4268 {
4269 // This is testing ECMA-262 compliance, not C++ API.
4270
4271 QScriptEngine eng;
4272 // switch - continue
4273 {
4274 QScriptValue ret = eng.evaluate("switch (1) { default: continue; }");
4275 QVERIFY(ret.isError());
4276 }
4277 // for - switch - case - continue
4278 {
4279 QScriptValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4280 " switch (i) {\n"
4281 " case 1: ++j; continue;\n"
4282 " case 100: ++j; continue;\n"
4283 " case 1000: ++j; continue;\n"
4284 " }\n"
4285 "}; j");
4286 QVERIFY(ret.isNumber());
4287 QCOMPARE(ret.toInt32(), 3);
4288 }
4289 // for - switch - case - default - continue
4290 {
4291 QScriptValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4292 " switch (i) {\n"
4293 " case 1: ++j; continue;\n"
4294 " case 100: ++j; continue;\n"
4295 " case 1000: ++j; continue;\n"
4296 " default: if (i < 50000) break; else continue;\n"
4297 " }\n"
4298 "}; j");
4299 QVERIFY(ret.isNumber());
4300 QCOMPARE(ret.toInt32(), 3);
4301 }
4302 // switch - for - continue
4303 {
4304 QScriptValue ret = eng.evaluate("j = 123; switch (j) {\n"
4305 " case 123:\n"
4306 " for (i = 0; i < 100000; ++i) {\n"
4307 " continue;\n"
4308 " }\n"
4309 "}; i\n");
4310 QVERIFY(ret.isNumber());
4311 QCOMPARE(ret.toInt32(), 100000);
4312 }
4313 // switch - switch - continue
4314 {
4315 QScriptValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }");
4316 QVERIFY(ret.isError());
4317 }
4318 // for - switch - switch - continue
4319 {
4320 QScriptValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4321 " switch (i) {\n"
4322 " case 1:\n"
4323 " switch (i) {\n"
4324 " case 1: ++j; continue;\n"
4325 " }\n"
4326 " }\n"
4327 "}; j");
4328 QVERIFY(ret.isNumber());
4329 QCOMPARE(ret.toInt32(), 1);
4330 }
4331 // switch - for - switch - continue
4332 {
4333 QScriptValue ret = eng.evaluate("j = 123; switch (j) {\n"
4334 " case 123:\n"
4335 " for (i = 0; i < 100000; ++i) {\n"
4336 " switch (i) {\n"
4337 " case 1:\n"
4338 " ++j; continue;\n"
4339 " }\n"
4340 " }\n"
4341 "}; i\n");
4342 QVERIFY(ret.isNumber());
4343 QCOMPARE(ret.toInt32(), 100000);
4344 }
4345 }
4346
jsShadowReadOnlyPrototypeProperty()4347 void tst_QScriptEngine::jsShadowReadOnlyPrototypeProperty()
4348 {
4349 // SpiderMonkey has different behavior than JSC and V8; it disallows
4350 // creating a property on the instance if there's a property with the
4351 // same name in the prototype, and that property is read-only. We
4352 // adopted that behavior in the old (4.5) QtScript back-end, but it
4353 // just seems weird -- and non-compliant. Adopt the JSC behavior instead.
4354 QScriptEngine eng;
4355 QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber());
4356 QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt32(), 123);
4357 QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBoolean());
4358 }
4359
toObject()4360 void tst_QScriptEngine::toObject()
4361 {
4362 QScriptEngine eng;
4363
4364 QVERIFY(!eng.toObject(eng.undefinedValue()).isValid());
4365
4366 QVERIFY(!eng.toObject(eng.nullValue()).isValid());
4367
4368 QScriptValue falskt(false);
4369 {
4370 QScriptValue tmp = eng.toObject(falskt);
4371 QVERIFY(tmp.isObject());
4372 QCOMPARE(tmp.toNumber(), falskt.toNumber());
4373 }
4374 QVERIFY(falskt.isBool());
4375
4376 QScriptValue sant(true);
4377 {
4378 QScriptValue tmp = eng.toObject(sant);
4379 QVERIFY(tmp.isObject());
4380 QCOMPARE(tmp.toNumber(), sant.toNumber());
4381 }
4382 QVERIFY(sant.isBool());
4383
4384 QScriptValue number(123.0);
4385 {
4386 QScriptValue tmp = eng.toObject(number);
4387 QVERIFY(tmp.isObject());
4388 QCOMPARE(tmp.toNumber(), number.toNumber());
4389 }
4390 QVERIFY(number.isNumber());
4391
4392 QScriptValue str = QScriptValue(&eng, QString("ciao"));
4393 {
4394 QScriptValue tmp = eng.toObject(str);
4395 QVERIFY(tmp.isObject());
4396 QCOMPARE(tmp.toString(), str.toString());
4397 }
4398 QVERIFY(str.isString());
4399
4400 QScriptValue object = eng.newObject();
4401 {
4402 QScriptValue tmp = eng.toObject(object);
4403 QVERIFY(tmp.isObject());
4404 QVERIFY(tmp.strictlyEquals(object));
4405 }
4406
4407 QScriptValue qobject = eng.newQObject(this);
4408 QVERIFY(eng.toObject(qobject).strictlyEquals(qobject));
4409
4410 QVERIFY(!eng.toObject(QScriptValue()).isValid());
4411
4412 // v1 constructors
4413
4414 QScriptValue boolValue(&eng, true);
4415 {
4416 QScriptValue ret = eng.toObject(boolValue);
4417 QVERIFY(ret.isObject());
4418 QCOMPARE(ret.toBool(), boolValue.toBool());
4419 }
4420 QVERIFY(boolValue.isBool());
4421
4422 QScriptValue numberValue(&eng, 123.0);
4423 {
4424 QScriptValue ret = eng.toObject(numberValue);
4425 QVERIFY(ret.isObject());
4426 QCOMPARE(ret.toNumber(), numberValue.toNumber());
4427 }
4428 QVERIFY(numberValue.isNumber());
4429
4430 QScriptValue stringValue(&eng, QString::fromLatin1("foo"));
4431 {
4432 QScriptValue ret = eng.toObject(stringValue);
4433 QVERIFY(ret.isObject());
4434 QCOMPARE(ret.toString(), stringValue.toString());
4435 }
4436 QVERIFY(stringValue.isString());
4437 }
4438
jsReservedWords_data()4439 void tst_QScriptEngine::jsReservedWords_data()
4440 {
4441 QTest::addColumn<QString>("word");
4442 QTest::newRow("break") << QString("break");
4443 QTest::newRow("case") << QString("case");
4444 QTest::newRow("catch") << QString("catch");
4445 QTest::newRow("continue") << QString("continue");
4446 QTest::newRow("default") << QString("default");
4447 QTest::newRow("delete") << QString("delete");
4448 QTest::newRow("do") << QString("do");
4449 QTest::newRow("else") << QString("else");
4450 QTest::newRow("false") << QString("false");
4451 QTest::newRow("finally") << QString("finally");
4452 QTest::newRow("for") << QString("for");
4453 QTest::newRow("function") << QString("function");
4454 QTest::newRow("if") << QString("if");
4455 QTest::newRow("in") << QString("in");
4456 QTest::newRow("instanceof") << QString("instanceof");
4457 QTest::newRow("new") << QString("new");
4458 QTest::newRow("null") << QString("null");
4459 QTest::newRow("return") << QString("return");
4460 QTest::newRow("switch") << QString("switch");
4461 QTest::newRow("this") << QString("this");
4462 QTest::newRow("throw") << QString("throw");
4463 QTest::newRow("true") << QString("true");
4464 QTest::newRow("try") << QString("try");
4465 QTest::newRow("typeof") << QString("typeof");
4466 QTest::newRow("var") << QString("var");
4467 QTest::newRow("void") << QString("void");
4468 QTest::newRow("while") << QString("while");
4469 QTest::newRow("with") << QString("with");
4470 }
4471
jsReservedWords()4472 void tst_QScriptEngine::jsReservedWords()
4473 {
4474 // See ECMA-262 Section 7.6.1, "Reserved Words".
4475 // We prefer that the implementation is less strict than the spec; e.g.
4476 // it's good to allow reserved words as identifiers in object literals,
4477 // and when accessing properties using dot notation.
4478
4479 QFETCH(QString, word);
4480 {
4481 QScriptEngine eng;
4482 QScriptValue ret = eng.evaluate(word + " = 123");
4483 QVERIFY(ret.isError());
4484 QString str = ret.toString();
4485 QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError"));
4486 }
4487 {
4488 QScriptEngine eng;
4489 QScriptValue ret = eng.evaluate("var " + word + " = 123");
4490 QVERIFY(ret.isError());
4491 QVERIFY(ret.toString().startsWith("SyntaxError"));
4492 }
4493 {
4494 QScriptEngine eng;
4495 QScriptValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4496 // in the old back-end and in SpiderMonkey this is allowed, but not in JSC
4497 QVERIFY(ret.isError());
4498 QVERIFY(ret.toString().startsWith("SyntaxError"));
4499 }
4500 {
4501 QScriptEngine eng;
4502 QScriptValue ret = eng.evaluate("o = { " + word + ": 123 }");
4503 // in the old back-end and in SpiderMonkey this is allowed, but not in JSC
4504 QVERIFY(ret.isError());
4505 QVERIFY(ret.toString().startsWith("SyntaxError"));
4506 }
4507 {
4508 // SpiderMonkey allows this, but we don't
4509 QScriptEngine eng;
4510 QScriptValue ret = eng.evaluate("function " + word + "() {}");
4511 QVERIFY(ret.isError());
4512 QVERIFY(ret.toString().startsWith("SyntaxError"));
4513 }
4514 }
4515
jsFutureReservedWords_data()4516 void tst_QScriptEngine::jsFutureReservedWords_data()
4517 {
4518 QTest::addColumn<QString>("word");
4519 QTest::addColumn<bool>("allowed");
4520 QTest::newRow("abstract") << QString("abstract") << true;
4521 QTest::newRow("boolean") << QString("boolean") << true;
4522 QTest::newRow("byte") << QString("byte") << true;
4523 QTest::newRow("char") << QString("char") << true;
4524 QTest::newRow("class") << QString("class") << false;
4525 QTest::newRow("const") << QString("const") << false;
4526 QTest::newRow("debugger") << QString("debugger") << false;
4527 QTest::newRow("double") << QString("double") << true;
4528 QTest::newRow("enum") << QString("enum") << false;
4529 QTest::newRow("export") << QString("export") << false;
4530 QTest::newRow("extends") << QString("extends") << false;
4531 QTest::newRow("final") << QString("final") << true;
4532 QTest::newRow("float") << QString("float") << true;
4533 QTest::newRow("goto") << QString("goto") << true;
4534 QTest::newRow("implements") << QString("implements") << true;
4535 QTest::newRow("import") << QString("import") << false;
4536 QTest::newRow("int") << QString("int") << true;
4537 QTest::newRow("interface") << QString("interface") << true;
4538 QTest::newRow("long") << QString("long") << true;
4539 QTest::newRow("native") << QString("native") << true;
4540 QTest::newRow("package") << QString("package") << true;
4541 QTest::newRow("private") << QString("private") << true;
4542 QTest::newRow("protected") << QString("protected") << true;
4543 QTest::newRow("public") << QString("public") << true;
4544 QTest::newRow("short") << QString("short") << true;
4545 QTest::newRow("static") << QString("static") << true;
4546 QTest::newRow("super") << QString("super") << false;
4547 QTest::newRow("synchronized") << QString("synchronized") << true;
4548 QTest::newRow("throws") << QString("throws") << true;
4549 QTest::newRow("transient") << QString("transient") << true;
4550 QTest::newRow("volatile") << QString("volatile") << true;
4551 }
4552
jsFutureReservedWords()4553 void tst_QScriptEngine::jsFutureReservedWords()
4554 {
4555 // See ECMA-262 Section 7.6.1.2, "Future Reserved Words".
4556 // In real-world implementations, most of these words are
4557 // actually allowed as normal identifiers.
4558
4559 QFETCH(QString, word);
4560 QFETCH(bool, allowed);
4561 {
4562 QScriptEngine eng;
4563 QScriptValue ret = eng.evaluate(word + " = 123");
4564 QCOMPARE(!ret.isError(), allowed);
4565 }
4566 {
4567 QScriptEngine eng;
4568 QScriptValue ret = eng.evaluate("var " + word + " = 123");
4569 QCOMPARE(!ret.isError(), allowed);
4570 }
4571 {
4572 // this should probably be allowed (see task 162567)
4573 QScriptEngine eng;
4574 QScriptValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4575 QCOMPARE(ret.isNumber(), allowed);
4576 QCOMPARE(!ret.isError(), allowed);
4577 }
4578 {
4579 // this should probably be allowed (see task 162567)
4580 QScriptEngine eng;
4581 QScriptValue ret = eng.evaluate("o = { " + word + ": 123 }");
4582 QCOMPARE(!ret.isError(), allowed);
4583 }
4584 }
4585
jsThrowInsideWithStatement()4586 void tst_QScriptEngine::jsThrowInsideWithStatement()
4587 {
4588 // This is testing ECMA-262 compliance, not C++ API.
4589
4590 // task 209988
4591 QScriptEngine eng;
4592 {
4593 QScriptValue ret = eng.evaluate(
4594 "try {"
4595 " o = { bad : \"bug\" };"
4596 " with (o) {"
4597 " throw 123;"
4598 " }"
4599 "} catch (e) {"
4600 " bad;"
4601 "}");
4602 QVERIFY(ret.isError());
4603 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4604 }
4605 {
4606 QScriptValue ret = eng.evaluate(
4607 "try {"
4608 " o = { bad : \"bug\" };"
4609 " with (o) {"
4610 " throw 123;"
4611 " }"
4612 "} finally {"
4613 " bad;"
4614 "}");
4615 QVERIFY(ret.isError());
4616 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4617 }
4618 {
4619 eng.clearExceptions();
4620 QScriptValue ret = eng.evaluate(
4621 "o = { bug : \"no bug\" };"
4622 "with (o) {"
4623 " try {"
4624 " throw 123;"
4625 " } finally {"
4626 " bug;"
4627 " }"
4628 "}");
4629 QVERIFY(ret.isNumber());
4630 QCOMPARE(ret.toInt32(), 123);
4631 QVERIFY(eng.hasUncaughtException());
4632 }
4633 {
4634 eng.clearExceptions();
4635 QScriptValue ret = eng.evaluate(
4636 "o = { bug : \"no bug\" };"
4637 "with (o) {"
4638 " throw 123;"
4639 "}");
4640 QVERIFY(ret.isNumber());
4641 QScriptValue ret2 = eng.evaluate("bug");
4642 QVERIFY(ret2.isError());
4643 QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError")));
4644 }
4645 }
4646
4647 class TestAgent : public QScriptEngineAgent
4648 {
4649 public:
TestAgent(QScriptEngine * engine)4650 TestAgent(QScriptEngine *engine) : QScriptEngineAgent(engine) {}
4651 };
4652
getSetAgent_ownership()4653 void tst_QScriptEngine::getSetAgent_ownership()
4654 {
4655 // engine deleted before agent --> agent deleted too
4656 QScriptEngine *eng = new QScriptEngine;
4657 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4658 TestAgent *agent = new TestAgent(eng);
4659 eng->setAgent(agent);
4660 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4661 eng->setAgent(0); // the engine maintains ownership of the old agent
4662 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4663 delete eng;
4664 }
4665
getSetAgent_deleteAgent()4666 void tst_QScriptEngine::getSetAgent_deleteAgent()
4667 {
4668 // agent deleted before engine --> engine's agent should become 0
4669 QScriptEngine *eng = new QScriptEngine;
4670 TestAgent *agent = new TestAgent(eng);
4671 eng->setAgent(agent);
4672 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4673 delete agent;
4674 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4675 eng->evaluate("(function(){ return 123; })()");
4676 delete eng;
4677 }
4678
getSetAgent_differentEngine()4679 void tst_QScriptEngine::getSetAgent_differentEngine()
4680 {
4681 QScriptEngine eng;
4682 QScriptEngine eng2;
4683 TestAgent *agent = new TestAgent(&eng);
4684 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine");
4685 eng2.setAgent(agent);
4686 QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0);
4687 }
4688
reentrancy_stringHandles()4689 void tst_QScriptEngine::reentrancy_stringHandles()
4690 {
4691 QScriptEngine eng1;
4692 QScriptEngine eng2;
4693 QScriptString s1 = eng1.toStringHandle("foo");
4694 QScriptString s2 = eng2.toStringHandle("foo");
4695 QVERIFY(s1 != s2);
4696 }
4697
reentrancy_processEventsInterval()4698 void tst_QScriptEngine::reentrancy_processEventsInterval()
4699 {
4700 QScriptEngine eng1;
4701 QScriptEngine eng2;
4702 eng1.setProcessEventsInterval(123);
4703 QCOMPARE(eng2.processEventsInterval(), -1);
4704 eng2.setProcessEventsInterval(456);
4705 QCOMPARE(eng1.processEventsInterval(), 123);
4706 }
4707
reentrancy_typeConversion()4708 void tst_QScriptEngine::reentrancy_typeConversion()
4709 {
4710 QScriptEngine eng1;
4711 QScriptEngine eng2;
4712 qScriptRegisterMetaType<Foo>(&eng1, fooToScriptValue, fooFromScriptValue);
4713 Foo foo;
4714 foo.x = 12;
4715 foo.y = 34;
4716 {
4717 QScriptValue fooVal = qScriptValueFromValue(&eng1, foo);
4718 QVERIFY(fooVal.isObject());
4719 QVERIFY(!fooVal.isVariant());
4720 QCOMPARE(fooVal.property("x").toInt32(), 12);
4721 QCOMPARE(fooVal.property("y").toInt32(), 34);
4722 fooVal.setProperty("x", 56);
4723 fooVal.setProperty("y", 78);
4724
4725 Foo foo2 = qScriptValueToValue<Foo>(fooVal);
4726 QCOMPARE(foo2.x, 56);
4727 QCOMPARE(foo2.y, 78);
4728 }
4729 {
4730 QScriptValue fooVal = qScriptValueFromValue(&eng2, foo);
4731 QVERIFY(fooVal.isVariant());
4732
4733 Foo foo2 = qScriptValueToValue<Foo>(fooVal);
4734 QCOMPARE(foo2.x, 12);
4735 QCOMPARE(foo2.y, 34);
4736 }
4737 QVERIFY(!eng1.defaultPrototype(qMetaTypeId<Foo>()).isValid());
4738 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
4739 QScriptValue proto1 = eng1.newObject();
4740 eng1.setDefaultPrototype(qMetaTypeId<Foo>(), proto1);
4741 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
4742 QScriptValue proto2 = eng2.newObject();
4743 eng2.setDefaultPrototype(qMetaTypeId<Foo>(), proto2);
4744 QVERIFY(eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
4745 QVERIFY(eng1.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(proto1));
4746 }
4747
reentrancy_globalObjectProperties()4748 void tst_QScriptEngine::reentrancy_globalObjectProperties()
4749 {
4750 QScriptEngine eng1;
4751 QScriptEngine eng2;
4752 QVERIFY(!eng2.globalObject().property("a").isValid());
4753 eng1.evaluate("a = 10");
4754 QVERIFY(eng1.globalObject().property("a").isNumber());
4755 QVERIFY(!eng2.globalObject().property("a").isValid());
4756 eng2.evaluate("a = 20");
4757 QVERIFY(eng2.globalObject().property("a").isNumber());
4758 QCOMPARE(eng1.globalObject().property("a").toInt32(), 10);
4759 }
4760
reentrancy_Array()4761 void tst_QScriptEngine::reentrancy_Array()
4762 {
4763 // weird bug with JSC backend
4764 {
4765 QScriptEngine eng;
4766 QCOMPARE(eng.evaluate("Array()").toString(), QString());
4767 eng.evaluate("Array.prototype.toString");
4768 QCOMPARE(eng.evaluate("Array()").toString(), QString());
4769 }
4770 {
4771 QScriptEngine eng;
4772 QCOMPARE(eng.evaluate("Array()").toString(), QString());
4773 }
4774 }
4775
reentrancy_objectCreation()4776 void tst_QScriptEngine::reentrancy_objectCreation()
4777 {
4778 QScriptEngine eng1;
4779 QScriptEngine eng2;
4780 {
4781 QScriptValue d1 = eng1.newDate(0);
4782 QScriptValue d2 = eng2.newDate(0);
4783 QCOMPARE(d1.toDateTime(), d2.toDateTime());
4784 QCOMPARE(d2.toDateTime(), d1.toDateTime());
4785 }
4786 {
4787 QScriptValue r1 = eng1.newRegExp("foo", "gim");
4788 QScriptValue r2 = eng2.newRegExp("foo", "gim");
4789 QCOMPARE(r1.toRegExp(), r2.toRegExp());
4790 QCOMPARE(r2.toRegExp(), r1.toRegExp());
4791 }
4792 {
4793 QScriptValue o1 = eng1.newQObject(this);
4794 QScriptValue o2 = eng2.newQObject(this);
4795 QCOMPARE(o1.toQObject(), o2.toQObject());
4796 QCOMPARE(o2.toQObject(), o1.toQObject());
4797 }
4798 {
4799 QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject);
4800 QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject);
4801 QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject());
4802 QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject());
4803 }
4804 }
4805
jsIncDecNonObjectProperty()4806 void tst_QScriptEngine::jsIncDecNonObjectProperty()
4807 {
4808 // This is testing ECMA-262 compliance, not C++ API.
4809
4810 QScriptEngine eng;
4811 {
4812 QScriptValue ret = eng.evaluate("var a; a.n++");
4813 QVERIFY(ret.isError());
4814 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4815 }
4816 {
4817 QScriptValue ret = eng.evaluate("var a; a.n--");
4818 QVERIFY(ret.isError());
4819 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4820 }
4821 {
4822 QScriptValue ret = eng.evaluate("var a = null; a.n++");
4823 QVERIFY(ret.isError());
4824 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4825 }
4826 {
4827 QScriptValue ret = eng.evaluate("var a = null; a.n--");
4828 QVERIFY(ret.isError());
4829 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4830 }
4831 {
4832 QScriptValue ret = eng.evaluate("var a; ++a.n");
4833 QVERIFY(ret.isError());
4834 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4835 }
4836 {
4837 QScriptValue ret = eng.evaluate("var a; --a.n");
4838 QVERIFY(ret.isError());
4839 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4840 }
4841 {
4842 QScriptValue ret = eng.evaluate("var a; a.n += 1");
4843 QVERIFY(ret.isError());
4844 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4845 }
4846 {
4847 QScriptValue ret = eng.evaluate("var a; a.n -= 1");
4848 QVERIFY(ret.isError());
4849 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
4850 }
4851 {
4852 QScriptValue ret = eng.evaluate("var a = 'ciao'; a.length++");
4853 QVERIFY(ret.isNumber());
4854 QCOMPARE(ret.toInt32(), 4);
4855 }
4856 {
4857 QScriptValue ret = eng.evaluate("var a = 'ciao'; a.length--");
4858 QVERIFY(ret.isNumber());
4859 QCOMPARE(ret.toInt32(), 4);
4860 }
4861 {
4862 QScriptValue ret = eng.evaluate("var a = 'ciao'; ++a.length");
4863 QVERIFY(ret.isNumber());
4864 QCOMPARE(ret.toInt32(), 5);
4865 }
4866 {
4867 QScriptValue ret = eng.evaluate("var a = 'ciao'; --a.length");
4868 QVERIFY(ret.isNumber());
4869 QCOMPARE(ret.toInt32(), 3);
4870 }
4871 }
4872
installTranslatorFunctions_data()4873 void tst_QScriptEngine::installTranslatorFunctions_data()
4874 {
4875 QTest::addColumn<bool>("useCustomGlobalObject");
4876
4877 QTest::newRow("Default global object") << false;
4878 QTest::newRow("Custom global object") << true;
4879 }
4880
installTranslatorFunctions()4881 void tst_QScriptEngine::installTranslatorFunctions()
4882 {
4883 QFETCH(bool, useCustomGlobalObject);
4884
4885 QScriptEngine eng;
4886 QScriptValue globalOrig = eng.globalObject();
4887 QScriptValue global;
4888 if (useCustomGlobalObject) {
4889 global = eng.newObject();
4890 eng.setGlobalObject(global);
4891 } else {
4892 global = globalOrig;
4893 }
4894 QVERIFY(!global.property("qsTranslate").isValid());
4895 QVERIFY(!global.property("QT_TRANSLATE_NOOP").isValid());
4896 QVERIFY(!global.property("qsTr").isValid());
4897 QVERIFY(!global.property("QT_TR_NOOP").isValid());
4898 QVERIFY(!global.property("qsTrId").isValid());
4899 QVERIFY(!global.property("QT_TRID_NOOP").isValid());
4900 QVERIFY(!globalOrig.property("String").property("prototype").property("arg").isValid());
4901
4902 eng.installTranslatorFunctions();
4903 QVERIFY(global.property("qsTranslate").isFunction());
4904 QVERIFY(global.property("QT_TRANSLATE_NOOP").isFunction());
4905 QVERIFY(global.property("qsTr").isFunction());
4906 QVERIFY(global.property("QT_TR_NOOP").isFunction());
4907 QVERIFY(global.property("qsTrId").isFunction());
4908 QVERIFY(global.property("QT_TRID_NOOP").isFunction());
4909 QVERIFY(globalOrig.property("String").property("prototype").property("arg").isFunction());
4910
4911 if (useCustomGlobalObject) {
4912 QVERIFY(!globalOrig.property("qsTranslate").isValid());
4913 QVERIFY(!globalOrig.property("QT_TRANSLATE_NOOP").isValid());
4914 QVERIFY(!globalOrig.property("qsTr").isValid());
4915 QVERIFY(!globalOrig.property("QT_TR_NOOP").isValid());
4916 QVERIFY(!globalOrig.property("qsTrId").isValid());
4917 QVERIFY(!globalOrig.property("QT_TRID_NOOP").isValid());
4918 }
4919
4920 {
4921 QScriptValue ret = eng.evaluate("qsTr('foo')");
4922 QVERIFY(ret.isString());
4923 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4924 }
4925 {
4926 QScriptValue ret = eng.evaluate("qsTranslate('foo', 'bar')");
4927 QVERIFY(ret.isString());
4928 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
4929 }
4930 {
4931 QScriptValue ret = eng.evaluate("QT_TR_NOOP('foo')");
4932 QVERIFY(ret.isString());
4933 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4934 }
4935 {
4936 QScriptValue ret = eng.evaluate("QT_TRANSLATE_NOOP('foo', 'bar')");
4937 QVERIFY(ret.isString());
4938 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
4939 }
4940 {
4941 QScriptValue ret = eng.evaluate("'foo%0'.arg('bar')");
4942 QVERIFY(ret.isString());
4943 QCOMPARE(ret.toString(), QString::fromLatin1("foobar"));
4944 }
4945 {
4946 QScriptValue ret = eng.evaluate("'foo%0'.arg(123)");
4947 QVERIFY(ret.isString());
4948 QCOMPARE(ret.toString(), QString::fromLatin1("foo123"));
4949 }
4950 {
4951 // Maybe this should throw an error?
4952 QScriptValue ret = eng.evaluate("'foo%0'.arg()");
4953 QVERIFY(ret.isString());
4954 QCOMPARE(ret.toString(), QString());
4955 }
4956
4957 {
4958 QScriptValue ret = eng.evaluate("qsTrId('foo')");
4959 QVERIFY(ret.isString());
4960 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4961 }
4962 {
4963 QScriptValue ret = eng.evaluate("QT_TRID_NOOP('foo')");
4964 QVERIFY(ret.isString());
4965 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4966 }
4967 QVERIFY(eng.evaluate("QT_TRID_NOOP()").isUndefined());
4968 }
4969
4970 class TranslationScope
4971 {
4972 public:
TranslationScope(const QString & fileName)4973 TranslationScope(const QString &fileName)
4974 {
4975 translator.load(fileName);
4976 QCoreApplication::instance()->installTranslator(&translator);
4977 }
~TranslationScope()4978 ~TranslationScope()
4979 {
4980 QCoreApplication::instance()->removeTranslator(&translator);
4981 }
4982
4983 private:
4984 QTranslator translator;
4985 };
4986
translateScript_data()4987 void tst_QScriptEngine::translateScript_data()
4988 {
4989 QTest::addColumn<QString>("expression");
4990 QTest::addColumn<QString>("fileName");
4991 QTest::addColumn<QString>("expectedTranslation");
4992
4993 QString fileName = QString::fromLatin1("translatable.js");
4994 // Top-level
4995 QTest::newRow("qsTr('One')@translatable.js")
4996 << QString::fromLatin1("qsTr('One')") << fileName << QString::fromLatin1("En");
4997 QTest::newRow("qsTr('Hello')@translatable.js")
4998 << QString::fromLatin1("qsTr('Hello')") << fileName << QString::fromLatin1("Hallo");
4999 // From function
5000 QTest::newRow("(function() { return qsTr('One'); })()@translatable.js")
5001 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName << QString::fromLatin1("En");
5002 QTest::newRow("(function() { return qsTr('Hello'); })()@translatable.js")
5003 << QString::fromLatin1("(function() { return qsTr('Hello'); })()") << fileName << QString::fromLatin1("Hallo");
5004 // From eval
5005 QTest::newRow("eval('qsTr(\\'One\\')')@translatable.js")
5006 << QString::fromLatin1("eval('qsTr(\\'One\\')')") << fileName << QString::fromLatin1("En");
5007 QTest::newRow("eval('qsTr(\\'Hello\\')')@translatable.js")
5008 << QString::fromLatin1("eval('qsTr(\\'Hello\\')')") << fileName << QString::fromLatin1("Hallo");
5009 // Plural
5010 QTest::newRow("qsTr('%n message(s) saved', '', 1)@translatable.js")
5011 << QString::fromLatin1("qsTr('%n message(s) saved', '', 1)") << fileName << QString::fromLatin1("1 melding lagret");
5012 QTest::newRow("qsTr('%n message(s) saved', '', 3).arg@translatable.js")
5013 << QString::fromLatin1("qsTr('%n message(s) saved', '', 3)") << fileName << QString::fromLatin1("3 meldinger lagret");
5014
5015 // Top-level
5016 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable.js")
5017 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName << QString::fromLatin1("To");
5018 QTest::newRow("qsTranslate('FooContext', 'Goodbye')@translatable.js")
5019 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye')") << fileName << QString::fromLatin1("Farvel");
5020 // From eval
5021 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')@translatable.js")
5022 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')") << fileName << QString::fromLatin1("To");
5023 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')@translatable.js")
5024 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')") << fileName << QString::fromLatin1("Farvel");
5025
5026 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')@translatable.js")
5027 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')") << fileName << QString::fromLatin1("Farvel");
5028 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')@translatable.js")
5029 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')") << fileName << QString::fromLatin1("Farvel");
5030
5031 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)@translatable.js")
5032 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)") << fileName << QString::fromLatin1("Goodbye");
5033
5034 QTest::newRow("qsTr('One', 'not the same one')@translatable.js")
5035 << QString::fromLatin1("qsTr('One', 'not the same one')") << fileName << QString::fromLatin1("Enda en");
5036
5037 QTest::newRow("qsTr('One', 'not the same one', 42)@translatable.js")
5038 << QString::fromLatin1("qsTr('One', 'not the same one', 42)") << fileName << QString::fromLatin1("One");
5039
5040 // Plural
5041 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)@translatable.js")
5042 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)") << fileName << QString::fromLatin1("1 fooaktig bar funnet");
5043 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)@translatable.js")
5044 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)") << fileName << QString::fromLatin1("2 fooaktige barer funnet");
5045
5046 // Don't exist in translation
5047 QTest::newRow("qsTr('Three')@translatable.js")
5048 << QString::fromLatin1("qsTr('Three')") << fileName << QString::fromLatin1("Three");
5049 QTest::newRow("qsTranslate('FooContext', 'So long')@translatable.js")
5050 << QString::fromLatin1("qsTranslate('FooContext', 'So long')") << fileName << QString::fromLatin1("So long");
5051 QTest::newRow("qsTranslate('BarContext', 'Goodbye')@translatable.js")
5052 << QString::fromLatin1("qsTranslate('BarContext', 'Goodbye')") << fileName << QString::fromLatin1("Goodbye");
5053
5054 // Translate strings from the second script (translatable2.js)
5055
5056 QString fileName2 = QString::fromLatin1("translatable2.js");
5057 QTest::newRow("qsTr('Three')@translatable2.js")
5058 << QString::fromLatin1("qsTr('Three')") << fileName2 << QString::fromLatin1("Tre");
5059 QTest::newRow("qsTr('Happy birthday!')@translatable2.js")
5060 << QString::fromLatin1("qsTr('Happy birthday!')") << fileName2 << QString::fromLatin1("Gratulerer med dagen!");
5061
5062 // Not translated because translation is only in translatable.js
5063 QTest::newRow("qsTr('One')@translatable2.js")
5064 << QString::fromLatin1("qsTr('One')") << fileName2 << QString::fromLatin1("One");
5065 QTest::newRow("(function() { return qsTr('One'); })()@translatable2.js")
5066 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName2 << QString::fromLatin1("One");
5067
5068 // For qsTranslate() the filename shouldn't matter
5069 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable2.js")
5070 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName2 << QString::fromLatin1("To");
5071 QTest::newRow("qsTranslate('BarContext', 'Congratulations!')@translatable.js")
5072 << QString::fromLatin1("qsTranslate('BarContext', 'Congratulations!')") << fileName << QString::fromLatin1("Gratulerer!");
5073 }
5074
translateScript()5075 void tst_QScriptEngine::translateScript()
5076 {
5077 QFETCH(QString, expression);
5078 QFETCH(QString, fileName);
5079 QFETCH(QString, expectedTranslation);
5080
5081 QScriptEngine engine;
5082
5083 TranslationScope tranScope(":/translations/translatable_la");
5084 engine.installTranslatorFunctions();
5085
5086 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5087 QVERIFY(!engine.hasUncaughtException());
5088 }
5089
translateScript_crossScript()5090 void tst_QScriptEngine::translateScript_crossScript()
5091 {
5092 QScriptEngine engine;
5093 TranslationScope tranScope(":/translations/translatable_la");
5094 engine.installTranslatorFunctions();
5095
5096 QString fileName = QString::fromLatin1("translatable.js");
5097 QString fileName2 = QString::fromLatin1("translatable2.js");
5098 // qsTr() should use the innermost filename as context
5099 engine.evaluate("function foo(s) { return bar(s); }", fileName);
5100 engine.evaluate("function bar(s) { return qsTr(s); }", fileName2);
5101 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5102 QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre"));
5103 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One"));
5104
5105 engine.evaluate("function foo(s) { return bar(s); }", fileName2);
5106 engine.evaluate("function bar(s) { return qsTr(s); }", fileName);
5107 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three"));
5108 QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En"));
5109 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En"));
5110 }
5111
callQsTr(QScriptContext * ctx,QScriptEngine * eng)5112 static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng)
5113 {
5114 return eng->globalObject().property("qsTr").call(ctx->thisObject(), ctx->argumentsObject());
5115 }
5116
translateScript_callQsTrFromNative()5117 void tst_QScriptEngine::translateScript_callQsTrFromNative()
5118 {
5119 QScriptEngine engine;
5120 TranslationScope tranScope(":/translations/translatable_la");
5121 engine.installTranslatorFunctions();
5122
5123 QString fileName = QString::fromLatin1("translatable.js");
5124 QString fileName2 = QString::fromLatin1("translatable2.js");
5125 // Calling qsTr() from a native function
5126 engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr));
5127 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En"));
5128 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One"));
5129 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three"));
5130 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5131 }
5132
translateScript_trNoOp()5133 void tst_QScriptEngine::translateScript_trNoOp()
5134 {
5135 QScriptEngine engine;
5136 TranslationScope tranScope(":/translations/translatable_la");
5137 engine.installTranslatorFunctions();
5138
5139 QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined());
5140 QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One"));
5141
5142 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined());
5143 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined());
5144 QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two"));
5145 }
5146
translateScript_callQsTrFromCpp()5147 void tst_QScriptEngine::translateScript_callQsTrFromCpp()
5148 {
5149 QScriptEngine engine;
5150 TranslationScope tranScope(":/translations/translatable_la");
5151 engine.installTranslatorFunctions();
5152
5153 // There is no context, but it shouldn't crash
5154 QCOMPARE(engine.globalObject().property("qsTr").call(
5155 QScriptValue(), QScriptValueList() << "One").toString(), QString::fromLatin1("One"));
5156 }
5157
translateWithInvalidArgs_data()5158 void tst_QScriptEngine::translateWithInvalidArgs_data()
5159 {
5160 QTest::addColumn<QString>("expression");
5161 QTest::addColumn<QString>("expectedError");
5162
5163 QTest::newRow("qsTr()") << "qsTr()" << "Error: qsTr() requires at least one argument";
5164 QTest::newRow("qsTr(123)") << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string";
5165 QTest::newRow("qsTr('foo', 123)") << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string";
5166 QTest::newRow("qsTr('foo', 'bar', 'baz')") << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number";
5167 QTest::newRow("qsTr('foo', 'bar', true)") << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number";
5168
5169 QTest::newRow("qsTranslate()") << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments";
5170 QTest::newRow("qsTranslate('foo')") << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments";
5171 QTest::newRow("qsTranslate(123, 'foo')") << "qsTranslate(123, 'foo')" << "Error: qsTranslate(): first argument (context) must be a string";
5172 QTest::newRow("qsTranslate('foo', 123)") << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string";
5173 QTest::newRow("qsTranslate('foo', 'bar', 123)") << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string";
5174 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string";
5175 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number";
5176 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'";
5177
5178 QTest::newRow("qsTrId()") << "qsTrId()" << "Error: qsTrId() requires at least one argument";
5179 QTest::newRow("qsTrId(123)") << "qsTrId(123)" << "TypeError: qsTrId(): first argument (id) must be a string";
5180 QTest::newRow("qsTrId('foo', 'bar')") << "qsTrId('foo', 'bar')" << "TypeError: qsTrId(): second argument (n) must be a number";
5181 }
5182
translateWithInvalidArgs()5183 void tst_QScriptEngine::translateWithInvalidArgs()
5184 {
5185 QFETCH(QString, expression);
5186 QFETCH(QString, expectedError);
5187 QScriptEngine engine;
5188 engine.installTranslatorFunctions();
5189 QScriptValue result = engine.evaluate(expression);
5190 QVERIFY(result.isError());
5191 QCOMPARE(result.toString(), expectedError);
5192 }
5193
translationContext_data()5194 void tst_QScriptEngine::translationContext_data()
5195 {
5196 QTest::addColumn<QString>("path");
5197 QTest::addColumn<QString>("text");
5198 QTest::addColumn<QString>("expectedTranslation");
5199
5200 QTest::newRow("translatable.js") << "translatable.js" << "One" << "En";
5201 QTest::newRow("/translatable.js") << "/translatable.js" << "One" << "En";
5202 QTest::newRow("/foo/translatable.js") << "/foo/translatable.js" << "One" << "En";
5203 QTest::newRow("/foo/bar/translatable.js") << "/foo/bar/translatable.js" << "One" << "En";
5204 QTest::newRow("./translatable.js") << "./translatable.js" << "One" << "En";
5205 QTest::newRow("../translatable.js") << "../translatable.js" << "One" << "En";
5206 QTest::newRow("foo/translatable.js") << "foo/translatable.js" << "One" << "En";
5207 QTest::newRow("file:///home/qt/translatable.js") << "file:///home/qt/translatable.js" << "One" << "En";
5208 QTest::newRow(":/resources/translatable.js") << ":/resources/translatable.js" << "One" << "En";
5209 QTest::newRow("/translatable.js.foo") << "/translatable.js.foo" << "One" << "En";
5210 QTest::newRow("/translatable.txt") << "/translatable.txt" << "One" << "En";
5211 QTest::newRow("translatable") << "translatable" << "One" << "En";
5212 QTest::newRow("foo/translatable") << "foo/translatable" << "One" << "En";
5213
5214 QTest::newRow("native separators")
5215 << (QDir::toNativeSeparators(QDir::currentPath()) + QDir::separator() + "translatable.js")
5216 << "One" << "En";
5217
5218 QTest::newRow("translatable.js/") << "translatable.js/" << "One" << "One";
5219 QTest::newRow("nosuchscript.js") << "" << "One" << "One";
5220 QTest::newRow("(empty)") << "" << "One" << "One";
5221 }
5222
translationContext()5223 void tst_QScriptEngine::translationContext()
5224 {
5225 TranslationScope tranScope(":/translations/translatable_la");
5226
5227 QScriptEngine engine;
5228 engine.installTranslatorFunctions();
5229
5230 QFETCH(QString, path);
5231 QFETCH(QString, text);
5232 QFETCH(QString, expectedTranslation);
5233 QScriptValue ret = engine.evaluate(QString::fromLatin1("qsTr('%0')").arg(text), path);
5234 QVERIFY(ret.isString());
5235 QCOMPARE(ret.toString(), expectedTranslation);
5236 }
5237
translateScriptIdBased()5238 void tst_QScriptEngine::translateScriptIdBased()
5239 {
5240 QScriptEngine engine;
5241
5242 TranslationScope tranScope(":/translations/idtranslatable_la");
5243 engine.installTranslatorFunctions();
5244
5245 QString fileName = QString::fromLatin1("idtranslatable.js");
5246
5247 QHash<QString, QString> expectedTranslations;
5248 expectedTranslations["qtn_foo_bar"] = "First string";
5249 expectedTranslations["qtn_needle"] = "Second string";
5250 expectedTranslations["qtn_haystack"] = "Third string";
5251 expectedTranslations["qtn_bar_baz"] = "Fourth string";
5252
5253 QHash<QString, QString>::const_iterator it;
5254 for (it = expectedTranslations.constBegin(); it != expectedTranslations.constEnd(); ++it) {
5255 for (int x = 0; x < 2; ++x) {
5256 QString fn;
5257 if (x)
5258 fn = fileName;
5259 // Top-level
5260 QCOMPARE(engine.evaluate(QString::fromLatin1("qsTrId('%0')")
5261 .arg(it.key()), fn).toString(),
5262 it.value());
5263 QCOMPARE(engine.evaluate(QString::fromLatin1("QT_TRID_NOOP('%0')")
5264 .arg(it.key()), fn).toString(),
5265 it.key());
5266 // From function
5267 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return qsTrId('%0'); })()")
5268 .arg(it.key()), fn).toString(),
5269 it.value());
5270 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return QT_TRID_NOOP('%0'); })()")
5271 .arg(it.key()), fn).toString(),
5272 it.key());
5273 }
5274 }
5275
5276 // Plural form
5277 QCOMPARE(engine.evaluate("qsTrId('qtn_bar_baz', 10)").toString(),
5278 QString::fromLatin1("10 fooish bar(s) found"));
5279 QCOMPARE(engine.evaluate("qsTrId('qtn_foo_bar', 10)").toString(),
5280 QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural
5281 }
5282
5283 // How to add a new test row:
5284 // - Find a nice list of Unicode characters to choose from
5285 // - Write source string/context/comment in .js using Unicode escape sequences (\uABCD)
5286 // - Update corresponding .ts file (e.g. lupdate foo.js -ts foo.ts -codecfortr UTF-8)
5287 // - Enter translation in Linguist
5288 // - Update corresponding .qm file (e.g. lrelease foo.ts)
5289 // - Evaluate script that performs translation; make sure the correct result is returned
5290 // (e.g. by setting the resulting string as the text of a QLabel and visually verifying
5291 // that it looks the same as what you entered in Linguist :-) )
5292 // - Generate the expectedTranslation column data using toUtf8().toHex()
translateScriptUnicode_data()5293 void tst_QScriptEngine::translateScriptUnicode_data()
5294 {
5295 QTest::addColumn<QString>("expression");
5296 QTest::addColumn<QString>("fileName");
5297 QTest::addColumn<QString>("expectedTranslation");
5298
5299 QString fileName = QString::fromLatin1("translatable-unicode.js");
5300 QTest::newRow("qsTr('H\\u2082O')@translatable-unicode.js")
5301 << QString::fromLatin1("qsTr('H\\u2082O')") << fileName << QString::fromUtf8("\xcd\xbb\xcd\xbc\xcd\xbd");
5302 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')@translatable-unicode.js")
5303 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << fileName << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5304 QTest::newRow("qsTr('\\u0391\\u0392\\u0393')@translatable-unicode.js")
5305 << QString::fromLatin1("qsTr('\\u0391\\u0392\\u0393')") << fileName << QString::fromUtf8("\xd3\x9c\xd2\xb4\xd1\xbc");
5306 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')@translatable-unicode.js")
5307 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')") << fileName << QString::fromUtf8("\xd8\xae\xd8\xb3\xd8\xb3");
5308 QTest::newRow("qsTr('H\\u2082O', 'not the same H\\u2082O')@translatable-unicode.js")
5309 << QString::fromLatin1("qsTr('H\\u2082O', 'not the same H\\u2082O')") << fileName << QString::fromUtf8("\xd4\xb6\xd5\x8a\xd5\x92");
5310 QTest::newRow("qsTr('H\\u2082O')")
5311 << QString::fromLatin1("qsTr('H\\u2082O')") << QString() << QString::fromUtf8("\x48\xe2\x82\x82\x4f");
5312 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')")
5313 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << QString() << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5314 }
5315
translateScriptUnicode()5316 void tst_QScriptEngine::translateScriptUnicode()
5317 {
5318 QFETCH(QString, expression);
5319 QFETCH(QString, fileName);
5320 QFETCH(QString, expectedTranslation);
5321
5322 QScriptEngine engine;
5323
5324 TranslationScope tranScope(":/translations/translatable-unicode");
5325 engine.installTranslatorFunctions();
5326
5327 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5328 QVERIFY(!engine.hasUncaughtException());
5329 }
5330
translateScriptUnicodeIdBased_data()5331 void tst_QScriptEngine::translateScriptUnicodeIdBased_data()
5332 {
5333 QTest::addColumn<QString>("expression");
5334 QTest::addColumn<QString>("expectedTranslation");
5335
5336 QTest::newRow("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1'')")
5337 << QString::fromLatin1("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1')") << QString::fromUtf8("\xc6\xa7\xc6\xb0\xc6\x88\xc8\xbc\xc8\x9d\xc8\xbf\xc8\x99");
5338 QTest::newRow("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')")
5339 << QString::fromLatin1("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')") << QString::fromUtf8("\xc7\xa0\xc8\xa1\xc8\x8b\xc8\x85\xc8\x95");
5340 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)")
5341 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)") << QString::fromUtf8("\x31\x30\x20\xc6\x92\xc6\xa1\xc7\x92\x28\xc8\x99\x29");
5342 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')")
5343 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')") << QString::fromUtf8("\xc6\x91\xc6\xb0\xc7\xb9");
5344 QTest::newRow("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')")
5345 << QString::fromLatin1("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')") << QString::fromUtf8("\xc7\x8d\xc6\x80\xc6\xa8\xc6\x90\xc6\x9e\xc6\xab");
5346 }
5347
translateScriptUnicodeIdBased()5348 void tst_QScriptEngine::translateScriptUnicodeIdBased()
5349 {
5350 QFETCH(QString, expression);
5351 QFETCH(QString, expectedTranslation);
5352
5353 QScriptEngine engine;
5354
5355 TranslationScope tranScope(":/translations/idtranslatable-unicode");
5356 engine.installTranslatorFunctions();
5357
5358 QCOMPARE(engine.evaluate(expression).toString(), expectedTranslation);
5359 QVERIFY(!engine.hasUncaughtException());
5360 }
5361
translateFromBuiltinCallback()5362 void tst_QScriptEngine::translateFromBuiltinCallback()
5363 {
5364 QScriptEngine eng;
5365 eng.installTranslatorFunctions();
5366
5367 // Callback has no translation context.
5368 eng.evaluate("function foo() { qsTr('foo'); }");
5369
5370 // Stack at translation time will be:
5371 // qsTr, foo, forEach, global
5372 // qsTr() needs to walk to the outer-most (global) frame before it finds
5373 // a translation context, and this should not crash.
5374 eng.evaluate("[10,20].forEach(foo)", "script.js");
5375 }
5376
functionScopes()5377 void tst_QScriptEngine::functionScopes()
5378 {
5379 QScriptEngine eng;
5380 {
5381 // top-level functions have only the global object in their scope
5382 QScriptValue fun = eng.evaluate("(function() {})");
5383 QVERIFY(fun.isFunction());
5384 QEXPECT_FAIL("", "QScriptValue::scope() is internal, not implemented", Abort);
5385 QVERIFY(fun.scope().isObject());
5386 QVERIFY(fun.scope().strictlyEquals(eng.globalObject()));
5387 QVERIFY(!eng.globalObject().scope().isValid());
5388 }
5389 {
5390 QScriptValue fun = eng.globalObject().property("Object");
5391 QVERIFY(fun.isFunction());
5392 // native built-in functions don't have scope
5393 QVERIFY(!fun.scope().isValid());
5394 }
5395 {
5396 // closure
5397 QScriptValue fun = eng.evaluate("(function(arg) { var foo = arg; return function() { return foo; }; })(123)");
5398 QVERIFY(fun.isFunction());
5399 {
5400 QScriptValue ret = fun.call();
5401 QVERIFY(ret.isNumber());
5402 QCOMPARE(ret.toInt32(), 123);
5403 }
5404 QScriptValue scope = fun.scope();
5405 QVERIFY(scope.isObject());
5406 {
5407 QScriptValue ret = scope.property("foo");
5408 QVERIFY(ret.isNumber());
5409 QCOMPARE(ret.toInt32(), 123);
5410 QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
5411 }
5412 {
5413 QScriptValue ret = scope.property("arg");
5414 QVERIFY(ret.isNumber());
5415 QCOMPARE(ret.toInt32(), 123);
5416 QCOMPARE(scope.propertyFlags("arg"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
5417 }
5418
5419 scope.setProperty("foo", 456);
5420 {
5421 QScriptValue ret = fun.call();
5422 QVERIFY(ret.isNumber());
5423 QCOMPARE(ret.toInt32(), 456);
5424 }
5425
5426 scope = scope.scope();
5427 QVERIFY(scope.isObject());
5428 QVERIFY(scope.strictlyEquals(eng.globalObject()));
5429 }
5430 }
5431
counter_inner(QScriptContext * ctx,QScriptEngine *)5432 static QScriptValue counter_inner(QScriptContext *ctx, QScriptEngine *)
5433 {
5434 QScriptValue outerAct = ctx->callee().scope();
5435 double count = outerAct.property("count").toNumber();
5436 outerAct.setProperty("count", count+1);
5437 return count;
5438 }
5439
counter(QScriptContext * ctx,QScriptEngine * eng)5440 static QScriptValue counter(QScriptContext *ctx, QScriptEngine *eng)
5441 {
5442 QScriptValue act = ctx->activationObject();
5443 act.setProperty("count", ctx->argument(0).toInt32());
5444 QScriptValue result = eng->newFunction(counter_inner);
5445 result.setScope(act);
5446 return result;
5447 }
5448
counter_hybrid(QScriptContext * ctx,QScriptEngine * eng)5449 static QScriptValue counter_hybrid(QScriptContext *ctx, QScriptEngine *eng)
5450 {
5451 QScriptValue act = ctx->activationObject();
5452 act.setProperty("count", ctx->argument(0).toInt32());
5453 return eng->evaluate("(function() { return count++; })");
5454 }
5455
nativeFunctionScopes()5456 void tst_QScriptEngine::nativeFunctionScopes()
5457 {
5458 QScriptEngine eng;
5459 {
5460 QScriptValue fun = eng.newFunction(counter);
5461 QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123);
5462 QVERIFY(cnt.isFunction());
5463 {
5464 QScriptValue ret = cnt.call();
5465 QVERIFY(ret.isNumber());
5466 QCOMPARE(ret.toInt32(), 123);
5467 }
5468 }
5469 {
5470 QScriptValue fun = eng.newFunction(counter_hybrid);
5471 QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123);
5472 QVERIFY(cnt.isFunction());
5473 {
5474 QScriptValue ret = cnt.call();
5475 QVERIFY(ret.isNumber());
5476 QCOMPARE(ret.toInt32(), 123);
5477 }
5478 }
5479
5480 //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain
5481 {
5482 QScriptEngine eng;
5483 eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n"
5484 "var c1 = counter(); var c2 = counter(); ");
5485 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5486 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5487 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5488 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5489 QVERIFY(!eng.hasUncaughtException());
5490 }
5491 {
5492 QScriptEngine eng;
5493 eng.globalObject().setProperty("counter", eng.newFunction(counter));
5494 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5495 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5496 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5497 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5498 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5499 QVERIFY(!eng.hasUncaughtException());
5500 }
5501 {
5502 QScriptEngine eng;
5503 eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid));
5504 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5505 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5506 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5507 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5508 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5509 QVERIFY(!eng.hasUncaughtException());
5510 }
5511 }
5512
createProgram(QScriptContext * ctx,QScriptEngine * eng)5513 static QScriptValue createProgram(QScriptContext *ctx, QScriptEngine *eng)
5514 {
5515 QString code = ctx->argument(0).toString();
5516 QScriptProgram result(code);
5517 return qScriptValueFromValue(eng, result);
5518 }
5519
evaluateProgram()5520 void tst_QScriptEngine::evaluateProgram()
5521 {
5522 QScriptEngine eng;
5523
5524 {
5525 QString code("1 + 2");
5526 QString fileName("hello.js");
5527 int lineNumber(123);
5528 QScriptProgram program(code, fileName, lineNumber);
5529 QVERIFY(!program.isNull());
5530 QCOMPARE(program.sourceCode(), code);
5531 QCOMPARE(program.fileName(), fileName);
5532 QCOMPARE(program.firstLineNumber(), lineNumber);
5533
5534 QScriptValue expected = eng.evaluate(code);
5535 for (int x = 0; x < 10; ++x) {
5536 QScriptValue ret = eng.evaluate(program);
5537 QVERIFY(ret.equals(expected));
5538 }
5539
5540 // operator=
5541 QScriptProgram sameProgram = program;
5542 QVERIFY(sameProgram == program);
5543 QVERIFY(eng.evaluate(sameProgram).equals(expected));
5544
5545 // copy constructor
5546 QScriptProgram sameProgram2(program);
5547 QVERIFY(sameProgram2 == program);
5548 QVERIFY(eng.evaluate(sameProgram2).equals(expected));
5549
5550 QScriptProgram differentProgram("2 + 3");
5551 QVERIFY(differentProgram != program);
5552 QVERIFY(!eng.evaluate(differentProgram).equals(expected));
5553 }
5554 }
5555
evaluateProgram_customScope()5556 void tst_QScriptEngine::evaluateProgram_customScope()
5557 {
5558 QScriptEngine eng;
5559 {
5560 QScriptProgram program("a");
5561 QVERIFY(!program.isNull());
5562 {
5563 QScriptValue ret = eng.evaluate(program);
5564 QVERIFY(ret.isError());
5565 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a"));
5566 }
5567
5568 QScriptValue obj = eng.newObject();
5569 obj.setProperty("a", 123);
5570 QScriptContext *ctx = eng.currentContext();
5571 ctx->pushScope(obj);
5572 {
5573 QScriptValue ret = eng.evaluate(program);
5574 QVERIFY(!ret.isError());
5575 QVERIFY(ret.equals(obj.property("a")));
5576 }
5577
5578 obj.setProperty("a", QScriptValue());
5579 {
5580 QScriptValue ret = eng.evaluate(program);
5581 QVERIFY(ret.isError());
5582 }
5583
5584 QScriptValue obj2 = eng.newObject();
5585 obj2.setProperty("a", 456);
5586 ctx->pushScope(obj2);
5587 {
5588 QScriptValue ret = eng.evaluate(program);
5589 QVERIFY(!ret.isError());
5590 QVERIFY(ret.equals(obj2.property("a")));
5591 }
5592
5593 ctx->popScope();
5594 }
5595 }
5596
evaluateProgram_closure()5597 void tst_QScriptEngine::evaluateProgram_closure()
5598 {
5599 QScriptEngine eng;
5600 {
5601 QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })");
5602 QVERIFY(!program.isNull());
5603 QScriptValue createCounter = eng.evaluate(program);
5604 QVERIFY(createCounter.isFunction());
5605 QScriptValue counter = createCounter.call();
5606 QVERIFY(counter.isFunction());
5607 {
5608 QScriptValue ret = counter.call();
5609 QVERIFY(ret.isNumber());
5610 }
5611 QScriptValue counter2 = createCounter.call();
5612 QVERIFY(counter2.isFunction());
5613 QVERIFY(!counter2.equals(counter));
5614 {
5615 QScriptValue ret = counter2.call();
5616 QVERIFY(ret.isNumber());
5617 }
5618 }
5619 }
5620
evaluateProgram_executeLater()5621 void tst_QScriptEngine::evaluateProgram_executeLater()
5622 {
5623 QScriptEngine eng;
5624 // Program created in a function call, then executed later
5625 {
5626 QScriptValue fun = eng.newFunction(createProgram);
5627 QScriptProgram program = qscriptvalue_cast<QScriptProgram>(
5628 fun.call(QScriptValue(), QScriptValueList() << "a + 1"));
5629 QVERIFY(!program.isNull());
5630 eng.globalObject().setProperty("a", QScriptValue());
5631 {
5632 QScriptValue ret = eng.evaluate(program);
5633 QVERIFY(ret.isError());
5634 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a"));
5635 }
5636 eng.globalObject().setProperty("a", 122);
5637 {
5638 QScriptValue ret = eng.evaluate(program);
5639 QVERIFY(!ret.isError());
5640 QVERIFY(ret.isNumber());
5641 QCOMPARE(ret.toInt32(), 123);
5642 }
5643 }
5644 }
5645
evaluateProgram_multipleEngines()5646 void tst_QScriptEngine::evaluateProgram_multipleEngines()
5647 {
5648 QScriptEngine eng;
5649 {
5650 QString code("1 + 2");
5651 QScriptProgram program(code);
5652 QVERIFY(!program.isNull());
5653 double expected = eng.evaluate(program).toNumber();
5654 for (int x = 0; x < 2; ++x) {
5655 QScriptEngine eng2;
5656 for (int y = 0; y < 2; ++y) {
5657 double ret = eng2.evaluate(program).toNumber();
5658 QCOMPARE(ret, expected);
5659 }
5660 }
5661 }
5662 }
5663
evaluateProgram_empty()5664 void tst_QScriptEngine::evaluateProgram_empty()
5665 {
5666 QScriptEngine eng;
5667 {
5668 QScriptProgram program;
5669 QVERIFY(program.isNull());
5670 QScriptValue ret = eng.evaluate(program);
5671 QVERIFY(!ret.isValid());
5672 }
5673 }
5674
collectGarbageAfterConnect()5675 void tst_QScriptEngine::collectGarbageAfterConnect()
5676 {
5677 // QTBUG-6366
5678 QScriptEngine engine;
5679 QPointer<QWidget> widget = new QWidget;
5680 engine.globalObject().setProperty(
5681 "widget", engine.newQObject(widget, QScriptEngine::ScriptOwnership));
5682 QVERIFY(engine.evaluate("widget.customContextMenuRequested.connect(\n"
5683 " function() { print('hello'); }\n"
5684 ");")
5685 .isUndefined());
5686 QVERIFY(widget != 0);
5687 engine.evaluate("widget = null;");
5688 // The connection should not keep the widget alive.
5689 collectGarbage_helper(engine);
5690 QVERIFY(widget == 0);
5691 }
5692
collectGarbageAfterNativeArguments()5693 void tst_QScriptEngine::collectGarbageAfterNativeArguments()
5694 {
5695 // QTBUG-17788
5696 QScriptEngine eng;
5697 QScriptContext *ctx = eng.pushContext();
5698 QScriptValue arguments = ctx->argumentsObject();
5699 // Shouldn't crash when marking the arguments object.
5700 collectGarbage_helper(eng);
5701 }
5702
constructQObjectFromThisObject(QScriptContext * ctx,QScriptEngine * eng)5703 static QScriptValue constructQObjectFromThisObject(QScriptContext *ctx, QScriptEngine *eng)
5704 {
5705 if (!ctx->isCalledAsConstructor()) {
5706 qWarning("%s: ctx->isCalledAsConstructor() returned false", Q_FUNC_INFO);
5707 return QScriptValue();
5708 }
5709 return eng->newQObject(ctx->thisObject(), new QObject, QScriptEngine::ScriptOwnership);
5710 }
5711
promoteThisObjectToQObjectInConstructor()5712 void tst_QScriptEngine::promoteThisObjectToQObjectInConstructor()
5713 {
5714 QScriptEngine engine;
5715 QScriptValue ctor = engine.newFunction(constructQObjectFromThisObject);
5716 engine.globalObject().setProperty("Ctor", ctor);
5717 QScriptValue object = engine.evaluate("new Ctor");
5718 QVERIFY(!object.isError());
5719 QVERIFY(object.isQObject());
5720 QVERIFY(object.toQObject() != 0);
5721 QVERIFY(object.property("objectName").isString());
5722 QVERIFY(object.property("deleteLater").isFunction());
5723 }
5724
minimal(QRegExp r)5725 static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
5726
qRegExpInport_data()5727 void tst_QScriptEngine::qRegExpInport_data()
5728 {
5729 QTest::addColumn<QRegExp>("rx");
5730 QTest::addColumn<QString>("string");
5731 QTest::addColumn<QString>("matched");
5732
5733 QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo";
5734 QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo";
5735 QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
5736 QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
5737 QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab";
5738 QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba";
5739 // this one will fail because we do not support the QRegExp::RegExp in JSC
5740 //QTest::newRow("not_greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp) << "aaaabaaba";
5741 QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt";
5742 QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt";
5743 QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string";
5744 QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string";
5745 QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba";
5746 QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
5747 QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
5748 QTest::newRow("html") << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>";
5749 QTest::newRow("html minimal") << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>";
5750 QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa";
5751 QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa";
5752 QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *";
5753 QTest::newRow(".? minimal") << minimal(QRegExp(".?")) << ".?";
5754 QTest::newRow(".+ minimal") << minimal(QRegExp(".+")) << ".+";
5755 QTest::newRow("[.?] minimal") << minimal(QRegExp("[.?]")) << ".?";
5756 QTest::newRow("[.+] minimal") << minimal(QRegExp("[.+]")) << ".+";
5757 }
5758
qRegExpInport()5759 void tst_QScriptEngine::qRegExpInport()
5760 {
5761 QFETCH(QRegExp, rx);
5762 QFETCH(QString, string);
5763
5764 QScriptEngine eng;
5765 QScriptValue rexp;
5766 rexp = eng.newRegExp(rx);
5767
5768 QCOMPARE(rexp.isValid(), true);
5769 QCOMPARE(rexp.isRegExp(), true);
5770 QVERIFY(rexp.isFunction());
5771
5772 QScriptValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
5773 QScriptValue result = func.call(QScriptValue(), QScriptValueList() << string << rexp);
5774
5775 rx.indexIn(string);
5776 for (int i = 0; i <= rx.captureCount(); i++) {
5777 QCOMPARE(result.property(i).toString(), rx.cap(i));
5778 }
5779 }
5780
5781 // QScriptValue::toDateTime() returns a local time, whereas JS dates
5782 // are always stored as UTC. QtScript must respect the current time
5783 // zone, and correctly adjust for daylight saving time that may be in
5784 // effect at a given date (QTBUG-9770).
dateRoundtripJSQtJS()5785 void tst_QScriptEngine::dateRoundtripJSQtJS()
5786 {
5787 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
5788 QScriptEngine eng;
5789 for (int i = 0; i < 8000; ++i) {
5790 QScriptValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
5791 QDateTime qtDate = jsDate.toDateTime();
5792 QScriptValue jsDate2 = eng.newDate(qtDate);
5793 if (jsDate2.toNumber() != jsDate.toNumber())
5794 QFAIL(qPrintable(jsDate.toString()));
5795 secs += 2*60*60;
5796 }
5797 }
5798
dateRoundtripQtJSQt()5799 void tst_QScriptEngine::dateRoundtripQtJSQt()
5800 {
5801 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
5802 QScriptEngine eng;
5803 for (int i = 0; i < 8000; ++i) {
5804 QScriptValue jsDate = eng.newDate(qtDate);
5805 QDateTime qtDate2 = jsDate.toDateTime();
5806 if (qtDate2 != qtDate)
5807 QFAIL(qPrintable(qtDate.toString()));
5808 qtDate = qtDate.addSecs(2*60*60);
5809 }
5810 }
5811
dateConversionJSQt()5812 void tst_QScriptEngine::dateConversionJSQt()
5813 {
5814 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
5815 QScriptEngine eng;
5816 for (int i = 0; i < 8000; ++i) {
5817 QScriptValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
5818 QDateTime qtDate = jsDate.toDateTime();
5819 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
5820 QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString();
5821 jsUTCDateStr.chop(5); // get rid of milliseconds (".000Z")
5822 jsUTCDateStr.append("Z"); // append the timezone specifier again
5823 if (qtUTCDateStr != jsUTCDateStr)
5824 QFAIL(qPrintable(jsDate.toString()));
5825 secs += 2*60*60;
5826 }
5827 }
5828
dateConversionQtJS()5829 void tst_QScriptEngine::dateConversionQtJS()
5830 {
5831 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
5832 QScriptEngine eng;
5833 for (int i = 0; i < 8000; ++i) {
5834 QScriptValue jsDate = eng.newDate(qtDate);
5835 QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString();
5836 jsUTCDateStr.chop(5); // get rid of milliseconds (".000Z")
5837 jsUTCDateStr.append("Z"); // append the timezone specifier again
5838 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
5839 if (jsUTCDateStr != qtUTCDateStr)
5840 QFAIL(qPrintable(qtDate.toString()));
5841 qtDate = qtDate.addSecs(2*60*60);
5842 }
5843 }
5844
createAnotherEngine(QScriptContext *,QScriptEngine *)5845 static QScriptValue createAnotherEngine(QScriptContext *, QScriptEngine *)
5846 {
5847 QScriptEngine eng;
5848 eng.evaluate("function foo(x, y) { return x + y; }" );
5849 eng.evaluate("hello = 5; world = 6" );
5850 return eng.evaluate("foo(hello,world)").toInt32();
5851 }
5852
5853
reentrency()5854 void tst_QScriptEngine::reentrency()
5855 {
5856 QScriptEngine eng;
5857 eng.globalObject().setProperty("foo", eng.newFunction(createAnotherEngine));
5858 eng.evaluate("function bar() { return foo(); } hello = 9; function getHello() { return hello; }");
5859 QCOMPARE(eng.evaluate("foo() + getHello() + foo()").toInt32(), 5+6 + 9 + 5+6);
5860 QCOMPARE(eng.evaluate("foo").call().toInt32(), 5+6);
5861 QCOMPARE(eng.evaluate("hello").toInt32(), 9);
5862 QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9);
5863 }
5864
newFixedStaticScopeObject()5865 void tst_QScriptEngine::newFixedStaticScopeObject()
5866 {
5867 // "Static scope objects" is an optimization we do for QML.
5868 // It enables the creation of JS objects that can guarantee to the
5869 // compiler that no properties will be added or removed. This enables
5870 // the compiler to generate a very simple (fast) property access, as
5871 // opposed to a full virtual lookup. Due to the inherent use of scope
5872 // chains in QML, this can make a huge difference (10x improvement for
5873 // benchmark in QTBUG-8576).
5874 // Ideally we would not need a special object type for this, and the
5875 // VM would dynamically optimize it to be fast...
5876 // See also QScriptEngine benchmark.
5877
5878 QScriptEngine eng;
5879 static const int propertyCount = 4;
5880 QString names[] = { "foo", "bar", "baz", "Math" };
5881 QScriptValue values[] = { 123, "ciao", true, false };
5882 QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable,
5883 QScriptValue::ReadOnly | QScriptValue::Undeletable,
5884 QScriptValue::SkipInEnumeration | QScriptValue::Undeletable,
5885 QScriptValue::Undeletable };
5886 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags);
5887
5888 // Query property.
5889 for (int i = 0; i < propertyCount; ++i) {
5890 for (int x = 0; x < 2; ++x) {
5891 if (x) {
5892 // Properties can't be deleted.
5893 scope.setProperty(names[i], QScriptValue());
5894 }
5895 QVERIFY(scope.property(names[i]).equals(values[i]));
5896 QCOMPARE(scope.propertyFlags(names[i]), flags[i]);
5897 }
5898 }
5899
5900 // Property that doesn't exist.
5901 QVERIFY(!scope.property("noSuchProperty").isValid());
5902 QCOMPARE(scope.propertyFlags("noSuchProperty"), QScriptValue::PropertyFlags());
5903
5904 // Write to writable property.
5905 {
5906 QScriptValue oldValue = scope.property("foo");
5907 QVERIFY(oldValue.isNumber());
5908 QScriptValue newValue = oldValue.toNumber() * 2;
5909 scope.setProperty("foo", newValue);
5910 QVERIFY(scope.property("foo").equals(newValue));
5911 scope.setProperty("foo", oldValue);
5912 QVERIFY(scope.property("foo").equals(oldValue));
5913 }
5914
5915 // Write to read-only property.
5916 scope.setProperty("bar", 456);
5917 QVERIFY(scope.property("bar").equals("ciao"));
5918
5919 // Iterate.
5920 {
5921 QScriptValueIterator it(scope);
5922 QSet<QString> iteratedNames;
5923 while (it.hasNext()) {
5924 it.next();
5925 iteratedNames.insert(it.name());
5926 }
5927 for (int i = 0; i < propertyCount; ++i)
5928 QVERIFY(iteratedNames.contains(names[i]));
5929 }
5930
5931 // Push it on the scope chain of a new context.
5932 QScriptContext *ctx = eng.pushContext();
5933 ctx->pushScope(scope);
5934 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
5935 QVERIFY(ctx->activationObject().equals(scope));
5936
5937 // Read property from JS.
5938 for (int i = 0; i < propertyCount; ++i) {
5939 for (int x = 0; x < 2; ++x) {
5940 if (x) {
5941 // Property can't be deleted from JS.
5942 QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i]));
5943 QVERIFY(ret.equals(false));
5944 }
5945 QVERIFY(eng.evaluate(names[i]).equals(values[i]));
5946 }
5947 }
5948
5949 // Property that doesn't exist.
5950 QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: Can't find variable: noSuchProperty"));
5951
5952 // Write property from JS.
5953 {
5954 QScriptValue oldValue = eng.evaluate("foo");
5955 QVERIFY(oldValue.isNumber());
5956 QScriptValue newValue = oldValue.toNumber() * 2;
5957 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
5958 scope.setProperty("foo", oldValue);
5959 QVERIFY(eng.evaluate("foo").equals(oldValue));
5960 }
5961
5962 // Write to read-only property.
5963 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
5964
5965 // Create a closure and return properties from there.
5966 {
5967 QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()");
5968 QVERIFY(props.isArray());
5969 // "foo" and "bar" come from scope object.
5970 QVERIFY(props.property(0).equals(scope.property("foo")));
5971 QVERIFY(props.property(1).equals(scope.property("bar")));
5972 // "baz" shadows property in scope object.
5973 QVERIFY(props.property(2).equals("shadow"));
5974 // "Math" comes from scope object, and shadows Global Object's "Math".
5975 QVERIFY(props.property(3).equals(scope.property("Math")));
5976 QVERIFY(!props.property(3).equals(eng.globalObject().property("Math")));
5977 // "Array" comes from Global Object.
5978 QVERIFY(props.property(4).equals(eng.globalObject().property("Array")));
5979 }
5980
5981 // As with normal JS, assigning to an undefined variable will create
5982 // the property on the Global Object, not the inner scope.
5983 QVERIFY(!eng.globalObject().property("newProperty").isValid());
5984 QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined());
5985 QVERIFY(!scope.property("newProperty").isValid());
5986 QVERIFY(eng.globalObject().property("newProperty").isNumber());
5987
5988 // Nested static scope.
5989 {
5990 static const int propertyCount2 = 2;
5991 QString names2[] = { "foo", "hum" };
5992 QScriptValue values2[] = { 321, "hello" };
5993 QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable,
5994 QScriptValue::ReadOnly | QScriptValue::Undeletable };
5995 QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2);
5996 ctx->pushScope(scope2);
5997
5998 // "foo" shadows scope.foo.
5999 QVERIFY(eng.evaluate("foo").equals(scope2.property("foo")));
6000 QVERIFY(!eng.evaluate("foo").equals(scope.property("foo")));
6001 // "hum" comes from scope2.
6002 QVERIFY(eng.evaluate("hum").equals(scope2.property("hum")));
6003 // "Array" comes from Global Object.
6004 QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array")));
6005
6006 ctx->popScope();
6007 }
6008
6009 QScriptValue fun = eng.evaluate("(function() { return foo; })");
6010 QVERIFY(fun.isFunction());
6011 eng.popContext();
6012 // Function's scope chain persists after popContext().
6013 QVERIFY(fun.call().equals(scope.property("foo")));
6014 }
6015
newGrowingStaticScopeObject()6016 void tst_QScriptEngine::newGrowingStaticScopeObject()
6017 {
6018 // The main use case for a growing static scope object is to set it as
6019 // the activation object of a QScriptContext, so that all JS variable
6020 // declarations end up in that object. It needs to be "growable" since
6021 // we don't know in advance how many variables a script will declare.
6022
6023 QScriptEngine eng;
6024 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng);
6025
6026 // Initially empty.
6027 QVERIFY(!QScriptValueIterator(scope).hasNext());
6028 QVERIFY(!scope.property("foo").isValid());
6029
6030 // Add a static property.
6031 scope.setProperty("foo", 123);
6032 QVERIFY(scope.property("foo").equals(123));
6033 QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
6034
6035 // Modify existing property.
6036 scope.setProperty("foo", 456);
6037 QVERIFY(scope.property("foo").equals(456));
6038
6039 // Add a read-only property.
6040 scope.setProperty("bar", "ciao", QScriptValue::ReadOnly);
6041 QVERIFY(scope.property("bar").equals("ciao"));
6042 QCOMPARE(scope.propertyFlags("bar"), QScriptValue::ReadOnly | QScriptValue::Undeletable);
6043
6044 // Attempt to modify read-only property.
6045 scope.setProperty("bar", "hello");
6046 QVERIFY(scope.property("bar").equals("ciao"));
6047
6048 // Properties can't be deleted.
6049 scope.setProperty("foo", QScriptValue());
6050 QVERIFY(scope.property("foo").equals(456));
6051 scope.setProperty("bar", QScriptValue());
6052 QVERIFY(scope.property("bar").equals("ciao"));
6053
6054 // Iterate.
6055 {
6056 QScriptValueIterator it(scope);
6057 QSet<QString> iteratedNames;
6058 while (it.hasNext()) {
6059 it.next();
6060 iteratedNames.insert(it.name());
6061 }
6062 QCOMPARE(iteratedNames.size(), 2);
6063 QVERIFY(iteratedNames.contains("foo"));
6064 QVERIFY(iteratedNames.contains("bar"));
6065 }
6066
6067 // Push it on the scope chain of a new context.
6068 QScriptContext *ctx = eng.pushContext();
6069 ctx->pushScope(scope);
6070 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6071 QVERIFY(ctx->activationObject().equals(scope));
6072
6073 // Read property from JS.
6074 QVERIFY(eng.evaluate("foo").equals(scope.property("foo")));
6075 QVERIFY(eng.evaluate("bar").equals(scope.property("bar")));
6076
6077 // Write property from JS.
6078 {
6079 QScriptValue oldValue = eng.evaluate("foo");
6080 QVERIFY(oldValue.isNumber());
6081 QScriptValue newValue = oldValue.toNumber() * 2;
6082 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6083 scope.setProperty("foo", oldValue);
6084 QVERIFY(eng.evaluate("foo").equals(oldValue));
6085 }
6086
6087 // Write to read-only property.
6088 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6089
6090 // Shadow property.
6091 QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math")));
6092 scope.setProperty("Math", "fake Math");
6093 QVERIFY(eng.evaluate("Math").equals(scope.property("Math")));
6094
6095 // Variable declarations will create properties on the scope.
6096 eng.evaluate("var baz = 456");
6097 QVERIFY(scope.property("baz").equals(456));
6098
6099 // Function declarations will create properties on the scope.
6100 eng.evaluate("function fun() { return baz; }");
6101 QVERIFY(scope.property("fun").isFunction());
6102 QVERIFY(scope.property("fun").call().equals(scope.property("baz")));
6103
6104 // Demonstrate the limitation of a growable static scope: Once a function that
6105 // uses the scope has been compiled, it won't pick up properties that are added
6106 // to the scope later.
6107 {
6108 QScriptValue fun = eng.evaluate("(function() { return futureProperty; })");
6109 QVERIFY(fun.isFunction());
6110 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6111 scope.setProperty("futureProperty", "added after the function was compiled");
6112 // If scope were dynamic, this would return the new property.
6113 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6114 }
6115
6116 eng.popContext();
6117 }
6118
6119 QT_BEGIN_NAMESPACE
Q_SCRIPT_DECLARE_QMETAOBJECT(QStandardItemModel,QObject *)6120 Q_SCRIPT_DECLARE_QMETAOBJECT(QStandardItemModel, QObject*)
6121 QT_END_NAMESPACE
6122
6123 void tst_QScriptEngine::scriptValueFromQMetaObject()
6124 {
6125 QScriptEngine eng;
6126 {
6127 QScriptValue meta = eng.scriptValueFromQMetaObject<QScriptEngine>();
6128 QVERIFY(meta.isQMetaObject());
6129 QCOMPARE(meta.toQMetaObject(), &QScriptEngine::staticMetaObject);
6130 // Because of missing Q_SCRIPT_DECLARE_QMETAOBJECT() for QScriptEngine.
6131 QVERIFY(!meta.construct().isValid());
6132 }
6133 {
6134 QScriptValue meta = eng.scriptValueFromQMetaObject<QStandardItemModel>();
6135 QVERIFY(meta.isQMetaObject());
6136 QCOMPARE(meta.toQMetaObject(), &QStandardItemModel::staticMetaObject);
6137 QScriptValue obj = meta.construct(QScriptValueList() << eng.newQObject(&eng));
6138 QVERIFY(obj.isQObject());
6139 QStandardItemModel *model = qobject_cast<QStandardItemModel*>(obj.toQObject());
6140 QVERIFY(model != 0);
6141 QCOMPARE(model->parent(), (QObject*)&eng);
6142 }
6143 }
6144
6145 // QTBUG-21896
stringListFromArrayWithEmptyElement()6146 void tst_QScriptEngine::stringListFromArrayWithEmptyElement()
6147 {
6148 QScriptEngine eng;
6149 QCOMPARE(qscriptvalue_cast<QStringList>(eng.evaluate("[,'hello']")),
6150 QStringList() << "" << "hello");
6151 }
6152
6153 QTEST_MAIN(tst_QScriptEngine)
6154 #include "tst_qscriptengine.moc"
6155