1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qjsengine.h>
46 #include <qjsvalueiterator.h>
47 #include <qgraphicsitem.h>
48 #include <qstandarditemmodel.h>
49 #include <QtCore/qnumeric.h>
52 Q_DECLARE_METATYPE(QList<int>)
53 Q_DECLARE_METATYPE(QObjectList)
55 // The JavaScriptCore GC marks the C stack. To try to ensure that there is
56 // no JSObject* left in stack memory by the compiler, we call this function
57 // to zap some bytes of memory before calling collectGarbage().
58 static void zapSomeStack()
61 memset(buf, 0, sizeof(buf));
64 static void collectGarbage_helper(QJSEngine &eng)
70 class tst_QJSEngine : public QObject
76 virtual ~tst_QJSEngine();
79 void constructWithParent();
80 #if 0 // FIXME: no QScriptContext
81 void currentContext();
82 void pushPopContext();
84 #if 0 // FIXME: No prototype API in QScriptEngine
85 void getSetDefaultPrototype_int();
86 void getSetDefaultPrototype_customType();
88 #if 0 // FIXME: no QScriptContext
90 void newFunctionWithArg();
91 void newFunctionWithProto();
95 void newArray_HooliganTask218092();
96 void newArray_HooliganTask233836();
98 #if 0 // FIXME: No prototype API in QScriptEngine
99 void newVariant_defaultPrototype();
101 #if 0 // ###FIXME: No QVariant object promotion API
102 void newVariant_promoteObject();
103 void newVariant_replaceValue();
105 void newVariant_valueOfToString();
106 #if 0 // ###FIXME: No QVariant object promotion API
107 void newVariant_promoteNonObject();
108 void newVariant_promoteNonQScriptObject();
115 void newQObject_ownership();
116 void newQObject_promoteObject();
117 void newQObject_sameQObject();
118 #if 0 // FIXME: No prototype API in QScriptEngine
119 void newQObject_defaultPrototype();
121 void newQObject_promoteNonObject();
122 void newQObject_promoteNonQScriptObject();
123 #if 0 // ### FIXME: No QScript Metaobject support right now
124 void newQMetaObject();
125 void newActivationObject();
127 #if 0 // ###FIXME: No setGlobalObject support - yay
128 void getSetGlobalObjectSimple();
129 void getSetGlobalObject();
131 void globalObjectProperties();
132 void globalObjectEquals();
133 void globalObjectProperties_enumerate();
134 void createGlobalObjectProperty();
135 void globalObjectGetterSetterProperty();
136 #if 0 // ###FIXME: No support for setting the global object
137 void customGlobalObjectWithPrototype();
139 void globalObjectWithCustomPrototype();
140 void builtinFunctionNames_data();
141 void builtinFunctionNames();
142 #if 0 // ###FIXME: No syntax checking result
143 void checkSyntax_data();
146 #if 0 // ###FIXME: No support for canEvaluate
147 void canEvaluate_data();
150 void evaluate_data();
152 #if 0 // ###FIXME: no support for c-style callbacks
153 void nestedEvaluate();
155 #if 0 // ### FIXME: No c-style callbacks
156 void uncaughtException();
158 void errorMessage_QT679();
159 void valueConversion_basic();
160 #if 0 // FIXME: No API for custom types
161 void valueConversion_customType();
162 void valueConversion_sequence();
164 void valueConversion_QVariant();
165 #if 0 // FIXME: No support for custom types
166 void valueConversion_hooliganTask248802();
168 void valueConversion_basic2();
169 void valueConversion_dateTime();
170 void valueConversion_regExp();
171 #if 0 // FIXME: No qScriptValueFromValue
172 void qScriptValueFromValue_noEngine();
174 #if 0 // ###FIXME: No QScriptContext
175 void importExtension();
176 void infiniteRecursion();
178 #if 0 // FIXME: No support for default prototypes
179 void castWithPrototypeChain();
181 void castWithMultipleInheritance();
182 void collectGarbage();
183 #if 0 // ###FIXME: no reportAdditionalMemoryCost API
184 void reportAdditionalMemoryCost();
186 void gcWithNestedDataStructure();
187 #if 0 // ###FIXME: No processEvents handling
188 void processEventsWhileRunning();
189 void processEventsWhileRunning_function();
190 void throwErrorFromProcessEvents_data();
191 void throwErrorFromProcessEvents();
192 void disableProcessEventsInterval();
195 void numberParsing_data();
196 void numberParsing();
197 void automaticSemicolonInsertion();
198 #if 0 // ###FIXME: no abortEvaluation API
199 void abortEvaluation_notEvaluating();
200 void abortEvaluation_data();
201 void abortEvaluation();
202 void abortEvaluation_tryCatch();
203 void abortEvaluation_fromNative();
204 void abortEvaluation_QTBUG9433();
206 #if 0 // ###FIXME: no QScriptEngine::isEvaluating
207 void isEvaluating_notEvaluating();
208 void isEvaluating_fromNative();
209 void isEvaluating_fromEvent();
211 #if 0 // ###FIXME: depracated
212 void printFunctionWithCustomHandler();
213 void printThrowsException();
215 void errorConstructors();
216 void argumentsProperty_globalContext();
217 void argumentsProperty_JS();
218 #if 0 // ###FIXME: no QScriptContext API
219 void argumentsProperty_evaluateInNativeFunction();
221 void jsNumberClass();
222 void jsForInStatement_simple();
223 void jsForInStatement_prototypeProperties();
224 void jsForInStatement_mutateWhileIterating();
225 void jsForInStatement_arrays();
226 void jsForInStatement_nullAndUndefined();
227 void jsFunctionDeclarationAsStatement();
228 void stringObjects();
229 void jsStringPrototypeReplaceBugs();
230 void getterSetterThisObject_global();
231 void getterSetterThisObject_plain();
232 void getterSetterThisObject_prototypeChain();
233 #if 0 // ###FIXME: no QScriptContext API
234 void getterSetterThisObject_activation();
236 void jsContinueInSwitch();
237 void jsShadowReadOnlyPrototypeProperty();
239 void jsReservedWords_data();
240 void jsReservedWords();
241 void jsFutureReservedWords_data();
242 void jsFutureReservedWords();
243 void jsThrowInsideWithStatement();
244 #if 0 // ###FIXME: No QScriptEngineAgent API
245 void getSetAgent_ownership();
246 void getSetAgent_deleteAgent();
247 void getSetAgent_differentEngine();
249 #if 0 // ###FIXME: No QScriptString API
250 void reentrancy_stringHandles();
252 #if 0 // ###FIXME: No processEventsInterval API
253 void reentrancy_processEventsInterval();
255 #if 0 // FIXME: No support for custom types
256 void reentrancy_typeConversion();
258 void reentrancy_globalObjectProperties();
259 void reentrancy_Array();
260 void reentrancy_objectCreation();
261 void jsIncDecNonObjectProperty();
262 #if 0 // ###FIXME: no installTranslatorFunctions API
263 void installTranslatorFunctions();
264 void translateScript_data();
265 void translateScript();
266 void translateScript_crossScript();
267 void translateScript_callQsTrFromNative();
268 void translateScript_trNoOp();
269 void translateScript_callQsTrFromCpp();
270 void translateWithInvalidArgs_data();
271 void translateWithInvalidArgs();
272 void translationContext_data();
273 void translationContext();
274 void translateScriptIdBased();
275 void translateScriptUnicode_data();
276 void translateScriptUnicode();
277 void translateScriptUnicodeIdBased_data();
278 void translateScriptUnicodeIdBased();
279 void translateFromBuiltinCallback();
281 #if 0 // ###FIXME: No QScriptValue::scope API
282 void functionScopes();
284 #if 0 // ###FIXME: No QScriptContext API
285 void nativeFunctionScopes();
287 #if 0 // ###FIXME: No QScriptProgram API
288 void evaluateProgram();
289 void evaluateProgram_customScope();
290 void evaluateProgram_closure();
291 void evaluateProgram_executeLater();
292 void evaluateProgram_multipleEngines();
293 void evaluateProgram_empty();
295 #if 0 // ###FIXME: No QScriptContext API
296 void collectGarbageAfterConnect();
297 void collectGarbageAfterNativeArguments();
298 void promoteThisObjectToQObjectInConstructor();
300 #if 0 // ###FIXME: No QScript MetaObject API
301 void scriptValueFromQMetaObject();
304 void qRegExpInport_data();
305 void qRegExpInport();
306 #if 0 // ###FIXME: No QScriptContext API
309 #if 0 // ###FIXME: No QSCriptDeclarativeClass API
310 void newFixedStaticScopeObject();
311 void newGrowingStaticScopeObject();
313 void dateRoundtripJSQtJS();
314 void dateRoundtripQtJSQt();
315 void dateConversionJSQt();
316 void dateConversionQtJS();
317 void functionPrototypeExtensions();
318 void threadedEngine();
321 tst_QJSEngine::tst_QJSEngine()
325 tst_QJSEngine::~tst_QJSEngine()
329 void tst_QJSEngine::constructWithParent()
331 QPointer<QJSEngine> ptr;
334 QJSEngine *engine = new QJSEngine(&obj);
340 #if 0 // FIXME: no QScriptContext
341 void tst_QJSEngine::currentContext()
344 QScriptContext *globalCtx = eng.currentContext();
345 QVERIFY(globalCtx != 0);
346 QVERIFY(globalCtx->parentContext() == 0);
347 QCOMPARE(globalCtx->engine(), &eng);
348 QCOMPARE(globalCtx->argumentCount(), 0);
349 QCOMPARE(globalCtx->backtrace().size(), 1);
350 QVERIFY(!globalCtx->isCalledAsConstructor());
351 QVERIFY(!globalCtx->callee().isValid());
352 QCOMPARE(globalCtx->state(), QScriptContext::NormalState);
353 QVERIFY(globalCtx->thisObject().strictlyEquals(eng.globalObject()));
354 QVERIFY(globalCtx->activationObject().strictlyEquals(eng.globalObject()));
355 QVERIFY(globalCtx->argumentsObject().isObject());
358 void tst_QJSEngine::pushPopContext()
361 QScriptContext *globalCtx = eng.currentContext();
362 QScriptContext *ctx = eng.pushContext();
364 QCOMPARE(ctx->parentContext(), globalCtx);
365 QVERIFY(!ctx->isCalledAsConstructor());
366 QVERIFY(!ctx->callee().isValid());
367 QVERIFY(ctx->thisObject().strictlyEquals(eng.globalObject()));
368 QCOMPARE(ctx->argumentCount(), 0);
369 QCOMPARE(ctx->backtrace().size(), 2);
370 QCOMPARE(ctx->engine(), &eng);
371 QCOMPARE(ctx->state(), QScriptContext::NormalState);
372 QVERIFY(ctx->activationObject().isObject());
373 QVERIFY(ctx->argumentsObject().isObject());
375 QScriptContext *ctx2 = eng.pushContext();
377 QCOMPARE(ctx2->parentContext(), ctx);
378 QVERIFY(!ctx2->activationObject().strictlyEquals(ctx->activationObject()));
379 QVERIFY(!ctx2->argumentsObject().strictlyEquals(ctx->argumentsObject()));
383 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
384 eng.popContext(); // ignored
385 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
386 eng.popContext(); // ignored
389 static QScriptValue myFunction(QScriptContext *, QScriptEngine *eng)
391 return eng->nullValue();
394 static QScriptValue myFunctionWithVoidArg(QScriptContext *, QScriptEngine *eng, void *)
396 return eng->nullValue();
399 static QScriptValue myThrowingFunction(QScriptContext *ctx, QScriptEngine *)
401 return ctx->throwError("foo");
404 static QScriptValue myFunctionThatReturns(QScriptContext *, QScriptEngine *eng)
406 return QScriptValue(eng, 42);
409 static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext *, QScriptEngine *)
411 return QScriptValue(1024);
414 static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext *, QScriptEngine *, void *arg)
416 QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg);
417 return QScriptValue(wrongEngine, 42);
420 static QScriptValue sumFunction(QScriptContext *context, QScriptEngine *engine)
424 for (int i = 0; i < context->argumentCount(); i++) {
425 QScriptValue n = context->argument(i);
427 sum += n.toInteger();
430 return QScriptValue(engine, sum);
433 void tst_QJSEngine::newFunction()
437 QScriptValue fun = eng.newFunction(myFunction);
438 QCOMPARE(fun.isValid(), true);
439 QCOMPARE(fun.isFunction(), true);
440 QCOMPARE(fun.isObject(), true);
441 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
442 // a prototype property is automatically constructed
444 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
445 QVERIFY(prot.isObject());
446 QVERIFY(prot.property("constructor").strictlyEquals(fun));
447 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
448 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
450 // prototype should be Function.prototype
451 QCOMPARE(fun.prototype().isValid(), true);
452 QCOMPARE(fun.prototype().isFunction(), true);
453 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
455 QCOMPARE(fun.call().isNull(), true);
456 QCOMPARE(fun.construct().isObject(), true);
460 void tst_QJSEngine::newFunctionWithArg()
464 QScriptValue fun = eng.newFunction(myFunctionWithVoidArg, (void*)this);
465 QVERIFY(fun.isFunction());
466 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
467 // a prototype property is automatically constructed
469 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
470 QVERIFY(prot.isObject());
471 QVERIFY(prot.property("constructor").strictlyEquals(fun));
472 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
473 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
475 // prototype should be Function.prototype
476 QCOMPARE(fun.prototype().isValid(), true);
477 QCOMPARE(fun.prototype().isFunction(), true);
478 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
480 QCOMPARE(fun.call().isNull(), true);
481 QCOMPARE(fun.construct().isObject(), true);
485 void tst_QJSEngine::newFunctionWithProto()
489 QScriptValue proto = eng.newObject();
490 QScriptValue fun = eng.newFunction(myFunction, proto);
491 QCOMPARE(fun.isValid(), true);
492 QCOMPARE(fun.isFunction(), true);
493 QCOMPARE(fun.isObject(), true);
494 // internal prototype should be Function.prototype
495 QCOMPARE(fun.prototype().isValid(), true);
496 QCOMPARE(fun.prototype().isFunction(), true);
497 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
498 // public prototype should be the one we passed
499 QCOMPARE(fun.property("prototype").strictlyEquals(proto), true);
500 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
501 QCOMPARE(proto.property("constructor").strictlyEquals(fun), true);
502 QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
504 QCOMPARE(fun.call().isNull(), true);
505 QCOMPARE(fun.construct().isObject(), true);
507 // whether the return value is correct
509 QScriptValue fun = eng.newFunction(myFunctionThatReturns);
510 QCOMPARE(fun.isValid(), true);
511 QCOMPARE(fun.isFunction(), true);
512 QCOMPARE(fun.isObject(), true);
514 QScriptValue result = fun.call();
515 QCOMPARE(result.isNumber(), true);
516 QCOMPARE(result.toInt(), 42);
518 // whether the return value is assigned to the correct engine
520 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine);
521 QCOMPARE(fun.isValid(), true);
522 QCOMPARE(fun.isFunction(), true);
523 QCOMPARE(fun.isObject(), true);
525 QScriptValue result = fun.call();
526 QCOMPARE(result.engine(), &eng);
527 QCOMPARE(result.isNumber(), true);
528 QCOMPARE(result.toInt(), 1024);
530 // whether the return value is undefined when returning a value with wrong engine
532 QScriptEngine wrongEngine;
534 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void *>(&wrongEngine));
535 QCOMPARE(fun.isValid(), true);
536 QCOMPARE(fun.isFunction(), true);
537 QCOMPARE(fun.isObject(), true);
539 QTest::ignoreMessage(QtWarningMsg, "QScriptValue::call(): Value from different engine returned from native function, returning undefined value instead.");
540 QScriptValue result = fun.call();
541 QCOMPARE(result.isValid(), true);
542 QCOMPARE(result.isUndefined(), true);
544 // checking if arguments are passed correctly
546 QScriptEngine wrongEngine;
548 QScriptValue fun = eng.newFunction(sumFunction);
549 QCOMPARE(fun.isValid(), true);
550 QCOMPARE(fun.isFunction(), true);
551 QCOMPARE(fun.isObject(), true);
553 QScriptValue result = fun.call();
554 QCOMPARE(result.isNumber(), true);
555 QCOMPARE(result.toInt(), 0);
557 result = fun.call(QScriptValue(), QScriptValueList() << 1);
558 QCOMPARE(result.isNumber(), true);
559 QCOMPARE(result.toInt(), 1);
561 result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3);
562 QCOMPARE(result.isNumber(), true);
563 QCOMPARE(result.toInt(), 6);
565 result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3 << 4);
566 QCOMPARE(result.isNumber(), true);
567 QCOMPARE(result.toInt(), 10);
572 void tst_QJSEngine::newObject()
575 QJSValue object = eng.newObject();
576 QCOMPARE(object.isValid(), true);
577 QCOMPARE(object.isObject(), true);
578 QCOMPARE(object.isFunction(), false);
579 // ###FIXME: No QScriptClass QCOMPARE(object.scriptClass(), (QScriptClass*)0);
580 // prototype should be Object.prototype
581 QCOMPARE(object.prototype().isValid(), true);
582 QCOMPARE(object.prototype().isObject(), true);
583 QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
586 void tst_QJSEngine::newArray()
589 QJSValue array = eng.newArray();
590 QCOMPARE(array.isValid(), true);
591 QCOMPARE(array.isArray(), true);
592 QCOMPARE(array.isObject(), true);
593 QVERIFY(!array.isFunction());
594 // ###FIXME: No QScriptClass QCOMPARE(array.scriptClass(), (QScriptClass*)0);
595 // prototype should be Array.prototype
596 QCOMPARE(array.prototype().isValid(), true);
597 QCOMPARE(array.prototype().isArray(), true);
598 QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
601 void tst_QJSEngine::newArray_HooliganTask218092()
605 QJSValue ret = eng.evaluate("[].splice(0, 0, 'a')");
606 QVERIFY(ret.isArray());
607 QCOMPARE(ret.property("length").toInt(), 0);
610 QJSValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
611 QVERIFY(ret.isArray());
612 QCOMPARE(ret.property("length").toInt(), 1);
615 QJSValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
616 QVERIFY(ret.isArray());
617 QCOMPARE(ret.property("length").toInt(), 1);
620 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
621 QVERIFY(ret.isArray());
622 QCOMPARE(ret.property("length").toInt(), 2);
625 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
626 QVERIFY(ret.isArray());
627 QCOMPARE(ret.property("length").toInt(), 2);
631 void tst_QJSEngine::newArray_HooliganTask233836()
635 // According to ECMA-262, this should cause a RangeError.
636 QJSValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')");
637 QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError")));
640 QJSValue ret = eng.newArray(0xFFFFFFFF);
641 QEXPECT_FAIL("", "The maximum length of arrays is defined by v8 currently and differs from QtScript", Abort);
642 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
643 ret.setProperty(0xFFFFFFFF, 123);
644 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
645 QVERIFY(ret.property(0xFFFFFFFF).isNumber());
646 QCOMPARE(ret.property(0xFFFFFFFF).toInt(), 123);
647 ret.setProperty(123, 456);
648 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
649 QVERIFY(ret.property(123).isNumber());
650 QCOMPARE(ret.property(123).toInt(), 456);
654 void tst_QJSEngine::newVariant()
658 QJSValue opaque = eng.newVariant(QVariant());
659 QCOMPARE(opaque.isValid(), true);
660 QCOMPARE(opaque.isVariant(), true);
661 QVERIFY(!opaque.isFunction());
662 QCOMPARE(opaque.isObject(), true);
663 QCOMPARE(opaque.prototype().isValid(), true);
664 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
665 QCOMPARE(opaque.prototype().isVariant(), true);
666 QVERIFY(opaque.property("valueOf").call(opaque).isUndefined());
670 #if 0 // FIXME: No prototype API in QScriptEngine
671 void tst_QJSEngine::newVariant_defaultPrototype()
673 // default prototype should be set automatically
676 QScriptValue proto = eng.newObject();
677 eng.setDefaultPrototype(qMetaTypeId<QString>(), proto);
678 QScriptValue ret = eng.newVariant(QVariant(QString::fromLatin1("hello")));
679 QVERIFY(ret.isVariant());
680 // ###FIXME: No QScriptClass QCOMPARE(ret.scriptClass(), (QScriptClass*)0);
681 QVERIFY(ret.prototype().strictlyEquals(proto));
682 eng.setDefaultPrototype(qMetaTypeId<QString>(), QScriptValue());
683 QScriptValue ret2 = eng.newVariant(QVariant(QString::fromLatin1("hello")));
684 QVERIFY(ret2.isVariant());
685 QVERIFY(!ret2.prototype().strictlyEquals(proto));
690 #if 0 // ###FIXME: No QVariant object promotion API
691 void tst_QJSEngine::newVariant_promoteObject()
693 // "promote" plain object to variant
696 QScriptValue object = eng.newObject();
697 object.setProperty("foo", eng.newObject());
698 object.setProperty("bar", object.property("foo"));
699 QVERIFY(object.property("foo").isObject());
700 QVERIFY(!object.property("foo").isVariant());
701 QScriptValue originalProto = object.property("foo").prototype();
702 QSKIP("It is not possible to promote plain object to a wrapper");
703 QScriptValue ret = eng.newVariant(object.property("foo"), QVariant(123));
704 QVERIFY(ret.isValid());
705 QVERIFY(ret.strictlyEquals(object.property("foo")));
706 QVERIFY(ret.isVariant());
707 QVERIFY(object.property("foo").isVariant());
708 QVERIFY(object.property("bar").isVariant());
709 QCOMPARE(ret.toVariant(), QVariant(123));
710 QVERIFY(ret.prototype().strictlyEquals(originalProto));
714 void tst_QJSEngine::newVariant_replaceValue()
716 // replace value of existing object
719 QScriptValue object = eng.newVariant(QVariant(123));
720 for (int x = 0; x < 2; ++x) {
721 QScriptValue ret = eng.newVariant(object, QVariant(456));
722 QVERIFY(ret.isValid());
723 QVERIFY(ret.strictlyEquals(object));
724 QVERIFY(ret.isVariant());
725 QCOMPARE(ret.toVariant(), QVariant(456));
731 void tst_QJSEngine::newVariant_valueOfToString()
733 // valueOf() and toString()
736 QJSValue object = eng.newVariant(QVariant(123));
737 QJSValue value = object.property("valueOf").call(object);
738 QVERIFY(value.isNumber());
739 QCOMPARE(value.toInt(), 123);
740 QCOMPARE(object.toString(), QString::fromLatin1("123"));
741 QCOMPARE(object.toVariant().toString(), object.toString());
744 QJSValue object = eng.newVariant(QVariant(QString::fromLatin1("hello")));
745 QJSValue value = object.property("valueOf").call(object);
746 QVERIFY(value.isString());
747 QCOMPARE(value.toString(), QString::fromLatin1("hello"));
748 QCOMPARE(object.toString(), QString::fromLatin1("hello"));
749 QCOMPARE(object.toVariant().toString(), object.toString());
752 QJSValue object = eng.newVariant(QVariant(false));
753 QJSValue value = object.property("valueOf").call(object);
754 QVERIFY(value.isBool());
755 QCOMPARE(value.toBool(), false);
756 QCOMPARE(object.toString(), QString::fromLatin1("false"));
757 QCOMPARE(object.toVariant().toString(), object.toString());
760 QJSValue object = eng.newVariant(QVariant(QPoint(10, 20)));
761 QJSValue value = object.property("valueOf").call(object);
762 QVERIFY(value.isObject());
763 QVERIFY(value.strictlyEquals(object));
764 QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)"));
768 #if 0 // ###FIXME: No QVariant object promotion API
769 void tst_QJSEngine::newVariant_promoteNonObject()
774 QScriptValue ret = eng.newVariant(123, var);
775 QVERIFY(ret.isVariant());
776 QCOMPARE(ret.toVariant(), var);
780 void tst_QJSEngine::newVariant_promoteNonQScriptObject()
782 QSKIP("This test relay on limitation of QtScript JSC implementation");
785 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newVariant(): changing class of non-QScriptObject not supported");
786 QScriptValue ret = eng.newVariant(eng.newArray(), 123);
787 QVERIFY(!ret.isValid());
792 void tst_QJSEngine::newRegExp()
794 QSKIP("Test failing - QTBUG-22238");
796 for (int x = 0; x < 2; ++x) {
799 rexp = eng.newRegExp("foo", "bar");
801 rexp = eng.newRegExp(QRegExp("foo"));
802 QCOMPARE(rexp.isValid(), true);
803 QCOMPARE(rexp.isRegExp(), true);
804 QCOMPARE(rexp.isObject(), true);
805 QVERIFY(rexp.isFunction()); // in JSC, RegExp objects are callable
806 // prototype should be RegExp.prototype
807 QCOMPARE(rexp.prototype().isValid(), true);
808 QCOMPARE(rexp.prototype().isObject(), true);
809 QCOMPARE(rexp.prototype().isRegExp(), false);
810 QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
812 QCOMPARE(rexp.toRegExp().pattern(), QRegExp("foo").pattern());
816 void tst_QJSEngine::jsRegExp()
818 QSKIP("Test failing - QTBUG-22238");
820 // See ECMA-262 Section 15.10, "RegExp Objects".
821 // These should really be JS-only tests, as they test the implementation's
822 // ECMA-compliance, not the C++ API. Compliance should already be covered
823 // by the Mozilla tests (qscriptjstestsuite).
824 // We can consider updating the expected results of this test if the
825 // RegExp implementation changes.
828 QJSValue r = eng.evaluate("/foo/gim");
829 QVERIFY(r.isRegExp());
830 QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim"));
832 QJSValue rxCtor = eng.globalObject().property("RegExp");
833 QJSValue r2 = rxCtor.call(QJSValue(), QJSValueList() << r);
834 QVERIFY(r2.isRegExp());
835 QVERIFY(r2.strictlyEquals(r));
837 QJSValue r3 = rxCtor.call(QJSValue(), QJSValueList() << r << "gim");
838 QVERIFY(r3.isError());
839 QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another
841 QJSValue r4 = rxCtor.call(QJSValue(), QJSValueList() << "foo" << "gim");
842 QVERIFY(r4.isRegExp());
844 QJSValue r5 = rxCtor.construct(QJSValueList() << r);
845 QVERIFY(r5.isRegExp());
846 QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
847 // In JSC, constructing a RegExp from another produces the same identical object.
848 // This is different from SpiderMonkey and old back-end.
849 QVERIFY(!r5.strictlyEquals(r));
851 QEXPECT_FAIL("", "V8 and jsc ignores invalid flags", Continue); //https://bugs.webkit.org/show_bug.cgi?id=41614
852 QJSValue r6 = rxCtor.construct(QJSValueList() << "foo" << "bar");
853 QVERIFY(r6.isError());
854 // QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
857 QJSValue r7 = eng.evaluate("/foo/gimp");
858 /* v8 and jsc ignores invalid flags
859 QVERIFY(r7.isError());
860 QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
863 // JSC doesn't complain about duplicate flags.
864 QJSValue r8 = eng.evaluate("/foo/migmigmig");
865 QVERIFY(r8.isRegExp());
866 QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim"));
868 QJSValue r9 = rxCtor.construct();
869 QVERIFY(r9.isRegExp());
870 QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));
872 QJSValue r10 = rxCtor.construct(QJSValueList() << "" << "gim");
873 QVERIFY(r10.isRegExp());
874 QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));
876 QJSValue r11 = rxCtor.construct(QJSValueList() << "{1.*}" << "g");
877 QVERIFY(r11.isRegExp());
878 QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
881 void tst_QJSEngine::newDate()
886 QJSValue date = eng.newDate(0);
887 QCOMPARE(date.isValid(), true);
888 QCOMPARE(date.isDate(), true);
889 QCOMPARE(date.isObject(), true);
890 QVERIFY(!date.isFunction());
891 // prototype should be Date.prototype
892 QCOMPARE(date.prototype().isValid(), true);
893 QCOMPARE(date.prototype().isDate(), true);
894 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
898 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
899 QJSValue date = eng.newDate(dt);
900 QCOMPARE(date.isValid(), true);
901 QCOMPARE(date.isDate(), true);
902 QCOMPARE(date.isObject(), true);
903 // prototype should be Date.prototype
904 QCOMPARE(date.prototype().isValid(), true);
905 QCOMPARE(date.prototype().isDate(), true);
906 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
908 QCOMPARE(date.toDateTime(), dt);
912 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
913 QJSValue date = eng.newDate(dt);
914 // toDateTime() result should be in local time
915 QCOMPARE(date.toDateTime(), dt.toLocalTime());
919 void tst_QJSEngine::jsParseDate()
922 // Date.parse() should return NaN when it fails
924 QJSValue ret = eng.evaluate("Date.parse()");
925 QVERIFY(ret.isNumber());
926 QVERIFY(qIsNaN(ret.toNumber()));
929 // Date.parse() should be able to parse the output of Date().toString()
931 QJSValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
932 QVERIFY(ret.isBool());
933 QCOMPARE(ret.toBool(), true);
937 void tst_QJSEngine::newQObject()
942 QJSValue qobject = eng.newQObject(0);
943 QCOMPARE(qobject.isValid(), true);
944 QCOMPARE(qobject.isNull(), true);
945 QCOMPARE(qobject.isObject(), false);
946 QCOMPARE(qobject.toQObject(), (QObject *)0);
949 QJSValue qobject = eng.newQObject(this);
950 QCOMPARE(qobject.isValid(), true);
951 QCOMPARE(qobject.isQObject(), true);
952 QCOMPARE(qobject.isObject(), true);
953 QCOMPARE(qobject.toQObject(), (QObject *)this);
954 QVERIFY(!qobject.isFunction());
955 // prototype should be QObject.prototype
956 QCOMPARE(qobject.prototype().isValid(), true);
957 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
958 QCOMPARE(qobject.prototype().isQObject(), true);
959 // ###FIXME: No QScriptClass QCOMPARE(qobject.scriptClass(), (QScriptClass*)0);
963 void tst_QJSEngine::newQObject_ownership()
967 QPointer<QObject> ptr = new QObject();
970 QJSValue v = eng.newQObject(ptr);
972 collectGarbage_helper(eng);
974 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
978 QPointer<QObject> ptr = new QObject(this);
981 QJSValue v = eng.newQObject(ptr);
983 QObject *before = ptr;
984 collectGarbage_helper(eng);
985 QVERIFY(ptr == before);
989 QObject *parent = new QObject();
990 QObject *child = new QObject(parent);
991 QJSValue v = eng.newQObject(child);
992 QCOMPARE(v.toQObject(), child);
994 QCOMPARE(v.toQObject(), (QObject *)0);
997 QPointer<QObject> ptr = new QObject();
1000 QJSValue v = eng.newQObject(ptr);
1002 collectGarbage_helper(eng);
1003 // no parent, so it should be like ScriptOwnership
1005 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
1009 QObject *parent = new QObject();
1010 QPointer<QObject> child = new QObject(parent);
1011 QVERIFY(child != 0);
1013 QJSValue v = eng.newQObject(child);
1015 collectGarbage_helper(eng);
1016 // has parent, so it should be like QtOwnership
1017 QVERIFY(child != 0);
1022 void tst_QJSEngine::newQObject_promoteObject()
1024 #if 0 // ### FIXME: object promotion is not supported
1026 // "promote" plain object to QObject
1028 QScriptValue obj = eng.newObject();
1029 QScriptValue originalProto = obj.prototype();
1030 QScriptValue ret = eng.newQObject(obj, this);
1031 QVERIFY(ret.isValid());
1032 QVERIFY(ret.isQObject());
1033 QVERIFY(ret.strictlyEquals(obj));
1034 QVERIFY(obj.isQObject());
1035 QCOMPARE(ret.toQObject(), (QObject *)this);
1036 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1037 QScriptValue val = ret.property("objectName");
1038 QVERIFY(val.isString());
1040 // "promote" variant object to QObject
1042 QScriptValue obj = eng.newVariant(123);
1043 QVERIFY(obj.isVariant());
1044 QScriptValue originalProto = obj.prototype();
1045 QScriptValue ret = eng.newQObject(obj, this);
1046 QVERIFY(ret.isQObject());
1047 QVERIFY(ret.strictlyEquals(obj));
1048 QVERIFY(obj.isQObject());
1049 QCOMPARE(ret.toQObject(), (QObject *)this);
1050 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1052 // replace QObject* of existing object
1054 QScriptValue object = eng.newVariant(123);
1055 QScriptValue originalProto = object.prototype();
1056 QObject otherQObject;
1057 for (int x = 0; x < 2; ++x) {
1058 QScriptValue ret = eng.newQObject(object, &otherQObject);
1059 QVERIFY(ret.isValid());
1060 QVERIFY(ret.isQObject());
1061 QVERIFY(ret.strictlyEquals(object));
1062 QCOMPARE(ret.toQObject(), (QObject *)&otherQObject);
1063 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1069 void tst_QJSEngine::newQObject_sameQObject()
1071 #if 0 // ###FIXME: No QObjectWrapOptions API
1072 QSKIP("This test strongly relies on strictlyEquals feature that would change in near future");
1074 // calling newQObject() several times with same object
1075 for (int x = 0; x < 2; ++x) {
1077 // the default is to create a new wrapper object
1078 QScriptValue obj1 = eng.newQObject(&qobj);
1079 QScriptValue obj2 = eng.newQObject(&qobj);
1080 QVERIFY(!obj2.strictlyEquals(obj1));
1082 QScriptEngine::QObjectWrapOptions opt = 0;
1083 bool preferExisting = (x != 0);
1085 opt |= QScriptEngine::PreferExistingWrapperObject;
1087 QScriptValue obj3 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
1088 QVERIFY(!obj3.strictlyEquals(obj2));
1089 QScriptValue obj4 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
1090 QCOMPARE(obj4.strictlyEquals(obj3), preferExisting);
1092 QScriptValue obj5 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
1093 QVERIFY(!obj5.strictlyEquals(obj4));
1094 QScriptValue obj6 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
1095 QCOMPARE(obj6.strictlyEquals(obj5), preferExisting);
1097 QScriptValue obj7 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
1098 QScriptEngine::ExcludeSuperClassMethods | opt);
1099 QVERIFY(!obj7.strictlyEquals(obj6));
1100 QScriptValue obj8 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
1101 QScriptEngine::ExcludeSuperClassMethods | opt);
1102 QCOMPARE(obj8.strictlyEquals(obj7), preferExisting);
1107 #if 0 // FIXME: No prototype API in QScriptEngine
1108 void tst_QJSEngine::newQObject_defaultPrototype()
1111 // newQObject() should set the default prototype, if one has been registered
1113 QScriptValue oldQObjectProto = eng.defaultPrototype(qMetaTypeId<QObject*>());
1115 QScriptValue qobjectProto = eng.newObject();
1116 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), qobjectProto);
1118 QScriptValue ret = eng.newQObject(this);
1119 QVERIFY(ret.prototype().equals(qobjectProto));
1121 QScriptValue tstProto = eng.newObject();
1122 int typeId = qRegisterMetaType<tst_QJSEngine*>("tst_QJSEngine*");
1123 eng.setDefaultPrototype(typeId, tstProto);
1125 QScriptValue ret = eng.newQObject(this);
1126 QVERIFY(ret.prototype().equals(tstProto));
1129 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), oldQObjectProto);
1130 eng.setDefaultPrototype(typeId, QScriptValue());
1135 void tst_QJSEngine::newQObject_promoteNonObject()
1137 #if 0 // ### FIXME: object promotion is not supported
1140 QScriptValue ret = eng.newQObject(123, this);
1141 QVERIFY(ret.isQObject());
1142 QCOMPARE(ret.toQObject(), this);
1147 void tst_QJSEngine::newQObject_promoteNonQScriptObject()
1149 #if 0 // ### FIXME: object promotion is not supported
1150 QSKIP("Promotion of non QScriptObjects kind of works (there is not difference between Object and Array, look at comments in newQObject implementation).");
1153 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newQObject(): changing class of non-QScriptObject not supported");
1154 QScriptValue ret = eng.newQObject(eng.newArray(), this);
1155 QVERIFY(!ret.isValid());
1160 #if 0 // ### FIXME: No QScript Metaobject support right now
1162 Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*)
1163 Q_SCRIPT_DECLARE_QMETAOBJECT(QWidget, QWidget*)
1166 static QScriptValue myConstructor(QScriptContext *ctx, QScriptEngine *eng)
1169 if (ctx->isCalledAsConstructor()) {
1170 obj = ctx->thisObject();
1172 obj = eng->newObject();
1173 obj.setPrototype(ctx->callee().property("prototype"));
1175 obj.setProperty("isCalledAsConstructor", QScriptValue(eng, ctx->isCalledAsConstructor()));
1179 static QScriptValue instanceofJS(const QScriptValue &inst, const QScriptValue &ctor)
1181 return inst.engine()->evaluate("(function(inst, ctor) { return inst instanceof ctor; })")
1182 .call(QScriptValue(), QScriptValueList() << inst << ctor);
1185 void tst_QJSEngine::newQMetaObject()
1189 QScriptValue qclass = eng.newQMetaObject<QObject>();
1190 QScriptValue qclass2 = eng.newQMetaObject<QWidget>();
1192 QScriptValue qclass = qScriptValueFromQMetaObject<QObject>(&eng);
1193 QScriptValue qclass2 = qScriptValueFromQMetaObject<QWidget>(&eng);
1195 QCOMPARE(qclass.isValid(), true);
1196 QCOMPARE(qclass.isQMetaObject(), true);
1197 QCOMPARE(qclass.toQMetaObject(), &QObject::staticMetaObject);
1198 QCOMPARE(qclass.isFunction(), true);
1199 QVERIFY(qclass.property("prototype").isObject());
1201 QCOMPARE(qclass2.isValid(), true);
1202 QCOMPARE(qclass2.isQMetaObject(), true);
1203 QCOMPARE(qclass2.toQMetaObject(), &QWidget::staticMetaObject);
1204 QCOMPARE(qclass2.isFunction(), true);
1205 QVERIFY(qclass2.property("prototype").isObject());
1207 // prototype should be QMetaObject.prototype
1208 QCOMPARE(qclass.prototype().isObject(), true);
1209 QCOMPARE(qclass2.prototype().isObject(), true);
1211 QScriptValue instance = qclass.construct();
1212 QCOMPARE(instance.isQObject(), true);
1213 QCOMPARE(instance.toQObject()->metaObject(), qclass.toQMetaObject());
1214 QEXPECT_FAIL("", "FIXME: newQMetaObject not implemented properly yet", Abort);
1215 QVERIFY(instance.instanceOf(qclass));
1216 QVERIFY(instanceofJS(instance, qclass).strictlyEquals(true));
1218 QScriptValue instance2 = qclass2.construct();
1219 QCOMPARE(instance2.isQObject(), true);
1220 QCOMPARE(instance2.toQObject()->metaObject(), qclass2.toQMetaObject());
1221 QVERIFY(instance2.instanceOf(qclass2));
1222 QVERIFY(instanceofJS(instance2, qclass2).strictlyEquals(true));
1223 QVERIFY(!instance2.instanceOf(qclass));
1224 QVERIFY(instanceofJS(instance2, qclass).strictlyEquals(false));
1226 QScriptValueList args;
1228 QScriptValue instance3 = qclass.construct(args);
1229 QCOMPARE(instance3.isQObject(), true);
1230 QCOMPARE(instance3.toQObject()->parent(), instance.toQObject());
1231 QVERIFY(instance3.instanceOf(qclass));
1232 QVERIFY(instanceofJS(instance3, qclass).strictlyEquals(true));
1233 QVERIFY(!instance3.instanceOf(qclass2));
1234 QVERIFY(instanceofJS(instance3, qclass2).strictlyEquals(false));
1237 QPointer<QObject> qpointer1 = instance.toQObject();
1238 QPointer<QObject> qpointer2 = instance2.toQObject();
1239 QPointer<QObject> qpointer3 = instance3.toQObject();
1245 // verify that AutoOwnership is in effect
1246 instance = QScriptValue();
1247 collectGarbage_helper(eng);
1249 QVERIFY(!qpointer1);
1251 QVERIFY(!qpointer3); // was child of instance
1253 QVERIFY(instance.toQObject() == 0);
1254 QVERIFY(instance3.toQObject() == 0); // was child of instance
1255 QVERIFY(instance2.toQObject() != 0);
1256 instance2 = QScriptValue();
1257 collectGarbage_helper(eng);
1258 QVERIFY(instance2.toQObject() == 0);
1260 // with custom constructor
1261 QScriptValue ctor = eng.newFunction(myConstructor);
1262 QScriptValue qclass3 = eng.newQMetaObject(&QObject::staticMetaObject, ctor);
1263 QVERIFY(qclass3.property("prototype").equals(ctor.property("prototype")));
1265 QScriptValue ret = qclass3.call();
1266 QVERIFY(ret.isObject());
1267 QVERIFY(ret.property("isCalledAsConstructor").isBool());
1268 QVERIFY(!ret.property("isCalledAsConstructor").toBool());
1269 QVERIFY(ret.instanceOf(qclass3));
1270 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1271 QVERIFY(!ret.instanceOf(qclass));
1272 QVERIFY(instanceofJS(ret, qclass).strictlyEquals(false));
1275 QScriptValue ret = qclass3.construct();
1276 QVERIFY(ret.isObject());
1277 QVERIFY(ret.property("isCalledAsConstructor").isBool());
1278 QVERIFY(ret.property("isCalledAsConstructor").toBool());
1279 QVERIFY(ret.instanceOf(qclass3));
1280 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1281 QVERIFY(!ret.instanceOf(qclass2));
1282 QVERIFY(instanceofJS(ret, qclass2).strictlyEquals(false));
1286 qclass2.setProperty("prototype", qclass.construct());
1287 QVERIFY(qclass2.construct().instanceOf(qclass));
1288 QVERIFY(instanceofJS(qclass2.construct(), qclass).strictlyEquals(true));
1290 // with meta-constructor
1291 QScriptValue qclass4 = eng.newQMetaObject(&QObject::staticMetaObject);
1293 QScriptValue inst = qclass4.construct();
1294 QVERIFY(inst.isQObject());
1295 QVERIFY(inst.toQObject() != 0);
1296 QCOMPARE(inst.toQObject()->parent(), (QObject*)0);
1297 QVERIFY(inst.instanceOf(qclass4));
1298 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1299 QVERIFY(!inst.instanceOf(qclass3));
1300 QVERIFY(instanceofJS(inst, qclass3).strictlyEquals(false));
1303 QScriptValue inst = qclass4.construct(QScriptValueList() << eng.newQObject(this));
1304 QVERIFY(inst.isQObject());
1305 QVERIFY(inst.toQObject() != 0);
1306 QCOMPARE(inst.toQObject()->parent(), (QObject*)this);
1307 QVERIFY(inst.instanceOf(qclass4));
1308 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1309 QVERIFY(!inst.instanceOf(qclass2));
1310 QVERIFY(instanceofJS(inst, qclass2).strictlyEquals(false));
1315 #if 0 // ###FIXME: No activation object support
1316 void tst_QJSEngine::newActivationObject()
1318 QSKIP("internal function not implemented in JSC-based back-end");
1320 QScriptValue act = eng.newActivationObject();
1321 QEXPECT_FAIL("", "", Continue);
1322 QCOMPARE(act.isValid(), true);
1323 QEXPECT_FAIL("", "", Continue);
1324 QCOMPARE(act.isObject(), true);
1325 QVERIFY(!act.isFunction());
1326 QScriptValue v(&eng, 123);
1327 act.setProperty("prop", v);
1328 QEXPECT_FAIL("", "", Continue);
1329 QCOMPARE(act.property("prop").strictlyEquals(v), true);
1330 QCOMPARE(act.scope().isValid(), false);
1331 QEXPECT_FAIL("", "", Continue);
1332 QVERIFY(act.prototype().isNull());
1336 #if 0 // ###FIXME: No setGlobalObject support - yay
1337 void tst_QJSEngine::getSetGlobalObjectSimple()
1339 QScriptEngine engine;
1340 QScriptValue object = engine.newObject();
1341 object.setProperty("foo", 123);
1342 engine.evaluate("var bar = 100");
1343 engine.setGlobalObject(object);
1344 engine.evaluate("rab = 100");
1345 QVERIFY(engine.globalObject().property("rab").isValid());
1346 QVERIFY(engine.globalObject().property("foo").isValid());
1347 QVERIFY(!engine.globalObject().property("bar").isValid());
1350 void tst_QJSEngine::getSetGlobalObject()
1353 QScriptValue glob = eng.globalObject();
1354 glob = QScriptValue(); // kill reference to old global object
1355 collectGarbage_helper(eng);
1357 glob = eng.globalObject();
1358 QCOMPARE(glob.isValid(), true);
1359 QCOMPARE(glob.isObject(), true);
1360 QVERIFY(!glob.isFunction());
1361 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(glob));
1362 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(glob));
1363 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1364 QCOMPARE(glob.toString(), QString::fromLatin1("[object global]"));
1365 // prototype should be Object.prototype
1366 QCOMPARE(glob.prototype().isValid(), true);
1367 QCOMPARE(glob.prototype().isObject(), true);
1368 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1369 QCOMPARE(glob.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
1371 eng.setGlobalObject(glob);
1372 QVERIFY(eng.globalObject().equals(glob));
1373 eng.setGlobalObject(123);
1374 QVERIFY(eng.globalObject().equals(glob));
1376 QScriptValue obj = eng.newObject();
1377 eng.setGlobalObject(obj);
1378 QVERIFY(eng.globalObject().strictlyEquals(obj));
1379 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1380 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1381 QVERIFY(eng.evaluate("this").strictlyEquals(obj));
1382 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1383 QCOMPARE(eng.globalObject().toString(), QString::fromLatin1("[object global]"));
1385 collectGarbage_helper(eng);
1386 glob = QScriptValue(); // kill reference to old global object
1387 collectGarbage_helper(eng);
1388 obj = eng.newObject();
1389 eng.setGlobalObject(obj);
1390 QVERIFY(eng.globalObject().strictlyEquals(obj));
1391 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1392 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1394 collectGarbage_helper(eng);
1395 QVERIFY(eng.globalObject().strictlyEquals(obj));
1396 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1397 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1399 QVERIFY(!obj.property("foo").isValid());
1400 eng.evaluate("var foo = 123");
1402 QScriptValue ret = obj.property("foo");
1403 QVERIFY(ret.isNumber());
1404 QCOMPARE(ret.toInt(), 123);
1407 QVERIFY(!obj.property("bar").isValid());
1408 eng.evaluate("bar = 456");
1410 QScriptValue ret = obj.property("bar");
1411 QVERIFY(ret.isNumber());
1412 QCOMPARE(ret.toInt(), 456);
1415 QVERIFY(!obj.property("baz").isValid());
1416 eng.evaluate("this['baz'] = 789");
1418 QScriptValue ret = obj.property("baz");
1419 QVERIFY(ret.isNumber());
1420 QCOMPARE(ret.toInt(), 789);
1424 QScriptValue ret = eng.evaluate("(function() { return this; })()");
1425 QVERIFY(ret.strictlyEquals(obj));
1430 QScriptValue ret = eng.evaluate("delete foo");
1431 QVERIFY(ret.isBool());
1432 QVERIFY(ret.toBool());
1433 QVERIFY(!obj.property("foo").isValid());
1436 // Getter/setter property.
1437 //the custom global object have an interceptor
1438 QVERIFY(eng.evaluate("this.__defineGetter__('oof', function() { return this.bar; })").isUndefined());
1439 QVERIFY(eng.evaluate("this.__defineSetter__('oof', function(v) { this.bar = v; })").isUndefined());
1440 QVERIFY(eng.evaluate("this.__lookupGetter__('oof')").isFunction());
1441 QVERIFY(eng.evaluate("this.__lookupSetter__('oof')").isFunction());
1442 eng.evaluate("oof = 123");
1443 QVERIFY(eng.evaluate("oof").equals(obj.property("bar")));
1447 QScriptValue ret = eng.evaluate("a = []; for (var p in this) a.push(p); a");
1448 QCOMPARE(ret.toString(), QString::fromLatin1("bar,baz,oof,p,a"));
1453 #if 0 // ###FIXME: no c-style callbacks
1454 static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *)
1456 if (ctx->argumentCount() > 0)
1457 ctx->thisObject().setProperty("foo", ctx->argument(0));
1458 return ctx->thisObject().property("foo");
1462 void tst_QJSEngine::globalObjectProperties()
1464 QSKIP("Test failing - QTBUG-22238");
1465 // See ECMA-262 Section 15.1, "The Global Object".
1468 QJSValue global = eng.globalObject();
1470 QVERIFY(global.property("NaN").isNumber());
1471 QVERIFY(qIsNaN(global.property("NaN").toNumber()));
1472 QCOMPARE(global.propertyFlags("NaN"), QJSValue::SkipInEnumeration | QJSValue::Undeletable);
1474 QVERIFY(global.property("Infinity").isNumber());
1475 QVERIFY(qIsInf(global.property("Infinity").toNumber()));
1476 QCOMPARE(global.propertyFlags("NaN"), QJSValue::SkipInEnumeration | QJSValue::Undeletable);
1478 QVERIFY(global.property("undefined").isUndefined());
1479 QCOMPARE(global.propertyFlags("undefined"), QJSValue::SkipInEnumeration | QJSValue::Undeletable);
1481 QVERIFY(global.property("eval").isFunction());
1482 QCOMPARE(global.propertyFlags("eval"), QJSValue::SkipInEnumeration);
1484 QVERIFY(global.property("parseInt").isFunction());
1485 QCOMPARE(global.propertyFlags("parseInt"), QJSValue::SkipInEnumeration);
1487 QVERIFY(global.property("parseFloat").isFunction());
1488 QCOMPARE(global.propertyFlags("parseFloat"), QJSValue::SkipInEnumeration);
1490 QVERIFY(global.property("isNaN").isFunction());
1491 QCOMPARE(global.propertyFlags("isNaN"), QJSValue::SkipInEnumeration);
1493 QVERIFY(global.property("isFinite").isFunction());
1494 QCOMPARE(global.propertyFlags("isFinite"), QJSValue::SkipInEnumeration);
1496 QVERIFY(global.property("decodeURI").isFunction());
1497 QCOMPARE(global.propertyFlags("decodeURI"), QJSValue::SkipInEnumeration);
1499 QVERIFY(global.property("decodeURIComponent").isFunction());
1500 QCOMPARE(global.propertyFlags("decodeURIComponent"), QJSValue::SkipInEnumeration);
1502 QVERIFY(global.property("encodeURI").isFunction());
1503 QCOMPARE(global.propertyFlags("encodeURI"), QJSValue::SkipInEnumeration);
1505 QVERIFY(global.property("encodeURIComponent").isFunction());
1506 QCOMPARE(global.propertyFlags("encodeURIComponent"), QJSValue::SkipInEnumeration);
1508 QVERIFY(global.property("Object").isFunction());
1509 QCOMPARE(global.propertyFlags("Object"), QJSValue::SkipInEnumeration);
1510 QVERIFY(global.property("Function").isFunction());
1511 QCOMPARE(global.propertyFlags("Function"), QJSValue::SkipInEnumeration);
1512 QVERIFY(global.property("Array").isFunction());
1513 QCOMPARE(global.propertyFlags("Array"), QJSValue::SkipInEnumeration);
1514 QVERIFY(global.property("String").isFunction());
1515 QCOMPARE(global.propertyFlags("String"), QJSValue::SkipInEnumeration);
1516 QVERIFY(global.property("Boolean").isFunction());
1517 QCOMPARE(global.propertyFlags("Boolean"), QJSValue::SkipInEnumeration);
1518 QVERIFY(global.property("Number").isFunction());
1519 QCOMPARE(global.propertyFlags("Number"), QJSValue::SkipInEnumeration);
1520 QVERIFY(global.property("Date").isFunction());
1521 QCOMPARE(global.propertyFlags("Date"), QJSValue::SkipInEnumeration);
1522 QVERIFY(global.property("RegExp").isFunction());
1523 QCOMPARE(global.propertyFlags("RegExp"), QJSValue::SkipInEnumeration);
1524 QVERIFY(global.property("Error").isFunction());
1525 QCOMPARE(global.propertyFlags("Error"), QJSValue::SkipInEnumeration);
1526 QVERIFY(global.property("EvalError").isFunction());
1527 QCOMPARE(global.propertyFlags("EvalError"), QJSValue::SkipInEnumeration);
1528 QVERIFY(global.property("RangeError").isFunction());
1529 QCOMPARE(global.propertyFlags("RangeError"), QJSValue::SkipInEnumeration);
1530 QVERIFY(global.property("ReferenceError").isFunction());
1531 QCOMPARE(global.propertyFlags("ReferenceError"), QJSValue::SkipInEnumeration);
1532 QVERIFY(global.property("SyntaxError").isFunction());
1533 QCOMPARE(global.propertyFlags("SyntaxError"), QJSValue::SkipInEnumeration);
1534 QVERIFY(global.property("TypeError").isFunction());
1535 QCOMPARE(global.propertyFlags("TypeError"), QJSValue::SkipInEnumeration);
1536 QVERIFY(global.property("URIError").isFunction());
1537 QCOMPARE(global.propertyFlags("URIError"), QJSValue::SkipInEnumeration);
1538 QVERIFY(global.property("Math").isObject());
1539 QVERIFY(!global.property("Math").isFunction());
1540 QCOMPARE(global.propertyFlags("Math"), QJSValue::SkipInEnumeration);
1543 void tst_QJSEngine::globalObjectEquals()
1546 QJSValue o = eng.globalObject();
1547 QVERIFY(o.strictlyEquals(eng.globalObject()));
1548 QVERIFY(o.equals(eng.globalObject()));
1551 void tst_QJSEngine::globalObjectProperties_enumerate()
1553 QSKIP("Test failing - QTBUG-22238");
1555 QJSValue global = eng.globalObject();
1557 QSet<QString> expectedNames;
1565 << "encodeURIComponent"
1582 << "decodeURIComponent"
1592 << "execScript" //execScript for IE compatibility.
1594 QSet<QString> actualNames;
1596 QJSValueIterator it(global);
1597 while (it.hasNext()) {
1599 actualNames.insert(it.name());
1603 QSet<QString> remainingNames = actualNames;
1605 QSet<QString>::const_iterator it;
1606 for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) {
1608 QVERIFY(actualNames.contains(name));
1609 remainingNames.remove(name);
1612 QVERIFY(remainingNames.isEmpty());
1615 void tst_QJSEngine::createGlobalObjectProperty()
1618 QJSValue global = eng.globalObject();
1619 // create property with no attributes
1621 QString name = QString::fromLatin1("foo");
1622 QVERIFY(!global.property(name).isValid());
1624 global.setProperty(name, val);
1625 QVERIFY(global.property(name).equals(val));
1626 QVERIFY(global.propertyFlags(name) == 0);
1627 global.setProperty(name, QJSValue());
1628 QVERIFY(!global.property(name).isValid());
1630 // create property with attributes
1631 #if 0 // ###FIXME: setProperty with flags is not supported
1633 QString name = QString::fromLatin1("bar");
1634 QVERIFY(!global.property(name).isValid());
1635 QScriptValue val(QString::fromLatin1("ciao"));
1636 QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration;
1637 global.setProperty(name, val, flags);
1638 QVERIFY(global.property(name).equals(val));
1639 //QEXPECT_FAIL("", "QTBUG-6134: custom Global Object properties don't retain attributes", Continue);
1640 QCOMPARE(global.propertyFlags(name), flags);
1641 global.setProperty(name, QScriptValue());
1642 QVERIFY(!global.property(name).isValid());
1647 void tst_QJSEngine::globalObjectGetterSetterProperty()
1649 #if 0 // ###FIXME: No c-style callbacks
1650 QScriptEngine engine;
1651 QScriptValue global = engine.globalObject();
1652 global.setProperty("bar", engine.newFunction(getSetFoo),
1653 QScriptValue::PropertySetter | QScriptValue::PropertyGetter);
1654 global.setProperty("foo", 123);
1655 QVERIFY(global.property("bar").equals(global.property("foo")));
1656 QVERIFY(engine.evaluate("bar").equals(global.property("foo")));
1657 global.setProperty("bar", 456);
1658 QVERIFY(global.property("bar").equals(global.property("foo")));
1660 engine.evaluate("__defineGetter__('baz', function() { return 789; })");
1661 QVERIFY(engine.evaluate("baz").equals(789));
1662 QVERIFY(global.property("baz").equals(789));
1666 #if 0 // ###FIXME: No support for setting the global object
1667 void tst_QJSEngine::customGlobalObjectWithPrototype()
1669 for (int x = 0; x < 2; ++x) {
1670 QScriptEngine engine;
1671 QScriptValue wrap = engine.newObject();
1672 QScriptValue global = engine.globalObject();
1673 QScriptValue originalGlobalProto = global.prototype();
1675 // Set prototype before setting global object
1676 wrap.setPrototype(global);
1677 QVERIFY(wrap.prototype().strictlyEquals(global));
1678 engine.setGlobalObject(wrap);
1680 // Set prototype after setting global object
1681 engine.setGlobalObject(wrap);
1682 wrap.setPrototype(global);
1683 QVERIFY(wrap.prototype().strictlyEquals(global));
1686 QScriptValue ret = engine.evaluate("print");
1687 QVERIFY(ret.isFunction());
1688 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1691 QScriptValue ret = engine.evaluate("this.print");
1692 QVERIFY(ret.isFunction());
1693 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1696 QScriptValue ret = engine.evaluate("hasOwnProperty('print')");
1697 QVERIFY(ret.isBool());
1698 if (x) QEXPECT_FAIL("", "Why?", Continue);
1699 QVERIFY(!ret.toBool());
1702 QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')");
1703 QVERIFY(ret.isBool());
1704 if (x) QEXPECT_FAIL("", "Why?", Continue);
1705 QVERIFY(!ret.toBool());
1708 QScriptValue anotherProto = engine.newObject();
1709 anotherProto.setProperty("anotherProtoProperty", 123);
1710 global.setPrototype(anotherProto);
1712 QScriptValue ret = engine.evaluate("print");
1713 QVERIFY(ret.isFunction());
1714 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1717 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1718 QVERIFY(ret.isNumber());
1719 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1722 QScriptValue ret = engine.evaluate("this.anotherProtoProperty");
1723 QVERIFY(ret.isNumber());
1724 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1727 wrap.setPrototype(anotherProto);
1729 QScriptValue ret = engine.evaluate("print");
1730 QVERIFY(ret.isError());
1731 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: print is not defined"));
1734 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1735 QVERIFY(ret.isNumber());
1736 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1738 QVERIFY(global.prototype().strictlyEquals(anotherProto));
1740 global.setPrototype(originalGlobalProto);
1741 engine.setGlobalObject(global);
1743 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1744 QVERIFY(ret.isError());
1745 QVERIFY(ret.toString().startsWith("ReferenceError: "));
1748 QScriptValue ret = engine.evaluate("print");
1749 QVERIFY(ret.isFunction());
1750 QVERIFY(ret.strictlyEquals(global.property("print")));
1752 QVERIFY(!anotherProto.property("print").isValid());
1757 void tst_QJSEngine::globalObjectWithCustomPrototype()
1760 QJSValue proto = engine.newObject();
1761 proto.setProperty("protoProperty", 123);
1762 QJSValue global = engine.globalObject();
1763 QJSValue originalProto = global.prototype();
1764 global.setPrototype(proto);
1766 QJSValue ret = engine.evaluate("protoProperty");
1767 QEXPECT_FAIL("", "Replacing the prototype of the global object is currently unsupported (see also v8 issue 1078)", Abort);
1768 QVERIFY(ret.isNumber());
1769 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1772 QJSValue ret = engine.evaluate("this.protoProperty");
1773 QVERIFY(ret.isNumber());
1774 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1777 QJSValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
1778 QVERIFY(ret.isBool());
1779 QVERIFY(!ret.toBool());
1782 QJSValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
1783 QVERIFY(ret.isBool());
1784 QVERIFY(!ret.toBool());
1787 // Custom prototype set from JS
1789 QJSValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
1790 QVERIFY(ret.isNumber());
1791 QVERIFY(ret.strictlyEquals(global.property("a")));
1795 void tst_QJSEngine::builtinFunctionNames_data()
1797 QTest::addColumn<QString>("expression");
1798 QTest::addColumn<QString>("expectedName");
1800 // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects".
1802 QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt");
1803 QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat");
1804 QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN");
1805 QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite");
1806 QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI");
1807 QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent");
1808 QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI");
1809 QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent");
1810 QTest::newRow("escape") << QString("escape") << QString("escape");
1811 QTest::newRow("unescape") << QString("unescape") << QString("unescape");
1813 QTest::newRow("Array") << QString("Array") << QString("Array");
1814 QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString");
1815 QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString");
1816 QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat");
1817 QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join");
1818 QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop");
1819 QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push");
1820 QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse");
1821 QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift");
1822 QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice");
1823 QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort");
1824 QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice");
1825 QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift");
1827 QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean");
1828 QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString");
1830 QTest::newRow("Date") << QString("Date") << QString("Date");
1831 QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString");
1832 QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString");
1833 QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString");
1834 QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString");
1835 QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString");
1836 QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString");
1837 QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf");
1838 QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime");
1839 QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear");
1840 QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear");
1841 QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear");
1842 QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth");
1843 QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth");
1844 QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate");
1845 QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate");
1846 QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay");
1847 QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay");
1848 QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours");
1849 QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours");
1850 QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes");
1851 QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes");
1852 QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds");
1853 QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds");
1854 QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds");
1855 QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds");
1856 QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset");
1857 QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime");
1858 QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds");
1859 QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds");
1860 QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds");
1861 QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds");
1862 QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes");
1863 QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes");
1864 QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours");
1865 QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours");
1866 QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate");
1867 QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate");
1868 QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth");
1869 QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth");
1870 QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear");
1871 QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear");
1872 QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear");
1873 QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString");
1874 QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString");
1876 QTest::newRow("Error") << QString("Error") << QString("Error");
1877 // QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace");
1878 QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString");
1880 QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError");
1881 QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError");
1882 QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError");
1883 QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError");
1884 QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError");
1885 QTest::newRow("URIError") << QString("URIError") << QString("URIError");
1887 QTest::newRow("Function") << QString("Function") << QString("Function");
1888 QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString");
1889 QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply");
1890 QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call");
1891 /* In V8, those function are only there for signals
1892 QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect");
1893 QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");*/
1895 QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs");
1896 QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos");
1897 QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin");
1898 QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan");
1899 QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2");
1900 QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil");
1901 QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos");
1902 QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp");
1903 QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor");
1904 QTest::newRow("Math.log") << QString("Math.log") << QString("log");
1905 QTest::newRow("Math.max") << QString("Math.max") << QString("max");
1906 QTest::newRow("Math.min") << QString("Math.min") << QString("min");
1907 QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
1908 QTest::newRow("Math.random") << QString("Math.random") << QString("random");
1909 QTest::newRow("Math.round") << QString("Math.round") << QString("round");
1910 QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
1911 QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
1912 QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
1914 QTest::newRow("Number") << QString("Number") << QString("Number");
1915 QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
1916 QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
1917 QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
1918 QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed");
1919 QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential");
1920 QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision");
1922 QTest::newRow("Object") << QString("Object") << QString("Object");
1923 QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString");
1924 QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString");
1925 QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf");
1926 QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty");
1927 QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf");
1928 QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable");
1929 QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__");
1930 QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__");
1932 QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp");
1933 QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec");
1934 QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test");
1935 QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString");
1937 QTest::newRow("String") << QString("String") << QString("String");
1938 QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString");
1939 QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf");
1940 QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
1941 QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
1942 QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
1943 QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
1944 QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
1945 QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
1946 QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match");
1947 QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace");
1948 QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
1949 QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
1950 QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
1951 QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
1952 QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
1953 QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
1954 QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase");
1955 QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase");
1958 void tst_QJSEngine::builtinFunctionNames()
1960 QFETCH(QString, expression);
1961 QFETCH(QString, expectedName);
1963 // The "name" property is actually non-standard, but JSC supports it.
1964 QJSValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression));
1965 QVERIFY(ret.isString());
1966 QCOMPARE(ret.toString(), expectedName);
1969 #if 0 // ###FIXME: No syntax checking result
1970 void tst_QJSEngine::checkSyntax_data()
1972 QTest::addColumn<QString>("code");
1973 QTest::addColumn<int>("expectedState");
1974 QTest::addColumn<int>("errorLineNumber");
1975 QTest::addColumn<int>("errorColumnNumber");
1976 QTest::addColumn<QString>("errorMessage");
1979 << QString("0") << int(QScriptSyntaxCheckResult::Valid)
1981 QTest::newRow("if (")
1982 << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate)
1983 << 0 << -1 << "Uncaught SyntaxError: Unexpected end of input";
1984 QTest::newRow("if else")
1985 << QString("\nif else") << int(QScriptSyntaxCheckResult::Error)
1986 << 2 << 3 << "Uncaught SyntaxError: Unexpected token else";
1987 QTest::newRow("foo[")
1988 << QString("foo[") << int(QScriptSyntaxCheckResult::Intermediate)
1989 << 1 << 4 << "Uncaught SyntaxError: Unexpected end of input";
1990 QTest::newRow("foo['bar']")
1991 << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid)
1995 << QString("/*") << int(QScriptSyntaxCheckResult::Error)
1996 << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
1997 QTest::newRow("/*\nMy comment")
1998 << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Error)
1999 << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2000 QTest::newRow("/*\nMy comment */\nfoo = 10")
2001 << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid)
2003 QTest::newRow("foo = 10 /*")
2004 << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Error)
2005 << 1 << 9 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2006 QTest::newRow("foo = 10; /*")
2007 << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Error)
2008 << 1 << 10 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2009 QTest::newRow("foo = 10 /* My comment */")
2010 << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid)
2013 QTest::newRow("/=/")
2014 << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2015 QTest::newRow("/=/g")
2016 << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2017 QTest::newRow("/a/")
2018 << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2019 QTest::newRow("/a/g")
2020 << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2023 void tst_QJSEngine::checkSyntax()
2025 QFETCH(QString, code);
2026 QFETCH(int, expectedState);
2027 QFETCH(int, errorLineNumber);
2028 QFETCH(int, errorColumnNumber);
2029 QFETCH(QString, errorMessage);
2031 QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code);
2032 QCOMPARE(int(result.state()), expectedState);
2033 QCOMPARE(result.errorLineNumber(), errorLineNumber);
2034 QCOMPARE(result.errorColumnNumber(), errorColumnNumber);
2035 QCOMPARE(result.errorMessage(), errorMessage);
2039 QScriptSyntaxCheckResult copy = result;
2040 QCOMPARE(copy.state(), result.state());
2041 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
2042 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
2043 QCOMPARE(copy.errorMessage(), result.errorMessage());
2046 QScriptSyntaxCheckResult copy(result);
2047 QCOMPARE(copy.state(), result.state());
2048 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
2049 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
2050 QCOMPARE(copy.errorMessage(), result.errorMessage());
2055 #if 0 // ###FIXME: No support for canEvaluate
2056 void tst_QJSEngine::canEvaluate_data()
2058 QTest::addColumn<QString>("code");
2059 QTest::addColumn<bool>("expectSuccess");
2061 QTest::newRow("") << QString("") << true;
2062 QTest::newRow("0") << QString("0") << true;
2063 QTest::newRow("!") << QString("!\n") << false;
2064 QTest::newRow("if (") << QString("if (\n") << false;
2065 QTest::newRow("if (10) //") << QString("if (10) //\n") << false;
2066 QTest::newRow("a = 1; if (") << QString("a = 1;\nif (\n") << false;
2067 QTest::newRow("./test.js") << QString("./test.js\n") << true;
2068 QTest::newRow("if (0) print(1)") << QString("if (0)\nprint(1)\n") << true;
2069 QTest::newRow("0 = ") << QString("0 = \n") << false;
2070 QTest::newRow("0 = 0") << QString("0 = 0\n") << true;
2071 QTest::newRow("foo[") << QString("foo[") << false;
2072 QTest::newRow("foo[") << QString("foo[\n") << false;
2073 QTest::newRow("foo['bar']") << QString("foo['bar']") << true;
2075 //v8 does thinks unterminated comments are error rather than unfinished
2076 // QTest::newRow("/*") << QString("/*") << false;
2077 // QTest::newRow("/*\nMy comment") << QString("/*\nMy comment") << false;
2078 QTest::newRow("/*\nMy comment */\nfoo = 10") << QString("/*\nMy comment */\nfoo = 10") << true;
2079 // QTest::newRow("foo = 10 /*") << QString("foo = 10 /*") << false;
2080 // QTest::newRow("foo = 10; /*") << QString("foo = 10; /*") << false;
2081 QTest::newRow("foo = 10 /* My comment */") << QString("foo = 10 /* My comment */") << true;
2083 QTest::newRow("/=/") << QString("/=/") << true;
2084 QTest::newRow("/=/g") << QString("/=/g") << true;
2085 QTest::newRow("/a/") << QString("/a/") << true;
2086 QTest::newRow("/a/g") << QString("/a/g") << true;
2089 void tst_QJSEngine::canEvaluate()
2091 QFETCH(QString, code);
2092 QFETCH(bool, expectSuccess);
2095 QCOMPARE(eng.canEvaluate(code), expectSuccess);
2099 void tst_QJSEngine::evaluate_data()
2101 QTest::addColumn<QString>("code");
2102 QTest::addColumn<int>("lineNumber");
2103 QTest::addColumn<bool>("expectHadError");
2104 QTest::addColumn<int>("expectErrorLineNumber");
2106 QTest::newRow("(newline)") << QString("\n") << -1 << false << -1;
2107 QTest::newRow("0 //") << QString("0 //") << -1 << false << -1;
2108 QTest::newRow("/* */") << QString("/* */") << -1 << false << -1;
2109 QTest::newRow("//") << QString("//") << -1 << false << -1;
2110 QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1;
2111 QTest::newRow("(empty)") << QString("") << -1 << false << -1;
2112 QTest::newRow("0") << QString("0") << -1 << false << -1;
2113 QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2;
2114 QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1;
2115 QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2;
2117 QTest::newRow("f()") << QString("function f()\n"
2120 " var b=\";\n" // here's the error
2125 QTest::newRow("0") << QString("0") << 10 << false << -1;
2126 QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 12;
2127 QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1;
2128 QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12;
2130 QTest::newRow("f()") << QString("function f()\n"
2134 " var b=\";\n" // here's the error
2137 << 10 << true << 15;
2138 QTest::newRow("functionThatDoesntExist()")
2139 << QString(";\n;\n;\nfunctionThatDoesntExist()")
2141 QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }")
2142 << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }")
2144 QTest::newRow("duplicateLabel: { duplicateLabel: ; }")
2145 << QString("duplicateLabel: { duplicateLabel: ; }")
2146 << 12 << true << 12;
2148 QTest::newRow("/=/") << QString("/=/") << -1 << false << -1;
2149 QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1;
2150 QTest::newRow("/a/") << QString("/a/") << -1 << false << -1;
2151 QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1;
2152 QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1;
2153 QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1;
2156 void tst_QJSEngine::evaluate()
2158 QFETCH(QString, code);
2159 QFETCH(int, lineNumber);
2160 QFETCH(bool, expectHadError);
2161 QFETCH(int, expectErrorLineNumber);
2165 if (lineNumber != -1)
2166 ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber);
2168 ret = eng.evaluate(code);
2169 QCOMPARE(eng.hasUncaughtException(), expectHadError);
2170 #if 0 // ###FIXME: No support for the line number of an uncaught exception
2171 QEXPECT_FAIL("f()", "SyntaxError do not report line number", Continue);
2172 QEXPECT_FAIL("duplicateLabel: { duplicateLabel: ; }", "SyntaxError do not report line number", Continue);
2173 QCOMPARE(eng.uncaughtExceptionLineNumber(), expectErrorLineNumber);
2175 if (eng.hasUncaughtException() && ret.isError()) {
2176 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
2177 QVERIFY(ret.property("lineNumber").strictlyEquals(QJSValue(&eng, expectErrorLineNumber)));
2179 #if 0 // ###FIXME: No support for the backtrace of an uncaught exception
2180 QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
2185 #if 0 // ###FIXME: no support for c-style callbacks
2186 static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng)
2188 QScriptValue result = eng->newObject();
2189 eng->evaluate("var bar = 'local';");
2190 result.setProperty("thisObjectIdBefore", ctx->thisObject().property("id"));
2191 QScriptValue evaluatedThisObject = eng->evaluate("this");
2192 result.setProperty("thisObjectIdAfter", ctx->thisObject().property("id"));
2193 result.setProperty("evaluatedThisObjectId", evaluatedThisObject.property("id"));
2194 result.setProperty("local_bar", eng->evaluate("bar"));
2199 // Tests that nested evaluate uses the "this" that was passed.
2200 void tst_QJSEngine::nestedEvaluate()
2203 QScriptValue fun = eng.newFunction(eval_nested);
2204 eng.globalObject().setProperty("fun", fun);
2205 // From JS function call
2207 QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()");
2208 QCOMPARE(result.property("local_bar").toString(), QString("local"));
2209 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
2210 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
2211 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
2212 QScriptValue bar = eng.evaluate("bar"); // Was introduced in local scope.
2213 QVERIFY(bar.isError());
2214 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
2216 // From QScriptValue::call()
2218 QScriptValue result = fun.call(eng.evaluate("p = { id:'foo' }") , QScriptValueList() );
2219 QCOMPARE(result.property("local_bar").toString(), QString("local"));
2220 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
2221 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
2222 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
2223 QScriptValue bar = eng.evaluate("bar");
2224 QVERIFY(bar.isError());
2225 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
2230 #if 0 // ### FIXME: No c-style callbacks
2231 void tst_QJSEngine::uncaughtException()
2234 QScriptValue fun = eng.newFunction(myFunction);
2235 QScriptValue throwFun = eng.newFunction(myThrowingFunction);
2236 for (int x = -1; x < 2; ++x) {
2238 QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", /*fileName=*/QString(), /*lineNumber=*/x);
2239 QVERIFY(eng.hasUncaughtException());
2240 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2241 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2242 (void)ret.toString();
2243 QVERIFY(eng.hasUncaughtException());
2244 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2245 QVERIFY(fun.call().isNull());
2246 QVERIFY(eng.hasUncaughtException());
2247 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2248 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2249 eng.clearExceptions();
2250 QVERIFY(!eng.hasUncaughtException());
2251 QCOMPARE(eng.uncaughtExceptionLineNumber(), -1);
2252 QVERIFY(!eng.uncaughtException().isValid());
2254 eng.evaluate("2 = 3");
2255 QVERIFY(eng.hasUncaughtException());
2256 QScriptValue ret2 = throwFun.call();
2257 QVERIFY(ret2.isError());
2258 QVERIFY(eng.hasUncaughtException());
2259 QVERIFY(eng.uncaughtException().strictlyEquals(ret2));
2260 QCOMPARE(eng.uncaughtExceptionLineNumber(), 0);
2261 eng.clearExceptions();
2262 QVERIFY(!eng.hasUncaughtException());
2263 eng.evaluate("1 + 2");
2264 QVERIFY(!eng.hasUncaughtException());
2267 QScriptValue ret = eng.evaluate("a = 10");
2268 QVERIFY(!eng.hasUncaughtException());
2269 QVERIFY(!eng.uncaughtException().isValid());
2272 QScriptValue ret = eng.evaluate("1 = 2");
2273 QVERIFY(eng.hasUncaughtException());
2274 eng.clearExceptions();
2275 QVERIFY(!eng.hasUncaughtException());
2278 eng.globalObject().setProperty("throwFun", throwFun);
2279 eng.evaluate("1;\nthrowFun();");
2280 QVERIFY(eng.hasUncaughtException());
2281 QCOMPARE(eng.uncaughtExceptionLineNumber(), 2);
2282 eng.clearExceptions();
2283 QVERIFY(!eng.hasUncaughtException());
2289 void tst_QJSEngine::errorMessage_QT679()
2292 engine.globalObject().setProperty("foo", 15);
2293 QJSValue error = engine.evaluate("'hello world';\nfoo.bar.blah");
2294 QVERIFY(error.isError());
2295 QVERIFY(error.toString().startsWith(QString::fromLatin1("TypeError: ")));
2301 Foo() : x(-1), y(-1) { }
2304 Q_DECLARE_METATYPE(Foo)
2305 Q_DECLARE_METATYPE(Foo*)
2307 #if 0 // FIXME: No prototype API in QScriptEngine
2308 void tst_QJSEngine::getSetDefaultPrototype_int()
2312 QScriptValue object = eng.newObject();
2313 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).isValid(), false);
2314 eng.setDefaultPrototype(qMetaTypeId<int>(), object);
2315 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).strictlyEquals(object), true);
2316 QScriptValue value = eng.newVariant(int(123));
2317 QCOMPARE(value.prototype().isObject(), true);
2318 QCOMPARE(value.prototype().strictlyEquals(object), true);
2320 eng.setDefaultPrototype(qMetaTypeId<int>(), QScriptValue());
2321 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).isValid(), false);
2322 QScriptValue value2 = eng.newVariant(int(123));
2323 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2326 void tst_QJSEngine::getSetDefaultPrototype_customType()
2330 QScriptValue object = eng.newObject();
2331 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).isValid(), false);
2332 eng.setDefaultPrototype(qMetaTypeId<Foo>(), object);
2333 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(object), true);
2334 QScriptValue value = eng.newVariant(qVariantFromValue(Foo()));
2335 QCOMPARE(value.prototype().isObject(), true);
2336 QCOMPARE(value.prototype().strictlyEquals(object), true);
2338 eng.setDefaultPrototype(qMetaTypeId<Foo>(), QScriptValue());
2339 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).isValid(), false);
2340 QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo()));
2341 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2345 static QJSValue fooToScriptValue(QJSEngine *eng, const Foo &foo)
2347 QJSValue obj = eng->newObject();
2348 obj.setProperty("x", QJSValue(eng, foo.x));
2349 obj.setProperty("y", QJSValue(eng, foo.y));
2353 static void fooFromScriptValue(const QJSValue &value, Foo &foo)
2355 foo.x = value.property("x").toInt();
2356 foo.y = value.property("y").toInt();
2359 static QJSValue fooToScriptValueV2(QJSEngine *eng, const Foo &foo)
2361 return QJSValue(eng, foo.x);
2364 static void fooFromScriptValueV2(const QJSValue &value, Foo &foo)
2366 foo.x = value.toInt();
2369 Q_DECLARE_METATYPE(QLinkedList<QString>)
2370 Q_DECLARE_METATYPE(QList<Foo>)
2371 Q_DECLARE_METATYPE(QVector<QChar>)
2372 Q_DECLARE_METATYPE(QStack<int>)
2373 Q_DECLARE_METATYPE(QQueue<char>)
2374 Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
2376 void tst_QJSEngine::valueConversion_basic()
2380 QJSValue num = eng.toScriptValue(123);
2381 QCOMPARE(num.isNumber(), true);
2382 QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true);
2384 int inum = eng.fromScriptValue<int>(num);
2385 QCOMPARE(inum, 123);
2387 QString snum = eng.fromScriptValue<QString>(num);
2388 QCOMPARE(snum, QLatin1String("123"));
2391 QJSValue num = eng.toScriptValue(123);
2392 QCOMPARE(num.isNumber(), true);
2393 QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true);
2395 int inum = eng.fromScriptValue<int>(num);
2396 QCOMPARE(inum, 123);
2398 QString snum = eng.fromScriptValue<QString>(num);
2399 QCOMPARE(snum, QLatin1String("123"));
2402 QJSValue num(&eng, 123);
2403 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
2404 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
2405 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
2406 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
2407 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
2408 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
2409 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
2410 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
2414 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
2415 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
2416 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
2417 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
2418 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
2419 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
2420 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
2421 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
2425 QJSValue num = eng.toScriptValue(Q_INT64_C(0x100000000));
2426 QCOMPARE(eng.fromScriptValue<qlonglong>(num), Q_INT64_C(0x100000000));
2427 QCOMPARE(eng.fromScriptValue<qulonglong>(num), Q_UINT64_C(0x100000000));
2431 QChar c = QLatin1Char('c');
2432 QJSValue str = QJSValue(&eng, QLatin1String("ciao"));
2433 QCOMPARE(eng.fromScriptValue<QChar>(str), c);
2434 QJSValue code = QJSValue(&eng, c.unicode());
2435 QCOMPARE(eng.fromScriptValue<QChar>(code), c);
2436 QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c);
2440 #if 0 // FIXME: No API for custom types
2441 void tst_QJSEngine::valueConversion_customType()
2445 // a type that we don't have built-in conversion of
2446 // (it's stored as a variant)
2447 QTime tm(1, 2, 3, 4);
2448 QScriptValue val = eng.toScriptValue(tm);
2449 QCOMPARE(eng.fromScriptValue<QTime>(val), tm);
2456 QScriptValue fooVal = eng.toScriptValue(foo);
2457 QCOMPARE(fooVal.isVariant(), true);
2459 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
2460 QCOMPARE(foo2.x, foo.x);
2461 QCOMPARE(foo2.y, foo.y);
2464 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValue, fooFromScriptValue);
2470 QScriptValue fooVal = eng.toScriptValue(foo);
2471 QCOMPARE(fooVal.isObject(), true);
2472 QVERIFY(fooVal.prototype().strictlyEquals(eng.evaluate("Object.prototype")));
2473 QCOMPARE(fooVal.property("x").strictlyEquals(QScriptValue(&eng, 12)), true);
2474 QCOMPARE(fooVal.property("y").strictlyEquals(QScriptValue(&eng, 34)), true);
2475 fooVal.setProperty("x", QScriptValue(&eng, 56));
2476 fooVal.setProperty("y", QScriptValue(&eng, 78));
2478 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
2479 QCOMPARE(foo2.x, 56);
2480 QCOMPARE(foo2.y, 78);
2482 QScriptValue fooProto = eng.newObject();
2483 eng.setDefaultPrototype(qMetaTypeId<Foo>(), fooProto);
2484 QScriptValue fooVal2 = eng.toScriptValue(foo2);
2485 QVERIFY(fooVal2.prototype().strictlyEquals(fooProto));
2486 QVERIFY(fooVal2.property("x").strictlyEquals(QScriptValue(&eng, 56)));
2487 QVERIFY(fooVal2.property("y").strictlyEquals(QScriptValue(&eng, 78)));
2491 void tst_QJSEngine::valueConversion_sequence()
2494 qScriptRegisterSequenceMetaType<QLinkedList<QString> >(&eng);
2497 QLinkedList<QString> lst;
2498 lst << QLatin1String("foo") << QLatin1String("bar");
2499 QScriptValue lstVal = eng.toScriptValue(lst);
2500 QCOMPARE(lstVal.isArray(), true);
2501 QCOMPARE(lstVal.property("length").toInt(), 2);
2502 QCOMPARE(lstVal.property("0").isString(), true);
2503 QCOMPARE(lstVal.property("0").toString(), QLatin1String("foo"));
2504 QCOMPARE(lstVal.property("1").isString(), true);
2505 QCOMPARE(lstVal.property("1").toString(), QLatin1String("bar"));
2508 qScriptRegisterSequenceMetaType<QList<Foo> >(&eng);
2509 qScriptRegisterSequenceMetaType<QStack<int> >(&eng);
2510 qScriptRegisterSequenceMetaType<QVector<QChar> >(&eng);
2511 qScriptRegisterSequenceMetaType<QQueue<char> >(&eng);
2512 qScriptRegisterSequenceMetaType<QLinkedList<QStack<int> > >(&eng);
2515 QLinkedList<QStack<int> > lst;
2516 QStack<int> first; first << 13 << 49; lst << first;
2517 QStack<int> second; second << 99999;lst << second;
2518 QScriptValue lstVal = eng.toScriptValue(lst);
2519 QCOMPARE(lstVal.isArray(), true);
2520 QCOMPARE(lstVal.property("length").toInt(), 2);
2521 QCOMPARE(lstVal.property("0").isArray(), true);
2522 QCOMPARE(lstVal.property("0").property("length").toInt(), 2);
2523 QCOMPARE(lstVal.property("0").property("0").toInt(), first.at(0));
2524 QCOMPARE(lstVal.property("0").property("1").toInt(), first.at(1));
2525 QCOMPARE(lstVal.property("1").isArray(), true);
2526 QCOMPARE(lstVal.property("1").property("length").toInt(), 1);
2527 QCOMPARE(lstVal.property("1").property("0").toInt(), second.at(0));
2528 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("0")), first);
2529 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("1")), second);
2530 QCOMPARE(qscriptvalue_cast<QLinkedList<QStack<int> > >(lstVal), lst);
2537 QScriptValue v = eng.toScriptValue(&foo);
2538 Foo *pfoo = qscriptvalue_cast<Foo*>(v);
2539 QCOMPARE(pfoo, &foo);
2543 QScriptValue v = eng.toScriptValue(pfoo);
2544 QCOMPARE(v.isNull(), true);
2545 QVERIFY(qscriptvalue_cast<Foo*>(v) == 0);
2549 // QList<int> and QObjectList should be converted from/to arrays by default
2553 QScriptValue val = eng.toScriptValue(lst);
2554 QVERIFY(val.isArray());
2555 QCOMPARE(val.property("length").toInt(), lst.size());
2556 QCOMPARE(val.property(0).toInt(), lst.at(0));
2557 QCOMPARE(val.property(1).toInt(), lst.at(1));
2558 QCOMPARE(val.property(2).toInt(), lst.at(2));
2560 QCOMPARE(qscriptvalue_cast<QList<int> >(val), lst);
2565 QScriptValue val = eng.toScriptValue(lst);
2566 QVERIFY(val.isArray());
2567 QCOMPARE(val.property("length").toInt(), lst.size());
2568 QCOMPARE(val.property(0).toQObject(), (QObject *)this);
2570 QCOMPARE(qscriptvalue_cast<QObjectList>(val), lst);
2575 void tst_QJSEngine::valueConversion_QVariant()
2578 // qScriptValueFromValue() should be "smart" when the argument is a QVariant
2580 QJSValue val = eng.toScriptValue(QVariant());
2581 QVERIFY(!val.isVariant());
2582 QVERIFY(val.isUndefined());
2584 // Checking nested QVariants
2587 QVariant tmp2(QMetaType::QVariant, &tmp1);
2588 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2590 QJSValue val1 = eng.toScriptValue(tmp1);
2591 QJSValue val2 = eng.toScriptValue(tmp2);
2592 QVERIFY(val1.isValid());
2593 QVERIFY(val2.isValid());
2594 QVERIFY(val1.isUndefined());
2595 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2596 QVERIFY(!val2.isUndefined());
2597 QVERIFY(!val1.isVariant());
2598 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2599 QVERIFY(val2.isVariant());
2603 QVariant tmp2(QMetaType::QVariant, &tmp1);
2604 QVariant tmp3(QMetaType::QVariant, &tmp2);
2605 QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int);
2606 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2607 QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant);
2609 QJSValue val1 = eng.toScriptValue(tmp2);
2610 QJSValue val2 = eng.toScriptValue(tmp3);
2611 QVERIFY(val1.isValid());
2612 QVERIFY(val2.isValid());
2613 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2614 QVERIFY(val1.isVariant());
2615 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2616 QVERIFY(val2.isVariant());
2617 QVERIFY(val1.toVariant().toInt() == 123);
2618 QVERIFY(eng.toScriptValue(val2.toVariant()).toVariant().toInt() == 123);
2621 QJSValue val = eng.toScriptValue(QVariant(true));
2622 QVERIFY(!val.isVariant());
2623 QVERIFY(val.isBool());
2624 QCOMPARE(val.toBool(), true);
2627 QJSValue val = eng.toScriptValue(QVariant(int(123)));
2628 QVERIFY(!val.isVariant());
2629 QVERIFY(val.isNumber());
2630 QCOMPARE(val.toNumber(), qreal(123));
2633 QJSValue val = eng.toScriptValue(QVariant(qreal(1.25)));
2634 QVERIFY(!val.isVariant());
2635 QVERIFY(val.isNumber());
2636 QCOMPARE(val.toNumber(), qreal(1.25));
2639 QString str = QString::fromLatin1("ciao");
2640 QJSValue val = eng.toScriptValue(QVariant(str));
2641 QVERIFY(!val.isVariant());
2642 QVERIFY(val.isString());
2643 QCOMPARE(val.toString(), str);
2646 QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
2647 QVERIFY(!val.isVariant());
2648 QVERIFY(val.isQObject());
2649 QCOMPARE(val.toQObject(), (QObject*)this);
2652 QVariant var = qVariantFromValue(QPoint(123, 456));
2653 QJSValue val = eng.toScriptValue(var);
2654 QVERIFY(val.isVariant());
2655 QCOMPARE(val.toVariant(), var);
2658 QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123));
2661 #if 0 // FIXME: No support for custom types
2662 void tst_QJSEngine::valueConversion_hooliganTask248802()
2665 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValueV2, fooFromScriptValueV2);
2667 QScriptValue num(&eng, 123);
2668 Foo foo = eng.fromScriptValue<Foo>(num);
2669 QCOMPARE(foo.x, 123);
2672 QScriptValue num(123);
2673 Foo foo = eng.fromScriptValue<Foo>(num);
2674 QCOMPARE(foo.x, -1);
2677 QScriptValue str(&eng, QLatin1String("123"));
2678 Foo foo = eng.fromScriptValue<Foo>(str);
2679 QCOMPARE(foo.x, 123);
2685 void tst_QJSEngine::valueConversion_basic2()
2688 // more built-in types
2690 QJSValue val = eng.toScriptValue(uint(123));
2691 QVERIFY(val.isNumber());
2692 QCOMPARE(val.toInt(), 123);
2695 QJSValue val = eng.toScriptValue(qulonglong(123));
2696 QVERIFY(val.isNumber());
2697 QCOMPARE(val.toInt(), 123);
2700 QJSValue val = eng.toScriptValue(float(123));
2701 QVERIFY(val.isNumber());
2702 QCOMPARE(val.toInt(), 123);
2705 QJSValue val = eng.toScriptValue(short(123));
2706 QVERIFY(val.isNumber());
2707 QCOMPARE(val.toInt(), 123);
2710 QJSValue val = eng.toScriptValue(ushort(123));
2711 QVERIFY(val.isNumber());
2712 QCOMPARE(val.toInt(), 123);
2715 QJSValue val = eng.toScriptValue(char(123));
2716 QVERIFY(val.isNumber());
2717 QCOMPARE(val.toInt(), 123);
2720 QJSValue val = eng.toScriptValue(uchar(123));
2721 QVERIFY(val.isNumber());
2722 QCOMPARE(val.toInt(), 123);
2726 void tst_QJSEngine::valueConversion_dateTime()
2730 QDateTime in = QDateTime::currentDateTime();
2731 QJSValue val = eng.toScriptValue(in);
2732 QVERIFY(val.isDate());
2733 QCOMPARE(val.toDateTime(), in);
2736 QDate in = QDate::currentDate();
2737 QJSValue val = eng.toScriptValue(in);
2738 QVERIFY(val.isDate());
2739 QCOMPARE(val.toDateTime().date(), in);
2743 void tst_QJSEngine::valueConversion_regExp()
2747 QRegExp in = QRegExp("foo");
2748 QJSValue val = eng.toScriptValue(in);
2749 QVERIFY(val.isRegExp());
2750 QRegExp out = val.toRegExp();
2751 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue);
2752 QCOMPARE(out.patternSyntax(), in.patternSyntax());
2753 QCOMPARE(out.pattern(), in.pattern());
2754 QCOMPARE(out.caseSensitivity(), in.caseSensitivity());
2755 QCOMPARE(out.isMinimal(), in.isMinimal());
2758 QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2);
2759 QJSValue val = eng.toScriptValue(in);
2760 QVERIFY(val.isRegExp());
2761 QCOMPARE(val.toRegExp(), in);
2764 QRegExp in = QRegExp("foo");
2765 in.setMinimal(true);
2766 QJSValue val = eng.toScriptValue(in);
2767 QVERIFY(val.isRegExp());
2768 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue);
2769 QCOMPARE(val.toRegExp().isMinimal(), in.isMinimal());
2773 #if 0 // FIXME: No qScriptValueFromValue
2774 void tst_QJSEngine::qScriptValueFromValue_noEngine()
2776 QVERIFY(!qScriptValueFromValue(0, 123).isValid());
2777 QVERIFY(!qScriptValueFromValue(0, QVariant(123)).isValid());
2781 #if 0 // ###FIXME: No QScriptContext
2782 static QScriptValue __import__(QScriptContext *ctx, QScriptEngine *eng)
2784 return eng->importExtension(ctx->argument(0).toString());
2787 void tst_QJSEngine::importExtension()
2789 QStringList libPaths = QCoreApplication::instance()->libraryPaths();
2790 QCoreApplication::instance()->setLibraryPaths(QStringList() << SRCDIR);
2792 QStringList availableExtensions;
2795 QVERIFY(eng.importedExtensions().isEmpty());
2796 QStringList ret = eng.availableExtensions();
2797 QCOMPARE(ret.size(), 4);
2798 QCOMPARE(ret.at(0), QString::fromLatin1("com"));
2799 QCOMPARE(ret.at(1), QString::fromLatin1("com.trolltech"));
2800 QCOMPARE(ret.at(2), QString::fromLatin1("com.trolltech.recursive"));
2801 QCOMPARE(ret.at(3), QString::fromLatin1("com.trolltech.syntaxerror"));
2802 availableExtensions = ret;
2805 // try to import something that doesn't exist
2808 QScriptValue ret = eng.importExtension("this.extension.does.not.exist");
2809 QCOMPARE(eng.hasUncaughtException(), true);
2810 QCOMPARE(ret.isError(), true);
2811 QCOMPARE(ret.toString(), QString::fromLatin1("Error: Unable to import this.extension.does.not.exist: no such extension"));
2816 for (int x = 0; x < 2; ++x) {
2817 QCOMPARE(eng.globalObject().property("com").isValid(), x == 1);
2818 QScriptValue ret = eng.importExtension("com.trolltech");
2819 QCOMPARE(eng.hasUncaughtException(), false);
2820 QCOMPARE(ret.isUndefined(), true);
2822 QScriptValue com = eng.globalObject().property("com");
2823 QCOMPARE(com.isObject(), true);
2824 QCOMPARE(com.property("wasDefinedAlready")
2825 .strictlyEquals(QScriptValue(&eng, false)), true);
2826 QCOMPARE(com.property("name")
2827 .strictlyEquals(QScriptValue(&eng, "com")), true);
2828 QCOMPARE(com.property("level")
2829 .strictlyEquals(QScriptValue(&eng, 1)), true);
2830 QVERIFY(com.property("originalPostInit").isUndefined());
2831 QVERIFY(com.property("postInitCallCount").strictlyEquals(1));
2833 QScriptValue trolltech = com.property("trolltech");
2834 QCOMPARE(trolltech.isObject(), true);
2835 QCOMPARE(trolltech.property("wasDefinedAlready")
2836 .strictlyEquals(QScriptValue(&eng, false)), true);
2837 QCOMPARE(trolltech.property("name")
2838 .strictlyEquals(QScriptValue(&eng, "com.trolltech")), true);
2839 QCOMPARE(trolltech.property("level")
2840 .strictlyEquals(QScriptValue(&eng, 2)), true);
2841 QVERIFY(trolltech.property("originalPostInit").isUndefined());
2842 QVERIFY(trolltech.property("postInitCallCount").strictlyEquals(1));
2844 QStringList imp = eng.importedExtensions();
2845 QCOMPARE(imp.size(), 2);
2846 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2847 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2848 QCOMPARE(eng.availableExtensions(), availableExtensions);
2851 // recursive import should throw an error
2854 QVERIFY(eng.importedExtensions().isEmpty());
2855 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2856 QScriptValue ret = eng.importExtension("com.trolltech.recursive");
2857 QCOMPARE(eng.hasUncaughtException(), true);
2858 QVERIFY(ret.isError());
2859 QCOMPARE(ret.toString(), QString::fromLatin1("Error: recursive import of com.trolltech.recursive"));
2860 QStringList imp = eng.importedExtensions();
2861 QCOMPARE(imp.size(), 2);
2862 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2863 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2864 QCOMPARE(eng.availableExtensions(), availableExtensions);
2869 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2870 for (int x = 0; x < 2; ++x) {
2872 QVERIFY(eng.importedExtensions().isEmpty());
2873 QScriptValue ret = eng.importExtension("com.trolltech.syntaxerror");
2874 QVERIFY(eng.hasUncaughtException());
2875 QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue);
2876 QCOMPARE(eng.uncaughtExceptionLineNumber(), 4);
2877 QVERIFY(ret.isError());
2878 QVERIFY(ret.toString().contains(QLatin1String("SyntaxError")));
2880 QStringList imp = eng.importedExtensions();
2881 QCOMPARE(imp.size(), 2);
2882 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2883 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2884 QCOMPARE(eng.availableExtensions(), availableExtensions);
2887 QCoreApplication::instance()->setLibraryPaths(libPaths);
2890 static QScriptValue recurse(QScriptContext *ctx, QScriptEngine *eng)
2893 return ctx->callee().call();
2896 static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng)
2899 return ctx->callee().construct();
2902 void tst_QJSEngine::infiniteRecursion()
2904 // Infinite recursion in JS should cause the VM to throw an error
2905 // when the JS stack is exhausted.
2906 // The exact error is back-end specific and subject to change.
2907 const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded");
2910 QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();");
2911 QCOMPARE(ret.isError(), true);
2912 QVERIFY(ret.toString().startsWith(stackOverflowError));
2914 #if 0 //The native C++ stack overflow before the JS stack
2916 QScriptValue fun = eng.newFunction(recurse);
2917 QScriptValue ret = fun.call();
2918 QCOMPARE(ret.isError(), true);
2919 QCOMPARE(ret.toString(), stackOverflowError);
2922 QScriptValue fun = eng.newFunction(recurse2);
2923 QScriptValue ret = fun.construct();
2924 QCOMPARE(ret.isError(), true);
2925 QCOMPARE(ret.toString(), stackOverflowError);
2935 struct Baz : public Bar {
2939 Q_DECLARE_METATYPE(Bar*)
2940 Q_DECLARE_METATYPE(Baz*)
2942 Q_DECLARE_METATYPE(QGradient)
2943 Q_DECLARE_METATYPE(QGradient*)
2944 Q_DECLARE_METATYPE(QLinearGradient)
2946 #if 0 // FIXME: No support for default prototypes
2947 class Zoo : public QObject
2953 Baz *toBaz(Bar *b) { return reinterpret_cast<Baz*>(b); }
2956 void tst_QJSEngine::castWithPrototypeChain()
2961 QScriptValue barProto = eng.toScriptValue(&bar);
2962 QScriptValue bazProto = eng.toScriptValue(&baz);
2963 eng.setDefaultPrototype(qMetaTypeId<Bar*>(), barProto);
2964 eng.setDefaultPrototype(qMetaTypeId<Baz*>(), bazProto);
2969 QScriptValue baz2Value = eng.toScriptValue(&baz2);
2971 // qscriptvalue_cast() does magic; if the QScriptValue contains
2972 // t of type T, and the target type is T*, &t is returned.
2973 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2975 QCOMPARE(pbaz->b, baz2.b);
2978 QScriptValue scriptZoo = eng.newQObject(&zoo);
2979 QScriptValue toBaz = scriptZoo.property("toBaz");
2980 QVERIFY(toBaz.isFunction());
2982 // no relation between Bar and Baz's proto --> casting fails
2984 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2989 QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value);
2990 QVERIFY(ret.isError());
2991 QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to toBaz(); candidates were\n toBaz(Bar*)"));
2994 // establish chain -- now casting should work
2995 // Why? because qscriptvalue_cast() does magic again.
2996 // It the instance itself is not of type T, qscriptvalue_cast()
2997 // searches the prototype chain for T, and if it finds one, it infers
2998 // that the instance can also be casted to that type. This cast is
2999 // _not_ safe and thus relies on the developer doing the right thing.
3000 // This is an undocumented feature to enable qscriptvalue_cast() to
3001 // be used by prototype functions to cast the JS this-object to C++.
3002 bazProto.setPrototype(barProto);
3005 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3007 QCOMPARE(pbar->a, baz2.a);
3011 QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value);
3012 QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue);
3013 QVERIFY(!ret.isError());
3014 QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue);
3015 QCOMPARE(qscriptvalue_cast<Baz*>(ret), pbaz);
3019 bazProto.setPrototype(barProto.prototype()); // kill chain
3021 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
3023 // should not work anymore
3024 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3028 bazProto.setPrototype(eng.newQObject(this));
3030 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
3032 // should not work now either
3033 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3038 QScriptValue b = eng.toScriptValue(QBrush());
3039 b.setPrototype(barProto);
3040 // this shows that a "wrong" cast is possible, if you
3041 // don't play by the rules (the pointer is actually a QBrush*)...
3042 Bar *pbar = qscriptvalue_cast<Bar*>(b);
3047 QScriptValue gradientProto = eng.toScriptValue(QGradient());
3048 QScriptValue linearGradientProto = eng.toScriptValue(QLinearGradient());
3049 linearGradientProto.setPrototype(gradientProto);
3050 QLinearGradient lg(10, 20, 30, 40);
3051 QScriptValue linearGradient = eng.toScriptValue(lg);
3053 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
3054 QVERIFY(pgrad == 0);
3056 linearGradient.setPrototype(linearGradientProto);
3058 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
3059 QVERIFY(pgrad != 0);
3060 QCOMPARE(pgrad->type(), QGradient::LinearGradient);
3061 QLinearGradient *plingrad = static_cast<QLinearGradient*>(pgrad);
3062 QCOMPARE(plingrad->start(), lg.start());
3063 QCOMPARE(plingrad->finalStop(), lg.finalStop());
3069 class Klazz : public QWidget,
3070 public QStandardItem,
3071 public QGraphicsItem
3073 Q_INTERFACES(QGraphicsItem)
3076 Klazz(QWidget *parent = 0) : QWidget(parent) { }
3077 virtual QRectF boundingRect() const { return QRectF(); }
3078 virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { }
3081 Q_DECLARE_METATYPE(Klazz*)
3082 Q_DECLARE_METATYPE(QStandardItem*)
3084 void tst_QJSEngine::castWithMultipleInheritance()
3088 QJSValue v = eng.newQObject(&klz);
3090 QCOMPARE(qjsvalue_cast<Klazz*>(v), &klz);
3091 QCOMPARE(qjsvalue_cast<QWidget*>(v), (QWidget *)&klz);
3092 QCOMPARE(qjsvalue_cast<QObject*>(v), (QObject *)&klz);
3093 QCOMPARE(qjsvalue_cast<QStandardItem*>(v), (QStandardItem *)&klz);
3094 QCOMPARE(qjsvalue_cast<QGraphicsItem*>(v), (QGraphicsItem *)&klz);
3097 void tst_QJSEngine::collectGarbage()
3100 eng.evaluate("a = new Object(); a = new Object(); a = new Object()");
3101 QJSValue a = eng.newObject();
3102 a = eng.newObject();
3103 a = eng.newObject();
3104 QPointer<QObject> ptr = new QObject();
3106 (void)eng.newQObject(ptr);
3107 collectGarbage_helper(eng);
3109 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
3113 #if 0 // ###FIXME: no reportAdditionalMemoryCost API
3114 void tst_QJSEngine::reportAdditionalMemoryCost()
3117 // There isn't any reliable way to test whether calling
3118 // this function affects garbage collection responsiveness;
3119 // the best we can do is call it with a few different values.
3120 for (int x = 0; x < 100; ++x) {
3121 eng.reportAdditionalMemoryCost(0);
3122 eng.reportAdditionalMemoryCost(10);
3123 eng.reportAdditionalMemoryCost(1000);
3124 eng.reportAdditionalMemoryCost(10000);
3125 eng.reportAdditionalMemoryCost(100000);
3126 eng.reportAdditionalMemoryCost(1000000);
3127 eng.reportAdditionalMemoryCost(10000000);
3128 eng.reportAdditionalMemoryCost(-1);
3129 eng.reportAdditionalMemoryCost(-1000);
3130 QScriptValue obj = eng.newObject();
3131 eng.collectGarbage();
3136 void tst_QJSEngine::gcWithNestedDataStructure()
3138 // The GC must be able to traverse deeply nested objects, otherwise this
3139 // test would crash.
3142 "function makeList(size)"
3146 " for (var i = 0; i < size; ++i) {"
3147 " l.data = i + \"\";"
3148 " l.next = { }; l = l.next;"
3153 QCOMPARE(eng.hasUncaughtException(), false);
3154 const int size = 200;
3155 QJSValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size));
3156 QCOMPARE(eng.hasUncaughtException(), false);
3157 for (int x = 0; x < 2; ++x) {
3159 eng.evaluate("gc()");
3161 // Make sure all the nodes are still alive.
3162 for (int i = 0; i < 200; ++i) {
3163 QCOMPARE(l.property("data").toString(), QString::number(i));
3164 l = l.property("next");
3169 #if 0 // ###FIXME: No processEvents handling
3170 class EventReceiver : public QObject
3177 bool event(QEvent *e) {
3178 received |= (e->type() == QEvent::User + 1);
3179 return QObject::event(e);
3185 void tst_QJSEngine::processEventsWhileRunning()
3187 for (int x = 0; x < 2; ++x) {
3192 // This is running for a silly amount of time just to make sure
3193 // the script doesn't finish before event processing is triggered.
3194 QString script = QString::fromLatin1(
3195 "var end = Number(new Date()) + 2000;"
3197 "while (Number(new Date()) < end) {"
3201 EventReceiver receiver;
3202 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3204 eng.evaluate(script);
3205 QVERIFY(!eng.hasUncaughtException());
3206 QVERIFY(!receiver.received);
3208 QCOMPARE(eng.processEventsInterval(), -1);
3209 eng.setProcessEventsInterval(100);
3210 eng.evaluate(script);
3211 QVERIFY(!eng.hasUncaughtException());
3212 QVERIFY(receiver.received);
3219 void tst_QJSEngine::processEventsWhileRunning_function()
3222 QScriptValue script = eng.evaluate(QString::fromLatin1(
3223 "(function() { var end = Number(new Date()) + 2000;"
3225 "while (Number(new Date()) < end) {"
3229 eng.setProcessEventsInterval(100);
3231 for (int x = 0; x < 2; ++x) {
3232 EventReceiver receiver;
3233 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3234 QVERIFY(!eng.hasUncaughtException());
3235 QVERIFY(!receiver.received);
3236 QCOMPARE(eng.processEventsInterval(), 100);
3238 if (x) script.call();
3239 else script.construct();
3241 QVERIFY(!eng.hasUncaughtException());
3242 QVERIFY(receiver.received);
3247 class EventReceiver2 : public QObject
3250 EventReceiver2(QScriptEngine *eng) {
3254 bool event(QEvent *e) {
3255 if (e->type() == QEvent::User + 1) {
3256 engine->currentContext()->throwError("Killed");
3258 return QObject::event(e);
3261 QScriptEngine *engine;
3264 void tst_QJSEngine::throwErrorFromProcessEvents_data()
3266 QTest::addColumn<QString>("script");
3267 QTest::addColumn<QString>("error");
3269 QTest::newRow("while (1)")
3270 << QString::fromLatin1("while (1) { }")
3271 << QString::fromLatin1("Error: Killed");
3272 QTest::newRow("while (1) i++")
3273 << QString::fromLatin1("i = 0; while (1) { i++; }")
3274 << QString::fromLatin1("Error: Killed");
3275 // Unlike abortEvaluation(), scripts should be able to catch the
3277 QTest::newRow("try catch")
3278 << QString::fromLatin1("try {"
3281 " throw new Error('Caught');"
3283 << QString::fromLatin1("Error: Caught");
3286 void tst_QJSEngine::throwErrorFromProcessEvents()
3288 QFETCH(QString, script);
3289 QFETCH(QString, error);
3293 EventReceiver2 receiver(&eng);
3294 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3296 eng.setProcessEventsInterval(100);
3297 QScriptValue ret = eng.evaluate(script);
3298 QVERIFY(ret.isError());
3299 QCOMPARE(ret.toString(), error);
3302 void tst_QJSEngine::disableProcessEventsInterval()
3305 eng.setProcessEventsInterval(100);
3306 QCOMPARE(eng.processEventsInterval(), 100);
3307 eng.setProcessEventsInterval(0);
3308 QCOMPARE(eng.processEventsInterval(), 0);
3309 eng.setProcessEventsInterval(-1);
3310 QCOMPARE(eng.processEventsInterval(), -1);
3311 eng.setProcessEventsInterval(-100);
3312 QCOMPARE(eng.processEventsInterval(), -100);
3317 void tst_QJSEngine::stacktrace()
3319 QString script = QString::fromLatin1(
3320 "function foo(counter) {\n"
3321 " switch (counter) {\n"
3322 " case 0: foo(counter+1); break;\n"
3323 " case 1: foo(counter+1); break;\n"
3324 " case 2: foo(counter+1); break;\n"
3325 " case 3: foo(counter+1); break;\n"
3326 " case 4: foo(counter+1); break;\n"
3328 " throw new Error('blah');\n"
3333 const QString fileName("testfile");
3335 QStringList backtrace;
3336 backtrace << "foo(5)@testfile:9"
3337 << "foo(4)@testfile:7"
3338 << "foo(3)@testfile:6"
3339 << "foo(2)@testfile:5"
3340 << "foo(1)@testfile:4"
3341 << "foo(0)@testfile:3"
3342 << "<global>()@testfile:12";
3345 QJSValue result = eng.evaluate(script, fileName);
3346 QVERIFY(eng.hasUncaughtException());
3347 QVERIFY(result.isError());
3349 // QEXPECT_FAIL("", "QTBUG-6139: uncaughtExceptionBacktrace() doesn't give the full backtrace", Abort);
3350 // ###FIXME: no uncahgutExceptionBacktrace: QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace);
3351 QVERIFY(eng.hasUncaughtException());
3352 QVERIFY(result.strictlyEquals(eng.uncaughtException()));
3354 // FIXME? it is not standard.
3355 //QCOMPARE(result.property("fileName").toString(), fileName);
3356 //QCOMPARE(result.property("lineNumber").toInt(), 9);
3358 QJSValue stack = result.property("stack");
3360 // FIXME? it is not standard.
3361 // QVERIFY(stack.isArray());
3362 //QCOMPARE(stack.property("length").toInt(), 7);
3364 QJSValueIterator it(stack);
3366 while (it.hasNext()) {
3368 QJSValue obj = it.value();
3369 QJSValue frame = obj.property("frame");
3371 QCOMPARE(obj.property("fileName").toString(), fileName);
3373 QJSValue callee = frame.property("arguments").property("callee");
3374 QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo")));
3375 QCOMPARE(obj.property("functionName").toString(), QString("foo"));
3376 int line = obj.property("lineNumber").toInt();
3380 QCOMPARE(line, 3 + counter);
3382 QVERIFY(frame.strictlyEquals(eng.globalObject()));
3383 QVERIFY(obj.property("functionName").toString().isEmpty());
3389 // FIXME? it is not standard.
3391 // QJSValue bt = result.property("backtrace").call(result);
3392 // QCOMPARE(qjsvalue_cast<QStringList>(bt), backtrace);
3395 // throw something that isn't an Error object
3396 eng.clearExceptions();
3397 // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3398 QString script2 = QString::fromLatin1(
3399 "function foo(counter) {\n"
3400 " switch (counter) {\n"
3401 " case 0: foo(counter+1); break;\n"
3402 " case 1: foo(counter+1); break;\n"
3403 " case 2: foo(counter+1); break;\n"
3404 " case 3: foo(counter+1); break;\n"
3405 " case 4: foo(counter+1); break;\n"
3407 " throw 'just a string';\n"
3412 QJSValue result2 = eng.evaluate(script2, fileName);
3413 QVERIFY(eng.hasUncaughtException());
3414 QVERIFY(!result2.isError());
3415 QVERIFY(result2.isString());
3417 // ###FIXME: No uncaughtExceptionBacktrace: QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace);
3418 QVERIFY(eng.hasUncaughtException());
3420 eng.clearExceptions();
3421 QVERIFY(!eng.hasUncaughtException());
3422 // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3425 void tst_QJSEngine::numberParsing_data()
3427 QTest::addColumn<QString>("string");
3428 QTest::addColumn<qreal>("expect");
3430 QTest::newRow("decimal 0") << QString("0") << qreal(0);
3431 QTest::newRow("octal 0") << QString("00") << qreal(00);
3432 QTest::newRow("hex 0") << QString("0x0") << qreal(0x0);
3433 QTest::newRow("decimal 100") << QString("100") << qreal(100);
3434 QTest::newRow("hex 100") << QString("0x100") << qreal(0x100);
3435 QTest::newRow("octal 100") << QString("0100") << qreal(0100);
3436 QTest::newRow("decimal 4G") << QString("4294967296") << qreal(Q_UINT64_C(4294967296));
3437 QTest::newRow("hex 4G") << QString("0x100000000") << qreal(Q_UINT64_C(0x100000000));
3438 QTest::newRow("octal 4G") << QString("040000000000") << qreal(Q_UINT64_C(040000000000));
3439 QTest::newRow("0.5") << QString("0.5") << qreal(0.5);
3440 QTest::newRow("1.5") << QString("1.5") << qreal(1.5);
3441 QTest::newRow("1e2") << QString("1e2") << qreal(100);
3444 void tst_QJSEngine::numberParsing()
3446 QFETCH(QString, string);
3447 QFETCH(qreal, expect);
3450 QJSValue ret = eng.evaluate(string);
3451 QVERIFY(ret.isNumber());
3452 qreal actual = ret.toNumber();
3453 QCOMPARE(actual, expect);
3456 // see ECMA-262, section 7.9
3457 // This is testing ECMA compliance, not our C++ API, but it's important that
3458 // the back-end is conformant in this regard.
3459 void tst_QJSEngine::automaticSemicolonInsertion()
3463 QJSValue ret = eng.evaluate("{ 1 2 } 3");
3464 QVERIFY(ret.isError());
3465 QVERIFY(ret.toString().contains("SyntaxError"));
3468 QJSValue ret = eng.evaluate("{ 1\n2 } 3");
3469 QVERIFY(ret.isNumber());
3470 QCOMPARE(ret.toInt(), 3);
3473 QJSValue ret = eng.evaluate("for (a; b\n)");
3474 QVERIFY(ret.isError());
3475 QVERIFY(ret.toString().contains("SyntaxError"));
3478 QJSValue ret = eng.evaluate("(function() { return\n1 + 2 })()");
3479 QVERIFY(ret.isUndefined());
3482 eng.evaluate("c = 2; b = 1");
3483 QJSValue ret = eng.evaluate("a = b\n++c");
3484 QVERIFY(ret.isNumber());
3485 QCOMPARE(ret.toInt(), 3);
3488 QJSValue ret = eng.evaluate("if (a > b)\nelse c = d");
3489 QVERIFY(ret.isError());
3490 QVERIFY(ret.toString().contains("SyntaxError"));
3493 eng.evaluate("function c() { return { foo: function() { return 5; } } }");
3494 eng.evaluate("b = 1; d = 2; e = 3");
3495 QJSValue ret = eng.evaluate("a = b + c\n(d + e).foo()");
3496 QVERIFY(ret.isNumber());
3497 QCOMPARE(ret.toInt(), 6);
3500 QJSValue ret = eng.evaluate("throw\n1");
3501 QVERIFY(ret.isError());
3502 QVERIFY(ret.toString().contains("SyntaxError"));
3505 QJSValue ret = eng.evaluate("a = Number(1)\n++a");
3506 QVERIFY(ret.isNumber());
3507 QCOMPARE(ret.toInt(), 2);
3510 // "a semicolon is never inserted automatically if the semicolon
3511 // would then be parsed as an empty statement"
3513 eng.evaluate("a = 123");
3514 QJSValue ret = eng.evaluate("if (0)\n ++a; a");
3515 QVERIFY(ret.isNumber());
3516 QCOMPARE(ret.toInt(), 123);
3519 eng.evaluate("a = 123");
3520 QJSValue ret = eng.evaluate("if (0)\n --a; a");
3521 QVERIFY(ret.isNumber());
3522 QCOMPARE(ret.toInt(), 123);
3525 eng.evaluate("a = 123");
3526 QJSValue ret = eng.evaluate("if ((0))\n ++a; a");
3527 QVERIFY(ret.isNumber());
3528 QCOMPARE(ret.toInt(), 123);
3531 eng.evaluate("a = 123");
3532 QJSValue ret = eng.evaluate("if ((0))\n --a; a");
3533 QVERIFY(ret.isNumber());
3534 QCOMPARE(ret.toInt(), 123);
3537 eng.evaluate("a = 123");
3538 QJSValue ret = eng.evaluate("if (0\n)\n ++a; a");
3539 QVERIFY(ret.isNumber());
3540 QCOMPARE(ret.toInt(), 123);
3543 eng.evaluate("a = 123");
3544 QJSValue ret = eng.evaluate("if (0\n ++a; a");
3545 QVERIFY(ret.isError());
3548 eng.evaluate("a = 123");
3549 QJSValue ret = eng.evaluate("if (0))\n ++a; a");
3550 QVERIFY(ret.isError());
3553 QJSValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n");
3554 QVERIFY(ret.isNumber());
3555 QCOMPARE(ret.toInt(), 10);
3558 QJSValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n");
3559 QVERIFY(ret.isNumber());
3560 QCOMPARE(ret.toInt(), 20);
3563 QJSValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n");
3564 QVERIFY(ret.isNumber());
3565 QCOMPARE(ret.toInt(), 10);
3568 QJSValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n");
3569 QVERIFY(ret.isNumber());
3570 QCOMPARE(ret.toInt(), 20);
3573 QJSValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n");
3574 QVERIFY(ret.isNumber());
3575 QCOMPARE(ret.toInt(), 10);
3578 QJSValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n");
3579 QVERIFY(ret.isNumber());
3580 QCOMPARE(ret.toInt(), 20);
3583 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n");
3584 QVERIFY(ret.isNumber());
3585 QCOMPARE(ret.toInt(), 3);
3588 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n");
3589 QVERIFY(ret.isNumber());
3590 QCOMPARE(ret.toInt(), 6);
3593 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n");
3594 QVERIFY(ret.isNumber());
3595 QCOMPARE(ret.toInt(), 3);
3598 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n");
3599 QVERIFY(ret.isNumber());
3600 QCOMPARE(ret.toInt(), 6);
3603 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n");
3604 QVERIFY(ret.isNumber());
3605 QCOMPARE(ret.toInt(), 5);
3608 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n");
3609 QVERIFY(ret.isNumber());
3610 QCOMPARE(ret.toInt(), 10);
3613 QJSValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n");
3614 QVERIFY(ret.isNumber());
3615 QCOMPARE(ret.toInt(), 15);
3618 QJSValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n");
3619 QVERIFY(ret.isNumber());
3620 QCOMPARE(ret.toInt(), 10);
3624 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n");
3625 QVERIFY(ret.isNumber());
3626 QCOMPARE(ret.toInt(), 2);
3629 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n");
3630 QVERIFY(ret.isNumber());
3631 QCOMPARE(ret.toInt(), 0);
3635 QJSValue ret = eng.evaluate("if (0)");
3636 QVERIFY(ret.isError());
3639 QJSValue ret = eng.evaluate("while (0)");
3640 QVERIFY(ret.isError());
3643 QJSValue ret = eng.evaluate("for (;;)");
3644 QVERIFY(ret.isError());
3647 QJSValue ret = eng.evaluate("for (p in this)");
3648 QVERIFY(ret.isError());
3651 QJSValue ret = eng.evaluate("with (this)");
3652 QVERIFY(ret.isError());
3655 QJSValue ret = eng.evaluate("do");
3656 QVERIFY(ret.isError());
3660 #if 0 // ###FIXME: no abortEvaluation API
3661 class EventReceiver3 : public QObject
3664 enum AbortionResult {
3671 EventReceiver3(QScriptEngine *eng) {
3676 bool event(QEvent *e) {
3677 if (e->type() == QEvent::User + 1) {
3678 switch (resultType) {
3680 engine->abortEvaluation();
3683 engine->abortEvaluation(QScriptValue(engine, QString::fromLatin1("Aborted")));
3686 engine->abortEvaluation(engine->currentContext()->throwError("AbortedWithError"));
3689 engine->abortEvaluation(QScriptValue(1234));
3692 return QObject::event(e);
3695 AbortionResult resultType;
3696 QScriptEngine *engine;
3699 static QScriptValue myFunctionAbortingEvaluation(QScriptContext *, QScriptEngine *eng)
3701 eng->abortEvaluation();
3702 return eng->nullValue(); // should be ignored
3705 void tst_QJSEngine::abortEvaluation_notEvaluating()
3709 eng.abortEvaluation();
3710 QVERIFY(!eng.hasUncaughtException());
3712 eng.abortEvaluation(123);
3714 QScriptValue ret = eng.evaluate("'ciao'");
3715 QVERIFY(ret.isString());
3716 QCOMPARE(ret.toString(), QString::fromLatin1("ciao"));
3720 void tst_QJSEngine::abortEvaluation_data()
3722 QTest::addColumn<QString>("script");
3724 QTest::newRow("while (1)")
3725 << QString::fromLatin1("while (1) { }");
3726 QTest::newRow("while (1) i++")
3727 << QString::fromLatin1("i = 0; while (1) { i++; }");
3728 QTest::newRow("try catch")
3729 << QString::fromLatin1("try {"
3732 " throw new Error('Caught');"
3736 void tst_QJSEngine::abortEvaluation()
3738 QFETCH(QString, script);
3741 EventReceiver3 receiver(&eng);
3743 eng.setProcessEventsInterval(100);
3744 for (int x = 0; x < 4; ++x) {
3745 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3746 receiver.resultType = EventReceiver3::AbortionResult(x);
3747 QScriptValue ret = eng.evaluate(script);
3748 switch (receiver.resultType) {
3749 case EventReceiver3::None:
3750 QVERIFY(!eng.hasUncaughtException());
3751 QVERIFY(!ret.isValid());
3753 case EventReceiver3::Number:
3754 QVERIFY(!eng.hasUncaughtException());
3755 QVERIFY(ret.isNumber());
3756 QCOMPARE(ret.toInt(), 1234);
3758 case EventReceiver3::String:
3759 QVERIFY(!eng.hasUncaughtException());
3760 QVERIFY(ret.isString());
3761 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3763 case EventReceiver3::Error:
3764 QVERIFY(eng.hasUncaughtException());
3765 QVERIFY(ret.isError());
3766 QCOMPARE(ret.toString(), QString::fromLatin1("Error: AbortedWithError"));
3773 void tst_QJSEngine::abortEvaluation_tryCatch()
3775 QSKIP("It crashes");
3777 EventReceiver3 receiver(&eng);
3778 eng.setProcessEventsInterval(100);
3779 // scripts cannot intercept the abortion with try/catch
3780 for (int y = 0; y < 4; ++y) {
3781 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3782 receiver.resultType = EventReceiver3::AbortionResult(y);
3783 QScriptValue ret = eng.evaluate(QString::fromLatin1(
3786 " (function() { while (1) { } })();\n"
3791 switch (receiver.resultType) {
3792 case EventReceiver3::None:
3793 QVERIFY(!eng.hasUncaughtException());
3794 QVERIFY(!ret.isValid());
3796 case EventReceiver3::Number:
3797 QVERIFY(!eng.hasUncaughtException());
3798 QVERIFY(ret.isNumber());
3799 QCOMPARE(ret.toInt(), 1234);
3801 case EventReceiver3::String:
3802 QVERIFY(!eng.hasUncaughtException());
3803 QVERIFY(ret.isString());
3804 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3806 case EventReceiver3::Error:
3807 QVERIFY(eng.hasUncaughtException());
3808 QVERIFY(ret.isError());
3814 void tst_QJSEngine::abortEvaluation_fromNative()
3817 QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation);
3818 eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun);
3819 QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()");
3820 QVERIFY(!ret.isValid());
3823 class ThreadedEngine : public QThread {
3827 QScriptEngine* m_engine;
3830 m_engine = new QScriptEngine();
3831 m_engine->setGlobalObject(m_engine->newQObject(this));
3832 m_engine->evaluate("while (1) { sleep(); }");
3840 m_engine->abortEvaluation();
3844 void tst_QJSEngine::abortEvaluation_QTBUG9433()
3846 ThreadedEngine engine;
3848 QVERIFY(engine.isRunning());
3850 for (uint i = 0; i < 50; ++i) { // up to ~2500 ms
3851 if (engine.isFinished())
3855 if (!engine.isFinished()) {
3858 QFAIL("abortEvaluation doesn't work");
3864 #if 0 // ###FIXME: no QScriptEngine::isEvaluating
3865 static QScriptValue myFunctionReturningIsEvaluating(QScriptContext *, QScriptEngine *eng)
3867 return QScriptValue(eng, eng->isEvaluating());
3870 class EventReceiver4 : public QObject
3873 EventReceiver4(QScriptEngine *eng) {
3875 wasEvaluating = false;
3878 bool event(QEvent *e) {
3879 if (e->type() == QEvent::User + 1) {
3880 wasEvaluating = engine->isEvaluating();
3882 return QObject::event(e);
3885 QScriptEngine *engine;
3889 void tst_QJSEngine::isEvaluating_notEvaluating()
3893 QVERIFY(!eng.isEvaluating());
3896 QVERIFY(!eng.isEvaluating());
3897 eng.evaluate("123");
3898 QVERIFY(!eng.isEvaluating());
3899 eng.evaluate("0 = 0");
3900 QVERIFY(!eng.isEvaluating());
3903 void tst_QJSEngine::isEvaluating_fromNative()
3906 QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating);
3907 eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun);
3908 QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()");
3909 QVERIFY(ret.isBool());
3910 QVERIFY(ret.toBool());
3912 QVERIFY(ret.isBool());
3913 QVERIFY(ret.toBool());
3914 ret = myFunctionReturningIsEvaluating(eng.currentContext(), &eng);
3915 QVERIFY(ret.isBool());
3916 QVERIFY(!ret.toBool());
3919 void tst_QJSEngine::isEvaluating_fromEvent()
3922 EventReceiver4 receiver(&eng);
3923 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3925 QString script = QString::fromLatin1(
3926 "var end = Number(new Date()) + 1000;"
3928 "while (Number(new Date()) < end) {"
3932 eng.setProcessEventsInterval(100);
3933 eng.evaluate(script);
3934 QVERIFY(receiver.wasEvaluating);
3938 static QtMsgType theMessageType;
3939 static QString theMessage;
3941 static void myMsgHandler(QtMsgType type, const char *msg)
3943 theMessageType = type;
3944 theMessage = QString::fromLatin1(msg);
3948 void tst_QJSEngine::printFunctionWithCustomHandler()
3950 // The built-in print() function passes the output to Qt's message
3951 // handler. By installing a custom message handler, the output can be
3952 // redirected without changing the print() function itself.
3953 // This behavior is not documented.
3955 QtMsgHandler oldHandler = qInstallMsgHandler(myMsgHandler);
3956 QVERIFY(eng.globalObject().property("print").isFunction());
3958 theMessageType = QtSystemMsg;
3959 QVERIFY(theMessage.isEmpty());
3960 QVERIFY(eng.evaluate("print('test')").isUndefined());
3961 QCOMPARE(theMessageType, QtDebugMsg);
3962 QCOMPARE(theMessage, QString::fromLatin1("test"));
3964 theMessageType = QtSystemMsg;
3966 QVERIFY(eng.evaluate("print(3, true, 'little pigs')").isUndefined());
3967 QCOMPARE(theMessageType, QtDebugMsg);
3968 QCOMPARE(theMessage, QString::fromLatin1("3 true little pigs"));
3970 qInstallMsgHandler(oldHandler);
3973 void tst_QJSEngine::printThrowsException()
3975 // If an argument to print() causes an exception to be thrown when
3976 // it's converted to a string, print() should propagate the exception.
3978 QJSValue ret = eng.evaluate("print({ toString: function() { throw 'foo'; } });");
3979 QVERIFY(eng.hasUncaughtException());
3980 QVERIFY(ret.strictlyEquals(QJSValue(&eng, QLatin1String("foo"))));
3984 void tst_QJSEngine::errorConstructors()
3987 QStringList prefixes;
3988 prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI";
3989 for (int x = 0; x < 3; ++x) {
3990 for (int i = 0; i < prefixes.size(); ++i) {
3991 QString name = prefixes.at(i) + QLatin1String("Error");
3992 QString code = QString(i+1, QLatin1Char('\n'));
3994 code += QLatin1String("throw ");
3996 code += QLatin1String("new ");
3997 code += name + QLatin1String("()");
3998 QJSValue ret = eng.evaluate(code);
3999 QVERIFY(ret.isError());
4000 QCOMPARE(eng.hasUncaughtException(), x == 0);
4001 eng.clearExceptions();
4002 QVERIFY(ret.toString().startsWith(name));
4003 //QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown
4004 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
4005 QCOMPARE(ret.property("lineNumber").toInt(), i+2);
4010 void tst_QJSEngine::argumentsProperty_globalContext()
4014 // Unlike function calls, the global context doesn't have an
4015 // arguments property.
4016 QJSValue ret = eng.evaluate("arguments");
4017 QVERIFY(ret.isError());
4018 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4020 eng.evaluate("arguments = 10");
4022 QJSValue ret = eng.evaluate("arguments");
4023 QVERIFY(ret.isNumber());
4024 QCOMPARE(ret.toInt(), 10);
4026 QVERIFY(eng.evaluate("delete arguments").toBool());
4027 QVERIFY(!eng.globalObject().property("arguments").isValid());
4030 void tst_QJSEngine::argumentsProperty_JS()
4034 eng.evaluate("o = { arguments: 123 }");
4035 QJSValue ret = eng.evaluate("with (o) { arguments; }");
4036 QVERIFY(ret.isNumber());
4037 QCOMPARE(ret.toInt(), 123);
4041 QVERIFY(!eng.globalObject().property("arguments").isValid());
4042 // This is testing ECMA-262 compliance. In function calls, "arguments"
4043 // appears like a local variable, and it can be replaced.
4044 QJSValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()");
4045 QVERIFY(ret.isNumber());
4046 QCOMPARE(ret.toInt(), 456);
4047 QVERIFY(!eng.globalObject().property("arguments").isValid());
4051 #if 0 // ###FIXME: no QScriptContext API
4052 static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng)
4054 // Since evaluate() is done in the current context, "arguments" should
4055 // refer to currentContext()->argumentsObject().
4056 // This is for consistency with the built-in JS eval() function.
4057 eng->evaluate("var a = arguments[0];");
4058 eng->evaluate("arguments[0] = 200;");
4059 return eng->evaluate("a + arguments[0]");
4062 void tst_QJSEngine::argumentsProperty_evaluateInNativeFunction()
4065 QScriptValue fun = eng.newFunction(argumentsProperty_fun);
4066 eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun));
4067 QScriptValue result = eng.evaluate("fun(18)");
4068 QVERIFY(result.isNumber());
4069 QCOMPARE(result.toInt(), 200+18);
4073 void tst_QJSEngine::jsNumberClass()
4075 // See ECMA-262 Section 15.7, "Number Objects".
4079 QJSValue ctor = eng.globalObject().property("Number");
4080 QVERIFY(ctor.property("length").isNumber());
4081 QCOMPARE(ctor.property("length").toNumber(), qreal(1));
4082 QJSValue proto = ctor.property("prototype");
4083 QVERIFY(proto.isObject());
4085 QJSValue::PropertyFlags flags = QJSValue::SkipInEnumeration
4086 | QJSValue::Undeletable
4087 | QJSValue::ReadOnly;
4088 QCOMPARE(ctor.propertyFlags("prototype"), flags);
4089 QVERIFY(ctor.property("MAX_VALUE").isNumber());
4090 QCOMPARE(ctor.propertyFlags("MAX_VALUE"), flags);
4091 QVERIFY(ctor.property("MIN_VALUE").isNumber());
4092 QCOMPARE(ctor.propertyFlags("MIN_VALUE"), flags);
4093 QVERIFY(ctor.property("NaN").isNumber());
4094 QCOMPARE(ctor.propertyFlags("NaN"), flags);
4095 QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
4096 QCOMPARE(ctor.propertyFlags("NEGATIVE_INFINITY"), flags);
4097 QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
4098 QCOMPARE(ctor.propertyFlags("POSITIVE_INFINITY"), flags);
4100 QVERIFY(proto.instanceOf(eng.globalObject().property("Object")));
4101 QCOMPARE(proto.toNumber(), qreal(0));
4102 QVERIFY(proto.property("constructor").strictlyEquals(ctor));
4105 QJSValue ret = eng.evaluate("Number()");
4106 QVERIFY(ret.isNumber());
4107 QCOMPARE(ret.toNumber(), qreal(0));
4110 QJSValue ret = eng.evaluate("Number(123)");
4111 QVERIFY(ret.isNumber());
4112 QCOMPARE(ret.toNumber(), qreal(123));
4115 QJSValue ret = eng.evaluate("Number('456')");
4116 QVERIFY(ret.isNumber());
4117 QCOMPARE(ret.toNumber(), qreal(456));
4120 QJSValue ret = eng.evaluate("new Number()");
4121 QVERIFY(!ret.isNumber());
4122 QVERIFY(ret.isObject());
4123 QCOMPARE(ret.toNumber(), qreal(0));
4126 QJSValue ret = eng.evaluate("new Number(123)");
4127 QVERIFY(!ret.isNumber());
4128 QVERIFY(ret.isObject());
4129 QCOMPARE(ret.toNumber(), qreal(123));
4132 QJSValue ret = eng.evaluate("new Number('456')");
4133 QVERIFY(!ret.isNumber());
4134 QVERIFY(ret.isObject());
4135 QCOMPARE(ret.toNumber(), qreal(456));
4138 QVERIFY(proto.property("toString").isFunction());
4140 QJSValue ret = eng.evaluate("new Number(123).toString()");
4141 QVERIFY(ret.isString());
4142 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4145 QJSValue ret = eng.evaluate("new Number(123).toString(8)");
4146 QVERIFY(ret.isString());
4147 QCOMPARE(ret.toString(), QString::fromLatin1("173"));
4150 QJSValue ret = eng.evaluate("new Number(123).toString(16)");
4151 QVERIFY(ret.isString());
4152 QCOMPARE(ret.toString(), QString::fromLatin1("7b"));
4154 QVERIFY(proto.property("toLocaleString").isFunction());
4156 QJSValue ret = eng.evaluate("new Number(123).toLocaleString()");
4157 QVERIFY(ret.isString());
4158 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4160 QVERIFY(proto.property("valueOf").isFunction());
4162 QJSValue ret = eng.evaluate("new Number(123).valueOf()");
4163 QVERIFY(ret.isNumber());
4164 QCOMPARE(ret.toNumber(), qreal(123));
4166 QVERIFY(proto.property("toExponential").isFunction());
4168 QJSValue ret = eng.evaluate("new Number(123).toExponential()");
4169 QVERIFY(ret.isString());
4170 QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2"));
4172 QVERIFY(proto.property("toFixed").isFunction());
4174 QJSValue ret = eng.evaluate("new Number(123).toFixed()");
4175 QVERIFY(ret.isString());
4176 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4178 QVERIFY(proto.property("toPrecision").isFunction());
4180 QJSValue ret = eng.evaluate("new Number(123).toPrecision()");
4181 QVERIFY(ret.isString());
4182 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4186 void tst_QJSEngine::jsForInStatement_simple()
4190 QJSValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r");
4191 QStringList lst = qjsvalue_cast<QStringList>(ret);
4192 QVERIFY(lst.isEmpty());
4195 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4196 "for (var p in o) r[r.length] = p; r");
4197 QStringList lst = qjsvalue_cast<QStringList>(ret);
4198 QCOMPARE(lst.size(), 1);
4199 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4202 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
4203 "for (var p in o) r[r.length] = p; r");
4204 QStringList lst = qjsvalue_cast<QStringList>(ret);
4205 QCOMPARE(lst.size(), 2);
4206 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4207 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
4211 void tst_QJSEngine::jsForInStatement_prototypeProperties()
4215 QJSValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];"
4216 "for (var p in o) r[r.length] = p; r");
4217 QStringList lst = qjsvalue_cast<QStringList>(ret);
4218 QCOMPARE(lst.size(), 1);
4219 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4222 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];"
4223 "for (var p in o) r[r.length] = p; r");
4224 QStringList lst = qjsvalue_cast<QStringList>(ret);
4225 QCOMPARE(lst.size(), 2);
4226 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4227 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
4230 // shadowed property
4231 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];"
4232 "for (var p in o) r[r.length] = p; r");
4233 QStringList lst = qjsvalue_cast<QStringList>(ret);
4234 QCOMPARE(lst.size(), 1);
4235 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4240 void tst_QJSEngine::jsForInStatement_mutateWhileIterating()
4243 // deleting property during enumeration
4245 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4246 "for (var p in o) { r[r.length] = p; delete r[p]; } r");
4247 QStringList lst = qjsvalue_cast<QStringList>(ret);
4248 QCOMPARE(lst.size(), 1);
4249 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4252 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
4253 "for (var p in o) { r[r.length] = p; delete o.q; } r");
4254 QStringList lst = qjsvalue_cast<QStringList>(ret);
4255 QCOMPARE(lst.size(), 1);
4256 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4259 // adding property during enumeration
4261 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4262 "for (var p in o) { r[r.length] = p; o.q = 456; } r");
4263 QStringList lst = qjsvalue_cast<QStringList>(ret);
4264 QCOMPARE(lst.size(), 1);
4265 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4270 void tst_QJSEngine::jsForInStatement_arrays()
4274 QJSValue ret = eng.evaluate("a = [123, 456]; r = [];"
4275 "for (var p in a) r[r.length] = p; r");
4276 QStringList lst = qjsvalue_cast<QStringList>(ret);
4277 QCOMPARE(lst.size(), 2);
4278 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4279 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4282 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];"
4283 "for (var p in a) r[r.length] = p; r");
4284 QStringList lst = qjsvalue_cast<QStringList>(ret);
4285 QCOMPARE(lst.size(), 3);
4286 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4287 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4288 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
4291 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';"
4292 "b = [111, 222, 333]; b.bar = 'baz';"
4293 "a.__proto__ = b; r = [];"
4294 "for (var p in a) r[r.length] = p; r");
4295 QStringList lst = qjsvalue_cast<QStringList>(ret);
4296 QCOMPARE(lst.size(), 5);
4297 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4298 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4299 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
4300 QCOMPARE(lst.at(3), QString::fromLatin1("2"));
4301 QCOMPARE(lst.at(4), QString::fromLatin1("bar"));
4305 void tst_QJSEngine::jsForInStatement_nullAndUndefined()
4309 QJSValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r");
4310 QVERIFY(ret.isBool());
4311 QVERIFY(ret.toBool());
4314 QJSValue ret = eng.evaluate("r = true; for (var p in null) r = false; r");
4315 QVERIFY(ret.isBool());
4316 QVERIFY(ret.toBool());
4320 void tst_QJSEngine::jsFunctionDeclarationAsStatement()
4322 // ECMA-262 does not allow function declarations to be used as statements,
4323 // but several popular implementations (including JSC) do. See the NOTE
4324 // at the beginning of chapter 12 in ECMA-262 5th edition, where it's
4325 // recommended that implementations either disallow this usage or issue
4327 // Since we had a bug report long ago about QtScript not supporting this
4328 // "feature" (and thus deviating from other implementations), we still
4329 // check this behavior.
4332 QVERIFY(!eng.globalObject().property("bar").isValid());
4333 eng.evaluate("function foo(arg) {\n"
4334 " if (arg == 'bar')\n"
4335 " function bar() { return 'bar'; }\n"
4337 " function baz() { return 'baz'; }\n"
4338 " return (arg == 'bar') ? bar : baz;\n"
4340 QVERIFY(!eng.globalObject().property("bar").isValid());
4341 QVERIFY(!eng.globalObject().property("baz").isValid());
4342 QVERIFY(eng.evaluate("foo").isFunction());
4344 QJSValue ret = eng.evaluate("foo('bar')");
4345 QVERIFY(ret.isFunction());
4346 QJSValue ret2 = ret.call(QJSValue());
4347 QCOMPARE(ret2.toString(), QString::fromLatin1("bar"));
4348 QVERIFY(!eng.globalObject().property("bar").isValid());
4349 QVERIFY(!eng.globalObject().property("baz").isValid());
4352 QJSValue ret = eng.evaluate("foo('baz')");
4353 QVERIFY(ret.isFunction());
4354 QJSValue ret2 = ret.call(QJSValue());
4355 QCOMPARE(ret2.toString(), QString::fromLatin1("baz"));
4356 QVERIFY(!eng.globalObject().property("bar").isValid());
4357 QVERIFY(!eng.globalObject().property("baz").isValid());
4361 void tst_QJSEngine::stringObjects()
4363 // See ECMA-262 Section 15.5, "String Objects".
4366 QString str("ciao");
4369 QJSValue obj = QJSValue(&eng, str).toObject();
4370 QCOMPARE(obj.property("length").toInt(), str.length());
4371 QCOMPARE(obj.propertyFlags("length"), QJSValue::PropertyFlags(QJSValue::Undeletable | QJSValue::SkipInEnumeration | QJSValue::ReadOnly));
4372 for (int i = 0; i < str.length(); ++i) {
4373 QString pname = QString::number(i);
4374 QVERIFY(obj.property(pname).isString());
4375 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4376 QEXPECT_FAIL("", "FIXME: This is V8 issue 862. ECMA script standard 15.5.5.2 compliance.", Continue);
4377 QCOMPARE(obj.propertyFlags(pname), QJSValue::PropertyFlags(QJSValue::Undeletable | QJSValue::ReadOnly));
4378 obj.setProperty(pname, QJSValue());
4379 obj.setProperty(pname, QJSValue(&eng, 123));
4380 QVERIFY(obj.property(pname).isString());
4381 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4383 QVERIFY(!obj.property("-1").isValid());
4384 QVERIFY(!obj.property(QString::number(str.length())).isValid());
4386 QJSValue val(&eng, 123);
4387 obj.setProperty("-1", val);
4388 QVERIFY(obj.property("-1").strictlyEquals(val));
4389 obj.setProperty("100", val);
4390 QVERIFY(obj.property("100").strictlyEquals(val));
4395 QJSValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r");
4396 QVERIFY(ret.isArray());
4397 QStringList lst = qjsvalue_cast<QStringList>(ret);
4398 QCOMPARE(lst.size(), str.length());
4399 for (int i = 0; i < str.length(); ++i)
4400 QCOMPARE(lst.at(i), QString::number(i));
4402 QJSValue ret2 = eng.evaluate("s[0] = 123; s[0]");
4403 QVERIFY(ret2.isString());
4404 QCOMPARE(ret2.toString().length(), 1);
4405 QCOMPARE(ret2.toString().at(0), str.at(0));
4407 QJSValue ret3 = eng.evaluate("s[-1] = 123; s[-1]");
4408 QVERIFY(ret3.isNumber());
4409 QCOMPARE(ret3.toInt(), 123);
4411 QJSValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]");
4412 QVERIFY(ret4.isNumber());
4413 QCOMPARE(ret4.toInt(), 456);
4415 QJSValue ret5 = eng.evaluate("delete s[0]");
4416 QVERIFY(ret5.isBool());
4417 QEXPECT_FAIL("", "FIXME: This is V8 bug, please report it! ECMA script standard 15.5.5.2", Abort);
4418 QVERIFY(!ret5.toBool());
4420 QJSValue ret6 = eng.evaluate("delete s[-1]");
4421 QVERIFY(ret6.isBool());
4422 QVERIFY(ret6.toBool());
4424 QJSValue ret7 = eng.evaluate("delete s[s.length]");
4425 QVERIFY(ret7.isBool());
4426 QVERIFY(ret7.toBool());
4430 void tst_QJSEngine::jsStringPrototypeReplaceBugs()
4435 QJSValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args");
4436 QVERIFY(ret.isArray());
4437 int len = ret.property("length").toInt();
4439 for (int i = 0; i < len; ++i) {
4440 QJSValue args = ret.property(i);
4441 QCOMPARE(args.property("length").toInt(), 4);
4442 QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string
4443 QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture
4444 QVERIFY(args.property(2).isNumber());
4445 QCOMPARE(args.property(2).toInt(), i*2); // index of match
4446 QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a"));
4451 QJSValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})");
4452 QVERIFY(ret.isString());
4453 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4457 void tst_QJSEngine::getterSetterThisObject_global()
4462 eng.evaluate("__defineGetter__('x', function() { return this; });");
4464 QJSValue ret = eng.evaluate("x");
4465 QVERIFY(ret.equals(eng.globalObject()));
4468 QJSValue ret = eng.evaluate("(function() { return x; })()");
4469 QVERIFY(ret.equals(eng.globalObject()));
4472 QJSValue ret = eng.evaluate("with (this) x");
4473 QVERIFY(ret.equals(eng.globalObject()));
4476 QJSValue ret = eng.evaluate("with ({}) x");
4477 QVERIFY(ret.equals(eng.globalObject()));
4480 QJSValue ret = eng.evaluate("(function() { with ({}) return x; })()");
4481 QVERIFY(ret.equals(eng.globalObject()));
4484 eng.evaluate("__defineSetter__('x', function() { return this; });");
4486 QJSValue ret = eng.evaluate("x = 'foo'");
4487 // SpiderMonkey says setter return value, JSC says RHS.
4488 QVERIFY(ret.isString());
4489 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4492 QJSValue ret = eng.evaluate("(function() { return x = 'foo'; })()");
4493 // SpiderMonkey says setter return value, JSC says RHS.
4494 QVERIFY(ret.isString());
4495 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4498 QJSValue ret = eng.evaluate("with (this) x = 'foo'");
4499 // SpiderMonkey says setter return value, JSC says RHS.
4500 QVERIFY(ret.isString());
4501 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4504 QJSValue ret = eng.evaluate("with ({}) x = 'foo'");
4505 // SpiderMonkey says setter return value, JSC says RHS.
4506 QVERIFY(ret.isString());
4507 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4510 QJSValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()");
4511 // SpiderMonkey says setter return value, JSC says RHS.
4512 QVERIFY(ret.isString());
4513 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4518 void tst_QJSEngine::getterSetterThisObject_plain()
4522 eng.evaluate("o = {}");
4524 eng.evaluate("o.__defineGetter__('x', function() { return this; })");
4525 QVERIFY(eng.evaluate("o.x === o").toBool());
4526 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4527 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
4528 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4530 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4531 // SpiderMonkey says setter return value, JSC says RHS.
4532 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
4533 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4534 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4538 void tst_QJSEngine::getterSetterThisObject_prototypeChain()
4542 eng.evaluate("o = {}; p = {}; o.__proto__ = p");
4544 eng.evaluate("p.__defineGetter__('x', function() { return this; })");
4545 QVERIFY(eng.evaluate("o.x === o").toBool());
4546 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4547 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
4548 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4549 eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o"));
4551 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4552 // SpiderMonkey says setter return value, JSC says RHS.
4553 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
4554 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4555 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4559 #if 0 // ###FIXME: no QScriptContext API
4560 void tst_QJSEngine::getterSetterThisObject_activation()
4564 QScriptContext *ctx = eng.pushContext();
4566 QScriptValue act = ctx->activationObject();
4567 act.setProperty("act", act);
4569 eng.evaluate("act.__defineGetter__('x', function() { return this; })");
4570 QVERIFY(eng.evaluate("x === act").toBool());
4571 QEXPECT_FAIL("", "QTBUG-17605: Not possible to implement local variables as getter/setter properties", Abort);
4572 QVERIFY(!eng.hasUncaughtException());
4573 QVERIFY(eng.evaluate("with (act) x").equals("foo"));
4574 QVERIFY(eng.evaluate("(function() { with (act) return x; })() === act").toBool());
4575 eng.evaluate("q = {}; with (act) with (q) x").equals(eng.evaluate("act"));
4576 eng.evaluate("with (q) with (act) x").equals(eng.evaluate("act"));
4578 eng.evaluate("act.__defineSetter__('x', function() { return this; });");
4579 QVERIFY(eng.evaluate("(x = 'foo') === 'foo'").toBool());
4580 QVERIFY(eng.evaluate("with (act) x = 'foo'").equals("foo"));
4581 QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals("foo"));
4587 void tst_QJSEngine::jsContinueInSwitch()
4589 // This is testing ECMA-262 compliance, not C++ API.
4592 // switch - continue
4594 QJSValue ret = eng.evaluate("switch (1) { default: continue; }");
4595 QVERIFY(ret.isError());
4597 // for - switch - case - continue
4599 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4601 " case 1: ++j; continue;\n"
4602 " case 100: ++j; continue;\n"
4603 " case 1000: ++j; continue;\n"
4606 QVERIFY(ret.isNumber());
4607 QCOMPARE(ret.toInt(), 3);
4609 // for - switch - case - default - continue
4611 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4613 " case 1: ++j; continue;\n"
4614 " case 100: ++j; continue;\n"
4615 " case 1000: ++j; continue;\n"
4616 " default: if (i < 50000) break; else continue;\n"
4619 QVERIFY(ret.isNumber());
4620 QCOMPARE(ret.toInt(), 3);
4622 // switch - for - continue
4624 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
4626 " for (i = 0; i < 100000; ++i) {\n"
4630 QVERIFY(ret.isNumber());
4631 QCOMPARE(ret.toInt(), 100000);
4633 // switch - switch - continue
4635 QJSValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }");
4636 QVERIFY(ret.isError());
4638 // for - switch - switch - continue
4640 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4644 " case 1: ++j; continue;\n"
4648 QVERIFY(ret.isNumber());
4649 QCOMPARE(ret.toInt(), 1);
4651 // switch - for - switch - continue
4653 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
4655 " for (i = 0; i < 100000; ++i) {\n"
4662 QVERIFY(ret.isNumber());
4663 QCOMPARE(ret.toInt(), 100000);
4667 void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty()
4669 // SpiderMonkey has different behavior than JSC and V8; it disallows
4670 // creating a property on the instance if there's a property with the
4671 // same name in the prototype, and that property is read-only. We
4672 // adopted that behavior in the old (4.5) QtScript back-end, but it
4673 // just seems weird -- and non-compliant. Adopt the JSC behavior instead.
4675 QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber());
4676 QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt(), 123);
4677 QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBool());
4680 void tst_QJSEngine::toObject()
4684 QVERIFY(!eng.toObject(eng.undefinedValue()).isValid());
4686 QVERIFY(!eng.toObject(eng.nullValue()).isValid());
4688 QJSValue falskt(false);
4690 QJSValue tmp = eng.toObject(falskt);
4691 QVERIFY(tmp.isObject());
4692 QCOMPARE(tmp.toNumber(), falskt.toNumber());
4694 QVERIFY(falskt.isBool());
4696 QJSValue sant(true);
4698 QJSValue tmp = eng.toObject(sant);
4699 QVERIFY(tmp.isObject());
4700 QCOMPARE(tmp.toNumber(), sant.toNumber());
4702 QVERIFY(sant.isBool());
4704 QJSValue number(123.0);
4706 QJSValue tmp = eng.toObject(number);
4707 QVERIFY(tmp.isObject());
4708 QCOMPARE(tmp.toNumber(), number.toNumber());
4710 QVERIFY(number.isNumber());
4712 QJSValue str = QJSValue(&eng, QString("ciao"));
4714 QJSValue tmp = eng.toObject(str);
4715 QVERIFY(tmp.isObject());
4716 QCOMPARE(tmp.toString(), str.toString());
4718 QVERIFY(str.isString());
4720 QJSValue object = eng.newObject();
4722 QJSValue tmp = eng.toObject(object);
4723 QVERIFY(tmp.isObject());
4724 QVERIFY(tmp.strictlyEquals(object));
4727 QJSValue qobject = eng.newQObject(this);
4728 QVERIFY(eng.toObject(qobject).strictlyEquals(qobject));
4730 QVERIFY(!eng.toObject(QJSValue()).isValid());
4734 QJSValue boolValue(&eng, true);
4736 QJSValue ret = eng.toObject(boolValue);
4737 QVERIFY(ret.isObject());
4738 QCOMPARE(ret.toBool(), boolValue.toBool());
4740 QVERIFY(boolValue.isBool());
4742 QJSValue numberValue(&eng, 123.0);
4744 QJSValue ret = eng.toObject(numberValue);
4745 QVERIFY(ret.isObject());
4746 QCOMPARE(ret.toNumber(), numberValue.toNumber());
4748 QVERIFY(numberValue.isNumber());
4750 QJSValue stringValue(&eng, QString::fromLatin1("foo"));
4752 QJSValue ret = eng.toObject(stringValue);
4753 QVERIFY(ret.isObject());
4754 QCOMPARE(ret.toString(), stringValue.toString());
4756 QVERIFY(stringValue.isString());
4759 void tst_QJSEngine::jsReservedWords_data()
4761 QTest::addColumn<QString>("word");
4762 QTest::newRow("break") << QString("break");
4763 QTest::newRow("case") << QString("case");
4764 QTest::newRow("catch") << QString("catch");
4765 QTest::newRow("continue") << QString("continue");
4766 QTest::newRow("default") << QString("default");
4767 QTest::newRow("delete") << QString("delete");
4768 QTest::newRow("do") << QString("do");
4769 QTest::newRow("else") << QString("else");
4770 QTest::newRow("false") << QString("false");
4771 QTest::newRow("finally") << QString("finally");
4772 QTest::newRow("for") << QString("for");
4773 QTest::newRow("function") << QString("function");
4774 QTest::newRow("if") << QString("if");
4775 QTest::newRow("in") << QString("in");
4776 QTest::newRow("instanceof") << QString("instanceof");
4777 QTest::newRow("new") << QString("new");
4778 QTest::newRow("null") << QString("null");
4779 QTest::newRow("return") << QString("return");
4780 QTest::newRow("switch") << QString("switch");
4781 QTest::newRow("this") << QString("this");
4782 QTest::newRow("throw") << QString("throw");
4783 QTest::newRow("true") << QString("true");
4784 QTest::newRow("try") << QString("try");
4785 QTest::newRow("typeof") << QString("typeof");
4786 QTest::newRow("var") << QString("var");
4787 QTest::newRow("void") << QString("void");
4788 QTest::newRow("while") << QString("while");
4789 QTest::newRow("with") << QString("with");
4792 void tst_QJSEngine::jsReservedWords()
4794 // See ECMA-262 Section 7.6.1, "Reserved Words".
4795 // We prefer that the implementation is less strict than the spec; e.g.
4796 // it's good to allow reserved words as identifiers in object literals,
4797 // and when accessing properties using dot notation.
4799 QFETCH(QString, word);
4802 QJSValue ret = eng.evaluate(word + " = 123");
4803 QVERIFY(ret.isError());
4804 QString str = ret.toString();
4805 QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError"));
4809 QJSValue ret = eng.evaluate("var " + word + " = 123");
4810 QVERIFY(ret.isError());
4811 QVERIFY(ret.toString().startsWith("SyntaxError"));
4815 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4816 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
4817 QVERIFY(!ret.isError());
4818 QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word)));
4822 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
4823 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
4824 QVERIFY(!ret.isError());
4825 QVERIFY(ret.property(word).isNumber());
4828 // SpiderMonkey allows this, but we don't
4830 QJSValue ret = eng.evaluate("function " + word + "() {}");
4831 QVERIFY(ret.isError());
4832 QVERIFY(ret.toString().startsWith("SyntaxError"));
4836 void tst_QJSEngine::jsFutureReservedWords_data()
4838 QTest::addColumn<QString>("word");
4839 QTest::addColumn<bool>("allowed");
4840 QTest::newRow("abstract") << QString("abstract") << true;
4841 QTest::newRow("boolean") << QString("boolean") << true;
4842 QTest::newRow("byte") << QString("byte") << true;
4843 QTest::newRow("char") << QString("char") << true;
4844 QTest::newRow("class") << QString("class") << false;
4845 QTest::newRow("const") << QString("const") << false;
4846 QTest::newRow("debugger") << QString("debugger") << false;
4847 QTest::newRow("double") << QString("double") << true;
4848 QTest::newRow("enum") << QString("enum") << false;
4849 QTest::newRow("export") << QString("export") << false;
4850 QTest::newRow("extends") << QString("extends") << false;
4851 QTest::newRow("final") << QString("final") << true;
4852 QTest::newRow("float") << QString("float") << true;
4853 QTest::newRow("goto") << QString("goto") << true;
4854 QTest::newRow("implements") << QString("implements") << true;
4855 QTest::newRow("import") << QString("import") << false;
4856 QTest::newRow("int") << QString("int") << true;
4857 QTest::newRow("interface") << QString("interface") << true;
4858 QTest::newRow("long") << QString("long") << true;
4859 QTest::newRow("native") << QString("native") << true;
4860 QTest::newRow("package") << QString("package") << true;
4861 QTest::newRow("private") << QString("private") << true;
4862 QTest::newRow("protected") << QString("protected") << true;
4863 QTest::newRow("public") << QString("public") << true;
4864 QTest::newRow("short") << QString("short") << true;
4865 QTest::newRow("static") << QString("static") << true;
4866 QTest::newRow("super") << QString("super") << false;
4867 QTest::newRow("synchronized") << QString("synchronized") << true;
4868 QTest::newRow("throws") << QString("throws") << true;
4869 QTest::newRow("transient") << QString("transient") << true;
4870 QTest::newRow("volatile") << QString("volatile") << true;
4873 void tst_QJSEngine::jsFutureReservedWords()
4876 // See ECMA-262 Section 7.6.1.2, "Future Reserved Words".
4877 // In real-world implementations, most of these words are
4878 // actually allowed as normal identifiers.
4880 QFETCH(QString, word);
4881 QFETCH(bool, allowed);
4884 QJSValue ret = eng.evaluate(word + " = 123");
4885 QCOMPARE(!ret.isError(), allowed);
4889 QJSValue ret = eng.evaluate("var " + word + " = 123");
4890 QCOMPARE(!ret.isError(), allowed);
4893 // this should probably be allowed (see task 162567)
4895 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4896 QCOMPARE(ret.isNumber(), allowed);
4897 QCOMPARE(!ret.isError(), allowed);
4900 // this should probably be allowed (see task 162567)
4902 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
4903 QCOMPARE(!ret.isError(), allowed);
4907 void tst_QJSEngine::jsThrowInsideWithStatement()
4909 // This is testing ECMA-262 compliance, not C++ API.
4914 QJSValue ret = eng.evaluate(
4916 " o = { bad : \"bug\" };"
4923 QVERIFY(ret.isError());
4924 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4927 QJSValue ret = eng.evaluate(
4929 " o = { bad : \"bug\" };"
4936 QVERIFY(ret.isError());
4937 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4940 eng.clearExceptions();
4941 QJSValue ret = eng.evaluate(
4942 "o = { bug : \"no bug\" };"
4950 QVERIFY(ret.isNumber());
4951 QCOMPARE(ret.toInt(), 123);
4952 QVERIFY(eng.hasUncaughtException());
4955 eng.clearExceptions();
4956 QJSValue ret = eng.evaluate(
4957 "o = { bug : \"no bug\" };"
4961 QVERIFY(ret.isNumber());
4962 QJSValue ret2 = eng.evaluate("bug");
4963 QVERIFY(ret2.isError());
4964 QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError")));
4968 #if 0 // ###FIXME: No QScriptEngineAgent API
4969 class TestAgent : public QScriptEngineAgent
4972 TestAgent(QScriptEngine *engine) : QScriptEngineAgent(engine) {}
4975 void tst_QJSEngine::getSetAgent_ownership()
4977 // engine deleted before agent --> agent deleted too
4978 QScriptEngine *eng = new QScriptEngine;
4979 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4980 TestAgent *agent = new TestAgent(eng);
4981 eng->setAgent(agent);
4982 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4983 eng->setAgent(0); // the engine maintains ownership of the old agent
4984 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4988 void tst_QJSEngine::getSetAgent_deleteAgent()
4990 // agent deleted before engine --> engine's agent should become 0
4991 QScriptEngine *eng = new QScriptEngine;
4992 TestAgent *agent = new TestAgent(eng);
4993 eng->setAgent(agent);
4994 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4996 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4997 eng->evaluate("(function(){ return 123; })()");
5001 void tst_QJSEngine::getSetAgent_differentEngine()
5005 TestAgent *agent = new TestAgent(&eng);
5006 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine");
5007 eng2.setAgent(agent);
5008 QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0);
5012 #if 0 // ###FIXME: No QScriptString API
5013 void tst_QJSEngine::reentrancy_stringHandles()
5017 QScriptString s1 = eng1.toStringHandle("foo");
5018 QScriptString s2 = eng2.toStringHandle("foo");
5023 #if 0 // ###FIXME: No processEventsInterval API
5024 void tst_QJSEngine::reentrancy_processEventsInterval()
5028 eng1.setProcessEventsInterval(123);
5029 QCOMPARE(eng2.processEventsInterval(), -1);
5030 eng2.setProcessEventsInterval(456);
5031 QCOMPARE(eng1.processEventsInterval(), 123);
5035 #if 0 // FIXME: No support for custom types
5036 void tst_QJSEngine::reentrancy_typeConversion()
5040 qScriptRegisterMetaType<Foo>(&eng1, fooToScriptValue, fooFromScriptValue);
5045 QScriptValue fooVal = qScriptValueFromValue(&eng1, foo);
5046 QVERIFY(fooVal.isObject());
5047 QVERIFY(!fooVal.isVariant());
5048 QCOMPARE(fooVal.property("x").toInt(), 12);
5049 QCOMPARE(fooVal.property("y").toInt(), 34);
5050 fooVal.setProperty("x", 56);
5051 fooVal.setProperty("y", 78);
5053 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
5054 QCOMPARE(foo2.x, 56);
5055 QCOMPARE(foo2.y, 78);
5058 QScriptValue fooVal = qScriptValueFromValue(&eng2, foo);
5059 QVERIFY(fooVal.isVariant());
5061 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
5062 QCOMPARE(foo2.x, 12);
5063 QCOMPARE(foo2.y, 34);
5065 QVERIFY(!eng1.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5066 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5067 QScriptValue proto1 = eng1.newObject();
5068 eng1.setDefaultPrototype(qMetaTypeId<Foo>(), proto1);
5069 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5070 QScriptValue proto2 = eng2.newObject();
5071 eng2.setDefaultPrototype(qMetaTypeId<Foo>(), proto2);
5072 QVERIFY(eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5073 QVERIFY(eng1.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(proto1));
5077 void tst_QJSEngine::reentrancy_globalObjectProperties()
5081 QVERIFY(!eng2.globalObject().property("a").isValid());
5082 eng1.evaluate("a = 10");
5083 QVERIFY(eng1.globalObject().property("a").isNumber());
5084 QVERIFY(!eng2.globalObject().property("a").isValid());
5085 eng2.evaluate("a = 20");
5086 QVERIFY(eng2.globalObject().property("a").isNumber());
5087 QCOMPARE(eng1.globalObject().property("a").toInt(), 10);
5090 void tst_QJSEngine::reentrancy_Array()
5092 // weird bug with JSC backend
5095 QCOMPARE(eng.evaluate("Array()").toString(), QString());
5096 eng.evaluate("Array.prototype.toString");
5097 QCOMPARE(eng.evaluate("Array()").toString(), QString());
5101 QCOMPARE(eng.evaluate("Array()").toString(), QString());
5105 void tst_QJSEngine::reentrancy_objectCreation()
5110 QJSValue d1 = eng1.newDate(0);
5111 QJSValue d2 = eng2.newDate(0);
5112 QCOMPARE(d1.toDateTime(), d2.toDateTime());
5113 QCOMPARE(d2.toDateTime(), d1.toDateTime());
5116 QJSValue r1 = eng1.newRegExp("foo", "gim");
5117 QJSValue r2 = eng2.newRegExp("foo", "gim");
5118 QCOMPARE(r1.toRegExp(), r2.toRegExp());
5119 QCOMPARE(r2.toRegExp(), r1.toRegExp());
5122 QJSValue o1 = eng1.newQObject(this);
5123 QJSValue o2 = eng2.newQObject(this);
5124 QCOMPARE(o1.toQObject(), o2.toQObject());
5125 QCOMPARE(o2.toQObject(), o1.toQObject());
5127 #if 0 // ###FIXME: No QScriptEngine::newQMetaObject API
5129 QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject);
5130 QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject);
5131 QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject());
5132 QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject());
5137 void tst_QJSEngine::jsIncDecNonObjectProperty()
5139 // This is testing ECMA-262 compliance, not C++ API.
5143 QJSValue ret = eng.evaluate("var a; a.n++");
5144 QVERIFY(ret.isError());
5145 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5148 QJSValue ret = eng.evaluate("var a; a.n--");
5149 QVERIFY(ret.isError());
5150 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5153 QJSValue ret = eng.evaluate("var a = null; a.n++");
5154 QVERIFY(ret.isError());
5155 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5158 QJSValue ret = eng.evaluate("var a = null; a.n--");
5159 QVERIFY(ret.isError());
5160 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5163 QJSValue ret = eng.evaluate("var a; ++a.n");
5164 QVERIFY(ret.isError());
5165 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5168 QJSValue ret = eng.evaluate("var a; --a.n");
5169 QVERIFY(ret.isError());
5170 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5173 QJSValue ret = eng.evaluate("var a; a.n += 1");
5174 QVERIFY(ret.isError());
5175 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5178 QJSValue ret = eng.evaluate("var a; a.n -= 1");
5179 QVERIFY(ret.isError());
5180 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5183 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length++");
5184 QVERIFY(ret.isNumber());
5185 QCOMPARE(ret.toInt(), 4);
5188 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length--");
5189 QVERIFY(ret.isNumber());
5190 QCOMPARE(ret.toInt(), 4);
5193 QJSValue ret = eng.evaluate("var a = 'ciao'; ++a.length");
5194 QVERIFY(ret.isNumber());
5195 QCOMPARE(ret.toInt(), 5);
5198 QJSValue ret = eng.evaluate("var a = 'ciao'; --a.length");
5199 QVERIFY(ret.isNumber());
5200 QCOMPARE(ret.toInt(), 3);
5204 #if 0 // ###FIXME: no installTranslatorFunctions API
5205 void tst_QJSEngine::installTranslatorFunctions()
5208 QScriptValue global = eng.globalObject();
5209 QVERIFY(!global.property("qsTranslate").isValid());
5210 QVERIFY(!global.property("QT_TRANSLATE_NOOP").isValid());
5211 QVERIFY(!global.property("qsTr").isValid());
5212 QVERIFY(!global.property("QT_TR_NOOP").isValid());
5213 QVERIFY(!global.property("qsTrId").isValid());
5214 QVERIFY(!global.property("QT_TRID_NOOP").isValid());
5215 QVERIFY(!global.property("String").property("prototype").property("arg").isValid());
5217 eng.installTranslatorFunctions();
5218 QVERIFY(global.property("qsTranslate").isFunction());
5219 QVERIFY(global.property("QT_TRANSLATE_NOOP").isFunction());
5220 QVERIFY(global.property("qsTr").isFunction());
5221 QVERIFY(global.property("QT_TR_NOOP").isFunction());
5222 QVERIFY(global.property("qsTrId").isFunction());
5223 QVERIFY(global.property("QT_TRID_NOOP").isFunction());
5224 QVERIFY(global.property("String").property("prototype").property("arg").isFunction());
5227 QScriptValue ret = eng.evaluate("qsTr('foo')");
5228 QVERIFY(ret.isString());
5229 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5232 QScriptValue ret = eng.evaluate("qsTranslate('foo', 'bar')");
5233 QVERIFY(ret.isString());
5234 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
5237 QScriptValue ret = eng.evaluate("QT_TR_NOOP('foo')");
5238 QVERIFY(ret.isString());
5239 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5242 QScriptValue ret = eng.evaluate("QT_TRANSLATE_NOOP('foo', 'bar')");
5243 QVERIFY(ret.isString());
5244 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
5247 QScriptValue ret = eng.evaluate("'foo%0'.arg('bar')");
5248 QEXPECT_FAIL("Custom global object", "FIXME: why we expect that String prototype exists?", Abort);
5249 QVERIFY(ret.isString());
5250 QCOMPARE(ret.toString(), QString::fromLatin1("foobar"));
5253 QScriptValue ret = eng.evaluate("'foo%0'.arg(123)");
5254 QVERIFY(ret.isString());
5255 QCOMPARE(ret.toString(), QString::fromLatin1("foo123"));
5258 // Maybe this should throw an error?
5259 QScriptValue ret = eng.evaluate("'foo%0'.arg()");
5260 QVERIFY(ret.isString());
5261 QCOMPARE(ret.toString(), QString());
5265 QScriptValue ret = eng.evaluate("qsTrId('foo')");
5266 QVERIFY(ret.isString());
5267 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5270 QScriptValue ret = eng.evaluate("QT_TRID_NOOP('foo')");
5271 QVERIFY(ret.isString());
5272 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5274 QVERIFY(eng.evaluate("QT_TRID_NOOP()").isUndefined());
5277 class TranslationScope
5280 TranslationScope(const QString &fileName)
5282 translator.load(fileName);
5283 QCoreApplication::instance()->installTranslator(&translator);
5287 QCoreApplication::instance()->removeTranslator(&translator);
5291 QTranslator translator;
5294 void tst_QJSEngine::translateScript_data()
5296 QTest::addColumn<QString>("expression");
5297 QTest::addColumn<QString>("fileName");
5298 QTest::addColumn<QString>("expectedTranslation");
5300 QString fileName = QString::fromLatin1("translatable.js");
5302 QTest::newRow("qsTr('One')@translatable.js")
5303 << QString::fromLatin1("qsTr('One')") << fileName << QString::fromLatin1("En");
5304 QTest::newRow("qsTr('Hello')@translatable.js")
5305 << QString::fromLatin1("qsTr('Hello')") << fileName << QString::fromLatin1("Hallo");
5307 QTest::newRow("(function() { return qsTr('One'); })()@translatable.js")
5308 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName << QString::fromLatin1("En");
5309 QTest::newRow("(function() { return qsTr('Hello'); })()@translatable.js")
5310 << QString::fromLatin1("(function() { return qsTr('Hello'); })()") << fileName << QString::fromLatin1("Hallo");
5312 QTest::newRow("eval('qsTr(\\'One\\')')@translatable.js")
5313 << QString::fromLatin1("eval('qsTr(\\'One\\')')") << fileName << QString::fromLatin1("En");
5314 QTest::newRow("eval('qsTr(\\'Hello\\')')@translatable.js")
5315 << QString::fromLatin1("eval('qsTr(\\'Hello\\')')") << fileName << QString::fromLatin1("Hallo");
5317 QTest::newRow("qsTr('%n message(s) saved', '', 1)@translatable.js")
5318 << QString::fromLatin1("qsTr('%n message(s) saved', '', 1)") << fileName << QString::fromLatin1("1 melding lagret");
5319 QTest::newRow("qsTr('%n message(s) saved', '', 3).arg@translatable.js")
5320 << QString::fromLatin1("qsTr('%n message(s) saved', '', 3)") << fileName << QString::fromLatin1("3 meldinger lagret");
5323 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable.js")
5324 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName << QString::fromLatin1("To");
5325 QTest::newRow("qsTranslate('FooContext', 'Goodbye')@translatable.js")
5326 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye')") << fileName << QString::fromLatin1("Farvel");
5328 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')@translatable.js")
5329 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')") << fileName << QString::fromLatin1("To");
5330 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')@translatable.js")
5331 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')") << fileName << QString::fromLatin1("Farvel");
5333 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')@translatable.js")
5334 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')") << fileName << QString::fromLatin1("Farvel");
5335 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')@translatable.js")
5336 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')") << fileName << QString::fromLatin1("Farvel");
5338 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)@translatable.js")
5339 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)") << fileName << QString::fromLatin1("Goodbye");
5341 QTest::newRow("qsTr('One', 'not the same one')@translatable.js")
5342 << QString::fromLatin1("qsTr('One', 'not the same one')") << fileName << QString::fromLatin1("Enda en");
5344 QTest::newRow("qsTr('One', 'not the same one', 42)@translatable.js")
5345 << QString::fromLatin1("qsTr('One', 'not the same one', 42)") << fileName << QString::fromLatin1("One");
5348 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)@translatable.js")
5349 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)") << fileName << QString::fromLatin1("1 fooaktig bar funnet");
5350 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)@translatable.js")
5351 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)") << fileName << QString::fromLatin1("2 fooaktige barer funnet");
5353 // Don't exist in translation
5354 QTest::newRow("qsTr('Three')@translatable.js")
5355 << QString::fromLatin1("qsTr('Three')") << fileName << QString::fromLatin1("Three");
5356 QTest::newRow("qsTranslate('FooContext', 'So long')@translatable.js")
5357 << QString::fromLatin1("qsTranslate('FooContext', 'So long')") << fileName << QString::fromLatin1("So long");
5358 QTest::newRow("qsTranslate('BarContext', 'Goodbye')@translatable.js")
5359 << QString::fromLatin1("qsTranslate('BarContext', 'Goodbye')") << fileName << QString::fromLatin1("Goodbye");
5361 // Translate strings from the second script (translatable2.js)
5363 QString fileName2 = QString::fromLatin1("translatable2.js");
5364 QTest::newRow("qsTr('Three')@translatable2.js")
5365 << QString::fromLatin1("qsTr('Three')") << fileName2 << QString::fromLatin1("Tre");
5366 QTest::newRow("qsTr('Happy birthday!')@translatable2.js")
5367 << QString::fromLatin1("qsTr('Happy birthday!')") << fileName2 << QString::fromLatin1("Gratulerer med dagen!");
5369 // Not translated because translation is only in translatable.js
5370 QTest::newRow("qsTr('One')@translatable2.js")
5371 << QString::fromLatin1("qsTr('One')") << fileName2 << QString::fromLatin1("One");
5372 QTest::newRow("(function() { return qsTr('One'); })()@translatable2.js")
5373 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName2 << QString::fromLatin1("One");
5375 // For qsTranslate() the filename shouldn't matter
5376 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable2.js")
5377 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName2 << QString::fromLatin1("To");
5378 QTest::newRow("qsTranslate('BarContext', 'Congratulations!')@translatable.js")
5379 << QString::fromLatin1("qsTranslate('BarContext', 'Congratulations!')") << fileName << QString::fromLatin1("Gratulerer!");
5382 void tst_QJSEngine::translateScript()
5384 QFETCH(QString, expression);
5385 QFETCH(QString, fileName);
5386 QFETCH(QString, expectedTranslation);
5388 QScriptEngine engine;
5390 TranslationScope tranScope(":/translations/translatable_la");
5391 engine.installTranslatorFunctions();
5393 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5394 QVERIFY(!engine.hasUncaughtException());
5397 void tst_QJSEngine::translateScript_crossScript()
5399 QScriptEngine engine;
5400 TranslationScope tranScope(":/translations/translatable_la");
5401 engine.installTranslatorFunctions();
5403 QString fileName = QString::fromLatin1("translatable.js");
5404 QString fileName2 = QString::fromLatin1("translatable2.js");
5405 // qsTr() should use the innermost filename as context
5406 engine.evaluate("function foo(s) { return bar(s); }", fileName);
5407 engine.evaluate("function bar(s) { return qsTr(s); }", fileName2);
5408 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5409 QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre"));
5410 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One"));
5412 engine.evaluate("function foo(s) { return bar(s); }", fileName2);
5413 engine.evaluate("function bar(s) { return qsTr(s); }", fileName);
5414 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three"));
5415 QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En"));
5416 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En"));
5419 static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng)
5421 return eng->globalObject().property("qsTr").call(ctx->thisObject(), ctx->argumentsObject());
5424 void tst_QJSEngine::translateScript_callQsTrFromNative()
5426 QScriptEngine engine;
5427 TranslationScope tranScope(":/translations/translatable_la");
5428 engine.installTranslatorFunctions();
5430 QString fileName = QString::fromLatin1("translatable.js");
5431 QString fileName2 = QString::fromLatin1("translatable2.js");
5432 // Calling qsTr() from a native function
5433 engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr));
5434 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En"));
5435 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One"));
5436 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three"));
5437 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5440 void tst_QJSEngine::translateScript_trNoOp()
5442 QScriptEngine engine;
5443 TranslationScope tranScope(":/translations/translatable_la");
5444 engine.installTranslatorFunctions();
5446 QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined());
5447 QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One"));
5449 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined());
5450 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined());
5451 QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two"));
5454 void tst_QJSEngine::translateScript_callQsTrFromCpp()
5456 QScriptEngine engine;
5457 TranslationScope tranScope(":/translations/translatable_la");
5458 engine.installTranslatorFunctions();
5460 // There is no context, but it shouldn't crash
5461 QCOMPARE(engine.globalObject().property("qsTr").call(
5462 QScriptValue(), QScriptValueList() << "One").toString(), QString::fromLatin1("One"));
5465 void tst_QJSEngine::translateWithInvalidArgs_data()
5467 QTest::addColumn<QString>("expression");
5468 QTest::addColumn<QString>("expectedError");
5470 QTest::newRow("qsTr()") << "qsTr()" << "Error: qsTr() requires at least one argument";
5471 QTest::newRow("qsTr(123)") << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string";
5472 QTest::newRow("qsTr('foo', 123)") << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string";
5473 QTest::newRow("qsTr('foo', 'bar', 'baz')") << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number";
5474 QTest::newRow("qsTr('foo', 'bar', true)") << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number";
5476 QTest::newRow("qsTranslate()") << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments";
5477 QTest::newRow("qsTranslate('foo')") << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments";
5478 QTest::newRow("qsTranslate(123, 'foo')") << "qsTranslate(123, 'foo')" << "Error: qsTranslate(): first argument (context) must be a string";
5479 QTest::newRow("qsTranslate('foo', 123)") << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string";
5480 QTest::newRow("qsTranslate('foo', 'bar', 123)") << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string";
5481 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string";
5482 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number";
5483 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'";
5485 QTest::newRow("qsTrId()") << "qsTrId()" << "Error: qsTrId() requires at least one argument";
5486 QTest::newRow("qsTrId(123)") << "qsTrId(123)" << "TypeError: qsTrId(): first argument (id) must be a string";
5487 QTest::newRow("qsTrId('foo', 'bar')") << "qsTrId('foo', 'bar')" << "TypeError: qsTrId(): second argument (n) must be a number";
5490 void tst_QJSEngine::translateWithInvalidArgs()
5492 QFETCH(QString, expression);
5493 QFETCH(QString, expectedError);
5494 QScriptEngine engine;
5495 engine.installTranslatorFunctions();
5496 QScriptValue result = engine.evaluate(expression);
5497 QVERIFY(result.isError());
5498 QCOMPARE(result.toString(), expectedError);
5501 void tst_QJSEngine::translationContext_data()
5503 QTest::addColumn<QString>("path");
5504 QTest::addColumn<QString>("text");
5505 QTest::addColumn<QString>("expectedTranslation");
5507 QTest::newRow("translatable.js") << "translatable.js" << "One" << "En";
5508 QTest::newRow("/translatable.js") << "/translatable.js" << "One" << "En";
5509 QTest::newRow("/foo/translatable.js") << "/foo/translatable.js" << "One" << "En";
5510 QTest::newRow("/foo/bar/translatable.js") << "/foo/bar/translatable.js" << "One" << "En";
5511 QTest::newRow("./translatable.js") << "./translatable.js" << "One" << "En";
5512 QTest::newRow("../translatable.js") << "../translatable.js" << "One" << "En";
5513 QTest::newRow("foo/translatable.js") << "foo/translatable.js" << "One" << "En";
5514 QTest::newRow("file:///home/qt/translatable.js") << "file:///home/qt/translatable.js" << "One" << "En";
5515 QTest::newRow(":/resources/translatable.js") << ":/resources/translatable.js" << "One" << "En";
5516 QTest::newRow("/translatable.js.foo") << "/translatable.js.foo" << "One" << "En";
5517 QTest::newRow("/translatable.txt") << "/translatable.txt" << "One" << "En";
5518 QTest::newRow("translatable") << "translatable" << "One" << "En";
5519 QTest::newRow("foo/translatable") << "foo/translatable" << "One" << "En";
5521 QTest::newRow("native separators")
5522 << (QDir::toNativeSeparators(QDir::currentPath()) + QDir::separator() + "translatable.js")
5525 QTest::newRow("translatable.js/") << "translatable.js/" << "One" << "One";
5526 QTest::newRow("nosuchscript.js") << "" << "One" << "One";
5527 QTest::newRow("(empty)") << "" << "One" << "One";
5530 void tst_QJSEngine::translationContext()
5532 TranslationScope tranScope(":/translations/translatable_la");
5534 QScriptEngine engine;
5535 engine.installTranslatorFunctions();
5537 QFETCH(QString, path);
5538 QFETCH(QString, text);
5539 QFETCH(QString, expectedTranslation);
5540 QScriptValue ret = engine.evaluate(QString::fromLatin1("qsTr('%0')").arg(text), path);
5541 QVERIFY(ret.isString());
5542 QCOMPARE(ret.toString(), expectedTranslation);
5545 void tst_QJSEngine::translateScriptIdBased()
5547 QScriptEngine engine;
5549 TranslationScope tranScope(":/translations/idtranslatable_la");
5550 engine.installTranslatorFunctions();
5552 QString fileName = QString::fromLatin1("idtranslatable.js");
5554 QHash<QString, QString> expectedTranslations;
5555 expectedTranslations["qtn_foo_bar"] = "First string";
5556 expectedTranslations["qtn_needle"] = "Second string";
5557 expectedTranslations["qtn_haystack"] = "Third string";
5558 expectedTranslations["qtn_bar_baz"] = "Fourth string";
5560 QHash<QString, QString>::const_iterator it;
5561 for (it = expectedTranslations.constBegin(); it != expectedTranslations.constEnd(); ++it) {
5562 for (int x = 0; x < 2; ++x) {
5567 QCOMPARE(engine.evaluate(QString::fromLatin1("qsTrId('%0')")
5568 .arg(it.key()), fn).toString(),
5570 QCOMPARE(engine.evaluate(QString::fromLatin1("QT_TRID_NOOP('%0')")
5571 .arg(it.key()), fn).toString(),
5574 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return qsTrId('%0'); })()")
5575 .arg(it.key()), fn).toString(),
5577 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return QT_TRID_NOOP('%0'); })()")
5578 .arg(it.key()), fn).toString(),
5584 QCOMPARE(engine.evaluate("qsTrId('qtn_bar_baz', 10)").toString(),
5585 QString::fromLatin1("10 fooish bar(s) found"));
5586 QCOMPARE(engine.evaluate("qsTrId('qtn_foo_bar', 10)").toString(),
5587 QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural
5590 // How to add a new test row:
5591 // - Find a nice list of Unicode characters to choose from
5592 // - Write source string/context/comment in .js using Unicode escape sequences (\uABCD)
5593 // - Update corresponding .ts file (e.g. lupdate foo.js -ts foo.ts -codecfortr UTF-8)
5594 // - Enter translation in Linguist
5595 // - Update corresponding .qm file (e.g. lrelease foo.ts)
5596 // - Evaluate script that performs translation; make sure the correct result is returned
5597 // (e.g. by setting the resulting string as the text of a QLabel and visually verifying
5598 // that it looks the same as what you entered in Linguist :-) )
5599 // - Generate the expectedTranslation column data using toUtf8().toHex()
5600 void tst_QJSEngine::translateScriptUnicode_data()
5602 QTest::addColumn<QString>("expression");
5603 QTest::addColumn<QString>("fileName");
5604 QTest::addColumn<QString>("expectedTranslation");
5606 QString fileName = QString::fromLatin1("translatable-unicode.js");
5607 QTest::newRow("qsTr('H\\u2082O')@translatable-unicode.js")
5608 << QString::fromLatin1("qsTr('H\\u2082O')") << fileName << QString::fromUtf8("\xcd\xbb\xcd\xbc\xcd\xbd");
5609 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')@translatable-unicode.js")
5610 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << fileName << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5611 QTest::newRow("qsTr('\\u0391\\u0392\\u0393')@translatable-unicode.js")
5612 << QString::fromLatin1("qsTr('\\u0391\\u0392\\u0393')") << fileName << QString::fromUtf8("\xd3\x9c\xd2\xb4\xd1\xbc");
5613 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')@translatable-unicode.js")
5614 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')") << fileName << QString::fromUtf8("\xd8\xae\xd8\xb3\xd8\xb3");
5615 QTest::newRow("qsTr('H\\u2082O', 'not the same H\\u2082O')@translatable-unicode.js")
5616 << QString::fromLatin1("qsTr('H\\u2082O', 'not the same H\\u2082O')") << fileName << QString::fromUtf8("\xd4\xb6\xd5\x8a\xd5\x92");
5617 QTest::newRow("qsTr('H\\u2082O')")
5618 << QString::fromLatin1("qsTr('H\\u2082O')") << QString() << QString::fromUtf8("\x48\xe2\x82\x82\x4f");
5619 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')")
5620 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << QString() << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5623 void tst_QJSEngine::translateScriptUnicode()
5625 QFETCH(QString, expression);
5626 QFETCH(QString, fileName);
5627 QFETCH(QString, expectedTranslation);
5629 QScriptEngine engine;
5631 TranslationScope tranScope(":/translations/translatable-unicode");
5632 engine.installTranslatorFunctions();
5634 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5635 QVERIFY(!engine.hasUncaughtException());
5638 void tst_QJSEngine::translateScriptUnicodeIdBased_data()
5640 QTest::addColumn<QString>("expression");
5641 QTest::addColumn<QString>("expectedTranslation");
5643 QTest::newRow("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1'')")
5644 << QString::fromLatin1("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1')") << QString::fromUtf8("\xc6\xa7\xc6\xb0\xc6\x88\xc8\xbc\xc8\x9d\xc8\xbf\xc8\x99");
5645 QTest::newRow("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')")
5646 << QString::fromLatin1("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')") << QString::fromUtf8("\xc7\xa0\xc8\xa1\xc8\x8b\xc8\x85\xc8\x95");
5647 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)")
5648 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)") << QString::fromUtf8("\x31\x30\x20\xc6\x92\xc6\xa1\xc7\x92\x28\xc8\x99\x29");
5649 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')")
5650 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')") << QString::fromUtf8("\xc6\x91\xc6\xb0\xc7\xb9");
5651 QTest::newRow("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')")
5652 << QString::fromLatin1("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')") << QString::fromUtf8("\xc7\x8d\xc6\x80\xc6\xa8\xc6\x90\xc6\x9e\xc6\xab");
5655 void tst_QJSEngine::translateScriptUnicodeIdBased()
5657 QFETCH(QString, expression);
5658 QFETCH(QString, expectedTranslation);
5660 QScriptEngine engine;
5662 TranslationScope tranScope(":/translations/idtranslatable-unicode");
5663 engine.installTranslatorFunctions();
5665 QCOMPARE(engine.evaluate(expression).toString(), expectedTranslation);
5666 QVERIFY(!engine.hasUncaughtException());
5669 void tst_QJSEngine::translateFromBuiltinCallback()
5672 eng.installTranslatorFunctions();
5674 // Callback has no translation context.
5675 eng.evaluate("function foo() { qsTr('foo'); }");
5677 // Stack at translation time will be:
5678 // qsTr, foo, forEach, global
5679 // qsTr() needs to walk to the outer-most (global) frame before it finds
5680 // a translation context, and this should not crash.
5681 eng.evaluate("[10,20].forEach(foo)", "script.js");
5685 #if 0 // ###FIXME: No QScriptValue::scope API
5686 void tst_QJSEngine::functionScopes()
5690 // top-level functions have only the global object in their scope
5691 QScriptValue fun = eng.evaluate("(function() {})");
5692 QVERIFY(fun.isFunction());
5693 QEXPECT_FAIL("", "QScriptValue::scope() is internal, not implemented", Abort);
5694 QVERIFY(fun.scope().isObject());
5695 QVERIFY(fun.scope().strictlyEquals(eng.globalObject()));
5696 QVERIFY(!eng.globalObject().scope().isValid());
5699 QScriptValue fun = eng.globalObject().property("Object");
5700 QVERIFY(fun.isFunction());
5701 // native built-in functions don't have scope
5702 QVERIFY(!fun.scope().isValid());
5706 QScriptValue fun = eng.evaluate("(function(arg) { var foo = arg; return function() { return foo; }; })(123)");
5707 QVERIFY(fun.isFunction());
5709 QScriptValue ret = fun.call();
5710 QVERIFY(ret.isNumber());
5711 QCOMPARE(ret.toInt(), 123);
5713 QScriptValue scope = fun.scope();
5714 QVERIFY(scope.isObject());
5716 QScriptValue ret = scope.property("foo");
5717 QVERIFY(ret.isNumber());
5718 QCOMPARE(ret.toInt(), 123);
5719 QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
5722 QScriptValue ret = scope.property("arg");
5723 QVERIFY(ret.isNumber());
5724 QCOMPARE(ret.toInt(), 123);
5725 QCOMPARE(scope.propertyFlags("arg"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
5728 scope.setProperty("foo", 456);
5730 QScriptValue ret = fun.call();
5731 QVERIFY(ret.isNumber());
5732 QCOMPARE(ret.toInt(), 456);
5735 scope = scope.scope();
5736 QVERIFY(scope.isObject());
5737 QVERIFY(scope.strictlyEquals(eng.globalObject()));
5742 #if 0 // ###FIXME: No QScriptContext API
5743 static QScriptValue counter_inner(QScriptContext *ctx, QScriptEngine *)
5745 QScriptValue outerAct = ctx->callee().scope();
5746 double count = outerAct.property("count").toNumber();
5747 outerAct.setProperty("count", count+1);
5751 static QScriptValue counter(QScriptContext *ctx, QScriptEngine *eng)
5753 QScriptValue act = ctx->activationObject();
5754 act.setProperty("count", ctx->argument(0).toInt());
5755 QScriptValue result = eng->newFunction(counter_inner);
5756 result.setScope(act);
5760 static QScriptValue counter_hybrid(QScriptContext *ctx, QScriptEngine *eng)
5762 QScriptValue act = ctx->activationObject();
5763 act.setProperty("count", ctx->argument(0).toInt());
5764 return eng->evaluate("(function() { return count++; })");
5767 void tst_QJSEngine::nativeFunctionScopes()
5771 QScriptValue fun = eng.newFunction(counter);
5772 QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123);
5773 QVERIFY(cnt.isFunction());
5775 QScriptValue ret = cnt.call();
5776 QVERIFY(ret.isNumber());
5777 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5778 QCOMPARE(ret.toInt(), 123);
5782 QScriptValue fun = eng.newFunction(counter_hybrid);
5783 QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123);
5784 QVERIFY(cnt.isFunction());
5786 QScriptValue ret = cnt.call();
5787 QVERIFY(ret.isNumber());
5788 QCOMPARE(ret.toInt(), 123);
5792 //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain
5795 eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n"
5796 "var c1 = counter(); var c2 = counter(); ");
5797 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5798 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5799 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5800 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5801 QVERIFY(!eng.hasUncaughtException());
5805 eng.globalObject().setProperty("counter", eng.newFunction(counter));
5806 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5807 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5808 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5809 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5810 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5811 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5812 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5813 QVERIFY(!eng.hasUncaughtException());
5817 eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid));
5818 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5819 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5820 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5821 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5822 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5823 QVERIFY(!eng.hasUncaughtException());
5828 #if 0 // ###FIXME: No QScriptProgram API
5829 static QScriptValue createProgram(QScriptContext *ctx, QScriptEngine *eng)
5831 QString code = ctx->argument(0).toString();
5832 QScriptProgram result(code);
5833 return qScriptValueFromValue(eng, result);
5836 void tst_QJSEngine::evaluateProgram()
5841 QString code("1 + 2");
5842 QString fileName("hello.js");
5843 int lineNumber(123);
5844 QScriptProgram program(code, fileName, lineNumber);
5845 QVERIFY(!program.isNull());
5846 QCOMPARE(program.sourceCode(), code);
5847 QCOMPARE(program.fileName(), fileName);
5848 QCOMPARE(program.firstLineNumber(), lineNumber);
5850 QScriptValue expected = eng.evaluate(code);
5851 for (int x = 0; x < 10; ++x) {
5852 QScriptValue ret = eng.evaluate(program);
5853 QVERIFY(ret.equals(expected));
5857 QScriptProgram sameProgram = program;
5858 QVERIFY(sameProgram == program);
5859 QVERIFY(eng.evaluate(sameProgram).equals(expected));
5862 QScriptProgram sameProgram2(program);
5863 QVERIFY(sameProgram2 == program);
5864 QVERIFY(eng.evaluate(sameProgram2).equals(expected));
5866 QScriptProgram differentProgram("2 + 3");
5867 QVERIFY(differentProgram != program);
5868 QVERIFY(!eng.evaluate(differentProgram).equals(expected));
5872 void tst_QJSEngine::evaluateProgram_customScope()
5876 QScriptProgram program("a");
5877 QVERIFY(!program.isNull());
5879 QScriptValue ret = eng.evaluate(program);
5880 QVERIFY(ret.isError());
5881 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined"));
5884 QScriptValue obj = eng.newObject();
5885 obj.setProperty("a", 123);
5886 QScriptContext *ctx = eng.currentContext();
5887 ctx->pushScope(obj);
5889 QScriptValue ret = eng.evaluate(program);
5890 QVERIFY(!ret.isError());
5891 QVERIFY(ret.equals(obj.property("a")));
5894 obj.setProperty("a", QScriptValue());
5896 QScriptValue ret = eng.evaluate(program);
5897 QVERIFY(ret.isError());
5900 QScriptValue obj2 = eng.newObject();
5901 obj2.setProperty("a", 456);
5902 ctx->pushScope(obj2);
5904 QScriptValue ret = eng.evaluate(program);
5905 QVERIFY(!ret.isError());
5906 QVERIFY(ret.equals(obj2.property("a")));
5913 void tst_QJSEngine::evaluateProgram_closure()
5917 QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })");
5918 QVERIFY(!program.isNull());
5919 QScriptValue createCounter = eng.evaluate(program);
5920 QVERIFY(createCounter.isFunction());
5921 QScriptValue counter = createCounter.call();
5922 QVERIFY(counter.isFunction());
5924 QScriptValue ret = counter.call();
5925 QVERIFY(ret.isNumber());
5927 QScriptValue counter2 = createCounter.call();
5928 QVERIFY(counter2.isFunction());
5929 QVERIFY(!counter2.equals(counter));
5931 QScriptValue ret = counter2.call();
5932 QVERIFY(ret.isNumber());
5937 void tst_QJSEngine::evaluateProgram_executeLater()
5940 // Program created in a function call, then executed later
5942 QScriptValue fun = eng.newFunction(createProgram);
5943 QScriptProgram program = qscriptvalue_cast<QScriptProgram>(
5944 fun.call(QScriptValue(), QScriptValueList() << "a + 1"));
5945 QVERIFY(!program.isNull());
5946 eng.globalObject().setProperty("a", QScriptValue());
5948 QScriptValue ret = eng.evaluate(program);
5949 QVERIFY(ret.isError());
5950 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined"));
5952 eng.globalObject().setProperty("a", 122);
5954 QScriptValue ret = eng.evaluate(program);
5955 QVERIFY(!ret.isError());
5956 QVERIFY(ret.isNumber());
5957 QCOMPARE(ret.toInt(), 123);
5962 void tst_QJSEngine::evaluateProgram_multipleEngines()
5966 QString code("1 + 2");
5967 QScriptProgram program(code);
5968 QVERIFY(!program.isNull());
5969 double expected = eng.evaluate(program).toNumber();
5970 for (int x = 0; x < 2; ++x) {
5972 for (int y = 0; y < 2; ++y) {
5973 double ret = eng2.evaluate(program).toNumber();
5974 QCOMPARE(ret, expected);
5980 void tst_QJSEngine::evaluateProgram_empty()
5984 QScriptProgram program;
5985 QVERIFY(program.isNull());
5986 QScriptValue ret = eng.evaluate(program);
5987 QVERIFY(!ret.isValid());
5992 #if 0 // ###FIXME: No ScriptOwnership API
5993 void tst_QJSEngine::collectGarbageAfterConnect()
5996 QScriptEngine engine;
5997 QPointer<QWidget> widget = new QWidget;
5998 engine.globalObject().setProperty(
5999 "widget", engine.newQObject(widget, QScriptEngine::ScriptOwnership));
6000 QVERIFY(engine.evaluate("widget.customContextMenuRequested.connect(\n"
6001 " function() { print('hello'); }\n"
6004 QVERIFY(widget != 0);
6005 engine.evaluate("widget = null;");
6006 // The connection should not keep the widget alive.
6007 collectGarbage_helper(engine);
6008 QVERIFY(widget == 0);
6012 #if 0 // ###FIXME: No QScriptContext API
6013 void tst_QJSEngine::collectGarbageAfterNativeArguments()
6017 QScriptContext *ctx = eng.pushContext();
6018 QScriptValue arguments = ctx->argumentsObject();
6019 // Shouldn't crash when marking the arguments object.
6020 collectGarbage_helper(eng);
6023 static QScriptValue constructQObjectFromThisObject(QScriptContext *ctx, QScriptEngine *eng)
6025 if (!ctx->isCalledAsConstructor()) {
6026 qWarning("%s: ctx->isCalledAsConstructor() returned false", Q_FUNC_INFO);
6027 return QScriptValue();
6029 return eng->newQObject(ctx->thisObject(), new QObject, QScriptEngine::ScriptOwnership);
6032 void tst_QJSEngine::promoteThisObjectToQObjectInConstructor()
6034 QScriptEngine engine;
6035 QScriptValue ctor = engine.newFunction(constructQObjectFromThisObject);
6036 engine.globalObject().setProperty("Ctor", ctor);
6037 QScriptValue object = engine.evaluate("new Ctor");
6038 QVERIFY(!object.isError());
6039 QVERIFY(object.isQObject());
6040 QVERIFY(object.toQObject() != 0);
6041 QVERIFY(object.property("objectName").isString());
6042 QVERIFY(object.property("deleteLater").isFunction());
6046 static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
6048 void tst_QJSEngine::qRegExpInport_data()
6050 QTest::addColumn<QRegExp>("rx");
6051 QTest::addColumn<QString>("string");
6052 QTest::addColumn<QString>("matched");
6054 QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo";
6055 QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo";
6056 QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
6057 QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
6058 QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab";
6059 QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba";
6060 // this one will fail because we do not support the QRegExp::RegExp in JSC
6061 //QTest::newRow("not_greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp) << "aaaabaaba";
6062 QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt";
6063 QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt";
6064 QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string";
6065 QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string";
6066 QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba";
6067 QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
6068 QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
6069 QTest::newRow("html") << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>";
6070 QTest::newRow("html minimal") << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>";
6071 QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa";
6072 QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa";
6073 QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *";
6074 QTest::newRow(".? minimal") << minimal(QRegExp(".?")) << ".?";
6075 QTest::newRow(".+ minimal") << minimal(QRegExp(".+")) << ".+";
6076 QTest::newRow("[.?] minimal") << minimal(QRegExp("[.?]")) << ".?";
6077 QTest::newRow("[.+] minimal") << minimal(QRegExp("[.+]")) << ".+";
6080 void tst_QJSEngine::qRegExpInport()
6082 QSKIP("Test failing - QTBUG-22238");
6083 QFETCH(QRegExp, rx);
6084 QFETCH(QString, string);
6088 rexp = eng.newRegExp(rx);
6090 QCOMPARE(rexp.isValid(), true);
6091 QCOMPARE(rexp.isRegExp(), true);
6092 QVERIFY(rexp.isFunction());
6094 QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
6095 QJSValue result = func.call(QJSValue(), QJSValueList() << string << rexp);
6098 for (int i = 0; i <= rx.captureCount(); i++) {
6099 QCOMPARE(result.property(i).toString(), rx.cap(i));
6103 // QScriptValue::toDateTime() returns a local time, whereas JS dates
6104 // are always stored as UTC. QtScript must respect the current time
6105 // zone, and correctly adjust for daylight saving time that may be in
6106 // effect at a given date (QTBUG-9770).
6107 void tst_QJSEngine::dateRoundtripJSQtJS()
6109 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
6111 for (int i = 0; i < 8000; ++i) {
6112 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
6113 QDateTime qtDate = jsDate.toDateTime();
6114 QJSValue jsDate2 = eng.newDate(qtDate);
6115 if (jsDate2.toNumber() != jsDate.toNumber())
6116 QFAIL(qPrintable(jsDate.toString()));
6121 void tst_QJSEngine::dateRoundtripQtJSQt()
6123 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
6125 for (int i = 0; i < 8000; ++i) {
6126 QJSValue jsDate = eng.newDate(qtDate);
6127 QDateTime qtDate2 = jsDate.toDateTime();
6128 if (qtDate2 != qtDate)
6129 QFAIL(qPrintable(qtDate.toString()));
6130 qtDate = qtDate.addSecs(2*60*60);
6134 void tst_QJSEngine::dateConversionJSQt()
6136 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
6138 for (int i = 0; i < 8000; ++i) {
6139 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
6140 QDateTime qtDate = jsDate.toDateTime();
6141 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
6142 QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString();
6143 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
6144 if (qtUTCDateStr != jsUTCDateStr)
6145 QFAIL(qPrintable(jsDate.toString()));
6150 void tst_QJSEngine::dateConversionQtJS()
6152 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
6154 for (int i = 0; i < 8000; ++i) {
6155 QJSValue jsDate = eng.newDate(qtDate);
6156 QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString();
6157 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
6158 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
6159 if (jsUTCDateStr != qtUTCDateStr)
6160 QFAIL(qPrintable(qtDate.toString()));
6161 qtDate = qtDate.addSecs(2*60*60);
6165 #if 0 // ###FIXME: No QScriptContext API
6166 static QScriptValue createAnotherEngine(QScriptContext *, QScriptEngine *)
6169 eng.evaluate("function foo(x, y) { return x + y; }" );
6170 eng.evaluate("hello = 5; world = 6" );
6171 return eng.evaluate("foo(hello,world)").toInt();
6175 void tst_QJSEngine::reentrency()
6178 eng.globalObject().setProperty("foo", eng.newFunction(createAnotherEngine));
6179 eng.evaluate("function bar() { return foo(); } hello = 9; function getHello() { return hello; }");
6180 QCOMPARE(eng.evaluate("foo() + getHello() + foo()").toInt(), 5+6 + 9 + 5+6);
6181 QCOMPARE(eng.evaluate("foo").call().toInt(), 5+6);
6182 QCOMPARE(eng.evaluate("hello").toInt(), 9);
6183 QCOMPARE(eng.evaluate("foo() + hello").toInt(), 5+6+9);
6187 #if 0 // ###FIXME: No QSCriptDeclarativeClass API
6188 void tst_QJSEngine::newFixedStaticScopeObject()
6190 // "Static scope objects" is an optimization we do for QML.
6191 // It enables the creation of JS objects that can guarantee to the
6192 // compiler that no properties will be added or removed. This enables
6193 // the compiler to generate a very simple (fast) property access, as
6194 // opposed to a full virtual lookup. Due to the inherent use of scope
6195 // chains in QML, this can make a huge difference (10x improvement for
6196 // benchmark in QTBUG-8576).
6197 // Ideally we would not need a special object type for this, and the
6198 // VM would dynamically optimize it to be fast...
6199 // See also QScriptEngine benchmark.
6202 static const int propertyCount = 4;
6203 QString names[] = { "foo", "bar", "baz", "Math" };
6204 QScriptValue values[] = { 123, "ciao", true, false };
6205 QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable,
6206 QScriptValue::ReadOnly | QScriptValue::Undeletable,
6207 QScriptValue::SkipInEnumeration | QScriptValue::Undeletable,
6208 QScriptValue::Undeletable };
6209 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags);
6212 for (int i = 0; i < propertyCount; ++i) {
6213 for (int x = 0; x < 2; ++x) {
6215 // Properties can't be deleted.
6216 scope.setProperty(names[i], QScriptValue());
6218 QVERIFY(scope.property(names[i]).equals(values[i]));
6219 QCOMPARE(scope.propertyFlags(names[i]), flags[i]);
6223 // Property that doesn't exist.
6224 QVERIFY(!scope.property("noSuchProperty").isValid());
6225 QCOMPARE(scope.propertyFlags("noSuchProperty"), QScriptValue::PropertyFlags());
6227 // Write to writable property.
6229 QScriptValue oldValue = scope.property("foo");
6230 QVERIFY(oldValue.isNumber());
6231 QScriptValue newValue = oldValue.toNumber() * 2;
6232 scope.setProperty("foo", newValue);
6233 QVERIFY(scope.property("foo").equals(newValue));
6234 scope.setProperty("foo", oldValue);
6235 QVERIFY(scope.property("foo").equals(oldValue));
6238 // Write to read-only property.
6239 scope.setProperty("bar", 456);
6240 QVERIFY(scope.property("bar").equals("ciao"));
6244 QScriptValueIterator it(scope);
6245 QSet<QString> iteratedNames;
6246 while (it.hasNext()) {
6248 iteratedNames.insert(it.name());
6250 for (int i = 0; i < propertyCount; ++i)
6251 QVERIFY(iteratedNames.contains(names[i]));
6254 // Push it on the scope chain of a new context.
6255 QScriptContext *ctx = eng.pushContext();
6256 ctx->pushScope(scope);
6257 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6258 QEXPECT_FAIL("", "activationObject has not been implemented yet", Continue);
6259 QVERIFY(ctx->activationObject().equals(scope));
6261 // Read property from JS.
6262 for (int i = 0; i < propertyCount; ++i) {
6263 for (int x = 0; x < 2; ++x) {
6265 // Property can't be deleted from JS.
6266 QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i]));
6267 QVERIFY(ret.equals(false));
6269 QVERIFY(eng.evaluate(names[i]).equals(values[i]));
6273 // Property that doesn't exist.
6274 QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: noSuchProperty is not defined"));
6276 // Write property from JS.
6278 QScriptValue oldValue = eng.evaluate("foo");
6279 QVERIFY(oldValue.isNumber());
6280 QScriptValue newValue = oldValue.toNumber() * 2;
6281 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6282 scope.setProperty("foo", oldValue);
6283 QVERIFY(eng.evaluate("foo").equals(oldValue));
6286 // Write to read-only property.
6287 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6289 // Create a closure and return properties from there.
6291 QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()");
6292 QVERIFY(props.isArray());
6293 // "foo" and "bar" come from scope object.
6294 QVERIFY(props.property(0).equals(scope.property("foo")));
6295 QVERIFY(props.property(1).equals(scope.property("bar")));
6296 // "baz" shadows property in scope object.
6297 QVERIFY(props.property(2).equals("shadow"));
6298 // "Math" comes from scope object, and shadows Global Object's "Math".
6299 QVERIFY(props.property(3).equals(scope.property("Math")));
6300 QVERIFY(!props.property(3).equals(eng.globalObject().property("Math")));
6301 // "Array" comes from Global Object.
6302 QVERIFY(props.property(4).equals(eng.globalObject().property("Array")));
6305 // As with normal JS, assigning to an undefined variable will create
6306 // the property on the Global Object, not the inner scope.
6307 QVERIFY(!eng.globalObject().property("newProperty").isValid());
6308 QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined());
6309 QVERIFY(!scope.property("newProperty").isValid());
6310 QVERIFY(eng.globalObject().property("newProperty").isNumber());
6312 // Nested static scope.
6314 static const int propertyCount2 = 2;
6315 QString names2[] = { "foo", "hum" };
6316 QScriptValue values2[] = { 321, "hello" };
6317 QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable,
6318 QScriptValue::ReadOnly | QScriptValue::Undeletable };
6319 QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2);
6320 ctx->pushScope(scope2);
6322 // "foo" shadows scope.foo.
6323 QVERIFY(eng.evaluate("foo").equals(scope2.property("foo")));
6324 QVERIFY(!eng.evaluate("foo").equals(scope.property("foo")));
6325 // "hum" comes from scope2.
6326 QVERIFY(eng.evaluate("hum").equals(scope2.property("hum")));
6327 // "Array" comes from Global Object.
6328 QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array")));
6333 QScriptValue fun = eng.evaluate("(function() { return foo; })");
6334 QVERIFY(fun.isFunction());
6336 // Function's scope chain persists after popContext().
6337 QVERIFY(fun.call().equals(scope.property("foo")));
6340 void tst_QJSEngine::newGrowingStaticScopeObject()
6342 // The main use case for a growing static scope object is to set it as
6343 // the activation object of a QScriptContext, so that all JS variable
6344 // declarations end up in that object. It needs to be "growable" since
6345 // we don't know in advance how many variables a script will declare.
6348 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng);
6351 QVERIFY(!QScriptValueIterator(scope).hasNext());
6352 QVERIFY(!scope.property("foo").isValid());
6354 // Add a static property.
6355 scope.setProperty("foo", 123);
6356 QVERIFY(scope.property("foo").equals(123));
6357 QEXPECT_FAIL("", "FIXME: newStaticScopeObject not properly implemented", Abort);
6358 QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
6360 // Modify existing property.
6361 scope.setProperty("foo", 456);
6362 QVERIFY(scope.property("foo").equals(456));
6364 // Add a read-only property.
6365 scope.setProperty("bar", "ciao", QScriptValue::ReadOnly);
6366 QVERIFY(scope.property("bar").equals("ciao"));
6367 QCOMPARE(scope.propertyFlags("bar"), QScriptValue::ReadOnly | QScriptValue::Undeletable);
6369 // Attempt to modify read-only property.
6370 scope.setProperty("bar", "hello");
6371 QVERIFY(scope.property("bar").equals("ciao"));
6373 // Properties can't be deleted.
6374 scope.setProperty("foo", QScriptValue());
6375 QVERIFY(scope.property("foo").equals(456));
6376 scope.setProperty("bar", QScriptValue());
6377 QVERIFY(scope.property("bar").equals("ciao"));
6381 QScriptValueIterator it(scope);
6382 QSet<QString> iteratedNames;
6383 while (it.hasNext()) {
6385 iteratedNames.insert(it.name());
6387 QCOMPARE(iteratedNames.size(), 2);
6388 QVERIFY(iteratedNames.contains("foo"));
6389 QVERIFY(iteratedNames.contains("bar"));
6392 // Push it on the scope chain of a new context.
6393 QScriptContext *ctx = eng.pushContext();
6394 ctx->pushScope(scope);
6395 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6396 QVERIFY(ctx->activationObject().equals(scope));
6398 // Read property from JS.
6399 QVERIFY(eng.evaluate("foo").equals(scope.property("foo")));
6400 QVERIFY(eng.evaluate("bar").equals(scope.property("bar")));
6402 // Write property from JS.
6404 QScriptValue oldValue = eng.evaluate("foo");
6405 QVERIFY(oldValue.isNumber());
6406 QScriptValue newValue = oldValue.toNumber() * 2;
6407 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6408 scope.setProperty("foo", oldValue);
6409 QVERIFY(eng.evaluate("foo").equals(oldValue));
6412 // Write to read-only property.
6413 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6416 QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math")));
6417 scope.setProperty("Math", "fake Math");
6418 QVERIFY(eng.evaluate("Math").equals(scope.property("Math")));
6420 // Variable declarations will create properties on the scope.
6421 eng.evaluate("var baz = 456");
6422 QVERIFY(scope.property("baz").equals(456));
6424 // Function declarations will create properties on the scope.
6425 eng.evaluate("function fun() { return baz; }");
6426 QVERIFY(scope.property("fun").isFunction());
6427 QVERIFY(scope.property("fun").call().equals(scope.property("baz")));
6429 // Demonstrate the limitation of a growable static scope: Once a function that
6430 // uses the scope has been compiled, it won't pick up properties that are added
6431 // to the scope later.
6433 QScriptValue fun = eng.evaluate("(function() { return futureProperty; })");
6434 QVERIFY(fun.isFunction());
6435 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6436 scope.setProperty("futureProperty", "added after the function was compiled");
6437 // If scope were dynamic, this would return the new property.
6438 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6445 #if 0 // ###FIXME: No QScript MetaObject API
6447 Q_SCRIPT_DECLARE_QMETAOBJECT(QStandardItemModel, QObject*)
6450 void tst_QJSEngine::scriptValueFromQMetaObject()
6454 QScriptValue meta = eng.scriptValueFromQMetaObject<QScriptEngine>();
6455 QVERIFY(meta.isQMetaObject());
6456 QCOMPARE(meta.toQMetaObject(), &QScriptEngine::staticMetaObject);
6457 // Because of missing Q_SCRIPT_DECLARE_QMETAOBJECT() for QScriptEngine.
6458 QEXPECT_FAIL("", "FIXME: because construct never returns invalid values", Continue);
6459 QVERIFY(!meta.construct().isValid());
6462 QScriptValue meta = eng.scriptValueFromQMetaObject<QStandardItemModel>();
6463 QVERIFY(meta.isQMetaObject());
6464 QCOMPARE(meta.toQMetaObject(), &QStandardItemModel::staticMetaObject);
6465 QScriptValue obj = meta.construct(QScriptValueList() << eng.newQObject(&eng));
6466 QVERIFY(obj.isQObject());
6467 QStandardItemModel *model = qobject_cast<QStandardItemModel*>(obj.toQObject());
6468 QVERIFY(model != 0);
6469 QCOMPARE(model->parent(), (QObject*)&eng);
6474 void tst_QJSEngine::functionPrototypeExtensions()
6476 // QJS adds connect and disconnect properties to Function.prototype.
6478 QJSValue funProto = eng.globalObject().property("Function").property("prototype");
6479 QVERIFY(funProto.isFunction());
6480 QVERIFY(funProto.property("connect").isFunction());
6481 QCOMPARE(funProto.propertyFlags("connect"), QJSValue::SkipInEnumeration);
6482 QVERIFY(funProto.property("disconnect").isFunction());
6483 QCOMPARE(funProto.propertyFlags("disconnect"), QJSValue::SkipInEnumeration);
6485 // No properties should appear in for-in statements.
6486 QJSValue props = eng.evaluate("props = []; for (var p in Function.prototype) props.push(p); props");
6487 QVERIFY(!eng.hasUncaughtException());
6488 QVERIFY(props.isArray());
6489 QCOMPARE(props.property("length").toInt(), 0);
6492 class ThreadedTestEngine : public QThread {
6498 ThreadedTestEngine()
6502 QJSEngine firstEngine;
6503 QJSEngine secondEngine;
6504 QJSValue value = firstEngine.evaluate("1");
6505 result = secondEngine.evaluate("1 + " + QString::number(value.toInteger())).toInteger();
6509 void tst_QJSEngine::threadedEngine()
6511 ThreadedTestEngine thread1;
6512 ThreadedTestEngine thread2;
6517 QCOMPARE(thread1.result, 2);
6518 QCOMPARE(thread2.result, 2);
6521 QTEST_MAIN(tst_QJSEngine)
6523 #include "tst_qjsengine.moc"