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

source code of qtscript/tests/auto/qscriptengine/tst_qscriptengine.cpp