1 /****************************************************************************
3 ** Copyright (C) 2011 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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qjsengine.h>
46 #include <qgraphicsitem.h>
47 #include <qstandarditemmodel.h>
48 #include <QtCore/qnumeric.h>
51 Q_DECLARE_METATYPE(QList<int>)
52 Q_DECLARE_METATYPE(QObjectList)
57 #if defined(Q_OS_SYMBIAN)
58 # define STRINGIFY(x) #x
59 # define TOSTRING(x) STRINGIFY(x)
60 # define SRCDIR "C:/Private/" TOSTRING(SYMBIAN_SRCDIR_UID)
63 // The JavaScriptCore GC marks the C stack. To try to ensure that there is
64 // no JSObject* left in stack memory by the compiler, we call this function
65 // to zap some bytes of memory before calling collectGarbage().
66 static void zapSomeStack()
69 memset(buf, 0, sizeof(buf));
72 static void collectGarbage_helper(QJSEngine &eng)
78 class tst_QJSEngine : public QObject
84 virtual ~tst_QJSEngine();
87 void constructWithParent();
88 #if 0 // FIXME: no QScriptContext
89 void currentContext();
90 void pushPopContext();
92 #if 0 // FIXME: No prototype API in QScriptEngine
93 void getSetDefaultPrototype_int();
94 void getSetDefaultPrototype_customType();
96 #if 0 // FIXME: no QScriptContext
98 void newFunctionWithArg();
99 void newFunctionWithProto();
103 void newArray_HooliganTask218092();
104 void newArray_HooliganTask233836();
106 #if 0 // FIXME: No prototype API in QScriptEngine
107 void newVariant_defaultPrototype();
109 #if 0 // ###FIXME: No QVariant object promotion API
110 void newVariant_promoteObject();
111 void newVariant_replaceValue();
113 void newVariant_valueOfToString();
114 #if 0 // ###FIXME: No QVariant object promotion API
115 void newVariant_promoteNonObject();
116 void newVariant_promoteNonQScriptObject();
123 void newQObject_ownership();
124 void newQObject_promoteObject();
125 void newQObject_sameQObject();
126 #if 0 // FIXME: No prototype API in QScriptEngine
127 void newQObject_defaultPrototype();
129 void newQObject_promoteNonObject();
130 void newQObject_promoteNonQScriptObject();
131 #if 0 // ### FIXME: No QScript Metaobject support right now
132 void newQMetaObject();
133 void newActivationObject();
135 #if 0 // ###FIXME: No setGlobalObject support - yay
136 void getSetGlobalObjectSimple();
137 void getSetGlobalObject();
139 void globalObjectProperties();
140 void globalObjectEquals();
141 #if 0 // ###FIXME: No QScriptValueIterator API
142 void globalObjectProperties_enumerate();
144 void createGlobalObjectProperty();
145 void globalObjectGetterSetterProperty();
146 #if 0 // ###FIXME: No support for setting the global object
147 void customGlobalObjectWithPrototype();
149 void globalObjectWithCustomPrototype();
150 void builtinFunctionNames_data();
151 void builtinFunctionNames();
152 #if 0 // ###FIXME: No syntax checking result
153 void checkSyntax_data();
156 #if 0 // ###FIXME: No support for canEvaluate
157 void canEvaluate_data();
160 void evaluate_data();
162 #if 0 // ###FIXME: no support for c-style callbacks
163 void nestedEvaluate();
165 #if 0 // ### FIXME: No c-style callbacks
166 void uncaughtException();
168 void errorMessage_QT679();
169 void valueConversion_basic();
170 #if 0 // FIXME: No API for custom types
171 void valueConversion_customType();
172 void valueConversion_sequence();
174 void valueConversion_QVariant();
175 #if 0 // FIXME: No support for custom types
176 void valueConversion_hooliganTask248802();
178 void valueConversion_basic2();
179 void valueConversion_dateTime();
180 void valueConversion_regExp();
181 #if 0 // FIXME: No qScriptValueFromValue
182 void qScriptValueFromValue_noEngine();
184 #if 0 // ###FIXME: No QScriptContext
185 void importExtension();
186 void infiniteRecursion();
188 #if 0 // FIXME: No support for default prototypes
189 void castWithPrototypeChain();
191 void castWithMultipleInheritance();
192 #if 0 // ###FIXME: ScriptOwnership
193 void collectGarbage();
195 #if 0 // ###FIXME: no reportAdditionalMemoryCost API
196 void reportAdditionalMemoryCost();
198 void gcWithNestedDataStructure();
199 #if 0 // ###FIXME: No processEvents handling
200 void processEventsWhileRunning();
201 void processEventsWhileRunning_function();
202 void throwErrorFromProcessEvents_data();
203 void throwErrorFromProcessEvents();
204 void disableProcessEventsInterval();
206 #if 0 // ###FIXME: No QScriptValueIterator API
209 void numberParsing_data();
210 void numberParsing();
211 void automaticSemicolonInsertion();
212 #if 0 // ###FIXME: no abortEvaluation API
213 void abortEvaluation_notEvaluating();
214 void abortEvaluation_data();
215 void abortEvaluation();
216 void abortEvaluation_tryCatch();
217 void abortEvaluation_fromNative();
218 void abortEvaluation_QTBUG9433();
220 #if 0 // ###FIXME: no QScriptEngine::isEvaluating
221 void isEvaluating_notEvaluating();
222 void isEvaluating_fromNative();
223 void isEvaluating_fromEvent();
225 #if 0 // ###FIXME: depracated
226 void printFunctionWithCustomHandler();
227 void printThrowsException();
229 void errorConstructors();
230 void argumentsProperty_globalContext();
231 void argumentsProperty_JS();
232 #if 0 // ###FIXME: no QScriptContext API
233 void argumentsProperty_evaluateInNativeFunction();
235 void jsNumberClass();
236 void jsForInStatement_simple();
237 void jsForInStatement_prototypeProperties();
238 void jsForInStatement_mutateWhileIterating();
239 void jsForInStatement_arrays();
240 void jsForInStatement_nullAndUndefined();
241 void jsFunctionDeclarationAsStatement();
242 void stringObjects();
243 void jsStringPrototypeReplaceBugs();
244 void getterSetterThisObject_global();
245 void getterSetterThisObject_plain();
246 void getterSetterThisObject_prototypeChain();
247 #if 0 // ###FIXME: no QScriptContext API
248 void getterSetterThisObject_activation();
250 void jsContinueInSwitch();
251 void jsShadowReadOnlyPrototypeProperty();
253 void jsReservedWords_data();
254 void jsReservedWords();
255 void jsFutureReservedWords_data();
256 void jsFutureReservedWords();
257 void jsThrowInsideWithStatement();
258 #if 0 // ###FIXME: No QScriptEngineAgent API
259 void getSetAgent_ownership();
260 void getSetAgent_deleteAgent();
261 void getSetAgent_differentEngine();
263 #if 0 // ###FIXME: No QScriptString API
264 void reentrancy_stringHandles();
266 #if 0 // ###FIXME: No processEventsInterval API
267 void reentrancy_processEventsInterval();
269 #if 0 // FIXME: No support for custom types
270 void reentrancy_typeConversion();
272 void reentrancy_globalObjectProperties();
273 void reentrancy_Array();
274 void reentrancy_objectCreation();
275 void jsIncDecNonObjectProperty();
276 #if 0 // ###FIXME: no installTranslatorFunctions API
277 void installTranslatorFunctions();
278 void translateScript_data();
279 void translateScript();
280 void translateScript_crossScript();
281 void translateScript_callQsTrFromNative();
282 void translateScript_trNoOp();
283 void translateScript_callQsTrFromCpp();
284 void translateWithInvalidArgs_data();
285 void translateWithInvalidArgs();
286 void translationContext_data();
287 void translationContext();
288 void translateScriptIdBased();
289 void translateScriptUnicode_data();
290 void translateScriptUnicode();
291 void translateScriptUnicodeIdBased_data();
292 void translateScriptUnicodeIdBased();
293 void translateFromBuiltinCallback();
295 #if 0 // ###FIXME: No QScriptValue::scope API
296 void functionScopes();
298 #if 0 // ###FIXME: No QScriptContext API
299 void nativeFunctionScopes();
301 #if 0 // ###FIXME: No QScriptProgram API
302 void evaluateProgram();
303 void evaluateProgram_customScope();
304 void evaluateProgram_closure();
305 void evaluateProgram_executeLater();
306 void evaluateProgram_multipleEngines();
307 void evaluateProgram_empty();
309 #if 0 // ###FIXME: No QScriptContext API
310 void collectGarbageAfterConnect();
311 void collectGarbageAfterNativeArguments();
312 void promoteThisObjectToQObjectInConstructor();
314 #if 0 // ###FIXME: No QScript MetaObject API
315 void scriptValueFromQMetaObject();
318 void qRegExpInport_data();
319 void qRegExpInport();
320 #if 0 // ###FIXME: No QScriptContext API
323 #if 0 // ###FIXME: No QSCriptDeclarativeClass API
324 void newFixedStaticScopeObject();
325 void newGrowingStaticScopeObject();
327 void dateRoundtripJSQtJS();
328 void dateRoundtripQtJSQt();
329 void dateConversionJSQt();
330 void dateConversionQtJS();
333 tst_QJSEngine::tst_QJSEngine()
337 tst_QJSEngine::~tst_QJSEngine()
341 void tst_QJSEngine::constructWithParent()
343 QPointer<QJSEngine> ptr;
346 QJSEngine *engine = new QJSEngine(&obj);
352 #if 0 // FIXME: no QScriptContext
353 void tst_QJSEngine::currentContext()
356 QScriptContext *globalCtx = eng.currentContext();
357 QVERIFY(globalCtx != 0);
358 QVERIFY(globalCtx->parentContext() == 0);
359 QCOMPARE(globalCtx->engine(), &eng);
360 QCOMPARE(globalCtx->argumentCount(), 0);
361 QCOMPARE(globalCtx->backtrace().size(), 1);
362 QVERIFY(!globalCtx->isCalledAsConstructor());
363 QVERIFY(!globalCtx->callee().isValid());
364 QCOMPARE(globalCtx->state(), QScriptContext::NormalState);
365 QVERIFY(globalCtx->thisObject().strictlyEquals(eng.globalObject()));
366 QVERIFY(globalCtx->activationObject().strictlyEquals(eng.globalObject()));
367 QVERIFY(globalCtx->argumentsObject().isObject());
370 void tst_QJSEngine::pushPopContext()
373 QScriptContext *globalCtx = eng.currentContext();
374 QScriptContext *ctx = eng.pushContext();
376 QCOMPARE(ctx->parentContext(), globalCtx);
377 QVERIFY(!ctx->isCalledAsConstructor());
378 QVERIFY(!ctx->callee().isValid());
379 QVERIFY(ctx->thisObject().strictlyEquals(eng.globalObject()));
380 QCOMPARE(ctx->argumentCount(), 0);
381 QCOMPARE(ctx->backtrace().size(), 2);
382 QCOMPARE(ctx->engine(), &eng);
383 QCOMPARE(ctx->state(), QScriptContext::NormalState);
384 QVERIFY(ctx->activationObject().isObject());
385 QVERIFY(ctx->argumentsObject().isObject());
387 QScriptContext *ctx2 = eng.pushContext();
389 QCOMPARE(ctx2->parentContext(), ctx);
390 QVERIFY(!ctx2->activationObject().strictlyEquals(ctx->activationObject()));
391 QVERIFY(!ctx2->argumentsObject().strictlyEquals(ctx->argumentsObject()));
395 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
396 eng.popContext(); // ignored
397 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
398 eng.popContext(); // ignored
401 static QScriptValue myFunction(QScriptContext *, QScriptEngine *eng)
403 return eng->nullValue();
406 static QScriptValue myFunctionWithVoidArg(QScriptContext *, QScriptEngine *eng, void *)
408 return eng->nullValue();
411 static QScriptValue myThrowingFunction(QScriptContext *ctx, QScriptEngine *)
413 return ctx->throwError("foo");
416 static QScriptValue myFunctionThatReturns(QScriptContext *, QScriptEngine *eng)
418 return QScriptValue(eng, 42);
421 static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext *, QScriptEngine *)
423 return QScriptValue(1024);
426 static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext *, QScriptEngine *, void *arg)
428 QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg);
429 return QScriptValue(wrongEngine, 42);
432 static QScriptValue sumFunction(QScriptContext *context, QScriptEngine *engine)
436 for (int i = 0; i < context->argumentCount(); i++) {
437 QScriptValue n = context->argument(i);
439 sum += n.toInteger();
442 return QScriptValue(engine, sum);
445 void tst_QJSEngine::newFunction()
449 QScriptValue fun = eng.newFunction(myFunction);
450 QCOMPARE(fun.isValid(), true);
451 QCOMPARE(fun.isFunction(), true);
452 QCOMPARE(fun.isObject(), true);
453 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
454 // a prototype property is automatically constructed
456 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
457 QVERIFY(prot.isObject());
458 QVERIFY(prot.property("constructor").strictlyEquals(fun));
459 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
460 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
462 // prototype should be Function.prototype
463 QCOMPARE(fun.prototype().isValid(), true);
464 QCOMPARE(fun.prototype().isFunction(), true);
465 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
467 QCOMPARE(fun.call().isNull(), true);
468 QCOMPARE(fun.construct().isObject(), true);
472 void tst_QJSEngine::newFunctionWithArg()
476 QScriptValue fun = eng.newFunction(myFunctionWithVoidArg, (void*)this);
477 QVERIFY(fun.isFunction());
478 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
479 // a prototype property is automatically constructed
481 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
482 QVERIFY(prot.isObject());
483 QVERIFY(prot.property("constructor").strictlyEquals(fun));
484 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
485 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
487 // prototype should be Function.prototype
488 QCOMPARE(fun.prototype().isValid(), true);
489 QCOMPARE(fun.prototype().isFunction(), true);
490 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
492 QCOMPARE(fun.call().isNull(), true);
493 QCOMPARE(fun.construct().isObject(), true);
497 void tst_QJSEngine::newFunctionWithProto()
501 QScriptValue proto = eng.newObject();
502 QScriptValue fun = eng.newFunction(myFunction, proto);
503 QCOMPARE(fun.isValid(), true);
504 QCOMPARE(fun.isFunction(), true);
505 QCOMPARE(fun.isObject(), true);
506 // internal prototype should be Function.prototype
507 QCOMPARE(fun.prototype().isValid(), true);
508 QCOMPARE(fun.prototype().isFunction(), true);
509 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
510 // public prototype should be the one we passed
511 QCOMPARE(fun.property("prototype").strictlyEquals(proto), true);
512 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
513 QCOMPARE(proto.property("constructor").strictlyEquals(fun), true);
514 QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);
516 QCOMPARE(fun.call().isNull(), true);
517 QCOMPARE(fun.construct().isObject(), true);
519 // whether the return value is correct
521 QScriptValue fun = eng.newFunction(myFunctionThatReturns);
522 QCOMPARE(fun.isValid(), true);
523 QCOMPARE(fun.isFunction(), true);
524 QCOMPARE(fun.isObject(), true);
526 QScriptValue result = fun.call();
527 QCOMPARE(result.isNumber(), true);
528 QCOMPARE(result.toInt32(), 42);
530 // whether the return value is assigned to the correct engine
532 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine);
533 QCOMPARE(fun.isValid(), true);
534 QCOMPARE(fun.isFunction(), true);
535 QCOMPARE(fun.isObject(), true);
537 QScriptValue result = fun.call();
538 QCOMPARE(result.engine(), &eng);
539 QCOMPARE(result.isNumber(), true);
540 QCOMPARE(result.toInt32(), 1024);
542 // whether the return value is undefined when returning a value with wrong engine
544 QScriptEngine wrongEngine;
546 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void *>(&wrongEngine));
547 QCOMPARE(fun.isValid(), true);
548 QCOMPARE(fun.isFunction(), true);
549 QCOMPARE(fun.isObject(), true);
551 QTest::ignoreMessage(QtWarningMsg, "QScriptValue::call(): Value from different engine returned from native function, returning undefined value instead.");
552 QScriptValue result = fun.call();
553 QCOMPARE(result.isValid(), true);
554 QCOMPARE(result.isUndefined(), true);
556 // checking if arguments are passed correctly
558 QScriptEngine wrongEngine;
560 QScriptValue fun = eng.newFunction(sumFunction);
561 QCOMPARE(fun.isValid(), true);
562 QCOMPARE(fun.isFunction(), true);
563 QCOMPARE(fun.isObject(), true);
565 QScriptValue result = fun.call();
566 QCOMPARE(result.isNumber(), true);
567 QCOMPARE(result.toInt32(), 0);
569 result = fun.call(QScriptValue(), QScriptValueList() << 1);
570 QCOMPARE(result.isNumber(), true);
571 QCOMPARE(result.toInt32(), 1);
573 result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3);
574 QCOMPARE(result.isNumber(), true);
575 QCOMPARE(result.toInt32(), 6);
577 result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3 << 4);
578 QCOMPARE(result.isNumber(), true);
579 QCOMPARE(result.toInt32(), 10);
584 void tst_QJSEngine::newObject()
587 QJSValue object = eng.newObject();
588 QCOMPARE(object.isValid(), true);
589 QCOMPARE(object.isObject(), true);
590 QCOMPARE(object.isFunction(), false);
591 // ###FIXME: No QScriptClass QCOMPARE(object.scriptClass(), (QScriptClass*)0);
592 // prototype should be Object.prototype
593 QCOMPARE(object.prototype().isValid(), true);
594 QCOMPARE(object.prototype().isObject(), true);
595 QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
598 void tst_QJSEngine::newArray()
601 QJSValue array = eng.newArray();
602 QCOMPARE(array.isValid(), true);
603 QCOMPARE(array.isArray(), true);
604 QCOMPARE(array.isObject(), true);
605 QVERIFY(!array.isFunction());
606 // ###FIXME: No QScriptClass QCOMPARE(array.scriptClass(), (QScriptClass*)0);
607 // prototype should be Array.prototype
608 QCOMPARE(array.prototype().isValid(), true);
609 QCOMPARE(array.prototype().isArray(), true);
610 QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
613 void tst_QJSEngine::newArray_HooliganTask218092()
617 QJSValue ret = eng.evaluate("[].splice(0, 0, 'a')");
618 QVERIFY(ret.isArray());
619 QCOMPARE(ret.property("length").toInt32(), 0);
622 QJSValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
623 QVERIFY(ret.isArray());
624 QCOMPARE(ret.property("length").toInt32(), 1);
627 QJSValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
628 QVERIFY(ret.isArray());
629 QCOMPARE(ret.property("length").toInt32(), 1);
632 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
633 QVERIFY(ret.isArray());
634 QCOMPARE(ret.property("length").toInt32(), 2);
637 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
638 QVERIFY(ret.isArray());
639 QCOMPARE(ret.property("length").toInt32(), 2);
643 void tst_QJSEngine::newArray_HooliganTask233836()
647 // According to ECMA-262, this should cause a RangeError.
648 QJSValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')");
649 QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError")));
652 QJSValue ret = eng.newArray(0xFFFFFFFF);
653 QEXPECT_FAIL("", "The maximum length of arrays is defined by v8 currently and differs from QtScript", Abort);
654 QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF));
655 ret.setProperty(0xFFFFFFFF, 123);
656 QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF));
657 QVERIFY(ret.property(0xFFFFFFFF).isNumber());
658 QCOMPARE(ret.property(0xFFFFFFFF).toInt32(), 123);
659 ret.setProperty(123, 456);
660 QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF));
661 QVERIFY(ret.property(123).isNumber());
662 QCOMPARE(ret.property(123).toInt32(), 456);
666 void tst_QJSEngine::newVariant()
670 QJSValue opaque = eng.newVariant(QVariant());
671 QCOMPARE(opaque.isValid(), true);
672 QCOMPARE(opaque.isVariant(), true);
673 QVERIFY(!opaque.isFunction());
674 QCOMPARE(opaque.isObject(), true);
675 QCOMPARE(opaque.prototype().isValid(), true);
676 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
677 QCOMPARE(opaque.prototype().isVariant(), true);
678 QVERIFY(opaque.property("valueOf").call(opaque).isUndefined());
682 #if 0 // FIXME: No prototype API in QScriptEngine
683 void tst_QJSEngine::newVariant_defaultPrototype()
685 // default prototype should be set automatically
688 QScriptValue proto = eng.newObject();
689 eng.setDefaultPrototype(qMetaTypeId<QString>(), proto);
690 QScriptValue ret = eng.newVariant(QVariant(QString::fromLatin1("hello")));
691 QVERIFY(ret.isVariant());
692 // ###FIXME: No QScriptClass QCOMPARE(ret.scriptClass(), (QScriptClass*)0);
693 QVERIFY(ret.prototype().strictlyEquals(proto));
694 eng.setDefaultPrototype(qMetaTypeId<QString>(), QScriptValue());
695 QScriptValue ret2 = eng.newVariant(QVariant(QString::fromLatin1("hello")));
696 QVERIFY(ret2.isVariant());
697 QVERIFY(!ret2.prototype().strictlyEquals(proto));
702 #if 0 // ###FIXME: No QVariant object promotion API
703 void tst_QJSEngine::newVariant_promoteObject()
705 // "promote" plain object to variant
708 QScriptValue object = eng.newObject();
709 object.setProperty("foo", eng.newObject());
710 object.setProperty("bar", object.property("foo"));
711 QVERIFY(object.property("foo").isObject());
712 QVERIFY(!object.property("foo").isVariant());
713 QScriptValue originalProto = object.property("foo").prototype();
714 QSKIP("It is not possible to promote plain object to a wrapper", SkipAll);
715 QScriptValue ret = eng.newVariant(object.property("foo"), QVariant(123));
716 QVERIFY(ret.isValid());
717 QVERIFY(ret.strictlyEquals(object.property("foo")));
718 QVERIFY(ret.isVariant());
719 QVERIFY(object.property("foo").isVariant());
720 QVERIFY(object.property("bar").isVariant());
721 QCOMPARE(ret.toVariant(), QVariant(123));
722 QVERIFY(ret.prototype().strictlyEquals(originalProto));
726 void tst_QJSEngine::newVariant_replaceValue()
728 // replace value of existing object
731 QScriptValue object = eng.newVariant(QVariant(123));
732 for (int x = 0; x < 2; ++x) {
733 QScriptValue ret = eng.newVariant(object, QVariant(456));
734 QVERIFY(ret.isValid());
735 QVERIFY(ret.strictlyEquals(object));
736 QVERIFY(ret.isVariant());
737 QCOMPARE(ret.toVariant(), QVariant(456));
743 void tst_QJSEngine::newVariant_valueOfToString()
745 // valueOf() and toString()
748 QJSValue object = eng.newVariant(QVariant(123));
749 QJSValue value = object.property("valueOf").call(object);
750 QVERIFY(value.isNumber());
751 QCOMPARE(value.toInt32(), 123);
752 QCOMPARE(object.toString(), QString::fromLatin1("123"));
753 QCOMPARE(object.toVariant().toString(), object.toString());
756 QJSValue object = eng.newVariant(QVariant(QString::fromLatin1("hello")));
757 QJSValue value = object.property("valueOf").call(object);
758 QVERIFY(value.isString());
759 QCOMPARE(value.toString(), QString::fromLatin1("hello"));
760 QCOMPARE(object.toString(), QString::fromLatin1("hello"));
761 QCOMPARE(object.toVariant().toString(), object.toString());
764 QJSValue object = eng.newVariant(QVariant(false));
765 QJSValue value = object.property("valueOf").call(object);
766 QVERIFY(value.isBoolean());
767 QCOMPARE(value.toBoolean(), false);
768 QCOMPARE(object.toString(), QString::fromLatin1("false"));
769 QCOMPARE(object.toVariant().toString(), object.toString());
772 QJSValue object = eng.newVariant(QVariant(QPoint(10, 20)));
773 QJSValue value = object.property("valueOf").call(object);
774 QVERIFY(value.isObject());
775 QVERIFY(value.strictlyEquals(object));
776 QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)"));
780 #if 0 // ###FIXME: No QVariant object promotion API
781 void tst_QJSEngine::newVariant_promoteNonObject()
786 QScriptValue ret = eng.newVariant(123, var);
787 QVERIFY(ret.isVariant());
788 QCOMPARE(ret.toVariant(), var);
792 void tst_QJSEngine::newVariant_promoteNonQScriptObject()
794 QSKIP("This test relay on limitation of QtScript JSC implementation", SkipAll);
797 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newVariant(): changing class of non-QScriptObject not supported");
798 QScriptValue ret = eng.newVariant(eng.newArray(), 123);
799 QVERIFY(!ret.isValid());
804 void tst_QJSEngine::newRegExp()
807 for (int x = 0; x < 2; ++x) {
810 rexp = eng.newRegExp("foo", "bar");
812 rexp = eng.newRegExp(QRegExp("foo"));
813 QCOMPARE(rexp.isValid(), true);
814 QCOMPARE(rexp.isRegExp(), true);
815 QCOMPARE(rexp.isObject(), true);
816 QVERIFY(rexp.isFunction()); // in JSC, RegExp objects are callable
817 // prototype should be RegExp.prototype
818 QCOMPARE(rexp.prototype().isValid(), true);
819 QCOMPARE(rexp.prototype().isObject(), true);
820 QCOMPARE(rexp.prototype().isRegExp(), false);
821 QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
823 QCOMPARE(rexp.toRegExp().pattern(), QRegExp("foo").pattern());
827 void tst_QJSEngine::jsRegExp()
829 // See ECMA-262 Section 15.10, "RegExp Objects".
830 // These should really be JS-only tests, as they test the implementation's
831 // ECMA-compliance, not the C++ API. Compliance should already be covered
832 // by the Mozilla tests (qscriptjstestsuite).
833 // We can consider updating the expected results of this test if the
834 // RegExp implementation changes.
837 QJSValue r = eng.evaluate("/foo/gim");
838 QVERIFY(r.isRegExp());
839 QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim"));
841 QJSValue rxCtor = eng.globalObject().property("RegExp");
842 QJSValue r2 = rxCtor.call(QJSValue(), QJSValueList() << r);
843 QVERIFY(r2.isRegExp());
844 QVERIFY(r2.strictlyEquals(r));
846 QJSValue r3 = rxCtor.call(QJSValue(), QJSValueList() << r << "gim");
847 QVERIFY(r3.isError());
848 QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another
850 QJSValue r4 = rxCtor.call(QJSValue(), QJSValueList() << "foo" << "gim");
851 QVERIFY(r4.isRegExp());
853 QJSValue r5 = rxCtor.construct(QJSValueList() << r);
854 QVERIFY(r5.isRegExp());
855 QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
856 // In JSC, constructing a RegExp from another produces the same identical object.
857 // This is different from SpiderMonkey and old back-end.
858 QVERIFY(!r5.strictlyEquals(r));
860 QEXPECT_FAIL("", "V8 and jsc ignores invalid flags", Continue); //https://bugs.webkit.org/show_bug.cgi?id=41614
861 QJSValue r6 = rxCtor.construct(QJSValueList() << "foo" << "bar");
862 QVERIFY(r6.isError());
863 // QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
866 QJSValue r7 = eng.evaluate("/foo/gimp");
867 /* v8 and jsc ignores invalid flags
868 QVERIFY(r7.isError());
869 QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
872 // JSC doesn't complain about duplicate flags.
873 QJSValue r8 = eng.evaluate("/foo/migmigmig");
874 QVERIFY(r8.isRegExp());
875 QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim"));
877 QJSValue r9 = rxCtor.construct();
878 QVERIFY(r9.isRegExp());
879 QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));
881 QJSValue r10 = rxCtor.construct(QJSValueList() << "" << "gim");
882 QVERIFY(r10.isRegExp());
883 QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));
885 QJSValue r11 = rxCtor.construct(QJSValueList() << "{1.*}" << "g");
886 QVERIFY(r11.isRegExp());
887 QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
890 void tst_QJSEngine::newDate()
895 QJSValue date = eng.newDate(0);
896 QCOMPARE(date.isValid(), true);
897 QCOMPARE(date.isDate(), true);
898 QCOMPARE(date.isObject(), true);
899 QVERIFY(!date.isFunction());
900 // prototype should be Date.prototype
901 QCOMPARE(date.prototype().isValid(), true);
902 QCOMPARE(date.prototype().isDate(), true);
903 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
907 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
908 QJSValue date = eng.newDate(dt);
909 QCOMPARE(date.isValid(), true);
910 QCOMPARE(date.isDate(), true);
911 QCOMPARE(date.isObject(), true);
912 // prototype should be Date.prototype
913 QCOMPARE(date.prototype().isValid(), true);
914 QCOMPARE(date.prototype().isDate(), true);
915 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
917 QCOMPARE(date.toDateTime(), dt);
921 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
922 QJSValue date = eng.newDate(dt);
923 // toDateTime() result should be in local time
924 QCOMPARE(date.toDateTime(), dt.toLocalTime());
928 void tst_QJSEngine::jsParseDate()
931 // Date.parse() should return NaN when it fails
933 QJSValue ret = eng.evaluate("Date.parse()");
934 QVERIFY(ret.isNumber());
935 QVERIFY(qIsNaN(ret.toNumber()));
938 // Date.parse() should be able to parse the output of Date().toString()
939 #ifndef Q_WS_WIN // TODO: Test and remove this since 169701 has been fixed
941 QJSValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
942 QVERIFY(ret.isBoolean());
943 QCOMPARE(ret.toBoolean(), true);
948 void tst_QJSEngine::newQObject()
953 QJSValue qobject = eng.newQObject(0);
954 QCOMPARE(qobject.isValid(), true);
955 QCOMPARE(qobject.isNull(), true);
956 QCOMPARE(qobject.isObject(), false);
957 QCOMPARE(qobject.toQObject(), (QObject *)0);
960 QJSValue qobject = eng.newQObject(this);
961 QCOMPARE(qobject.isValid(), true);
962 QCOMPARE(qobject.isQObject(), true);
963 QCOMPARE(qobject.isObject(), true);
964 QCOMPARE(qobject.toQObject(), (QObject *)this);
965 QVERIFY(!qobject.isFunction());
966 // prototype should be QObject.prototype
967 QCOMPARE(qobject.prototype().isValid(), true);
968 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
969 QCOMPARE(qobject.prototype().isQObject(), true);
970 // ###FIXME: No QScriptClass QCOMPARE(qobject.scriptClass(), (QScriptClass*)0);
974 void tst_QJSEngine::newQObject_ownership()
976 #if 0 // FIXME: ownership tests need to be revivewed
979 QPointer<QObject> ptr = new QObject();
982 QScriptValue v = eng.newQObject(ptr, QScriptEngine::ScriptOwnership);
984 eng.evaluate("gc()");
986 QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue);
990 QPointer<QObject> ptr = new QObject();
993 QScriptValue v = eng.newQObject(ptr, QScriptEngine::QtOwnership);
995 QObject *before = ptr;
996 eng.evaluate("gc()");
997 QVERIFY(ptr == before);
1001 QObject *parent = new QObject();
1002 QObject *child = new QObject(parent);
1003 QScriptValue v = eng.newQObject(child, QScriptEngine::QtOwnership);
1004 QCOMPARE(v.toQObject(), child);
1006 QCOMPARE(v.toQObject(), (QObject *)0);
1009 QPointer<QObject> ptr = new QObject();
1012 QScriptValue v = eng.newQObject(ptr, QScriptEngine::AutoOwnership);
1014 eng.evaluate("gc()");
1015 // no parent, so it should be like ScriptOwnership
1017 QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue);
1021 QObject *parent = new QObject();
1022 QPointer<QObject> child = new QObject(parent);
1023 QVERIFY(child != 0);
1025 QScriptValue v = eng.newQObject(child, QScriptEngine::AutoOwnership);
1027 eng.evaluate("gc()");
1028 // has parent, so it should be like QtOwnership
1029 QVERIFY(child != 0);
1035 void tst_QJSEngine::newQObject_promoteObject()
1037 #if 0 // ### FIXME: object promotion is not supported
1039 // "promote" plain object to QObject
1041 QScriptValue obj = eng.newObject();
1042 QScriptValue originalProto = obj.prototype();
1043 QScriptValue ret = eng.newQObject(obj, this);
1044 QVERIFY(ret.isValid());
1045 QVERIFY(ret.isQObject());
1046 QVERIFY(ret.strictlyEquals(obj));
1047 QVERIFY(obj.isQObject());
1048 QCOMPARE(ret.toQObject(), (QObject *)this);
1049 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1050 QScriptValue val = ret.property("objectName");
1051 QVERIFY(val.isString());
1053 // "promote" variant object to QObject
1055 QScriptValue obj = eng.newVariant(123);
1056 QVERIFY(obj.isVariant());
1057 QScriptValue originalProto = obj.prototype();
1058 QScriptValue ret = eng.newQObject(obj, this);
1059 QVERIFY(ret.isQObject());
1060 QVERIFY(ret.strictlyEquals(obj));
1061 QVERIFY(obj.isQObject());
1062 QCOMPARE(ret.toQObject(), (QObject *)this);
1063 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1065 // replace QObject* of existing object
1067 QScriptValue object = eng.newVariant(123);
1068 QScriptValue originalProto = object.prototype();
1069 QObject otherQObject;
1070 for (int x = 0; x < 2; ++x) {
1071 QScriptValue ret = eng.newQObject(object, &otherQObject);
1072 QVERIFY(ret.isValid());
1073 QVERIFY(ret.isQObject());
1074 QVERIFY(ret.strictlyEquals(object));
1075 QCOMPARE(ret.toQObject(), (QObject *)&otherQObject);
1076 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1082 void tst_QJSEngine::newQObject_sameQObject()
1084 #if 0 // ###FIXME: No QObjectWrapOptions API
1085 QSKIP("This test stongly relay on strictlyEquals feature that would change in near future", SkipAll);
1087 // calling newQObject() several times with same object
1088 for (int x = 0; x < 2; ++x) {
1090 // the default is to create a new wrapper object
1091 QScriptValue obj1 = eng.newQObject(&qobj);
1092 QScriptValue obj2 = eng.newQObject(&qobj);
1093 QVERIFY(!obj2.strictlyEquals(obj1));
1095 QScriptEngine::QObjectWrapOptions opt = 0;
1096 bool preferExisting = (x != 0);
1098 opt |= QScriptEngine::PreferExistingWrapperObject;
1100 QScriptValue obj3 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
1101 QVERIFY(!obj3.strictlyEquals(obj2));
1102 QScriptValue obj4 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
1103 QCOMPARE(obj4.strictlyEquals(obj3), preferExisting);
1105 QScriptValue obj5 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
1106 QVERIFY(!obj5.strictlyEquals(obj4));
1107 QScriptValue obj6 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
1108 QCOMPARE(obj6.strictlyEquals(obj5), preferExisting);
1110 QScriptValue obj7 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
1111 QScriptEngine::ExcludeSuperClassMethods | opt);
1112 QVERIFY(!obj7.strictlyEquals(obj6));
1113 QScriptValue obj8 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
1114 QScriptEngine::ExcludeSuperClassMethods | opt);
1115 QCOMPARE(obj8.strictlyEquals(obj7), preferExisting);
1120 #if 0 // FIXME: No prototype API in QScriptEngine
1121 void tst_QJSEngine::newQObject_defaultPrototype()
1124 // newQObject() should set the default prototype, if one has been registered
1126 QScriptValue oldQObjectProto = eng.defaultPrototype(qMetaTypeId<QObject*>());
1128 QScriptValue qobjectProto = eng.newObject();
1129 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), qobjectProto);
1131 QScriptValue ret = eng.newQObject(this);
1132 QVERIFY(ret.prototype().equals(qobjectProto));
1134 QScriptValue tstProto = eng.newObject();
1135 int typeId = qRegisterMetaType<tst_QJSEngine*>("tst_QJSEngine*");
1136 eng.setDefaultPrototype(typeId, tstProto);
1138 QScriptValue ret = eng.newQObject(this);
1139 QVERIFY(ret.prototype().equals(tstProto));
1142 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), oldQObjectProto);
1143 eng.setDefaultPrototype(typeId, QScriptValue());
1148 void tst_QJSEngine::newQObject_promoteNonObject()
1150 #if 0 // ### FIXME: object promotion is not supported
1153 QScriptValue ret = eng.newQObject(123, this);
1154 QVERIFY(ret.isQObject());
1155 QCOMPARE(ret.toQObject(), this);
1160 void tst_QJSEngine::newQObject_promoteNonQScriptObject()
1162 #if 0 // ### FIXME: object promotion is not supported
1163 QSKIP("Promotion of non QScriptObjects kind of works (there is not difference between Object and Array, look at comments in newQObject implementation).", SkipAll);
1166 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newQObject(): changing class of non-QScriptObject not supported");
1167 QScriptValue ret = eng.newQObject(eng.newArray(), this);
1168 QVERIFY(!ret.isValid());
1173 #if 0 // ### FIXME: No QScript Metaobject support right now
1175 Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*)
1176 Q_SCRIPT_DECLARE_QMETAOBJECT(QWidget, QWidget*)
1179 static QScriptValue myConstructor(QScriptContext *ctx, QScriptEngine *eng)
1182 if (ctx->isCalledAsConstructor()) {
1183 obj = ctx->thisObject();
1185 obj = eng->newObject();
1186 obj.setPrototype(ctx->callee().property("prototype"));
1188 obj.setProperty("isCalledAsConstructor", QScriptValue(eng, ctx->isCalledAsConstructor()));
1192 static QScriptValue instanceofJS(const QScriptValue &inst, const QScriptValue &ctor)
1194 return inst.engine()->evaluate("(function(inst, ctor) { return inst instanceof ctor; })")
1195 .call(QScriptValue(), QScriptValueList() << inst << ctor);
1198 void tst_QJSEngine::newQMetaObject()
1202 QScriptValue qclass = eng.newQMetaObject<QObject>();
1203 QScriptValue qclass2 = eng.newQMetaObject<QWidget>();
1205 QScriptValue qclass = qScriptValueFromQMetaObject<QObject>(&eng);
1206 QScriptValue qclass2 = qScriptValueFromQMetaObject<QWidget>(&eng);
1208 QCOMPARE(qclass.isValid(), true);
1209 QCOMPARE(qclass.isQMetaObject(), true);
1210 QCOMPARE(qclass.toQMetaObject(), &QObject::staticMetaObject);
1211 QCOMPARE(qclass.isFunction(), true);
1212 QVERIFY(qclass.property("prototype").isObject());
1214 QCOMPARE(qclass2.isValid(), true);
1215 QCOMPARE(qclass2.isQMetaObject(), true);
1216 QCOMPARE(qclass2.toQMetaObject(), &QWidget::staticMetaObject);
1217 QCOMPARE(qclass2.isFunction(), true);
1218 QVERIFY(qclass2.property("prototype").isObject());
1220 // prototype should be QMetaObject.prototype
1221 QCOMPARE(qclass.prototype().isObject(), true);
1222 QCOMPARE(qclass2.prototype().isObject(), true);
1224 QScriptValue instance = qclass.construct();
1225 QCOMPARE(instance.isQObject(), true);
1226 QCOMPARE(instance.toQObject()->metaObject(), qclass.toQMetaObject());
1227 QEXPECT_FAIL("", "FIXME: newQMetaObject not implemented properly yet", Abort);
1228 QVERIFY(instance.instanceOf(qclass));
1229 QVERIFY(instanceofJS(instance, qclass).strictlyEquals(true));
1231 QScriptValue instance2 = qclass2.construct();
1232 QCOMPARE(instance2.isQObject(), true);
1233 QCOMPARE(instance2.toQObject()->metaObject(), qclass2.toQMetaObject());
1234 QVERIFY(instance2.instanceOf(qclass2));
1235 QVERIFY(instanceofJS(instance2, qclass2).strictlyEquals(true));
1236 QVERIFY(!instance2.instanceOf(qclass));
1237 QVERIFY(instanceofJS(instance2, qclass).strictlyEquals(false));
1239 QScriptValueList args;
1241 QScriptValue instance3 = qclass.construct(args);
1242 QCOMPARE(instance3.isQObject(), true);
1243 QCOMPARE(instance3.toQObject()->parent(), instance.toQObject());
1244 QVERIFY(instance3.instanceOf(qclass));
1245 QVERIFY(instanceofJS(instance3, qclass).strictlyEquals(true));
1246 QVERIFY(!instance3.instanceOf(qclass2));
1247 QVERIFY(instanceofJS(instance3, qclass2).strictlyEquals(false));
1250 QPointer<QObject> qpointer1 = instance.toQObject();
1251 QPointer<QObject> qpointer2 = instance2.toQObject();
1252 QPointer<QObject> qpointer3 = instance3.toQObject();
1258 // verify that AutoOwnership is in effect
1259 instance = QScriptValue();
1260 collectGarbage_helper(eng);
1262 QVERIFY(!qpointer1);
1264 QVERIFY(!qpointer3); // was child of instance
1266 QVERIFY(instance.toQObject() == 0);
1267 QVERIFY(instance3.toQObject() == 0); // was child of instance
1268 QVERIFY(instance2.toQObject() != 0);
1269 instance2 = QScriptValue();
1270 collectGarbage_helper(eng);
1271 QVERIFY(instance2.toQObject() == 0);
1273 // with custom constructor
1274 QScriptValue ctor = eng.newFunction(myConstructor);
1275 QScriptValue qclass3 = eng.newQMetaObject(&QObject::staticMetaObject, ctor);
1276 QVERIFY(qclass3.property("prototype").equals(ctor.property("prototype")));
1278 QScriptValue ret = qclass3.call();
1279 QVERIFY(ret.isObject());
1280 QVERIFY(ret.property("isCalledAsConstructor").isBoolean());
1281 QVERIFY(!ret.property("isCalledAsConstructor").toBoolean());
1282 QVERIFY(ret.instanceOf(qclass3));
1283 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1284 QVERIFY(!ret.instanceOf(qclass));
1285 QVERIFY(instanceofJS(ret, qclass).strictlyEquals(false));
1288 QScriptValue ret = qclass3.construct();
1289 QVERIFY(ret.isObject());
1290 QVERIFY(ret.property("isCalledAsConstructor").isBoolean());
1291 QVERIFY(ret.property("isCalledAsConstructor").toBoolean());
1292 QVERIFY(ret.instanceOf(qclass3));
1293 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1294 QVERIFY(!ret.instanceOf(qclass2));
1295 QVERIFY(instanceofJS(ret, qclass2).strictlyEquals(false));
1299 qclass2.setProperty("prototype", qclass.construct());
1300 QVERIFY(qclass2.construct().instanceOf(qclass));
1301 QVERIFY(instanceofJS(qclass2.construct(), qclass).strictlyEquals(true));
1303 // with meta-constructor
1304 QScriptValue qclass4 = eng.newQMetaObject(&QObject::staticMetaObject);
1306 QScriptValue inst = qclass4.construct();
1307 QVERIFY(inst.isQObject());
1308 QVERIFY(inst.toQObject() != 0);
1309 QCOMPARE(inst.toQObject()->parent(), (QObject*)0);
1310 QVERIFY(inst.instanceOf(qclass4));
1311 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1312 QVERIFY(!inst.instanceOf(qclass3));
1313 QVERIFY(instanceofJS(inst, qclass3).strictlyEquals(false));
1316 QScriptValue inst = qclass4.construct(QScriptValueList() << eng.newQObject(this));
1317 QVERIFY(inst.isQObject());
1318 QVERIFY(inst.toQObject() != 0);
1319 QCOMPARE(inst.toQObject()->parent(), (QObject*)this);
1320 QVERIFY(inst.instanceOf(qclass4));
1321 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1322 QVERIFY(!inst.instanceOf(qclass2));
1323 QVERIFY(instanceofJS(inst, qclass2).strictlyEquals(false));
1328 #if 0 // ###FIXME: No activation object support
1329 void tst_QJSEngine::newActivationObject()
1331 QSKIP("internal function not implemented in JSC-based back-end", SkipAll);
1333 QScriptValue act = eng.newActivationObject();
1334 QEXPECT_FAIL("", "", Continue);
1335 QCOMPARE(act.isValid(), true);
1336 QEXPECT_FAIL("", "", Continue);
1337 QCOMPARE(act.isObject(), true);
1338 QVERIFY(!act.isFunction());
1339 QScriptValue v(&eng, 123);
1340 act.setProperty("prop", v);
1341 QEXPECT_FAIL("", "", Continue);
1342 QCOMPARE(act.property("prop").strictlyEquals(v), true);
1343 QCOMPARE(act.scope().isValid(), false);
1344 QEXPECT_FAIL("", "", Continue);
1345 QVERIFY(act.prototype().isNull());
1349 #if 0 // ###FIXME: No setGlobalObject support - yay
1350 void tst_QJSEngine::getSetGlobalObjectSimple()
1352 QScriptEngine engine;
1353 QScriptValue object = engine.newObject();
1354 object.setProperty("foo", 123);
1355 engine.evaluate("var bar = 100");
1356 engine.setGlobalObject(object);
1357 engine.evaluate("rab = 100");
1358 QVERIFY(engine.globalObject().property("rab").isValid());
1359 QVERIFY(engine.globalObject().property("foo").isValid());
1360 QVERIFY(!engine.globalObject().property("bar").isValid());
1363 void tst_QJSEngine::getSetGlobalObject()
1366 QScriptValue glob = eng.globalObject();
1367 glob = QScriptValue(); // kill reference to old global object
1368 collectGarbage_helper(eng);
1370 glob = eng.globalObject();
1371 QCOMPARE(glob.isValid(), true);
1372 QCOMPARE(glob.isObject(), true);
1373 QVERIFY(!glob.isFunction());
1374 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(glob));
1375 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(glob));
1376 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1377 QCOMPARE(glob.toString(), QString::fromLatin1("[object global]"));
1378 // prototype should be Object.prototype
1379 QCOMPARE(glob.prototype().isValid(), true);
1380 QCOMPARE(glob.prototype().isObject(), true);
1381 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1382 QCOMPARE(glob.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
1384 eng.setGlobalObject(glob);
1385 QVERIFY(eng.globalObject().equals(glob));
1386 eng.setGlobalObject(123);
1387 QVERIFY(eng.globalObject().equals(glob));
1389 QScriptValue obj = eng.newObject();
1390 eng.setGlobalObject(obj);
1391 QVERIFY(eng.globalObject().strictlyEquals(obj));
1392 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1393 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1394 QVERIFY(eng.evaluate("this").strictlyEquals(obj));
1395 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1396 QCOMPARE(eng.globalObject().toString(), QString::fromLatin1("[object global]"));
1398 collectGarbage_helper(eng);
1399 glob = QScriptValue(); // kill reference to old global object
1400 collectGarbage_helper(eng);
1401 obj = eng.newObject();
1402 eng.setGlobalObject(obj);
1403 QVERIFY(eng.globalObject().strictlyEquals(obj));
1404 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1405 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1407 collectGarbage_helper(eng);
1408 QVERIFY(eng.globalObject().strictlyEquals(obj));
1409 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1410 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1412 QVERIFY(!obj.property("foo").isValid());
1413 eng.evaluate("var foo = 123");
1415 QScriptValue ret = obj.property("foo");
1416 QVERIFY(ret.isNumber());
1417 QCOMPARE(ret.toInt32(), 123);
1420 QVERIFY(!obj.property("bar").isValid());
1421 eng.evaluate("bar = 456");
1423 QScriptValue ret = obj.property("bar");
1424 QVERIFY(ret.isNumber());
1425 QCOMPARE(ret.toInt32(), 456);
1428 QVERIFY(!obj.property("baz").isValid());
1429 eng.evaluate("this['baz'] = 789");
1431 QScriptValue ret = obj.property("baz");
1432 QVERIFY(ret.isNumber());
1433 QCOMPARE(ret.toInt32(), 789);
1437 QScriptValue ret = eng.evaluate("(function() { return this; })()");
1438 QVERIFY(ret.strictlyEquals(obj));
1443 QScriptValue ret = eng.evaluate("delete foo");
1444 QVERIFY(ret.isBool());
1445 QVERIFY(ret.toBool());
1446 QVERIFY(!obj.property("foo").isValid());
1449 // Getter/setter property.
1450 //the custom global object have an interceptor
1451 QVERIFY(eng.evaluate("this.__defineGetter__('oof', function() { return this.bar; })").isUndefined());
1452 QVERIFY(eng.evaluate("this.__defineSetter__('oof', function(v) { this.bar = v; })").isUndefined());
1453 QVERIFY(eng.evaluate("this.__lookupGetter__('oof')").isFunction());
1454 QVERIFY(eng.evaluate("this.__lookupSetter__('oof')").isFunction());
1455 eng.evaluate("oof = 123");
1456 QVERIFY(eng.evaluate("oof").equals(obj.property("bar")));
1460 QScriptValue ret = eng.evaluate("a = []; for (var p in this) a.push(p); a");
1461 QCOMPARE(ret.toString(), QString::fromLatin1("bar,baz,oof,p,a"));
1466 #if 0 // ###FIXME: no c-style callbacks
1467 static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *)
1469 if (ctx->argumentCount() > 0)
1470 ctx->thisObject().setProperty("foo", ctx->argument(0));
1471 return ctx->thisObject().property("foo");
1475 void tst_QJSEngine::globalObjectProperties()
1477 // See ECMA-262 Section 15.1, "The Global Object".
1480 QJSValue global = eng.globalObject();
1482 QVERIFY(global.property("NaN").isNumber());
1483 QVERIFY(qIsNaN(global.property("NaN").toNumber()));
1484 QCOMPARE(global.propertyFlags("NaN"), QJSValue::SkipInEnumeration | QJSValue::Undeletable);
1486 QVERIFY(global.property("Infinity").isNumber());
1487 QVERIFY(qIsInf(global.property("Infinity").toNumber()));
1488 QCOMPARE(global.propertyFlags("NaN"), QJSValue::SkipInEnumeration | QJSValue::Undeletable);
1490 QVERIFY(global.property("undefined").isUndefined());
1491 QCOMPARE(global.propertyFlags("undefined"), QJSValue::SkipInEnumeration | QJSValue::Undeletable);
1493 QVERIFY(global.property("eval").isFunction());
1494 QCOMPARE(global.propertyFlags("eval"), QJSValue::SkipInEnumeration);
1496 QVERIFY(global.property("parseInt").isFunction());
1497 QCOMPARE(global.propertyFlags("parseInt"), QJSValue::SkipInEnumeration);
1499 QVERIFY(global.property("parseFloat").isFunction());
1500 QCOMPARE(global.propertyFlags("parseFloat"), QJSValue::SkipInEnumeration);
1502 QVERIFY(global.property("isNaN").isFunction());
1503 QCOMPARE(global.propertyFlags("isNaN"), QJSValue::SkipInEnumeration);
1505 QVERIFY(global.property("isFinite").isFunction());
1506 QCOMPARE(global.propertyFlags("isFinite"), QJSValue::SkipInEnumeration);
1508 QVERIFY(global.property("decodeURI").isFunction());
1509 QCOMPARE(global.propertyFlags("decodeURI"), QJSValue::SkipInEnumeration);
1511 QVERIFY(global.property("decodeURIComponent").isFunction());
1512 QCOMPARE(global.propertyFlags("decodeURIComponent"), QJSValue::SkipInEnumeration);
1514 QVERIFY(global.property("encodeURI").isFunction());
1515 QCOMPARE(global.propertyFlags("encodeURI"), QJSValue::SkipInEnumeration);
1517 QVERIFY(global.property("encodeURIComponent").isFunction());
1518 QCOMPARE(global.propertyFlags("encodeURIComponent"), QJSValue::SkipInEnumeration);
1520 QVERIFY(global.property("Object").isFunction());
1521 QCOMPARE(global.propertyFlags("Object"), QJSValue::SkipInEnumeration);
1522 QVERIFY(global.property("Function").isFunction());
1523 QCOMPARE(global.propertyFlags("Function"), QJSValue::SkipInEnumeration);
1524 QVERIFY(global.property("Array").isFunction());
1525 QCOMPARE(global.propertyFlags("Array"), QJSValue::SkipInEnumeration);
1526 QVERIFY(global.property("String").isFunction());
1527 QCOMPARE(global.propertyFlags("String"), QJSValue::SkipInEnumeration);
1528 QVERIFY(global.property("Boolean").isFunction());
1529 QCOMPARE(global.propertyFlags("Boolean"), QJSValue::SkipInEnumeration);
1530 QVERIFY(global.property("Number").isFunction());
1531 QCOMPARE(global.propertyFlags("Number"), QJSValue::SkipInEnumeration);
1532 QVERIFY(global.property("Date").isFunction());
1533 QCOMPARE(global.propertyFlags("Date"), QJSValue::SkipInEnumeration);
1534 QVERIFY(global.property("RegExp").isFunction());
1535 QCOMPARE(global.propertyFlags("RegExp"), QJSValue::SkipInEnumeration);
1536 QVERIFY(global.property("Error").isFunction());
1537 QCOMPARE(global.propertyFlags("Error"), QJSValue::SkipInEnumeration);
1538 QVERIFY(global.property("EvalError").isFunction());
1539 QCOMPARE(global.propertyFlags("EvalError"), QJSValue::SkipInEnumeration);
1540 QVERIFY(global.property("RangeError").isFunction());
1541 QCOMPARE(global.propertyFlags("RangeError"), QJSValue::SkipInEnumeration);
1542 QVERIFY(global.property("ReferenceError").isFunction());
1543 QCOMPARE(global.propertyFlags("ReferenceError"), QJSValue::SkipInEnumeration);
1544 QVERIFY(global.property("SyntaxError").isFunction());
1545 QCOMPARE(global.propertyFlags("SyntaxError"), QJSValue::SkipInEnumeration);
1546 QVERIFY(global.property("TypeError").isFunction());
1547 QCOMPARE(global.propertyFlags("TypeError"), QJSValue::SkipInEnumeration);
1548 QVERIFY(global.property("URIError").isFunction());
1549 QCOMPARE(global.propertyFlags("URIError"), QJSValue::SkipInEnumeration);
1550 QVERIFY(global.property("Math").isObject());
1551 QVERIFY(!global.property("Math").isFunction());
1552 QCOMPARE(global.propertyFlags("Math"), QJSValue::SkipInEnumeration);
1555 void tst_QJSEngine::globalObjectEquals()
1558 QJSValue o = eng.globalObject();
1559 QVERIFY(o.strictlyEquals(eng.globalObject()));
1560 QVERIFY(o.equals(eng.globalObject()));
1563 #if 0 // ###FIXME: No QScriptValueIterator API
1564 void tst_QJSEngine::globalObjectProperties_enumerate()
1567 QScriptValue global = eng.globalObject();
1569 QSet<QString> expectedNames;
1577 << "encodeURIComponent"
1594 << "decodeURIComponent"
1608 << "execScript" //execScript for IE compatibility.
1610 QSet<QString> actualNames;
1612 QScriptValueIterator it(global);
1613 while (it.hasNext()) {
1615 actualNames.insert(it.name());
1619 QSet<QString> remainingNames = actualNames;
1621 QSet<QString>::const_iterator it;
1622 for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) {
1624 QVERIFY(actualNames.contains(name));
1625 remainingNames.remove(name);
1628 QVERIFY(remainingNames.isEmpty());
1632 void tst_QJSEngine::createGlobalObjectProperty()
1635 QJSValue global = eng.globalObject();
1636 // create property with no attributes
1638 QString name = QString::fromLatin1("foo");
1639 QVERIFY(!global.property(name).isValid());
1641 global.setProperty(name, val);
1642 QVERIFY(global.property(name).equals(val));
1643 QVERIFY(global.propertyFlags(name) == 0);
1644 global.setProperty(name, QJSValue());
1645 QVERIFY(!global.property(name).isValid());
1647 // create property with attributes
1648 #if 0 // ###FIXME: setProperty with flags is not supported
1650 QString name = QString::fromLatin1("bar");
1651 QVERIFY(!global.property(name).isValid());
1652 QScriptValue val(QString::fromLatin1("ciao"));
1653 QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration;
1654 global.setProperty(name, val, flags);
1655 QVERIFY(global.property(name).equals(val));
1656 //QEXPECT_FAIL("", "QTBUG-6134: custom Global Object properties don't retain attributes", Continue);
1657 QCOMPARE(global.propertyFlags(name), flags);
1658 global.setProperty(name, QScriptValue());
1659 QVERIFY(!global.property(name).isValid());
1664 void tst_QJSEngine::globalObjectGetterSetterProperty()
1666 #if 0 // ###FIXME: No c-style callbacks
1667 QScriptEngine engine;
1668 QScriptValue global = engine.globalObject();
1669 global.setProperty("bar", engine.newFunction(getSetFoo),
1670 QScriptValue::PropertySetter | QScriptValue::PropertyGetter);
1671 global.setProperty("foo", 123);
1672 QVERIFY(global.property("bar").equals(global.property("foo")));
1673 QVERIFY(engine.evaluate("bar").equals(global.property("foo")));
1674 global.setProperty("bar", 456);
1675 QVERIFY(global.property("bar").equals(global.property("foo")));
1677 engine.evaluate("__defineGetter__('baz', function() { return 789; })");
1678 QVERIFY(engine.evaluate("baz").equals(789));
1679 QVERIFY(global.property("baz").equals(789));
1683 #if 0 // ###FIXME: No support for setting the global object
1684 void tst_QJSEngine::customGlobalObjectWithPrototype()
1686 for (int x = 0; x < 2; ++x) {
1687 QScriptEngine engine;
1688 QScriptValue wrap = engine.newObject();
1689 QScriptValue global = engine.globalObject();
1690 QScriptValue originalGlobalProto = global.prototype();
1692 // Set prototype before setting global object
1693 wrap.setPrototype(global);
1694 QVERIFY(wrap.prototype().strictlyEquals(global));
1695 engine.setGlobalObject(wrap);
1697 // Set prototype after setting global object
1698 engine.setGlobalObject(wrap);
1699 wrap.setPrototype(global);
1700 QVERIFY(wrap.prototype().strictlyEquals(global));
1703 QScriptValue ret = engine.evaluate("print");
1704 QVERIFY(ret.isFunction());
1705 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1708 QScriptValue ret = engine.evaluate("this.print");
1709 QVERIFY(ret.isFunction());
1710 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1713 QScriptValue ret = engine.evaluate("hasOwnProperty('print')");
1714 QVERIFY(ret.isBool());
1715 if (x) QEXPECT_FAIL("", "Why?", Continue);
1716 QVERIFY(!ret.toBool());
1719 QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')");
1720 QVERIFY(ret.isBool());
1721 if (x) QEXPECT_FAIL("", "Why?", Continue);
1722 QVERIFY(!ret.toBool());
1725 QScriptValue anotherProto = engine.newObject();
1726 anotherProto.setProperty("anotherProtoProperty", 123);
1727 global.setPrototype(anotherProto);
1729 QScriptValue ret = engine.evaluate("print");
1730 QVERIFY(ret.isFunction());
1731 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1734 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1735 QVERIFY(ret.isNumber());
1736 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1739 QScriptValue ret = engine.evaluate("this.anotherProtoProperty");
1740 QVERIFY(ret.isNumber());
1741 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1744 wrap.setPrototype(anotherProto);
1746 QScriptValue ret = engine.evaluate("print");
1747 QVERIFY(ret.isError());
1748 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: print is not defined"));
1751 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1752 QVERIFY(ret.isNumber());
1753 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1755 QVERIFY(global.prototype().strictlyEquals(anotherProto));
1757 global.setPrototype(originalGlobalProto);
1758 engine.setGlobalObject(global);
1760 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1761 QVERIFY(ret.isError());
1762 QVERIFY(ret.toString().startsWith("ReferenceError: "));
1765 QScriptValue ret = engine.evaluate("print");
1766 QVERIFY(ret.isFunction());
1767 QVERIFY(ret.strictlyEquals(global.property("print")));
1769 QVERIFY(!anotherProto.property("print").isValid());
1774 void tst_QJSEngine::globalObjectWithCustomPrototype()
1777 QJSValue proto = engine.newObject();
1778 proto.setProperty("protoProperty", 123);
1779 QJSValue global = engine.globalObject();
1780 QJSValue originalProto = global.prototype();
1781 global.setPrototype(proto);
1783 QJSValue ret = engine.evaluate("protoProperty");
1784 QEXPECT_FAIL("", "Replacing the prototype of the global object is currently unsupported (see also v8 issue 1078)", Abort);
1785 QVERIFY(ret.isNumber());
1786 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1789 QJSValue ret = engine.evaluate("this.protoProperty");
1790 QVERIFY(ret.isNumber());
1791 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1794 QJSValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
1795 QVERIFY(ret.isBool());
1796 QVERIFY(!ret.toBool());
1799 QJSValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
1800 QVERIFY(ret.isBool());
1801 QVERIFY(!ret.toBool());
1804 // Custom prototype set from JS
1806 QJSValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
1807 QVERIFY(ret.isNumber());
1808 QVERIFY(ret.strictlyEquals(global.property("a")));
1812 void tst_QJSEngine::builtinFunctionNames_data()
1814 QTest::addColumn<QString>("expression");
1815 QTest::addColumn<QString>("expectedName");
1817 // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects".
1819 QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt");
1820 QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat");
1821 QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN");
1822 QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite");
1823 QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI");
1824 QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent");
1825 QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI");
1826 QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent");
1827 QTest::newRow("escape") << QString("escape") << QString("escape");
1828 QTest::newRow("unescape") << QString("unescape") << QString("unescape");
1830 QTest::newRow("Array") << QString("Array") << QString("Array");
1831 QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString");
1832 QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString");
1833 QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat");
1834 QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join");
1835 QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop");
1836 QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push");
1837 QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse");
1838 QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift");
1839 QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice");
1840 QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort");
1841 QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice");
1842 QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift");
1844 QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean");
1845 QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString");
1847 QTest::newRow("Date") << QString("Date") << QString("Date");
1848 QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString");
1849 QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString");
1850 QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString");
1851 QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString");
1852 QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString");
1853 QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString");
1854 QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf");
1855 QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime");
1856 QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear");
1857 QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear");
1858 QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear");
1859 QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth");
1860 QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth");
1861 QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate");
1862 QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate");
1863 QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay");
1864 QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay");
1865 QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours");
1866 QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours");
1867 QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes");
1868 QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes");
1869 QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds");
1870 QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds");
1871 QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds");
1872 QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds");
1873 QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset");
1874 QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime");
1875 QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds");
1876 QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds");
1877 QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds");
1878 QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds");
1879 QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes");
1880 QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes");
1881 QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours");
1882 QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours");
1883 QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate");
1884 QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate");
1885 QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth");
1886 QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth");
1887 QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear");
1888 QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear");
1889 QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear");
1890 QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString");
1891 QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString");
1893 QTest::newRow("Error") << QString("Error") << QString("Error");
1894 // QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace");
1895 QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString");
1897 QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError");
1898 QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError");
1899 QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError");
1900 QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError");
1901 QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError");
1902 QTest::newRow("URIError") << QString("URIError") << QString("URIError");
1904 QTest::newRow("Function") << QString("Function") << QString("Function");
1905 QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString");
1906 QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply");
1907 QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call");
1908 /* In V8, those function are only there for signals
1909 QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect");
1910 QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");*/
1912 QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs");
1913 QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos");
1914 QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin");
1915 QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan");
1916 QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2");
1917 QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil");
1918 QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos");
1919 QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp");
1920 QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor");
1921 QTest::newRow("Math.log") << QString("Math.log") << QString("log");
1922 QTest::newRow("Math.max") << QString("Math.max") << QString("max");
1923 QTest::newRow("Math.min") << QString("Math.min") << QString("min");
1924 QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
1925 QTest::newRow("Math.random") << QString("Math.random") << QString("random");
1926 QTest::newRow("Math.round") << QString("Math.round") << QString("round");
1927 QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
1928 QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
1929 QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
1931 QTest::newRow("Number") << QString("Number") << QString("Number");
1932 QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
1933 QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
1934 QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
1935 QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed");
1936 QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential");
1937 QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision");
1939 QTest::newRow("Object") << QString("Object") << QString("Object");
1940 QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString");
1941 QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString");
1942 QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf");
1943 QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty");
1944 QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf");
1945 QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable");
1946 QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__");
1947 QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__");
1949 QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp");
1950 QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec");
1951 QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test");
1952 QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString");
1954 QTest::newRow("String") << QString("String") << QString("String");
1955 QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString");
1956 QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf");
1957 QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
1958 QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
1959 QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
1960 QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
1961 QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
1962 QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
1963 QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match");
1964 QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace");
1965 QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
1966 QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
1967 QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
1968 QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
1969 QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
1970 QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
1971 QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase");
1972 QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase");
1975 void tst_QJSEngine::builtinFunctionNames()
1977 QFETCH(QString, expression);
1978 QFETCH(QString, expectedName);
1980 // The "name" property is actually non-standard, but JSC supports it.
1981 QJSValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression));
1982 QVERIFY(ret.isString());
1983 QCOMPARE(ret.toString(), expectedName);
1986 #if 0 // ###FIXME: No syntax checking result
1987 void tst_QJSEngine::checkSyntax_data()
1989 QTest::addColumn<QString>("code");
1990 QTest::addColumn<int>("expectedState");
1991 QTest::addColumn<int>("errorLineNumber");
1992 QTest::addColumn<int>("errorColumnNumber");
1993 QTest::addColumn<QString>("errorMessage");
1996 << QString("0") << int(QScriptSyntaxCheckResult::Valid)
1998 QTest::newRow("if (")
1999 << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate)
2000 << 0 << -1 << "Uncaught SyntaxError: Unexpected end of input";
2001 QTest::newRow("if else")
2002 << QString("\nif else") << int(QScriptSyntaxCheckResult::Error)
2003 << 2 << 3 << "Uncaught SyntaxError: Unexpected token else";
2004 QTest::newRow("foo[")
2005 << QString("foo[") << int(QScriptSyntaxCheckResult::Intermediate)
2006 << 1 << 4 << "Uncaught SyntaxError: Unexpected end of input";
2007 QTest::newRow("foo['bar']")
2008 << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid)
2012 << QString("/*") << int(QScriptSyntaxCheckResult::Error)
2013 << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2014 QTest::newRow("/*\nMy comment")
2015 << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Error)
2016 << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2017 QTest::newRow("/*\nMy comment */\nfoo = 10")
2018 << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid)
2020 QTest::newRow("foo = 10 /*")
2021 << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Error)
2022 << 1 << 9 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2023 QTest::newRow("foo = 10; /*")
2024 << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Error)
2025 << 1 << 10 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
2026 QTest::newRow("foo = 10 /* My comment */")
2027 << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid)
2030 QTest::newRow("/=/")
2031 << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2032 QTest::newRow("/=/g")
2033 << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2034 QTest::newRow("/a/")
2035 << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2036 QTest::newRow("/a/g")
2037 << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
2040 void tst_QJSEngine::checkSyntax()
2042 QFETCH(QString, code);
2043 QFETCH(int, expectedState);
2044 QFETCH(int, errorLineNumber);
2045 QFETCH(int, errorColumnNumber);
2046 QFETCH(QString, errorMessage);
2048 QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code);
2049 QCOMPARE(int(result.state()), expectedState);
2050 QCOMPARE(result.errorLineNumber(), errorLineNumber);
2051 QCOMPARE(result.errorColumnNumber(), errorColumnNumber);
2052 QCOMPARE(result.errorMessage(), errorMessage);
2056 QScriptSyntaxCheckResult copy = result;
2057 QCOMPARE(copy.state(), result.state());
2058 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
2059 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
2060 QCOMPARE(copy.errorMessage(), result.errorMessage());
2063 QScriptSyntaxCheckResult copy(result);
2064 QCOMPARE(copy.state(), result.state());
2065 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
2066 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
2067 QCOMPARE(copy.errorMessage(), result.errorMessage());
2072 #if 0 // ###FIXME: No support for canEvaluate
2073 void tst_QJSEngine::canEvaluate_data()
2075 QTest::addColumn<QString>("code");
2076 QTest::addColumn<bool>("expectSuccess");
2078 QTest::newRow("") << QString("") << true;
2079 QTest::newRow("0") << QString("0") << true;
2080 QTest::newRow("!") << QString("!\n") << false;
2081 QTest::newRow("if (") << QString("if (\n") << false;
2082 QTest::newRow("if (10) //") << QString("if (10) //\n") << false;
2083 QTest::newRow("a = 1; if (") << QString("a = 1;\nif (\n") << false;
2084 QTest::newRow("./test.js") << QString("./test.js\n") << true;
2085 QTest::newRow("if (0) print(1)") << QString("if (0)\nprint(1)\n") << true;
2086 QTest::newRow("0 = ") << QString("0 = \n") << false;
2087 QTest::newRow("0 = 0") << QString("0 = 0\n") << true;
2088 QTest::newRow("foo[") << QString("foo[") << false;
2089 QTest::newRow("foo[") << QString("foo[\n") << false;
2090 QTest::newRow("foo['bar']") << QString("foo['bar']") << true;
2092 //v8 does thinks unterminated comments are error rather than unfinished
2093 // QTest::newRow("/*") << QString("/*") << false;
2094 // QTest::newRow("/*\nMy comment") << QString("/*\nMy comment") << false;
2095 QTest::newRow("/*\nMy comment */\nfoo = 10") << QString("/*\nMy comment */\nfoo = 10") << true;
2096 // QTest::newRow("foo = 10 /*") << QString("foo = 10 /*") << false;
2097 // QTest::newRow("foo = 10; /*") << QString("foo = 10; /*") << false;
2098 QTest::newRow("foo = 10 /* My comment */") << QString("foo = 10 /* My comment */") << true;
2100 QTest::newRow("/=/") << QString("/=/") << true;
2101 QTest::newRow("/=/g") << QString("/=/g") << true;
2102 QTest::newRow("/a/") << QString("/a/") << true;
2103 QTest::newRow("/a/g") << QString("/a/g") << true;
2106 void tst_QJSEngine::canEvaluate()
2108 QFETCH(QString, code);
2109 QFETCH(bool, expectSuccess);
2112 QCOMPARE(eng.canEvaluate(code), expectSuccess);
2116 void tst_QJSEngine::evaluate_data()
2118 QTest::addColumn<QString>("code");
2119 QTest::addColumn<int>("lineNumber");
2120 QTest::addColumn<bool>("expectHadError");
2121 QTest::addColumn<int>("expectErrorLineNumber");
2123 QTest::newRow("(newline)") << QString("\n") << -1 << false << -1;
2124 QTest::newRow("0 //") << QString("0 //") << -1 << false << -1;
2125 QTest::newRow("/* */") << QString("/* */") << -1 << false << -1;
2126 QTest::newRow("//") << QString("//") << -1 << false << -1;
2127 QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1;
2128 QTest::newRow("(empty)") << QString("") << -1 << false << -1;
2129 QTest::newRow("0") << QString("0") << -1 << false << -1;
2130 QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2;
2131 QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1;
2132 QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2;
2134 QTest::newRow("f()") << QString("function f()\n"
2137 " var b=\";\n" // here's the error
2142 QTest::newRow("0") << QString("0") << 10 << false << -1;
2143 QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 12;
2144 QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1;
2145 QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12;
2147 QTest::newRow("f()") << QString("function f()\n"
2151 " var b=\";\n" // here's the error
2154 << 10 << true << 15;
2155 QTest::newRow("functionThatDoesntExist()")
2156 << QString(";\n;\n;\nfunctionThatDoesntExist()")
2158 QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }")
2159 << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }")
2161 QTest::newRow("duplicateLabel: { duplicateLabel: ; }")
2162 << QString("duplicateLabel: { duplicateLabel: ; }")
2163 << 12 << true << 12;
2165 QTest::newRow("/=/") << QString("/=/") << -1 << false << -1;
2166 QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1;
2167 QTest::newRow("/a/") << QString("/a/") << -1 << false << -1;
2168 QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1;
2169 QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1;
2170 QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1;
2173 void tst_QJSEngine::evaluate()
2175 QFETCH(QString, code);
2176 QFETCH(int, lineNumber);
2177 QFETCH(bool, expectHadError);
2178 QFETCH(int, expectErrorLineNumber);
2182 if (lineNumber != -1)
2183 ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber);
2185 ret = eng.evaluate(code);
2186 QEXPECT_FAIL("/a/gimp", "v8 ignore invalid flags", Abort);
2187 QCOMPARE(eng.hasUncaughtException(), expectHadError);
2188 #if 0 // ###FIXME: No support for the line number of an uncaught exception
2189 QEXPECT_FAIL("f()", "SyntaxError do not report line number", Continue);
2190 QEXPECT_FAIL("duplicateLabel: { duplicateLabel: ; }", "SyntaxError do not report line number", Continue);
2191 QCOMPARE(eng.uncaughtExceptionLineNumber(), expectErrorLineNumber);
2193 if (eng.hasUncaughtException() && ret.isError()) {
2194 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
2195 QVERIFY(ret.property("lineNumber").strictlyEquals(QJSValue(&eng, expectErrorLineNumber)));
2197 #if 0 // ###FIXME: No support for the backtrace of an uncaught exception
2198 QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
2203 #if 0 // ###FIXME: no support for c-style callbacks
2204 static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng)
2206 QScriptValue result = eng->newObject();
2207 eng->evaluate("var bar = 'local';");
2208 result.setProperty("thisObjectIdBefore", ctx->thisObject().property("id"));
2209 QScriptValue evaluatedThisObject = eng->evaluate("this");
2210 result.setProperty("thisObjectIdAfter", ctx->thisObject().property("id"));
2211 result.setProperty("evaluatedThisObjectId", evaluatedThisObject.property("id"));
2212 result.setProperty("local_bar", eng->evaluate("bar"));
2217 // Tests that nested evaluate uses the "this" that was passed.
2218 void tst_QJSEngine::nestedEvaluate()
2221 QScriptValue fun = eng.newFunction(eval_nested);
2222 eng.globalObject().setProperty("fun", fun);
2223 // From JS function call
2225 QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()");
2226 QCOMPARE(result.property("local_bar").toString(), QString("local"));
2227 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
2228 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
2229 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
2230 QScriptValue bar = eng.evaluate("bar"); // Was introduced in local scope.
2231 QVERIFY(bar.isError());
2232 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
2234 // From QScriptValue::call()
2236 QScriptValue result = fun.call(eng.evaluate("p = { id:'foo' }") , QScriptValueList() );
2237 QCOMPARE(result.property("local_bar").toString(), QString("local"));
2238 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
2239 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
2240 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
2241 QScriptValue bar = eng.evaluate("bar");
2242 QVERIFY(bar.isError());
2243 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
2248 #if 0 // ### FIXME: No c-style callbacks
2249 void tst_QJSEngine::uncaughtException()
2252 QScriptValue fun = eng.newFunction(myFunction);
2253 QScriptValue throwFun = eng.newFunction(myThrowingFunction);
2254 for (int x = -1; x < 2; ++x) {
2256 QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", /*fileName=*/QString(), /*lineNumber=*/x);
2257 QVERIFY(eng.hasUncaughtException());
2258 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2259 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2260 (void)ret.toString();
2261 QVERIFY(eng.hasUncaughtException());
2262 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2263 QVERIFY(fun.call().isNull());
2264 QVERIFY(eng.hasUncaughtException());
2265 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2266 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2267 eng.clearExceptions();
2268 QVERIFY(!eng.hasUncaughtException());
2269 QCOMPARE(eng.uncaughtExceptionLineNumber(), -1);
2270 QVERIFY(!eng.uncaughtException().isValid());
2272 eng.evaluate("2 = 3");
2273 QVERIFY(eng.hasUncaughtException());
2274 QScriptValue ret2 = throwFun.call();
2275 QVERIFY(ret2.isError());
2276 QVERIFY(eng.hasUncaughtException());
2277 QVERIFY(eng.uncaughtException().strictlyEquals(ret2));
2278 QCOMPARE(eng.uncaughtExceptionLineNumber(), 0);
2279 eng.clearExceptions();
2280 QVERIFY(!eng.hasUncaughtException());
2281 eng.evaluate("1 + 2");
2282 QVERIFY(!eng.hasUncaughtException());
2285 QScriptValue ret = eng.evaluate("a = 10");
2286 QVERIFY(!eng.hasUncaughtException());
2287 QVERIFY(!eng.uncaughtException().isValid());
2290 QScriptValue ret = eng.evaluate("1 = 2");
2291 QVERIFY(eng.hasUncaughtException());
2292 eng.clearExceptions();
2293 QVERIFY(!eng.hasUncaughtException());
2296 eng.globalObject().setProperty("throwFun", throwFun);
2297 eng.evaluate("1;\nthrowFun();");
2298 QVERIFY(eng.hasUncaughtException());
2299 QCOMPARE(eng.uncaughtExceptionLineNumber(), 2);
2300 eng.clearExceptions();
2301 QVERIFY(!eng.hasUncaughtException());
2307 void tst_QJSEngine::errorMessage_QT679()
2310 engine.globalObject().setProperty("foo", 15);
2311 QJSValue error = engine.evaluate("'hello world';\nfoo.bar.blah");
2312 QVERIFY(error.isError());
2313 QVERIFY(error.toString().startsWith(QString::fromLatin1("TypeError: ")));
2319 Foo() : x(-1), y(-1) { }
2322 Q_DECLARE_METATYPE(Foo)
2323 Q_DECLARE_METATYPE(Foo*)
2325 #if 0 // FIXME: No prototype API in QScriptEngine
2326 void tst_QJSEngine::getSetDefaultPrototype_int()
2330 QScriptValue object = eng.newObject();
2331 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).isValid(), false);
2332 eng.setDefaultPrototype(qMetaTypeId<int>(), object);
2333 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).strictlyEquals(object), true);
2334 QScriptValue value = eng.newVariant(int(123));
2335 QCOMPARE(value.prototype().isObject(), true);
2336 QCOMPARE(value.prototype().strictlyEquals(object), true);
2338 eng.setDefaultPrototype(qMetaTypeId<int>(), QScriptValue());
2339 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).isValid(), false);
2340 QScriptValue value2 = eng.newVariant(int(123));
2341 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2344 void tst_QJSEngine::getSetDefaultPrototype_customType()
2348 QScriptValue object = eng.newObject();
2349 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).isValid(), false);
2350 eng.setDefaultPrototype(qMetaTypeId<Foo>(), object);
2351 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(object), true);
2352 QScriptValue value = eng.newVariant(qVariantFromValue(Foo()));
2353 QCOMPARE(value.prototype().isObject(), true);
2354 QCOMPARE(value.prototype().strictlyEquals(object), true);
2356 eng.setDefaultPrototype(qMetaTypeId<Foo>(), QScriptValue());
2357 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).isValid(), false);
2358 QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo()));
2359 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2363 static QJSValue fooToScriptValue(QJSEngine *eng, const Foo &foo)
2365 QJSValue obj = eng->newObject();
2366 obj.setProperty("x", QJSValue(eng, foo.x));
2367 obj.setProperty("y", QJSValue(eng, foo.y));
2371 static void fooFromScriptValue(const QJSValue &value, Foo &foo)
2373 foo.x = value.property("x").toInt32();
2374 foo.y = value.property("y").toInt32();
2377 static QJSValue fooToScriptValueV2(QJSEngine *eng, const Foo &foo)
2379 return QJSValue(eng, foo.x);
2382 static void fooFromScriptValueV2(const QJSValue &value, Foo &foo)
2384 foo.x = value.toInt32();
2387 Q_DECLARE_METATYPE(QLinkedList<QString>)
2388 Q_DECLARE_METATYPE(QList<Foo>)
2389 Q_DECLARE_METATYPE(QVector<QChar>)
2390 Q_DECLARE_METATYPE(QStack<int>)
2391 Q_DECLARE_METATYPE(QQueue<char>)
2392 Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
2394 void tst_QJSEngine::valueConversion_basic()
2398 QJSValue num = eng.toScriptValue(123);
2399 QCOMPARE(num.isNumber(), true);
2400 QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true);
2402 int inum = eng.fromScriptValue<int>(num);
2403 QCOMPARE(inum, 123);
2405 QString snum = eng.fromScriptValue<QString>(num);
2406 QCOMPARE(snum, QLatin1String("123"));
2409 QJSValue num = eng.toScriptValue(123);
2410 QCOMPARE(num.isNumber(), true);
2411 QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true);
2413 int inum = eng.fromScriptValue<int>(num);
2414 QCOMPARE(inum, 123);
2416 QString snum = eng.fromScriptValue<QString>(num);
2417 QCOMPARE(snum, QLatin1String("123"));
2420 QJSValue num(&eng, 123);
2421 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
2422 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
2423 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
2424 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
2425 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
2426 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
2427 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
2428 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
2432 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
2433 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
2434 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
2435 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
2436 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
2437 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
2438 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
2439 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
2443 QJSValue num = eng.toScriptValue(Q_INT64_C(0x100000000));
2444 QCOMPARE(eng.fromScriptValue<qlonglong>(num), Q_INT64_C(0x100000000));
2445 QCOMPARE(eng.fromScriptValue<qulonglong>(num), Q_UINT64_C(0x100000000));
2449 QChar c = QLatin1Char('c');
2450 QJSValue str = QJSValue(&eng, QLatin1String("ciao"));
2451 QCOMPARE(eng.fromScriptValue<QChar>(str), c);
2452 QJSValue code = QJSValue(&eng, c.unicode());
2453 QCOMPARE(eng.fromScriptValue<QChar>(code), c);
2454 QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c);
2458 #if 0 // FIXME: No API for custom types
2459 void tst_QJSEngine::valueConversion_customType()
2463 // a type that we don't have built-in conversion of
2464 // (it's stored as a variant)
2465 QTime tm(1, 2, 3, 4);
2466 QScriptValue val = eng.toScriptValue(tm);
2467 QCOMPARE(eng.fromScriptValue<QTime>(val), tm);
2474 QScriptValue fooVal = eng.toScriptValue(foo);
2475 QCOMPARE(fooVal.isVariant(), true);
2477 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
2478 QCOMPARE(foo2.x, foo.x);
2479 QCOMPARE(foo2.y, foo.y);
2482 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValue, fooFromScriptValue);
2488 QScriptValue fooVal = eng.toScriptValue(foo);
2489 QCOMPARE(fooVal.isObject(), true);
2490 QVERIFY(fooVal.prototype().strictlyEquals(eng.evaluate("Object.prototype")));
2491 QCOMPARE(fooVal.property("x").strictlyEquals(QScriptValue(&eng, 12)), true);
2492 QCOMPARE(fooVal.property("y").strictlyEquals(QScriptValue(&eng, 34)), true);
2493 fooVal.setProperty("x", QScriptValue(&eng, 56));
2494 fooVal.setProperty("y", QScriptValue(&eng, 78));
2496 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
2497 QCOMPARE(foo2.x, 56);
2498 QCOMPARE(foo2.y, 78);
2500 QScriptValue fooProto = eng.newObject();
2501 eng.setDefaultPrototype(qMetaTypeId<Foo>(), fooProto);
2502 QScriptValue fooVal2 = eng.toScriptValue(foo2);
2503 QVERIFY(fooVal2.prototype().strictlyEquals(fooProto));
2504 QVERIFY(fooVal2.property("x").strictlyEquals(QScriptValue(&eng, 56)));
2505 QVERIFY(fooVal2.property("y").strictlyEquals(QScriptValue(&eng, 78)));
2509 void tst_QJSEngine::valueConversion_sequence()
2512 qScriptRegisterSequenceMetaType<QLinkedList<QString> >(&eng);
2515 QLinkedList<QString> lst;
2516 lst << QLatin1String("foo") << QLatin1String("bar");
2517 QScriptValue lstVal = eng.toScriptValue(lst);
2518 QCOMPARE(lstVal.isArray(), true);
2519 QCOMPARE(lstVal.property("length").toInt32(), 2);
2520 QCOMPARE(lstVal.property("0").isString(), true);
2521 QCOMPARE(lstVal.property("0").toString(), QLatin1String("foo"));
2522 QCOMPARE(lstVal.property("1").isString(), true);
2523 QCOMPARE(lstVal.property("1").toString(), QLatin1String("bar"));
2526 qScriptRegisterSequenceMetaType<QList<Foo> >(&eng);
2527 qScriptRegisterSequenceMetaType<QStack<int> >(&eng);
2528 qScriptRegisterSequenceMetaType<QVector<QChar> >(&eng);
2529 qScriptRegisterSequenceMetaType<QQueue<char> >(&eng);
2530 qScriptRegisterSequenceMetaType<QLinkedList<QStack<int> > >(&eng);
2533 QLinkedList<QStack<int> > lst;
2534 QStack<int> first; first << 13 << 49; lst << first;
2535 QStack<int> second; second << 99999;lst << second;
2536 QScriptValue lstVal = eng.toScriptValue(lst);
2537 QCOMPARE(lstVal.isArray(), true);
2538 QCOMPARE(lstVal.property("length").toInt32(), 2);
2539 QCOMPARE(lstVal.property("0").isArray(), true);
2540 QCOMPARE(lstVal.property("0").property("length").toInt32(), 2);
2541 QCOMPARE(lstVal.property("0").property("0").toInt32(), first.at(0));
2542 QCOMPARE(lstVal.property("0").property("1").toInt32(), first.at(1));
2543 QCOMPARE(lstVal.property("1").isArray(), true);
2544 QCOMPARE(lstVal.property("1").property("length").toInt32(), 1);
2545 QCOMPARE(lstVal.property("1").property("0").toInt32(), second.at(0));
2546 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("0")), first);
2547 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("1")), second);
2548 QCOMPARE(qscriptvalue_cast<QLinkedList<QStack<int> > >(lstVal), lst);
2555 QScriptValue v = eng.toScriptValue(&foo);
2556 Foo *pfoo = qscriptvalue_cast<Foo*>(v);
2557 QCOMPARE(pfoo, &foo);
2561 QScriptValue v = eng.toScriptValue(pfoo);
2562 QCOMPARE(v.isNull(), true);
2563 QVERIFY(qscriptvalue_cast<Foo*>(v) == 0);
2567 // QList<int> and QObjectList should be converted from/to arrays by default
2571 QScriptValue val = eng.toScriptValue(lst);
2572 QVERIFY(val.isArray());
2573 QCOMPARE(val.property("length").toInt32(), lst.size());
2574 QCOMPARE(val.property(0).toInt32(), lst.at(0));
2575 QCOMPARE(val.property(1).toInt32(), lst.at(1));
2576 QCOMPARE(val.property(2).toInt32(), lst.at(2));
2578 QCOMPARE(qscriptvalue_cast<QList<int> >(val), lst);
2583 QScriptValue val = eng.toScriptValue(lst);
2584 QVERIFY(val.isArray());
2585 QCOMPARE(val.property("length").toInt32(), lst.size());
2586 QCOMPARE(val.property(0).toQObject(), (QObject *)this);
2588 QCOMPARE(qscriptvalue_cast<QObjectList>(val), lst);
2593 void tst_QJSEngine::valueConversion_QVariant()
2596 // qScriptValueFromValue() should be "smart" when the argument is a QVariant
2598 QJSValue val = eng.toScriptValue(QVariant());
2599 QVERIFY(!val.isVariant());
2600 QVERIFY(val.isUndefined());
2602 // Checking nested QVariants
2605 QVariant tmp2(QMetaType::QVariant, &tmp1);
2606 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2608 QJSValue val1 = eng.toScriptValue(tmp1);
2609 QJSValue val2 = eng.toScriptValue(tmp2);
2610 QVERIFY(val1.isValid());
2611 QVERIFY(val2.isValid());
2612 QVERIFY(val1.isUndefined());
2613 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2614 QVERIFY(!val2.isUndefined());
2615 QVERIFY(!val1.isVariant());
2616 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2617 QVERIFY(val2.isVariant());
2621 QVariant tmp2(QMetaType::QVariant, &tmp1);
2622 QVariant tmp3(QMetaType::QVariant, &tmp2);
2623 QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int);
2624 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2625 QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant);
2627 QJSValue val1 = eng.toScriptValue(tmp2);
2628 QJSValue val2 = eng.toScriptValue(tmp3);
2629 QVERIFY(val1.isValid());
2630 QVERIFY(val2.isValid());
2631 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2632 QVERIFY(val1.isVariant());
2633 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2634 QVERIFY(val2.isVariant());
2635 QVERIFY(val1.toVariant().toInt() == 123);
2636 QVERIFY(eng.toScriptValue(val2.toVariant()).toVariant().toInt() == 123);
2639 QJSValue val = eng.toScriptValue(QVariant(true));
2640 QVERIFY(!val.isVariant());
2641 QVERIFY(val.isBoolean());
2642 QCOMPARE(val.toBoolean(), true);
2645 QJSValue val = eng.toScriptValue(QVariant(int(123)));
2646 QVERIFY(!val.isVariant());
2647 QVERIFY(val.isNumber());
2648 QCOMPARE(val.toNumber(), qreal(123));
2651 QJSValue val = eng.toScriptValue(QVariant(qreal(1.25)));
2652 QVERIFY(!val.isVariant());
2653 QVERIFY(val.isNumber());
2654 QCOMPARE(val.toNumber(), qreal(1.25));
2657 QString str = QString::fromLatin1("ciao");
2658 QJSValue val = eng.toScriptValue(QVariant(str));
2659 QVERIFY(!val.isVariant());
2660 QVERIFY(val.isString());
2661 QCOMPARE(val.toString(), str);
2664 QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
2665 QVERIFY(!val.isVariant());
2666 QVERIFY(val.isQObject());
2667 QCOMPARE(val.toQObject(), (QObject*)this);
2670 QVariant var = qVariantFromValue(QPoint(123, 456));
2671 QJSValue val = eng.toScriptValue(var);
2672 QVERIFY(val.isVariant());
2673 QCOMPARE(val.toVariant(), var);
2676 QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123));
2679 #if 0 // FIXME: No support for custom types
2680 void tst_QJSEngine::valueConversion_hooliganTask248802()
2683 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValueV2, fooFromScriptValueV2);
2685 QScriptValue num(&eng, 123);
2686 Foo foo = eng.fromScriptValue<Foo>(num);
2687 QCOMPARE(foo.x, 123);
2690 QScriptValue num(123);
2691 Foo foo = eng.fromScriptValue<Foo>(num);
2692 QCOMPARE(foo.x, -1);
2695 QScriptValue str(&eng, QLatin1String("123"));
2696 Foo foo = eng.fromScriptValue<Foo>(str);
2697 QCOMPARE(foo.x, 123);
2703 void tst_QJSEngine::valueConversion_basic2()
2706 // more built-in types
2708 QJSValue val = eng.toScriptValue(uint(123));
2709 QVERIFY(val.isNumber());
2710 QCOMPARE(val.toInt32(), 123);
2713 QJSValue val = eng.toScriptValue(qulonglong(123));
2714 QVERIFY(val.isNumber());
2715 QCOMPARE(val.toInt32(), 123);
2718 QJSValue val = eng.toScriptValue(float(123));
2719 QVERIFY(val.isNumber());
2720 QCOMPARE(val.toInt32(), 123);
2723 QJSValue val = eng.toScriptValue(short(123));
2724 QVERIFY(val.isNumber());
2725 QCOMPARE(val.toInt32(), 123);
2728 QJSValue val = eng.toScriptValue(ushort(123));
2729 QVERIFY(val.isNumber());
2730 QCOMPARE(val.toInt32(), 123);
2733 QJSValue val = eng.toScriptValue(char(123));
2734 QVERIFY(val.isNumber());
2735 QCOMPARE(val.toInt32(), 123);
2738 QJSValue val = eng.toScriptValue(uchar(123));
2739 QVERIFY(val.isNumber());
2740 QCOMPARE(val.toInt32(), 123);
2744 void tst_QJSEngine::valueConversion_dateTime()
2748 QDateTime in = QDateTime::currentDateTime();
2749 QJSValue val = eng.toScriptValue(in);
2750 QVERIFY(val.isDate());
2751 QCOMPARE(val.toDateTime(), in);
2754 QDate in = QDate::currentDate();
2755 QJSValue val = eng.toScriptValue(in);
2756 QVERIFY(val.isDate());
2757 QCOMPARE(val.toDateTime().date(), in);
2761 void tst_QJSEngine::valueConversion_regExp()
2765 QRegExp in = QRegExp("foo");
2766 QJSValue val = eng.toScriptValue(in);
2767 QVERIFY(val.isRegExp());
2768 QRegExp out = val.toRegExp();
2769 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue);
2770 QCOMPARE(out.patternSyntax(), in.patternSyntax());
2771 QCOMPARE(out.pattern(), in.pattern());
2772 QCOMPARE(out.caseSensitivity(), in.caseSensitivity());
2773 QCOMPARE(out.isMinimal(), in.isMinimal());
2776 QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2);
2777 QJSValue val = eng.toScriptValue(in);
2778 QVERIFY(val.isRegExp());
2779 QCOMPARE(val.toRegExp(), in);
2782 QRegExp in = QRegExp("foo");
2783 in.setMinimal(true);
2784 QJSValue val = eng.toScriptValue(in);
2785 QVERIFY(val.isRegExp());
2786 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue);
2787 QCOMPARE(val.toRegExp().isMinimal(), in.isMinimal());
2791 #if 0 // FIXME: No qScriptValueFromValue
2792 void tst_QJSEngine::qScriptValueFromValue_noEngine()
2794 QVERIFY(!qScriptValueFromValue(0, 123).isValid());
2795 QVERIFY(!qScriptValueFromValue(0, QVariant(123)).isValid());
2799 #if 0 // ###FIXME: No QScriptContext
2800 static QScriptValue __import__(QScriptContext *ctx, QScriptEngine *eng)
2802 return eng->importExtension(ctx->argument(0).toString());
2805 void tst_QJSEngine::importExtension()
2807 QStringList libPaths = QCoreApplication::instance()->libraryPaths();
2808 QCoreApplication::instance()->setLibraryPaths(QStringList() << SRCDIR);
2810 QStringList availableExtensions;
2813 QVERIFY(eng.importedExtensions().isEmpty());
2814 QStringList ret = eng.availableExtensions();
2815 QCOMPARE(ret.size(), 4);
2816 QCOMPARE(ret.at(0), QString::fromLatin1("com"));
2817 QCOMPARE(ret.at(1), QString::fromLatin1("com.trolltech"));
2818 QCOMPARE(ret.at(2), QString::fromLatin1("com.trolltech.recursive"));
2819 QCOMPARE(ret.at(3), QString::fromLatin1("com.trolltech.syntaxerror"));
2820 availableExtensions = ret;
2823 // try to import something that doesn't exist
2826 QScriptValue ret = eng.importExtension("this.extension.does.not.exist");
2827 QCOMPARE(eng.hasUncaughtException(), true);
2828 QCOMPARE(ret.isError(), true);
2829 QCOMPARE(ret.toString(), QString::fromLatin1("Error: Unable to import this.extension.does.not.exist: no such extension"));
2834 for (int x = 0; x < 2; ++x) {
2835 QCOMPARE(eng.globalObject().property("com").isValid(), x == 1);
2836 QScriptValue ret = eng.importExtension("com.trolltech");
2837 QCOMPARE(eng.hasUncaughtException(), false);
2838 QCOMPARE(ret.isUndefined(), true);
2840 QScriptValue com = eng.globalObject().property("com");
2841 QCOMPARE(com.isObject(), true);
2842 QCOMPARE(com.property("wasDefinedAlready")
2843 .strictlyEquals(QScriptValue(&eng, false)), true);
2844 QCOMPARE(com.property("name")
2845 .strictlyEquals(QScriptValue(&eng, "com")), true);
2846 QCOMPARE(com.property("level")
2847 .strictlyEquals(QScriptValue(&eng, 1)), true);
2848 QVERIFY(com.property("originalPostInit").isUndefined());
2849 QVERIFY(com.property("postInitCallCount").strictlyEquals(1));
2851 QScriptValue trolltech = com.property("trolltech");
2852 QCOMPARE(trolltech.isObject(), true);
2853 QCOMPARE(trolltech.property("wasDefinedAlready")
2854 .strictlyEquals(QScriptValue(&eng, false)), true);
2855 QCOMPARE(trolltech.property("name")
2856 .strictlyEquals(QScriptValue(&eng, "com.trolltech")), true);
2857 QCOMPARE(trolltech.property("level")
2858 .strictlyEquals(QScriptValue(&eng, 2)), true);
2859 QVERIFY(trolltech.property("originalPostInit").isUndefined());
2860 QVERIFY(trolltech.property("postInitCallCount").strictlyEquals(1));
2862 QStringList imp = eng.importedExtensions();
2863 QCOMPARE(imp.size(), 2);
2864 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2865 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2866 QCOMPARE(eng.availableExtensions(), availableExtensions);
2869 // recursive import should throw an error
2872 QVERIFY(eng.importedExtensions().isEmpty());
2873 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2874 QScriptValue ret = eng.importExtension("com.trolltech.recursive");
2875 QCOMPARE(eng.hasUncaughtException(), true);
2876 QVERIFY(ret.isError());
2877 QCOMPARE(ret.toString(), QString::fromLatin1("Error: recursive import of com.trolltech.recursive"));
2878 QStringList imp = eng.importedExtensions();
2879 QCOMPARE(imp.size(), 2);
2880 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2881 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2882 QCOMPARE(eng.availableExtensions(), availableExtensions);
2887 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2888 for (int x = 0; x < 2; ++x) {
2890 QVERIFY(eng.importedExtensions().isEmpty());
2891 QScriptValue ret = eng.importExtension("com.trolltech.syntaxerror");
2892 QVERIFY(eng.hasUncaughtException());
2893 QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue);
2894 QCOMPARE(eng.uncaughtExceptionLineNumber(), 4);
2895 QVERIFY(ret.isError());
2896 QVERIFY(ret.toString().contains(QLatin1String("SyntaxError")));
2898 QStringList imp = eng.importedExtensions();
2899 QCOMPARE(imp.size(), 2);
2900 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2901 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2902 QCOMPARE(eng.availableExtensions(), availableExtensions);
2905 QCoreApplication::instance()->setLibraryPaths(libPaths);
2908 static QScriptValue recurse(QScriptContext *ctx, QScriptEngine *eng)
2911 return ctx->callee().call();
2914 static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng)
2917 return ctx->callee().construct();
2920 void tst_QJSEngine::infiniteRecursion()
2922 // Infinite recursion in JS should cause the VM to throw an error
2923 // when the JS stack is exhausted.
2924 // The exact error is back-end specific and subject to change.
2925 const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded");
2928 QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();");
2929 QCOMPARE(ret.isError(), true);
2930 QVERIFY(ret.toString().startsWith(stackOverflowError));
2932 #if 0 //The native C++ stack overflow before the JS stack
2934 QScriptValue fun = eng.newFunction(recurse);
2935 QScriptValue ret = fun.call();
2936 QCOMPARE(ret.isError(), true);
2937 QCOMPARE(ret.toString(), stackOverflowError);
2940 QScriptValue fun = eng.newFunction(recurse2);
2941 QScriptValue ret = fun.construct();
2942 QCOMPARE(ret.isError(), true);
2943 QCOMPARE(ret.toString(), stackOverflowError);
2953 struct Baz : public Bar {
2957 Q_DECLARE_METATYPE(Bar*)
2958 Q_DECLARE_METATYPE(Baz*)
2960 Q_DECLARE_METATYPE(QGradient)
2961 Q_DECLARE_METATYPE(QGradient*)
2962 Q_DECLARE_METATYPE(QLinearGradient)
2964 #if 0 // FIXME: No support for default prototypes
2965 class Zoo : public QObject
2971 Baz *toBaz(Bar *b) { return reinterpret_cast<Baz*>(b); }
2974 void tst_QJSEngine::castWithPrototypeChain()
2979 QScriptValue barProto = eng.toScriptValue(&bar);
2980 QScriptValue bazProto = eng.toScriptValue(&baz);
2981 eng.setDefaultPrototype(qMetaTypeId<Bar*>(), barProto);
2982 eng.setDefaultPrototype(qMetaTypeId<Baz*>(), bazProto);
2987 QScriptValue baz2Value = eng.toScriptValue(&baz2);
2989 // qscriptvalue_cast() does magic; if the QScriptValue contains
2990 // t of type T, and the target type is T*, &t is returned.
2991 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2993 QCOMPARE(pbaz->b, baz2.b);
2996 QScriptValue scriptZoo = eng.newQObject(&zoo);
2997 QScriptValue toBaz = scriptZoo.property("toBaz");
2998 QVERIFY(toBaz.isFunction());
3000 // no relation between Bar and Baz's proto --> casting fails
3002 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3007 QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value);
3008 QVERIFY(ret.isError());
3009 QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to toBaz(); candidates were\n toBaz(Bar*)"));
3012 // establish chain -- now casting should work
3013 // Why? because qscriptvalue_cast() does magic again.
3014 // It the instance itself is not of type T, qscriptvalue_cast()
3015 // searches the prototype chain for T, and if it finds one, it infers
3016 // that the instance can also be casted to that type. This cast is
3017 // _not_ safe and thus relies on the developer doing the right thing.
3018 // This is an undocumented feature to enable qscriptvalue_cast() to
3019 // be used by prototype functions to cast the JS this-object to C++.
3020 bazProto.setPrototype(barProto);
3023 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3025 QCOMPARE(pbar->a, baz2.a);
3029 QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value);
3030 QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue);
3031 QVERIFY(!ret.isError());
3032 QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue);
3033 QCOMPARE(qscriptvalue_cast<Baz*>(ret), pbaz);
3037 bazProto.setPrototype(barProto.prototype()); // kill chain
3039 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
3041 // should not work anymore
3042 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3046 bazProto.setPrototype(eng.newQObject(this));
3048 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
3050 // should not work now either
3051 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3056 QScriptValue b = eng.toScriptValue(QBrush());
3057 b.setPrototype(barProto);
3058 // this shows that a "wrong" cast is possible, if you
3059 // don't play by the rules (the pointer is actually a QBrush*)...
3060 Bar *pbar = qscriptvalue_cast<Bar*>(b);
3065 QScriptValue gradientProto = eng.toScriptValue(QGradient());
3066 QScriptValue linearGradientProto = eng.toScriptValue(QLinearGradient());
3067 linearGradientProto.setPrototype(gradientProto);
3068 QLinearGradient lg(10, 20, 30, 40);
3069 QScriptValue linearGradient = eng.toScriptValue(lg);
3071 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
3072 QVERIFY(pgrad == 0);
3074 linearGradient.setPrototype(linearGradientProto);
3076 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
3077 QVERIFY(pgrad != 0);
3078 QCOMPARE(pgrad->type(), QGradient::LinearGradient);
3079 QLinearGradient *plingrad = static_cast<QLinearGradient*>(pgrad);
3080 QCOMPARE(plingrad->start(), lg.start());
3081 QCOMPARE(plingrad->finalStop(), lg.finalStop());
3087 class Klazz : public QWidget,
3088 public QStandardItem,
3089 public QGraphicsItem
3093 Klazz(QWidget *parent = 0) : QWidget(parent) { }
3094 virtual QRectF boundingRect() const { return QRectF(); }
3095 virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { }
3098 Q_DECLARE_METATYPE(Klazz*)
3099 Q_DECLARE_METATYPE(QStandardItem*)
3101 void tst_QJSEngine::castWithMultipleInheritance()
3105 QJSValue v = eng.newQObject(&klz);
3107 QCOMPARE(qjsvalue_cast<Klazz*>(v), &klz);
3108 QCOMPARE(qjsvalue_cast<QWidget*>(v), (QWidget *)&klz);
3109 QCOMPARE(qjsvalue_cast<QObject*>(v), (QObject *)&klz);
3110 QCOMPARE(qjsvalue_cast<QStandardItem*>(v), (QStandardItem *)&klz);
3111 QCOMPARE(qjsvalue_cast<QGraphicsItem*>(v), (QGraphicsItem *)&klz);
3114 #if 0 // ###FIXME: ScriptOwnership
3115 void tst_QJSEngine::collectGarbage()
3118 eng.evaluate("a = new Object(); a = new Object(); a = new Object()");
3119 QScriptValue a = eng.newObject();
3120 a = eng.newObject();
3121 a = eng.newObject();
3122 QPointer<QObject> ptr = new QObject();
3124 (void)eng.newQObject(ptr, QScriptEngine::ScriptOwnership);
3125 collectGarbage_helper(eng);
3130 #if 0 // ###FIXME: no reportAdditionalMemoryCost API
3131 void tst_QJSEngine::reportAdditionalMemoryCost()
3134 // There isn't any reliable way to test whether calling
3135 // this function affects garbage collection responsiveness;
3136 // the best we can do is call it with a few different values.
3137 for (int x = 0; x < 100; ++x) {
3138 eng.reportAdditionalMemoryCost(0);
3139 eng.reportAdditionalMemoryCost(10);
3140 eng.reportAdditionalMemoryCost(1000);
3141 eng.reportAdditionalMemoryCost(10000);
3142 eng.reportAdditionalMemoryCost(100000);
3143 eng.reportAdditionalMemoryCost(1000000);
3144 eng.reportAdditionalMemoryCost(10000000);
3145 eng.reportAdditionalMemoryCost(-1);
3146 eng.reportAdditionalMemoryCost(-1000);
3147 QScriptValue obj = eng.newObject();
3148 eng.collectGarbage();
3153 void tst_QJSEngine::gcWithNestedDataStructure()
3155 // The GC must be able to traverse deeply nested objects, otherwise this
3156 // test would crash.
3159 "function makeList(size)"
3163 " for (var i = 0; i < size; ++i) {"
3164 " l.data = i + \"\";"
3165 " l.next = { }; l = l.next;"
3170 QCOMPARE(eng.hasUncaughtException(), false);
3171 const int size = 200;
3172 QJSValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size));
3173 QCOMPARE(eng.hasUncaughtException(), false);
3174 for (int x = 0; x < 2; ++x) {
3176 eng.evaluate("gc()");
3178 // Make sure all the nodes are still alive.
3179 for (int i = 0; i < 200; ++i) {
3180 QCOMPARE(l.property("data").toString(), QString::number(i));
3181 l = l.property("next");
3186 #if 0 // ###FIXME: No processEvents handling
3187 class EventReceiver : public QObject
3194 bool event(QEvent *e) {
3195 received |= (e->type() == QEvent::User + 1);
3196 return QObject::event(e);
3202 void tst_QJSEngine::processEventsWhileRunning()
3204 for (int x = 0; x < 2; ++x) {
3209 // This is running for a silly amount of time just to make sure
3210 // the script doesn't finish before event processing is triggered.
3211 QString script = QString::fromLatin1(
3212 "var end = Number(new Date()) + 2000;"
3214 "while (Number(new Date()) < end) {"
3218 EventReceiver receiver;
3219 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3221 eng.evaluate(script);
3222 QVERIFY(!eng.hasUncaughtException());
3223 QVERIFY(!receiver.received);
3225 QCOMPARE(eng.processEventsInterval(), -1);
3226 eng.setProcessEventsInterval(100);
3227 eng.evaluate(script);
3228 QVERIFY(!eng.hasUncaughtException());
3229 QVERIFY(receiver.received);
3236 void tst_QJSEngine::processEventsWhileRunning_function()
3239 QScriptValue script = eng.evaluate(QString::fromLatin1(
3240 "(function() { var end = Number(new Date()) + 2000;"
3242 "while (Number(new Date()) < end) {"
3246 eng.setProcessEventsInterval(100);
3248 for (int x = 0; x < 2; ++x) {
3249 EventReceiver receiver;
3250 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3251 QVERIFY(!eng.hasUncaughtException());
3252 QVERIFY(!receiver.received);
3253 QCOMPARE(eng.processEventsInterval(), 100);
3255 if (x) script.call();
3256 else script.construct();
3258 QVERIFY(!eng.hasUncaughtException());
3259 QVERIFY(receiver.received);
3264 class EventReceiver2 : public QObject
3267 EventReceiver2(QScriptEngine *eng) {
3271 bool event(QEvent *e) {
3272 if (e->type() == QEvent::User + 1) {
3273 engine->currentContext()->throwError("Killed");
3275 return QObject::event(e);
3278 QScriptEngine *engine;
3281 void tst_QJSEngine::throwErrorFromProcessEvents_data()
3283 QTest::addColumn<QString>("script");
3284 QTest::addColumn<QString>("error");
3286 QTest::newRow("while (1)")
3287 << QString::fromLatin1("while (1) { }")
3288 << QString::fromLatin1("Error: Killed");
3289 QTest::newRow("while (1) i++")
3290 << QString::fromLatin1("i = 0; while (1) { i++; }")
3291 << QString::fromLatin1("Error: Killed");
3292 // Unlike abortEvaluation(), scripts should be able to catch the
3294 QTest::newRow("try catch")
3295 << QString::fromLatin1("try {"
3298 " throw new Error('Caught');"
3300 << QString::fromLatin1("Error: Caught");
3303 void tst_QJSEngine::throwErrorFromProcessEvents()
3305 QFETCH(QString, script);
3306 QFETCH(QString, error);
3310 EventReceiver2 receiver(&eng);
3311 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3313 eng.setProcessEventsInterval(100);
3314 QScriptValue ret = eng.evaluate(script);
3315 QVERIFY(ret.isError());
3316 QCOMPARE(ret.toString(), error);
3319 void tst_QJSEngine::disableProcessEventsInterval()
3322 eng.setProcessEventsInterval(100);
3323 QCOMPARE(eng.processEventsInterval(), 100);
3324 eng.setProcessEventsInterval(0);
3325 QCOMPARE(eng.processEventsInterval(), 0);
3326 eng.setProcessEventsInterval(-1);
3327 QCOMPARE(eng.processEventsInterval(), -1);
3328 eng.setProcessEventsInterval(-100);
3329 QCOMPARE(eng.processEventsInterval(), -100);
3333 #if 0 // ###FIXME: No QScriptValueIterator API
3334 void tst_QJSEngine::stacktrace()
3336 QString script = QString::fromLatin1(
3337 "function foo(counter) {\n"
3338 " switch (counter) {\n"
3339 " case 0: foo(counter+1); break;\n"
3340 " case 1: foo(counter+1); break;\n"
3341 " case 2: foo(counter+1); break;\n"
3342 " case 3: foo(counter+1); break;\n"
3343 " case 4: foo(counter+1); break;\n"
3345 " throw new Error('blah');\n"
3350 const QString fileName("testfile");
3352 QStringList backtrace;
3353 backtrace << "foo(5)@testfile:9"
3354 << "foo(4)@testfile:7"
3355 << "foo(3)@testfile:6"
3356 << "foo(2)@testfile:5"
3357 << "foo(1)@testfile:4"
3358 << "foo(0)@testfile:3"
3359 << "<global>()@testfile:12";
3362 QScriptValue result = eng.evaluate(script, fileName);
3363 QVERIFY(eng.hasUncaughtException());
3364 QVERIFY(result.isError());
3366 QEXPECT_FAIL("", "QTBUG-6139: uncaughtExceptionBacktrace() doesn't give the full backtrace", Abort);
3367 // ###FIXME: no uncahgutExceptionBacktrace: QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace);
3368 QVERIFY(eng.hasUncaughtException());
3369 QVERIFY(result.strictlyEquals(eng.uncaughtException()));
3371 QCOMPARE(result.property("fileName").toString(), fileName);
3372 QCOMPARE(result.property("lineNumber").toInt32(), 9);
3374 QScriptValue stack = result.property("stack");
3375 QVERIFY(stack.isArray());
3377 QCOMPARE(stack.property("length").toInt32(), 7);
3379 QScriptValueIterator it(stack);
3381 while (it.hasNext()) {
3383 QScriptValue obj = it.value();
3384 QScriptValue frame = obj.property("frame");
3386 QCOMPARE(obj.property("fileName").toString(), fileName);
3388 QScriptValue callee = frame.property("arguments").property("callee");
3389 QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo")));
3390 QCOMPARE(obj.property("functionName").toString(), QString("foo"));
3391 int line = obj.property("lineNumber").toInt32();
3395 QCOMPARE(line, 3 + counter);
3397 QVERIFY(frame.strictlyEquals(eng.globalObject()));
3398 QVERIFY(obj.property("functionName").toString().isEmpty());
3405 QScriptValue bt = result.property("backtrace").call(result);
3406 QCOMPARE(qscriptvalue_cast<QStringList>(bt), backtrace);
3409 // throw something that isn't an Error object
3410 eng.clearExceptions();
3411 // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3412 QString script2 = QString::fromLatin1(
3413 "function foo(counter) {\n"
3414 " switch (counter) {\n"
3415 " case 0: foo(counter+1); break;\n"
3416 " case 1: foo(counter+1); break;\n"
3417 " case 2: foo(counter+1); break;\n"
3418 " case 3: foo(counter+1); break;\n"
3419 " case 4: foo(counter+1); break;\n"
3421 " throw 'just a string';\n"
3426 QScriptValue result2 = eng.evaluate(script2, fileName);
3427 QVERIFY(eng.hasUncaughtException());
3428 QVERIFY(!result2.isError());
3429 QVERIFY(result2.isString());
3431 // ###FIXME: No uncaughtExceptionBacktrace: QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace);
3432 QVERIFY(eng.hasUncaughtException());
3434 eng.clearExceptions();
3435 QVERIFY(!eng.hasUncaughtException());
3436 // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3440 void tst_QJSEngine::numberParsing_data()
3442 QTest::addColumn<QString>("string");
3443 QTest::addColumn<qreal>("expect");
3445 QTest::newRow("decimal 0") << QString("0") << qreal(0);
3446 QTest::newRow("octal 0") << QString("00") << qreal(00);
3447 QTest::newRow("hex 0") << QString("0x0") << qreal(0x0);
3448 QTest::newRow("decimal 100") << QString("100") << qreal(100);
3449 QTest::newRow("hex 100") << QString("0x100") << qreal(0x100);
3450 QTest::newRow("octal 100") << QString("0100") << qreal(0100);
3451 QTest::newRow("decimal 4G") << QString("4294967296") << qreal(Q_UINT64_C(4294967296));
3452 QTest::newRow("hex 4G") << QString("0x100000000") << qreal(Q_UINT64_C(0x100000000));
3453 QTest::newRow("octal 4G") << QString("040000000000") << qreal(Q_UINT64_C(040000000000));
3454 QTest::newRow("0.5") << QString("0.5") << qreal(0.5);
3455 QTest::newRow("1.5") << QString("1.5") << qreal(1.5);
3456 QTest::newRow("1e2") << QString("1e2") << qreal(100);
3459 void tst_QJSEngine::numberParsing()
3461 QFETCH(QString, string);
3462 QFETCH(qreal, expect);
3465 QJSValue ret = eng.evaluate(string);
3466 QVERIFY(ret.isNumber());
3467 qreal actual = ret.toNumber();
3468 QCOMPARE(actual, expect);
3471 // see ECMA-262, section 7.9
3472 // This is testing ECMA compliance, not our C++ API, but it's important that
3473 // the back-end is conformant in this regard.
3474 void tst_QJSEngine::automaticSemicolonInsertion()
3478 QJSValue ret = eng.evaluate("{ 1 2 } 3");
3479 QVERIFY(ret.isError());
3480 QVERIFY(ret.toString().contains("SyntaxError"));
3483 QJSValue ret = eng.evaluate("{ 1\n2 } 3");
3484 QVERIFY(ret.isNumber());
3485 QCOMPARE(ret.toInt32(), 3);
3488 QJSValue ret = eng.evaluate("for (a; b\n)");
3489 QVERIFY(ret.isError());
3490 QVERIFY(ret.toString().contains("SyntaxError"));
3493 QJSValue ret = eng.evaluate("(function() { return\n1 + 2 })()");
3494 QVERIFY(ret.isUndefined());
3497 eng.evaluate("c = 2; b = 1");
3498 QJSValue ret = eng.evaluate("a = b\n++c");
3499 QVERIFY(ret.isNumber());
3500 QCOMPARE(ret.toInt32(), 3);
3503 QJSValue ret = eng.evaluate("if (a > b)\nelse c = d");
3504 QVERIFY(ret.isError());
3505 QVERIFY(ret.toString().contains("SyntaxError"));
3508 eng.evaluate("function c() { return { foo: function() { return 5; } } }");
3509 eng.evaluate("b = 1; d = 2; e = 3");
3510 QJSValue ret = eng.evaluate("a = b + c\n(d + e).foo()");
3511 QVERIFY(ret.isNumber());
3512 QCOMPARE(ret.toInt32(), 6);
3515 QJSValue ret = eng.evaluate("throw\n1");
3516 QVERIFY(ret.isError());
3517 QVERIFY(ret.toString().contains("SyntaxError"));
3520 QJSValue ret = eng.evaluate("a = Number(1)\n++a");
3521 QVERIFY(ret.isNumber());
3522 QCOMPARE(ret.toInt32(), 2);
3525 // "a semicolon is never inserted automatically if the semicolon
3526 // would then be parsed as an empty statement"
3528 eng.evaluate("a = 123");
3529 QJSValue ret = eng.evaluate("if (0)\n ++a; a");
3530 QVERIFY(ret.isNumber());
3531 QCOMPARE(ret.toInt32(), 123);
3534 eng.evaluate("a = 123");
3535 QJSValue ret = eng.evaluate("if (0)\n --a; a");
3536 QVERIFY(ret.isNumber());
3537 QCOMPARE(ret.toInt32(), 123);
3540 eng.evaluate("a = 123");
3541 QJSValue ret = eng.evaluate("if ((0))\n ++a; a");
3542 QVERIFY(ret.isNumber());
3543 QCOMPARE(ret.toInt32(), 123);
3546 eng.evaluate("a = 123");
3547 QJSValue ret = eng.evaluate("if ((0))\n --a; a");
3548 QVERIFY(ret.isNumber());
3549 QCOMPARE(ret.toInt32(), 123);
3552 eng.evaluate("a = 123");
3553 QJSValue ret = eng.evaluate("if (0\n)\n ++a; a");
3554 QVERIFY(ret.isNumber());
3555 QCOMPARE(ret.toInt32(), 123);
3558 eng.evaluate("a = 123");
3559 QJSValue ret = eng.evaluate("if (0\n ++a; a");
3560 QVERIFY(ret.isError());
3563 eng.evaluate("a = 123");
3564 QJSValue ret = eng.evaluate("if (0))\n ++a; a");
3565 QVERIFY(ret.isError());
3568 QJSValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n");
3569 QVERIFY(ret.isNumber());
3570 QCOMPARE(ret.toInt32(), 10);
3573 QJSValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n");
3574 QVERIFY(ret.isNumber());
3575 QCOMPARE(ret.toInt32(), 20);
3578 QJSValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n");
3579 QVERIFY(ret.isNumber());
3580 QCOMPARE(ret.toInt32(), 10);
3583 QJSValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n");
3584 QVERIFY(ret.isNumber());
3585 QCOMPARE(ret.toInt32(), 20);
3588 QJSValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n");
3589 QVERIFY(ret.isNumber());
3590 QCOMPARE(ret.toInt32(), 10);
3593 QJSValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n");
3594 QVERIFY(ret.isNumber());
3595 QCOMPARE(ret.toInt32(), 20);
3598 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n");
3599 QVERIFY(ret.isNumber());
3600 QCOMPARE(ret.toInt32(), 3);
3603 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n");
3604 QVERIFY(ret.isNumber());
3605 QCOMPARE(ret.toInt32(), 6);
3608 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n");
3609 QVERIFY(ret.isNumber());
3610 QCOMPARE(ret.toInt32(), 3);
3613 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n");
3614 QVERIFY(ret.isNumber());
3615 QCOMPARE(ret.toInt32(), 6);
3618 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n");
3619 QVERIFY(ret.isNumber());
3620 QCOMPARE(ret.toInt32(), 5);
3623 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n");
3624 QVERIFY(ret.isNumber());
3625 QCOMPARE(ret.toInt32(), 10);
3628 QJSValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n");
3629 QVERIFY(ret.isNumber());
3630 QCOMPARE(ret.toInt32(), 15);
3633 QJSValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n");
3634 QVERIFY(ret.isNumber());
3635 QCOMPARE(ret.toInt32(), 10);
3639 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n");
3640 QVERIFY(ret.isNumber());
3641 QCOMPARE(ret.toInt32(), 2);
3644 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n");
3645 QVERIFY(ret.isNumber());
3646 QCOMPARE(ret.toInt32(), 0);
3650 QJSValue ret = eng.evaluate("if (0)");
3651 QVERIFY(ret.isError());
3654 QJSValue ret = eng.evaluate("while (0)");
3655 QVERIFY(ret.isError());
3658 QJSValue ret = eng.evaluate("for (;;)");
3659 QVERIFY(ret.isError());
3662 QJSValue ret = eng.evaluate("for (p in this)");
3663 QVERIFY(ret.isError());
3666 QJSValue ret = eng.evaluate("with (this)");
3667 QVERIFY(ret.isError());
3670 QJSValue ret = eng.evaluate("do");
3671 QVERIFY(ret.isError());
3675 #if 0 // ###FIXME: no abortEvaluation API
3676 class EventReceiver3 : public QObject
3679 enum AbortionResult {
3686 EventReceiver3(QScriptEngine *eng) {
3691 bool event(QEvent *e) {
3692 if (e->type() == QEvent::User + 1) {
3693 switch (resultType) {
3695 engine->abortEvaluation();
3698 engine->abortEvaluation(QScriptValue(engine, QString::fromLatin1("Aborted")));
3701 engine->abortEvaluation(engine->currentContext()->throwError("AbortedWithError"));
3704 engine->abortEvaluation(QScriptValue(1234));
3707 return QObject::event(e);
3710 AbortionResult resultType;
3711 QScriptEngine *engine;
3714 static QScriptValue myFunctionAbortingEvaluation(QScriptContext *, QScriptEngine *eng)
3716 eng->abortEvaluation();
3717 return eng->nullValue(); // should be ignored
3720 void tst_QJSEngine::abortEvaluation_notEvaluating()
3724 eng.abortEvaluation();
3725 QVERIFY(!eng.hasUncaughtException());
3727 eng.abortEvaluation(123);
3729 QScriptValue ret = eng.evaluate("'ciao'");
3730 QVERIFY(ret.isString());
3731 QCOMPARE(ret.toString(), QString::fromLatin1("ciao"));
3735 void tst_QJSEngine::abortEvaluation_data()
3737 QTest::addColumn<QString>("script");
3739 QTest::newRow("while (1)")
3740 << QString::fromLatin1("while (1) { }");
3741 QTest::newRow("while (1) i++")
3742 << QString::fromLatin1("i = 0; while (1) { i++; }");
3743 QTest::newRow("try catch")
3744 << QString::fromLatin1("try {"
3747 " throw new Error('Caught');"
3751 void tst_QJSEngine::abortEvaluation()
3753 QFETCH(QString, script);
3756 EventReceiver3 receiver(&eng);
3758 eng.setProcessEventsInterval(100);
3759 for (int x = 0; x < 4; ++x) {
3760 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3761 receiver.resultType = EventReceiver3::AbortionResult(x);
3762 QScriptValue ret = eng.evaluate(script);
3763 switch (receiver.resultType) {
3764 case EventReceiver3::None:
3765 QVERIFY(!eng.hasUncaughtException());
3766 QVERIFY(!ret.isValid());
3768 case EventReceiver3::Number:
3769 QVERIFY(!eng.hasUncaughtException());
3770 QVERIFY(ret.isNumber());
3771 QCOMPARE(ret.toInt32(), 1234);
3773 case EventReceiver3::String:
3774 QVERIFY(!eng.hasUncaughtException());
3775 QVERIFY(ret.isString());
3776 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3778 case EventReceiver3::Error:
3779 QVERIFY(eng.hasUncaughtException());
3780 QVERIFY(ret.isError());
3781 QCOMPARE(ret.toString(), QString::fromLatin1("Error: AbortedWithError"));
3788 void tst_QJSEngine::abortEvaluation_tryCatch()
3790 QSKIP("It crashes", SkipAll);
3792 EventReceiver3 receiver(&eng);
3793 eng.setProcessEventsInterval(100);
3794 // scripts cannot intercept the abortion with try/catch
3795 for (int y = 0; y < 4; ++y) {
3796 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3797 receiver.resultType = EventReceiver3::AbortionResult(y);
3798 QScriptValue ret = eng.evaluate(QString::fromLatin1(
3801 " (function() { while (1) { } })();\n"
3806 switch (receiver.resultType) {
3807 case EventReceiver3::None:
3808 QVERIFY(!eng.hasUncaughtException());
3809 QVERIFY(!ret.isValid());
3811 case EventReceiver3::Number:
3812 QVERIFY(!eng.hasUncaughtException());
3813 QVERIFY(ret.isNumber());
3814 QCOMPARE(ret.toInt32(), 1234);
3816 case EventReceiver3::String:
3817 QVERIFY(!eng.hasUncaughtException());
3818 QVERIFY(ret.isString());
3819 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3821 case EventReceiver3::Error:
3822 QVERIFY(eng.hasUncaughtException());
3823 QVERIFY(ret.isError());
3829 void tst_QJSEngine::abortEvaluation_fromNative()
3832 QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation);
3833 eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun);
3834 QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()");
3835 QVERIFY(!ret.isValid());
3838 class ThreadedEngine : public QThread {
3842 QScriptEngine* m_engine;
3845 m_engine = new QScriptEngine();
3846 m_engine->setGlobalObject(m_engine->newQObject(this));
3847 m_engine->evaluate("while (1) { sleep(); }");
3855 m_engine->abortEvaluation();
3859 void tst_QJSEngine::abortEvaluation_QTBUG9433()
3861 ThreadedEngine engine;
3863 QVERIFY(engine.isRunning());
3865 for (uint i = 0; i < 50; ++i) { // up to ~2500 ms
3866 if (engine.isFinished())
3870 if (!engine.isFinished()) {
3873 QFAIL("abortEvaluation doesn't work");
3879 #if 0 // ###FIXME: no QScriptEngine::isEvaluating
3880 static QScriptValue myFunctionReturningIsEvaluating(QScriptContext *, QScriptEngine *eng)
3882 return QScriptValue(eng, eng->isEvaluating());
3885 class EventReceiver4 : public QObject
3888 EventReceiver4(QScriptEngine *eng) {
3890 wasEvaluating = false;
3893 bool event(QEvent *e) {
3894 if (e->type() == QEvent::User + 1) {
3895 wasEvaluating = engine->isEvaluating();
3897 return QObject::event(e);
3900 QScriptEngine *engine;
3904 void tst_QJSEngine::isEvaluating_notEvaluating()
3908 QVERIFY(!eng.isEvaluating());
3911 QVERIFY(!eng.isEvaluating());
3912 eng.evaluate("123");
3913 QVERIFY(!eng.isEvaluating());
3914 eng.evaluate("0 = 0");
3915 QVERIFY(!eng.isEvaluating());
3918 void tst_QJSEngine::isEvaluating_fromNative()
3921 QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating);
3922 eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun);
3923 QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()");
3924 QVERIFY(ret.isBoolean());
3925 QVERIFY(ret.toBoolean());
3927 QVERIFY(ret.isBoolean());
3928 QVERIFY(ret.toBoolean());
3929 ret = myFunctionReturningIsEvaluating(eng.currentContext(), &eng);
3930 QVERIFY(ret.isBoolean());
3931 QVERIFY(!ret.toBoolean());
3934 void tst_QJSEngine::isEvaluating_fromEvent()
3937 EventReceiver4 receiver(&eng);
3938 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3940 QString script = QString::fromLatin1(
3941 "var end = Number(new Date()) + 1000;"
3943 "while (Number(new Date()) < end) {"
3947 eng.setProcessEventsInterval(100);
3948 eng.evaluate(script);
3949 QVERIFY(receiver.wasEvaluating);
3953 static QtMsgType theMessageType;
3954 static QString theMessage;
3956 static void myMsgHandler(QtMsgType type, const char *msg)
3958 theMessageType = type;
3959 theMessage = QString::fromLatin1(msg);
3963 void tst_QJSEngine::printFunctionWithCustomHandler()
3965 // The built-in print() function passes the output to Qt's message
3966 // handler. By installing a custom message handler, the output can be
3967 // redirected without changing the print() function itself.
3968 // This behavior is not documented.
3970 QtMsgHandler oldHandler = qInstallMsgHandler(myMsgHandler);
3971 QVERIFY(eng.globalObject().property("print").isFunction());
3973 theMessageType = QtSystemMsg;
3974 QVERIFY(theMessage.isEmpty());
3975 QVERIFY(eng.evaluate("print('test')").isUndefined());
3976 QCOMPARE(theMessageType, QtDebugMsg);
3977 QCOMPARE(theMessage, QString::fromLatin1("test"));
3979 theMessageType = QtSystemMsg;
3981 QVERIFY(eng.evaluate("print(3, true, 'little pigs')").isUndefined());
3982 QCOMPARE(theMessageType, QtDebugMsg);
3983 QCOMPARE(theMessage, QString::fromLatin1("3 true little pigs"));
3985 qInstallMsgHandler(oldHandler);
3988 void tst_QJSEngine::printThrowsException()
3990 // If an argument to print() causes an exception to be thrown when
3991 // it's converted to a string, print() should propagate the exception.
3993 QJSValue ret = eng.evaluate("print({ toString: function() { throw 'foo'; } });");
3994 QVERIFY(eng.hasUncaughtException());
3995 QVERIFY(ret.strictlyEquals(QJSValue(&eng, QLatin1String("foo"))));
3999 void tst_QJSEngine::errorConstructors()
4002 QStringList prefixes;
4003 prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI";
4004 for (int x = 0; x < 3; ++x) {
4005 for (int i = 0; i < prefixes.size(); ++i) {
4006 QString name = prefixes.at(i) + QLatin1String("Error");
4007 QString code = QString(i+1, QLatin1Char('\n'));
4009 code += QLatin1String("throw ");
4011 code += QLatin1String("new ");
4012 code += name + QLatin1String("()");
4013 QJSValue ret = eng.evaluate(code);
4014 QVERIFY(ret.isError());
4015 QCOMPARE(eng.hasUncaughtException(), x == 0);
4016 eng.clearExceptions();
4017 QVERIFY(ret.toString().startsWith(name));
4018 //QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown
4019 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
4020 QCOMPARE(ret.property("lineNumber").toInt32(), i+2);
4025 void tst_QJSEngine::argumentsProperty_globalContext()
4029 // Unlike function calls, the global context doesn't have an
4030 // arguments property.
4031 QJSValue ret = eng.evaluate("arguments");
4032 QVERIFY(ret.isError());
4033 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4035 eng.evaluate("arguments = 10");
4037 QJSValue ret = eng.evaluate("arguments");
4038 QVERIFY(ret.isNumber());
4039 QCOMPARE(ret.toInt32(), 10);
4041 QVERIFY(eng.evaluate("delete arguments").toBoolean());
4042 QVERIFY(!eng.globalObject().property("arguments").isValid());
4045 void tst_QJSEngine::argumentsProperty_JS()
4049 eng.evaluate("o = { arguments: 123 }");
4050 QJSValue ret = eng.evaluate("with (o) { arguments; }");
4051 QVERIFY(ret.isNumber());
4052 QCOMPARE(ret.toInt32(), 123);
4056 QVERIFY(!eng.globalObject().property("arguments").isValid());
4057 // This is testing ECMA-262 compliance. In function calls, "arguments"
4058 // appears like a local variable, and it can be replaced.
4059 QJSValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()");
4060 QVERIFY(ret.isNumber());
4061 QCOMPARE(ret.toInt32(), 456);
4062 QVERIFY(!eng.globalObject().property("arguments").isValid());
4066 #if 0 // ###FIXME: no QScriptContext API
4067 static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng)
4069 // Since evaluate() is done in the current context, "arguments" should
4070 // refer to currentContext()->argumentsObject().
4071 // This is for consistency with the built-in JS eval() function.
4072 eng->evaluate("var a = arguments[0];");
4073 eng->evaluate("arguments[0] = 200;");
4074 return eng->evaluate("a + arguments[0]");
4077 void tst_QJSEngine::argumentsProperty_evaluateInNativeFunction()
4080 QScriptValue fun = eng.newFunction(argumentsProperty_fun);
4081 eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun));
4082 QScriptValue result = eng.evaluate("fun(18)");
4083 QVERIFY(result.isNumber());
4084 QCOMPARE(result.toInt32(), 200+18);
4088 void tst_QJSEngine::jsNumberClass()
4090 // See ECMA-262 Section 15.7, "Number Objects".
4094 QJSValue ctor = eng.globalObject().property("Number");
4095 QVERIFY(ctor.property("length").isNumber());
4096 QCOMPARE(ctor.property("length").toNumber(), qreal(1));
4097 QJSValue proto = ctor.property("prototype");
4098 QVERIFY(proto.isObject());
4100 QJSValue::PropertyFlags flags = QJSValue::SkipInEnumeration
4101 | QJSValue::Undeletable
4102 | QJSValue::ReadOnly;
4103 QCOMPARE(ctor.propertyFlags("prototype"), flags);
4104 QVERIFY(ctor.property("MAX_VALUE").isNumber());
4105 QCOMPARE(ctor.propertyFlags("MAX_VALUE"), flags);
4106 QVERIFY(ctor.property("MIN_VALUE").isNumber());
4107 QCOMPARE(ctor.propertyFlags("MIN_VALUE"), flags);
4108 QVERIFY(ctor.property("NaN").isNumber());
4109 QCOMPARE(ctor.propertyFlags("NaN"), flags);
4110 QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
4111 QCOMPARE(ctor.propertyFlags("NEGATIVE_INFINITY"), flags);
4112 QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
4113 QCOMPARE(ctor.propertyFlags("POSITIVE_INFINITY"), flags);
4115 QVERIFY(proto.instanceOf(eng.globalObject().property("Object")));
4116 QCOMPARE(proto.toNumber(), qreal(0));
4117 QVERIFY(proto.property("constructor").strictlyEquals(ctor));
4120 QJSValue ret = eng.evaluate("Number()");
4121 QVERIFY(ret.isNumber());
4122 QCOMPARE(ret.toNumber(), qreal(0));
4125 QJSValue ret = eng.evaluate("Number(123)");
4126 QVERIFY(ret.isNumber());
4127 QCOMPARE(ret.toNumber(), qreal(123));
4130 QJSValue ret = eng.evaluate("Number('456')");
4131 QVERIFY(ret.isNumber());
4132 QCOMPARE(ret.toNumber(), qreal(456));
4135 QJSValue ret = eng.evaluate("new Number()");
4136 QVERIFY(!ret.isNumber());
4137 QVERIFY(ret.isObject());
4138 QCOMPARE(ret.toNumber(), qreal(0));
4141 QJSValue ret = eng.evaluate("new Number(123)");
4142 QVERIFY(!ret.isNumber());
4143 QVERIFY(ret.isObject());
4144 QCOMPARE(ret.toNumber(), qreal(123));
4147 QJSValue ret = eng.evaluate("new Number('456')");
4148 QVERIFY(!ret.isNumber());
4149 QVERIFY(ret.isObject());
4150 QCOMPARE(ret.toNumber(), qreal(456));
4153 QVERIFY(proto.property("toString").isFunction());
4155 QJSValue ret = eng.evaluate("new Number(123).toString()");
4156 QVERIFY(ret.isString());
4157 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4160 QJSValue ret = eng.evaluate("new Number(123).toString(8)");
4161 QVERIFY(ret.isString());
4162 QCOMPARE(ret.toString(), QString::fromLatin1("173"));
4165 QJSValue ret = eng.evaluate("new Number(123).toString(16)");
4166 QVERIFY(ret.isString());
4167 QCOMPARE(ret.toString(), QString::fromLatin1("7b"));
4169 QVERIFY(proto.property("toLocaleString").isFunction());
4171 QJSValue ret = eng.evaluate("new Number(123).toLocaleString()");
4172 QVERIFY(ret.isString());
4173 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4175 QVERIFY(proto.property("valueOf").isFunction());
4177 QJSValue ret = eng.evaluate("new Number(123).valueOf()");
4178 QVERIFY(ret.isNumber());
4179 QCOMPARE(ret.toNumber(), qreal(123));
4181 QVERIFY(proto.property("toExponential").isFunction());
4183 QJSValue ret = eng.evaluate("new Number(123).toExponential()");
4184 QVERIFY(ret.isString());
4185 QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2"));
4187 QVERIFY(proto.property("toFixed").isFunction());
4189 QJSValue ret = eng.evaluate("new Number(123).toFixed()");
4190 QVERIFY(ret.isString());
4191 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4193 QVERIFY(proto.property("toPrecision").isFunction());
4195 QJSValue ret = eng.evaluate("new Number(123).toPrecision()");
4196 QVERIFY(ret.isString());
4197 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4201 void tst_QJSEngine::jsForInStatement_simple()
4205 QJSValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r");
4206 QStringList lst = qjsvalue_cast<QStringList>(ret);
4207 QVERIFY(lst.isEmpty());
4210 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4211 "for (var p in o) r[r.length] = p; r");
4212 QStringList lst = qjsvalue_cast<QStringList>(ret);
4213 QCOMPARE(lst.size(), 1);
4214 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4217 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
4218 "for (var p in o) r[r.length] = p; r");
4219 QStringList lst = qjsvalue_cast<QStringList>(ret);
4220 QCOMPARE(lst.size(), 2);
4221 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4222 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
4226 void tst_QJSEngine::jsForInStatement_prototypeProperties()
4230 QJSValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];"
4231 "for (var p in o) r[r.length] = p; r");
4232 QStringList lst = qjsvalue_cast<QStringList>(ret);
4233 QCOMPARE(lst.size(), 1);
4234 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4237 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];"
4238 "for (var p in o) r[r.length] = p; r");
4239 QStringList lst = qjsvalue_cast<QStringList>(ret);
4240 QCOMPARE(lst.size(), 2);
4241 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4242 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
4245 // shadowed property
4246 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];"
4247 "for (var p in o) r[r.length] = p; r");
4248 QStringList lst = qjsvalue_cast<QStringList>(ret);
4249 QCOMPARE(lst.size(), 1);
4250 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4255 void tst_QJSEngine::jsForInStatement_mutateWhileIterating()
4258 // deleting property during enumeration
4260 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4261 "for (var p in o) { r[r.length] = p; delete r[p]; } r");
4262 QStringList lst = qjsvalue_cast<QStringList>(ret);
4263 QCOMPARE(lst.size(), 1);
4264 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4267 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
4268 "for (var p in o) { r[r.length] = p; delete o.q; } r");
4269 QStringList lst = qjsvalue_cast<QStringList>(ret);
4270 QCOMPARE(lst.size(), 1);
4271 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4274 // adding property during enumeration
4276 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4277 "for (var p in o) { r[r.length] = p; o.q = 456; } r");
4278 QStringList lst = qjsvalue_cast<QStringList>(ret);
4279 QCOMPARE(lst.size(), 1);
4280 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4285 void tst_QJSEngine::jsForInStatement_arrays()
4289 QJSValue ret = eng.evaluate("a = [123, 456]; r = [];"
4290 "for (var p in a) r[r.length] = p; r");
4291 QStringList lst = qjsvalue_cast<QStringList>(ret);
4292 QCOMPARE(lst.size(), 2);
4293 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4294 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4297 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];"
4298 "for (var p in a) r[r.length] = p; r");
4299 QStringList lst = qjsvalue_cast<QStringList>(ret);
4300 QCOMPARE(lst.size(), 3);
4301 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4302 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4303 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
4306 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';"
4307 "b = [111, 222, 333]; b.bar = 'baz';"
4308 "a.__proto__ = b; r = [];"
4309 "for (var p in a) r[r.length] = p; r");
4310 QStringList lst = qjsvalue_cast<QStringList>(ret);
4311 QCOMPARE(lst.size(), 5);
4312 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4313 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4314 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
4315 QCOMPARE(lst.at(3), QString::fromLatin1("2"));
4316 QCOMPARE(lst.at(4), QString::fromLatin1("bar"));
4320 void tst_QJSEngine::jsForInStatement_nullAndUndefined()
4324 QJSValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r");
4325 QVERIFY(ret.isBool());
4326 QVERIFY(ret.toBool());
4329 QJSValue ret = eng.evaluate("r = true; for (var p in null) r = false; r");
4330 QVERIFY(ret.isBool());
4331 QVERIFY(ret.toBool());
4335 void tst_QJSEngine::jsFunctionDeclarationAsStatement()
4337 // ECMA-262 does not allow function declarations to be used as statements,
4338 // but several popular implementations (including JSC) do. See the NOTE
4339 // at the beginning of chapter 12 in ECMA-262 5th edition, where it's
4340 // recommended that implementations either disallow this usage or issue
4342 // Since we had a bug report long ago about QtScript not supporting this
4343 // "feature" (and thus deviating from other implementations), we still
4344 // check this behavior.
4347 QVERIFY(!eng.globalObject().property("bar").isValid());
4348 eng.evaluate("function foo(arg) {\n"
4349 " if (arg == 'bar')\n"
4350 " function bar() { return 'bar'; }\n"
4352 " function baz() { return 'baz'; }\n"
4353 " return (arg == 'bar') ? bar : baz;\n"
4355 QVERIFY(!eng.globalObject().property("bar").isValid());
4356 QVERIFY(!eng.globalObject().property("baz").isValid());
4357 QVERIFY(eng.evaluate("foo").isFunction());
4359 QJSValue ret = eng.evaluate("foo('bar')");
4360 QVERIFY(ret.isFunction());
4361 QJSValue ret2 = ret.call(QJSValue());
4362 QCOMPARE(ret2.toString(), QString::fromLatin1("bar"));
4363 QVERIFY(!eng.globalObject().property("bar").isValid());
4364 QVERIFY(!eng.globalObject().property("baz").isValid());
4367 QJSValue ret = eng.evaluate("foo('baz')");
4368 QVERIFY(ret.isFunction());
4369 QJSValue ret2 = ret.call(QJSValue());
4370 QCOMPARE(ret2.toString(), QString::fromLatin1("baz"));
4371 QVERIFY(!eng.globalObject().property("bar").isValid());
4372 QVERIFY(!eng.globalObject().property("baz").isValid());
4376 void tst_QJSEngine::stringObjects()
4378 // See ECMA-262 Section 15.5, "String Objects".
4381 QString str("ciao");
4384 QJSValue obj = QJSValue(&eng, str).toObject();
4385 QCOMPARE(obj.property("length").toInt32(), str.length());
4386 QCOMPARE(obj.propertyFlags("length"), QJSValue::PropertyFlags(QJSValue::Undeletable | QJSValue::SkipInEnumeration | QJSValue::ReadOnly));
4387 for (int i = 0; i < str.length(); ++i) {
4388 QString pname = QString::number(i);
4389 QVERIFY(obj.property(pname).isString());
4390 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4391 QEXPECT_FAIL("", "FIXME: This is V8 issue 862. ECMA script standard 15.5.5.2 compliance.", Continue);
4392 QCOMPARE(obj.propertyFlags(pname), QJSValue::PropertyFlags(QJSValue::Undeletable | QJSValue::ReadOnly));
4393 obj.setProperty(pname, QJSValue());
4394 obj.setProperty(pname, QJSValue(&eng, 123));
4395 QVERIFY(obj.property(pname).isString());
4396 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4398 QVERIFY(!obj.property("-1").isValid());
4399 QVERIFY(!obj.property(QString::number(str.length())).isValid());
4401 QJSValue val(&eng, 123);
4402 obj.setProperty("-1", val);
4403 QVERIFY(obj.property("-1").strictlyEquals(val));
4404 obj.setProperty("100", val);
4405 QVERIFY(obj.property("100").strictlyEquals(val));
4410 QJSValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r");
4411 QVERIFY(ret.isArray());
4412 QStringList lst = qjsvalue_cast<QStringList>(ret);
4413 QCOMPARE(lst.size(), str.length());
4414 for (int i = 0; i < str.length(); ++i)
4415 QCOMPARE(lst.at(i), QString::number(i));
4417 QJSValue ret2 = eng.evaluate("s[0] = 123; s[0]");
4418 QVERIFY(ret2.isString());
4419 QCOMPARE(ret2.toString().length(), 1);
4420 QCOMPARE(ret2.toString().at(0), str.at(0));
4422 QJSValue ret3 = eng.evaluate("s[-1] = 123; s[-1]");
4423 QVERIFY(ret3.isNumber());
4424 QCOMPARE(ret3.toInt32(), 123);
4426 QJSValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]");
4427 QVERIFY(ret4.isNumber());
4428 QCOMPARE(ret4.toInt32(), 456);
4430 QJSValue ret5 = eng.evaluate("delete s[0]");
4431 QVERIFY(ret5.isBoolean());
4432 QEXPECT_FAIL("", "FIXME: This is V8 bug, please report it! ECMA script standard 15.5.5.2", Abort);
4433 QVERIFY(!ret5.toBoolean());
4435 QJSValue ret6 = eng.evaluate("delete s[-1]");
4436 QVERIFY(ret6.isBoolean());
4437 QVERIFY(ret6.toBoolean());
4439 QJSValue ret7 = eng.evaluate("delete s[s.length]");
4440 QVERIFY(ret7.isBoolean());
4441 QVERIFY(ret7.toBoolean());
4445 void tst_QJSEngine::jsStringPrototypeReplaceBugs()
4450 QJSValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args");
4451 QVERIFY(ret.isArray());
4452 int len = ret.property("length").toInt32();
4454 for (int i = 0; i < len; ++i) {
4455 QJSValue args = ret.property(i);
4456 QCOMPARE(args.property("length").toInt32(), 4);
4457 QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string
4458 QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture
4459 QVERIFY(args.property(2).isNumber());
4460 QCOMPARE(args.property(2).toInt32(), i*2); // index of match
4461 QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a"));
4466 QJSValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})");
4467 QVERIFY(ret.isString());
4468 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4472 void tst_QJSEngine::getterSetterThisObject_global()
4477 eng.evaluate("__defineGetter__('x', function() { return this; });");
4479 QJSValue ret = eng.evaluate("x");
4480 QVERIFY(ret.equals(eng.globalObject()));
4483 QJSValue ret = eng.evaluate("(function() { return x; })()");
4484 QVERIFY(ret.equals(eng.globalObject()));
4487 QJSValue ret = eng.evaluate("with (this) x");
4488 QVERIFY(ret.equals(eng.globalObject()));
4491 QJSValue ret = eng.evaluate("with ({}) x");
4492 QVERIFY(ret.equals(eng.globalObject()));
4495 QJSValue ret = eng.evaluate("(function() { with ({}) return x; })()");
4496 QVERIFY(ret.equals(eng.globalObject()));
4499 eng.evaluate("__defineSetter__('x', function() { return this; });");
4501 QJSValue ret = eng.evaluate("x = 'foo'");
4502 // SpiderMonkey says setter return value, JSC says RHS.
4503 QVERIFY(ret.isString());
4504 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4507 QJSValue ret = eng.evaluate("(function() { return x = 'foo'; })()");
4508 // SpiderMonkey says setter return value, JSC says RHS.
4509 QVERIFY(ret.isString());
4510 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4513 QJSValue ret = eng.evaluate("with (this) x = 'foo'");
4514 // SpiderMonkey says setter return value, JSC says RHS.
4515 QVERIFY(ret.isString());
4516 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4519 QJSValue ret = eng.evaluate("with ({}) x = 'foo'");
4520 // SpiderMonkey says setter return value, JSC says RHS.
4521 QVERIFY(ret.isString());
4522 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4525 QJSValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()");
4526 // SpiderMonkey says setter return value, JSC says RHS.
4527 QVERIFY(ret.isString());
4528 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4533 void tst_QJSEngine::getterSetterThisObject_plain()
4537 eng.evaluate("o = {}");
4539 eng.evaluate("o.__defineGetter__('x', function() { return this; })");
4540 QVERIFY(eng.evaluate("o.x === o").toBoolean());
4541 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4542 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBoolean());
4543 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4545 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4546 // SpiderMonkey says setter return value, JSC says RHS.
4547 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean());
4548 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4549 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4553 void tst_QJSEngine::getterSetterThisObject_prototypeChain()
4557 eng.evaluate("o = {}; p = {}; o.__proto__ = p");
4559 eng.evaluate("p.__defineGetter__('x', function() { return this; })");
4560 QVERIFY(eng.evaluate("o.x === o").toBoolean());
4561 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4562 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBoolean());
4563 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4564 eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o"));
4566 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4567 // SpiderMonkey says setter return value, JSC says RHS.
4568 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean());
4569 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4570 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4574 #if 0 // ###FIXME: no QScriptContext API
4575 void tst_QJSEngine::getterSetterThisObject_activation()
4579 QScriptContext *ctx = eng.pushContext();
4581 QScriptValue act = ctx->activationObject();
4582 act.setProperty("act", act);
4584 eng.evaluate("act.__defineGetter__('x', function() { return this; })");
4585 QVERIFY(eng.evaluate("x === act").toBoolean());
4586 QEXPECT_FAIL("", "QTBUG-17605: Not possible to implement local variables as getter/setter properties", Abort);
4587 QVERIFY(!eng.hasUncaughtException());
4588 QVERIFY(eng.evaluate("with (act) x").equals("foo"));
4589 QVERIFY(eng.evaluate("(function() { with (act) return x; })() === act").toBoolean());
4590 eng.evaluate("q = {}; with (act) with (q) x").equals(eng.evaluate("act"));
4591 eng.evaluate("with (q) with (act) x").equals(eng.evaluate("act"));
4593 eng.evaluate("act.__defineSetter__('x', function() { return this; });");
4594 QVERIFY(eng.evaluate("(x = 'foo') === 'foo'").toBoolean());
4595 QVERIFY(eng.evaluate("with (act) x = 'foo'").equals("foo"));
4596 QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals("foo"));
4602 void tst_QJSEngine::jsContinueInSwitch()
4604 // This is testing ECMA-262 compliance, not C++ API.
4607 // switch - continue
4609 QJSValue ret = eng.evaluate("switch (1) { default: continue; }");
4610 QVERIFY(ret.isError());
4612 // for - switch - case - continue
4614 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4616 " case 1: ++j; continue;\n"
4617 " case 100: ++j; continue;\n"
4618 " case 1000: ++j; continue;\n"
4621 QVERIFY(ret.isNumber());
4622 QCOMPARE(ret.toInt32(), 3);
4624 // for - switch - case - default - continue
4626 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4628 " case 1: ++j; continue;\n"
4629 " case 100: ++j; continue;\n"
4630 " case 1000: ++j; continue;\n"
4631 " default: if (i < 50000) break; else continue;\n"
4634 QVERIFY(ret.isNumber());
4635 QCOMPARE(ret.toInt32(), 3);
4637 // switch - for - continue
4639 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
4641 " for (i = 0; i < 100000; ++i) {\n"
4645 QVERIFY(ret.isNumber());
4646 QCOMPARE(ret.toInt32(), 100000);
4648 // switch - switch - continue
4650 QJSValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }");
4651 QVERIFY(ret.isError());
4653 // for - switch - switch - continue
4655 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4659 " case 1: ++j; continue;\n"
4663 QVERIFY(ret.isNumber());
4664 QCOMPARE(ret.toInt32(), 1);
4666 // switch - for - switch - continue
4668 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
4670 " for (i = 0; i < 100000; ++i) {\n"
4677 QVERIFY(ret.isNumber());
4678 QCOMPARE(ret.toInt32(), 100000);
4682 void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty()
4684 // SpiderMonkey has different behavior than JSC and V8; it disallows
4685 // creating a property on the instance if there's a property with the
4686 // same name in the prototype, and that property is read-only. We
4687 // adopted that behavior in the old (4.5) QtScript back-end, but it
4688 // just seems weird -- and non-compliant. Adopt the JSC behavior instead.
4690 QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber());
4691 QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt32(), 123);
4692 QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBoolean());
4695 void tst_QJSEngine::toObject()
4699 QVERIFY(!eng.toObject(eng.undefinedValue()).isValid());
4701 QVERIFY(!eng.toObject(eng.nullValue()).isValid());
4703 QJSValue falskt(false);
4705 QJSValue tmp = eng.toObject(falskt);
4706 QVERIFY(tmp.isObject());
4707 QCOMPARE(tmp.toNumber(), falskt.toNumber());
4709 QVERIFY(falskt.isBool());
4711 QJSValue sant(true);
4713 QJSValue tmp = eng.toObject(sant);
4714 QVERIFY(tmp.isObject());
4715 QCOMPARE(tmp.toNumber(), sant.toNumber());
4717 QVERIFY(sant.isBool());
4719 QJSValue number(123.0);
4721 QJSValue tmp = eng.toObject(number);
4722 QVERIFY(tmp.isObject());
4723 QCOMPARE(tmp.toNumber(), number.toNumber());
4725 QVERIFY(number.isNumber());
4727 QJSValue str = QJSValue(&eng, QString("ciao"));
4729 QJSValue tmp = eng.toObject(str);
4730 QVERIFY(tmp.isObject());
4731 QCOMPARE(tmp.toString(), str.toString());
4733 QVERIFY(str.isString());
4735 QJSValue object = eng.newObject();
4737 QJSValue tmp = eng.toObject(object);
4738 QVERIFY(tmp.isObject());
4739 QVERIFY(tmp.strictlyEquals(object));
4742 QJSValue qobject = eng.newQObject(this);
4743 QVERIFY(eng.toObject(qobject).strictlyEquals(qobject));
4745 QVERIFY(!eng.toObject(QJSValue()).isValid());
4749 QJSValue boolValue(&eng, true);
4751 QJSValue ret = eng.toObject(boolValue);
4752 QVERIFY(ret.isObject());
4753 QCOMPARE(ret.toBool(), boolValue.toBool());
4755 QVERIFY(boolValue.isBool());
4757 QJSValue numberValue(&eng, 123.0);
4759 QJSValue ret = eng.toObject(numberValue);
4760 QVERIFY(ret.isObject());
4761 QCOMPARE(ret.toNumber(), numberValue.toNumber());
4763 QVERIFY(numberValue.isNumber());
4765 QJSValue stringValue(&eng, QString::fromLatin1("foo"));
4767 QJSValue ret = eng.toObject(stringValue);
4768 QVERIFY(ret.isObject());
4769 QCOMPARE(ret.toString(), stringValue.toString());
4771 QVERIFY(stringValue.isString());
4774 void tst_QJSEngine::jsReservedWords_data()
4776 QTest::addColumn<QString>("word");
4777 QTest::newRow("break") << QString("break");
4778 QTest::newRow("case") << QString("case");
4779 QTest::newRow("catch") << QString("catch");
4780 QTest::newRow("continue") << QString("continue");
4781 QTest::newRow("default") << QString("default");
4782 QTest::newRow("delete") << QString("delete");
4783 QTest::newRow("do") << QString("do");
4784 QTest::newRow("else") << QString("else");
4785 QTest::newRow("false") << QString("false");
4786 QTest::newRow("finally") << QString("finally");
4787 QTest::newRow("for") << QString("for");
4788 QTest::newRow("function") << QString("function");
4789 QTest::newRow("if") << QString("if");
4790 QTest::newRow("in") << QString("in");
4791 QTest::newRow("instanceof") << QString("instanceof");
4792 QTest::newRow("new") << QString("new");
4793 QTest::newRow("null") << QString("null");
4794 QTest::newRow("return") << QString("return");
4795 QTest::newRow("switch") << QString("switch");
4796 QTest::newRow("this") << QString("this");
4797 QTest::newRow("throw") << QString("throw");
4798 QTest::newRow("true") << QString("true");
4799 QTest::newRow("try") << QString("try");
4800 QTest::newRow("typeof") << QString("typeof");
4801 QTest::newRow("var") << QString("var");
4802 QTest::newRow("void") << QString("void");
4803 QTest::newRow("while") << QString("while");
4804 QTest::newRow("with") << QString("with");
4807 void tst_QJSEngine::jsReservedWords()
4809 // See ECMA-262 Section 7.6.1, "Reserved Words".
4810 // We prefer that the implementation is less strict than the spec; e.g.
4811 // it's good to allow reserved words as identifiers in object literals,
4812 // and when accessing properties using dot notation.
4814 QFETCH(QString, word);
4817 QJSValue ret = eng.evaluate(word + " = 123");
4818 QVERIFY(ret.isError());
4819 QString str = ret.toString();
4820 QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError"));
4824 QJSValue ret = eng.evaluate("var " + word + " = 123");
4825 QVERIFY(ret.isError());
4826 QVERIFY(ret.toString().startsWith("SyntaxError"));
4830 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4831 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
4832 QVERIFY(!ret.isError());
4833 QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word)));
4837 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
4838 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
4839 QVERIFY(!ret.isError());
4840 QVERIFY(ret.property(word).isNumber());
4843 // SpiderMonkey allows this, but we don't
4845 QJSValue ret = eng.evaluate("function " + word + "() {}");
4846 QVERIFY(ret.isError());
4847 QVERIFY(ret.toString().startsWith("SyntaxError"));
4851 void tst_QJSEngine::jsFutureReservedWords_data()
4853 QTest::addColumn<QString>("word");
4854 QTest::addColumn<bool>("allowed");
4855 QTest::newRow("abstract") << QString("abstract") << true;
4856 QTest::newRow("boolean") << QString("boolean") << true;
4857 QTest::newRow("byte") << QString("byte") << true;
4858 QTest::newRow("char") << QString("char") << true;
4859 QTest::newRow("class") << QString("class") << false;
4860 QTest::newRow("const") << QString("const") << false;
4861 QTest::newRow("debugger") << QString("debugger") << false;
4862 QTest::newRow("double") << QString("double") << true;
4863 QTest::newRow("enum") << QString("enum") << false;
4864 QTest::newRow("export") << QString("export") << false;
4865 QTest::newRow("extends") << QString("extends") << false;
4866 QTest::newRow("final") << QString("final") << true;
4867 QTest::newRow("float") << QString("float") << true;
4868 QTest::newRow("goto") << QString("goto") << true;
4869 QTest::newRow("implements") << QString("implements") << true;
4870 QTest::newRow("import") << QString("import") << false;
4871 QTest::newRow("int") << QString("int") << true;
4872 QTest::newRow("interface") << QString("interface") << true;
4873 QTest::newRow("long") << QString("long") << true;
4874 QTest::newRow("native") << QString("native") << true;
4875 QTest::newRow("package") << QString("package") << true;
4876 QTest::newRow("private") << QString("private") << true;
4877 QTest::newRow("protected") << QString("protected") << true;
4878 QTest::newRow("public") << QString("public") << true;
4879 QTest::newRow("short") << QString("short") << true;
4880 QTest::newRow("static") << QString("static") << true;
4881 QTest::newRow("super") << QString("super") << false;
4882 QTest::newRow("synchronized") << QString("synchronized") << true;
4883 QTest::newRow("throws") << QString("throws") << true;
4884 QTest::newRow("transient") << QString("transient") << true;
4885 QTest::newRow("volatile") << QString("volatile") << true;
4888 void tst_QJSEngine::jsFutureReservedWords()
4890 QSKIP("Fails", SkipAll);
4891 // See ECMA-262 Section 7.6.1.2, "Future Reserved Words".
4892 // In real-world implementations, most of these words are
4893 // actually allowed as normal identifiers.
4895 QFETCH(QString, word);
4896 QFETCH(bool, allowed);
4899 QJSValue ret = eng.evaluate(word + " = 123");
4900 QCOMPARE(!ret.isError(), allowed);
4904 QJSValue ret = eng.evaluate("var " + word + " = 123");
4905 QCOMPARE(!ret.isError(), allowed);
4908 // this should probably be allowed (see task 162567)
4910 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4911 QCOMPARE(ret.isNumber(), allowed);
4912 QCOMPARE(!ret.isError(), allowed);
4915 // this should probably be allowed (see task 162567)
4917 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
4918 QCOMPARE(!ret.isError(), allowed);
4922 void tst_QJSEngine::jsThrowInsideWithStatement()
4924 // This is testing ECMA-262 compliance, not C++ API.
4929 QJSValue ret = eng.evaluate(
4931 " o = { bad : \"bug\" };"
4938 QVERIFY(ret.isError());
4939 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4942 QJSValue ret = eng.evaluate(
4944 " o = { bad : \"bug\" };"
4951 QVERIFY(ret.isError());
4952 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4955 eng.clearExceptions();
4956 QJSValue ret = eng.evaluate(
4957 "o = { bug : \"no bug\" };"
4965 QVERIFY(ret.isNumber());
4966 QCOMPARE(ret.toInt32(), 123);
4967 QVERIFY(eng.hasUncaughtException());
4970 eng.clearExceptions();
4971 QJSValue ret = eng.evaluate(
4972 "o = { bug : \"no bug\" };"
4976 QVERIFY(ret.isNumber());
4977 QJSValue ret2 = eng.evaluate("bug");
4978 QVERIFY(ret2.isError());
4979 QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError")));
4983 #if 0 // ###FIXME: No QScriptEngineAgent API
4984 class TestAgent : public QScriptEngineAgent
4987 TestAgent(QScriptEngine *engine) : QScriptEngineAgent(engine) {}
4990 void tst_QJSEngine::getSetAgent_ownership()
4992 // engine deleted before agent --> agent deleted too
4993 QScriptEngine *eng = new QScriptEngine;
4994 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4995 TestAgent *agent = new TestAgent(eng);
4996 eng->setAgent(agent);
4997 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4998 eng->setAgent(0); // the engine maintains ownership of the old agent
4999 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
5003 void tst_QJSEngine::getSetAgent_deleteAgent()
5005 // agent deleted before engine --> engine's agent should become 0
5006 QScriptEngine *eng = new QScriptEngine;
5007 TestAgent *agent = new TestAgent(eng);
5008 eng->setAgent(agent);
5009 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
5011 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
5012 eng->evaluate("(function(){ return 123; })()");
5016 void tst_QJSEngine::getSetAgent_differentEngine()
5020 TestAgent *agent = new TestAgent(&eng);
5021 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine");
5022 eng2.setAgent(agent);
5023 QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0);
5027 #if 0 // ###FIXME: No QScriptString API
5028 void tst_QJSEngine::reentrancy_stringHandles()
5032 QScriptString s1 = eng1.toStringHandle("foo");
5033 QScriptString s2 = eng2.toStringHandle("foo");
5038 #if 0 // ###FIXME: No processEventsInterval API
5039 void tst_QJSEngine::reentrancy_processEventsInterval()
5043 eng1.setProcessEventsInterval(123);
5044 QCOMPARE(eng2.processEventsInterval(), -1);
5045 eng2.setProcessEventsInterval(456);
5046 QCOMPARE(eng1.processEventsInterval(), 123);
5050 #if 0 // FIXME: No support for custom types
5051 void tst_QJSEngine::reentrancy_typeConversion()
5055 qScriptRegisterMetaType<Foo>(&eng1, fooToScriptValue, fooFromScriptValue);
5060 QScriptValue fooVal = qScriptValueFromValue(&eng1, foo);
5061 QVERIFY(fooVal.isObject());
5062 QVERIFY(!fooVal.isVariant());
5063 QCOMPARE(fooVal.property("x").toInt32(), 12);
5064 QCOMPARE(fooVal.property("y").toInt32(), 34);
5065 fooVal.setProperty("x", 56);
5066 fooVal.setProperty("y", 78);
5068 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
5069 QCOMPARE(foo2.x, 56);
5070 QCOMPARE(foo2.y, 78);
5073 QScriptValue fooVal = qScriptValueFromValue(&eng2, foo);
5074 QVERIFY(fooVal.isVariant());
5076 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
5077 QCOMPARE(foo2.x, 12);
5078 QCOMPARE(foo2.y, 34);
5080 QVERIFY(!eng1.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5081 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5082 QScriptValue proto1 = eng1.newObject();
5083 eng1.setDefaultPrototype(qMetaTypeId<Foo>(), proto1);
5084 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5085 QScriptValue proto2 = eng2.newObject();
5086 eng2.setDefaultPrototype(qMetaTypeId<Foo>(), proto2);
5087 QVERIFY(eng2.defaultPrototype(qMetaTypeId<Foo>()).isValid());
5088 QVERIFY(eng1.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(proto1));
5092 void tst_QJSEngine::reentrancy_globalObjectProperties()
5096 QVERIFY(!eng2.globalObject().property("a").isValid());
5097 eng1.evaluate("a = 10");
5098 QVERIFY(eng1.globalObject().property("a").isNumber());
5099 QVERIFY(!eng2.globalObject().property("a").isValid());
5100 eng2.evaluate("a = 20");
5101 QVERIFY(eng2.globalObject().property("a").isNumber());
5102 QCOMPARE(eng1.globalObject().property("a").toInt32(), 10);
5105 void tst_QJSEngine::reentrancy_Array()
5107 // weird bug with JSC backend
5110 QCOMPARE(eng.evaluate("Array()").toString(), QString());
5111 eng.evaluate("Array.prototype.toString");
5112 QCOMPARE(eng.evaluate("Array()").toString(), QString());
5116 QCOMPARE(eng.evaluate("Array()").toString(), QString());
5120 void tst_QJSEngine::reentrancy_objectCreation()
5125 QJSValue d1 = eng1.newDate(0);
5126 QJSValue d2 = eng2.newDate(0);
5127 QCOMPARE(d1.toDateTime(), d2.toDateTime());
5128 QCOMPARE(d2.toDateTime(), d1.toDateTime());
5131 QJSValue r1 = eng1.newRegExp("foo", "gim");
5132 QJSValue r2 = eng2.newRegExp("foo", "gim");
5133 QCOMPARE(r1.toRegExp(), r2.toRegExp());
5134 QCOMPARE(r2.toRegExp(), r1.toRegExp());
5137 QJSValue o1 = eng1.newQObject(this);
5138 QJSValue o2 = eng2.newQObject(this);
5139 QCOMPARE(o1.toQObject(), o2.toQObject());
5140 QCOMPARE(o2.toQObject(), o1.toQObject());
5142 #if 0 // ###FIXME: No QScriptEngine::newQMetaObject API
5144 QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject);
5145 QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject);
5146 QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject());
5147 QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject());
5152 void tst_QJSEngine::jsIncDecNonObjectProperty()
5154 // This is testing ECMA-262 compliance, not C++ API.
5158 QJSValue ret = eng.evaluate("var a; 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 = null; a.n++");
5169 QVERIFY(ret.isError());
5170 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5173 QJSValue ret = eng.evaluate("var a = null; a.n--");
5174 QVERIFY(ret.isError());
5175 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5178 QJSValue ret = eng.evaluate("var a; ++a.n");
5179 QVERIFY(ret.isError());
5180 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5183 QJSValue ret = eng.evaluate("var a; --a.n");
5184 QVERIFY(ret.isError());
5185 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5188 QJSValue ret = eng.evaluate("var a; a.n += 1");
5189 QVERIFY(ret.isError());
5190 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5193 QJSValue ret = eng.evaluate("var a; a.n -= 1");
5194 QVERIFY(ret.isError());
5195 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5198 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length++");
5199 QVERIFY(ret.isNumber());
5200 QCOMPARE(ret.toInt32(), 4);
5203 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length--");
5204 QVERIFY(ret.isNumber());
5205 QCOMPARE(ret.toInt32(), 4);
5208 QJSValue ret = eng.evaluate("var a = 'ciao'; ++a.length");
5209 QVERIFY(ret.isNumber());
5210 QCOMPARE(ret.toInt32(), 5);
5213 QJSValue ret = eng.evaluate("var a = 'ciao'; --a.length");
5214 QVERIFY(ret.isNumber());
5215 QCOMPARE(ret.toInt32(), 3);
5219 #if 0 // ###FIXME: no installTranslatorFunctions API
5220 void tst_QJSEngine::installTranslatorFunctions()
5223 QScriptValue global = eng.globalObject();
5224 QVERIFY(!global.property("qsTranslate").isValid());
5225 QVERIFY(!global.property("QT_TRANSLATE_NOOP").isValid());
5226 QVERIFY(!global.property("qsTr").isValid());
5227 QVERIFY(!global.property("QT_TR_NOOP").isValid());
5228 QVERIFY(!global.property("qsTrId").isValid());
5229 QVERIFY(!global.property("QT_TRID_NOOP").isValid());
5230 QVERIFY(!global.property("String").property("prototype").property("arg").isValid());
5232 eng.installTranslatorFunctions();
5233 QVERIFY(global.property("qsTranslate").isFunction());
5234 QVERIFY(global.property("QT_TRANSLATE_NOOP").isFunction());
5235 QVERIFY(global.property("qsTr").isFunction());
5236 QVERIFY(global.property("QT_TR_NOOP").isFunction());
5237 QVERIFY(global.property("qsTrId").isFunction());
5238 QVERIFY(global.property("QT_TRID_NOOP").isFunction());
5239 QVERIFY(global.property("String").property("prototype").property("arg").isFunction());
5242 QScriptValue ret = eng.evaluate("qsTr('foo')");
5243 QVERIFY(ret.isString());
5244 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5247 QScriptValue ret = eng.evaluate("qsTranslate('foo', 'bar')");
5248 QVERIFY(ret.isString());
5249 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
5252 QScriptValue ret = eng.evaluate("QT_TR_NOOP('foo')");
5253 QVERIFY(ret.isString());
5254 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5257 QScriptValue ret = eng.evaluate("QT_TRANSLATE_NOOP('foo', 'bar')");
5258 QVERIFY(ret.isString());
5259 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
5262 QScriptValue ret = eng.evaluate("'foo%0'.arg('bar')");
5263 QEXPECT_FAIL("Custom global object", "FIXME: why we expect that String prototype exists?", Abort);
5264 QVERIFY(ret.isString());
5265 QCOMPARE(ret.toString(), QString::fromLatin1("foobar"));
5268 QScriptValue ret = eng.evaluate("'foo%0'.arg(123)");
5269 QVERIFY(ret.isString());
5270 QCOMPARE(ret.toString(), QString::fromLatin1("foo123"));
5273 // Maybe this should throw an error?
5274 QScriptValue ret = eng.evaluate("'foo%0'.arg()");
5275 QVERIFY(ret.isString());
5276 QCOMPARE(ret.toString(), QString());
5280 QScriptValue ret = eng.evaluate("qsTrId('foo')");
5281 QVERIFY(ret.isString());
5282 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5285 QScriptValue ret = eng.evaluate("QT_TRID_NOOP('foo')");
5286 QVERIFY(ret.isString());
5287 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5289 QVERIFY(eng.evaluate("QT_TRID_NOOP()").isUndefined());
5292 class TranslationScope
5295 TranslationScope(const QString &fileName)
5297 translator.load(fileName);
5298 QCoreApplication::instance()->installTranslator(&translator);
5302 QCoreApplication::instance()->removeTranslator(&translator);
5306 QTranslator translator;
5309 void tst_QJSEngine::translateScript_data()
5311 QTest::addColumn<QString>("expression");
5312 QTest::addColumn<QString>("fileName");
5313 QTest::addColumn<QString>("expectedTranslation");
5315 QString fileName = QString::fromLatin1("translatable.js");
5317 QTest::newRow("qsTr('One')@translatable.js")
5318 << QString::fromLatin1("qsTr('One')") << fileName << QString::fromLatin1("En");
5319 QTest::newRow("qsTr('Hello')@translatable.js")
5320 << QString::fromLatin1("qsTr('Hello')") << fileName << QString::fromLatin1("Hallo");
5322 QTest::newRow("(function() { return qsTr('One'); })()@translatable.js")
5323 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName << QString::fromLatin1("En");
5324 QTest::newRow("(function() { return qsTr('Hello'); })()@translatable.js")
5325 << QString::fromLatin1("(function() { return qsTr('Hello'); })()") << fileName << QString::fromLatin1("Hallo");
5327 QTest::newRow("eval('qsTr(\\'One\\')')@translatable.js")
5328 << QString::fromLatin1("eval('qsTr(\\'One\\')')") << fileName << QString::fromLatin1("En");
5329 QTest::newRow("eval('qsTr(\\'Hello\\')')@translatable.js")
5330 << QString::fromLatin1("eval('qsTr(\\'Hello\\')')") << fileName << QString::fromLatin1("Hallo");
5332 QTest::newRow("qsTr('%n message(s) saved', '', 1)@translatable.js")
5333 << QString::fromLatin1("qsTr('%n message(s) saved', '', 1)") << fileName << QString::fromLatin1("1 melding lagret");
5334 QTest::newRow("qsTr('%n message(s) saved', '', 3).arg@translatable.js")
5335 << QString::fromLatin1("qsTr('%n message(s) saved', '', 3)") << fileName << QString::fromLatin1("3 meldinger lagret");
5338 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable.js")
5339 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName << QString::fromLatin1("To");
5340 QTest::newRow("qsTranslate('FooContext', 'Goodbye')@translatable.js")
5341 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye')") << fileName << QString::fromLatin1("Farvel");
5343 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')@translatable.js")
5344 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')") << fileName << QString::fromLatin1("To");
5345 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')@translatable.js")
5346 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')") << fileName << QString::fromLatin1("Farvel");
5348 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')@translatable.js")
5349 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')") << fileName << QString::fromLatin1("Farvel");
5350 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')@translatable.js")
5351 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')") << fileName << QString::fromLatin1("Farvel");
5353 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)@translatable.js")
5354 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)") << fileName << QString::fromLatin1("Goodbye");
5356 QTest::newRow("qsTr('One', 'not the same one')@translatable.js")
5357 << QString::fromLatin1("qsTr('One', 'not the same one')") << fileName << QString::fromLatin1("Enda en");
5359 QTest::newRow("qsTr('One', 'not the same one', 42)@translatable.js")
5360 << QString::fromLatin1("qsTr('One', 'not the same one', 42)") << fileName << QString::fromLatin1("One");
5363 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)@translatable.js")
5364 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)") << fileName << QString::fromLatin1("1 fooaktig bar funnet");
5365 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)@translatable.js")
5366 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)") << fileName << QString::fromLatin1("2 fooaktige barer funnet");
5368 // Don't exist in translation
5369 QTest::newRow("qsTr('Three')@translatable.js")
5370 << QString::fromLatin1("qsTr('Three')") << fileName << QString::fromLatin1("Three");
5371 QTest::newRow("qsTranslate('FooContext', 'So long')@translatable.js")
5372 << QString::fromLatin1("qsTranslate('FooContext', 'So long')") << fileName << QString::fromLatin1("So long");
5373 QTest::newRow("qsTranslate('BarContext', 'Goodbye')@translatable.js")
5374 << QString::fromLatin1("qsTranslate('BarContext', 'Goodbye')") << fileName << QString::fromLatin1("Goodbye");
5376 // Translate strings from the second script (translatable2.js)
5378 QString fileName2 = QString::fromLatin1("translatable2.js");
5379 QTest::newRow("qsTr('Three')@translatable2.js")
5380 << QString::fromLatin1("qsTr('Three')") << fileName2 << QString::fromLatin1("Tre");
5381 QTest::newRow("qsTr('Happy birthday!')@translatable2.js")
5382 << QString::fromLatin1("qsTr('Happy birthday!')") << fileName2 << QString::fromLatin1("Gratulerer med dagen!");
5384 // Not translated because translation is only in translatable.js
5385 QTest::newRow("qsTr('One')@translatable2.js")
5386 << QString::fromLatin1("qsTr('One')") << fileName2 << QString::fromLatin1("One");
5387 QTest::newRow("(function() { return qsTr('One'); })()@translatable2.js")
5388 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName2 << QString::fromLatin1("One");
5390 // For qsTranslate() the filename shouldn't matter
5391 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable2.js")
5392 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName2 << QString::fromLatin1("To");
5393 QTest::newRow("qsTranslate('BarContext', 'Congratulations!')@translatable.js")
5394 << QString::fromLatin1("qsTranslate('BarContext', 'Congratulations!')") << fileName << QString::fromLatin1("Gratulerer!");
5397 void tst_QJSEngine::translateScript()
5399 QFETCH(QString, expression);
5400 QFETCH(QString, fileName);
5401 QFETCH(QString, expectedTranslation);
5403 QScriptEngine engine;
5405 TranslationScope tranScope(":/translations/translatable_la");
5406 engine.installTranslatorFunctions();
5408 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5409 QVERIFY(!engine.hasUncaughtException());
5412 void tst_QJSEngine::translateScript_crossScript()
5414 QScriptEngine engine;
5415 TranslationScope tranScope(":/translations/translatable_la");
5416 engine.installTranslatorFunctions();
5418 QString fileName = QString::fromLatin1("translatable.js");
5419 QString fileName2 = QString::fromLatin1("translatable2.js");
5420 // qsTr() should use the innermost filename as context
5421 engine.evaluate("function foo(s) { return bar(s); }", fileName);
5422 engine.evaluate("function bar(s) { return qsTr(s); }", fileName2);
5423 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5424 QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre"));
5425 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One"));
5427 engine.evaluate("function foo(s) { return bar(s); }", fileName2);
5428 engine.evaluate("function bar(s) { return qsTr(s); }", fileName);
5429 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three"));
5430 QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En"));
5431 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En"));
5434 static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng)
5436 return eng->globalObject().property("qsTr").call(ctx->thisObject(), ctx->argumentsObject());
5439 void tst_QJSEngine::translateScript_callQsTrFromNative()
5441 QScriptEngine engine;
5442 TranslationScope tranScope(":/translations/translatable_la");
5443 engine.installTranslatorFunctions();
5445 QString fileName = QString::fromLatin1("translatable.js");
5446 QString fileName2 = QString::fromLatin1("translatable2.js");
5447 // Calling qsTr() from a native function
5448 engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr));
5449 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En"));
5450 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One"));
5451 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three"));
5452 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5455 void tst_QJSEngine::translateScript_trNoOp()
5457 QScriptEngine engine;
5458 TranslationScope tranScope(":/translations/translatable_la");
5459 engine.installTranslatorFunctions();
5461 QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined());
5462 QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One"));
5464 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined());
5465 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined());
5466 QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two"));
5469 void tst_QJSEngine::translateScript_callQsTrFromCpp()
5471 QScriptEngine engine;
5472 TranslationScope tranScope(":/translations/translatable_la");
5473 engine.installTranslatorFunctions();
5475 // There is no context, but it shouldn't crash
5476 QCOMPARE(engine.globalObject().property("qsTr").call(
5477 QScriptValue(), QScriptValueList() << "One").toString(), QString::fromLatin1("One"));
5480 void tst_QJSEngine::translateWithInvalidArgs_data()
5482 QTest::addColumn<QString>("expression");
5483 QTest::addColumn<QString>("expectedError");
5485 QTest::newRow("qsTr()") << "qsTr()" << "Error: qsTr() requires at least one argument";
5486 QTest::newRow("qsTr(123)") << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string";
5487 QTest::newRow("qsTr('foo', 123)") << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string";
5488 QTest::newRow("qsTr('foo', 'bar', 'baz')") << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number";
5489 QTest::newRow("qsTr('foo', 'bar', true)") << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number";
5491 QTest::newRow("qsTranslate()") << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments";
5492 QTest::newRow("qsTranslate('foo')") << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments";
5493 QTest::newRow("qsTranslate(123, 'foo')") << "qsTranslate(123, 'foo')" << "Error: qsTranslate(): first argument (context) must be a string";
5494 QTest::newRow("qsTranslate('foo', 123)") << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string";
5495 QTest::newRow("qsTranslate('foo', 'bar', 123)") << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string";
5496 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string";
5497 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number";
5498 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'";
5500 QTest::newRow("qsTrId()") << "qsTrId()" << "Error: qsTrId() requires at least one argument";
5501 QTest::newRow("qsTrId(123)") << "qsTrId(123)" << "TypeError: qsTrId(): first argument (id) must be a string";
5502 QTest::newRow("qsTrId('foo', 'bar')") << "qsTrId('foo', 'bar')" << "TypeError: qsTrId(): second argument (n) must be a number";
5505 void tst_QJSEngine::translateWithInvalidArgs()
5507 QFETCH(QString, expression);
5508 QFETCH(QString, expectedError);
5509 QScriptEngine engine;
5510 engine.installTranslatorFunctions();
5511 QScriptValue result = engine.evaluate(expression);
5512 QVERIFY(result.isError());
5513 QCOMPARE(result.toString(), expectedError);
5516 void tst_QJSEngine::translationContext_data()
5518 QTest::addColumn<QString>("path");
5519 QTest::addColumn<QString>("text");
5520 QTest::addColumn<QString>("expectedTranslation");
5522 QTest::newRow("translatable.js") << "translatable.js" << "One" << "En";
5523 QTest::newRow("/translatable.js") << "/translatable.js" << "One" << "En";
5524 QTest::newRow("/foo/translatable.js") << "/foo/translatable.js" << "One" << "En";
5525 QTest::newRow("/foo/bar/translatable.js") << "/foo/bar/translatable.js" << "One" << "En";
5526 QTest::newRow("./translatable.js") << "./translatable.js" << "One" << "En";
5527 QTest::newRow("../translatable.js") << "../translatable.js" << "One" << "En";
5528 QTest::newRow("foo/translatable.js") << "foo/translatable.js" << "One" << "En";
5529 QTest::newRow("file:///home/qt/translatable.js") << "file:///home/qt/translatable.js" << "One" << "En";
5530 QTest::newRow(":/resources/translatable.js") << ":/resources/translatable.js" << "One" << "En";
5531 QTest::newRow("/translatable.js.foo") << "/translatable.js.foo" << "One" << "En";
5532 QTest::newRow("/translatable.txt") << "/translatable.txt" << "One" << "En";
5533 QTest::newRow("translatable") << "translatable" << "One" << "En";
5534 QTest::newRow("foo/translatable") << "foo/translatable" << "One" << "En";
5536 QTest::newRow("native separators")
5537 << (QDir::toNativeSeparators(QDir::currentPath()) + QDir::separator() + "translatable.js")
5540 QTest::newRow("translatable.js/") << "translatable.js/" << "One" << "One";
5541 QTest::newRow("nosuchscript.js") << "" << "One" << "One";
5542 QTest::newRow("(empty)") << "" << "One" << "One";
5545 void tst_QJSEngine::translationContext()
5547 TranslationScope tranScope(":/translations/translatable_la");
5549 QScriptEngine engine;
5550 engine.installTranslatorFunctions();
5552 QFETCH(QString, path);
5553 QFETCH(QString, text);
5554 QFETCH(QString, expectedTranslation);
5555 QScriptValue ret = engine.evaluate(QString::fromLatin1("qsTr('%0')").arg(text), path);
5556 QVERIFY(ret.isString());
5557 QCOMPARE(ret.toString(), expectedTranslation);
5560 void tst_QJSEngine::translateScriptIdBased()
5562 QScriptEngine engine;
5564 TranslationScope tranScope(":/translations/idtranslatable_la");
5565 engine.installTranslatorFunctions();
5567 QString fileName = QString::fromLatin1("idtranslatable.js");
5569 QHash<QString, QString> expectedTranslations;
5570 expectedTranslations["qtn_foo_bar"] = "First string";
5571 expectedTranslations["qtn_needle"] = "Second string";
5572 expectedTranslations["qtn_haystack"] = "Third string";
5573 expectedTranslations["qtn_bar_baz"] = "Fourth string";
5575 QHash<QString, QString>::const_iterator it;
5576 for (it = expectedTranslations.constBegin(); it != expectedTranslations.constEnd(); ++it) {
5577 for (int x = 0; x < 2; ++x) {
5582 QCOMPARE(engine.evaluate(QString::fromLatin1("qsTrId('%0')")
5583 .arg(it.key()), fn).toString(),
5585 QCOMPARE(engine.evaluate(QString::fromLatin1("QT_TRID_NOOP('%0')")
5586 .arg(it.key()), fn).toString(),
5589 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return qsTrId('%0'); })()")
5590 .arg(it.key()), fn).toString(),
5592 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return QT_TRID_NOOP('%0'); })()")
5593 .arg(it.key()), fn).toString(),
5599 QCOMPARE(engine.evaluate("qsTrId('qtn_bar_baz', 10)").toString(),
5600 QString::fromLatin1("10 fooish bar(s) found"));
5601 QCOMPARE(engine.evaluate("qsTrId('qtn_foo_bar', 10)").toString(),
5602 QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural
5605 // How to add a new test row:
5606 // - Find a nice list of Unicode characters to choose from
5607 // - Write source string/context/comment in .js using Unicode escape sequences (\uABCD)
5608 // - Update corresponding .ts file (e.g. lupdate foo.js -ts foo.ts -codecfortr UTF-8)
5609 // - Enter translation in Linguist
5610 // - Update corresponding .qm file (e.g. lrelease foo.ts)
5611 // - Evaluate script that performs translation; make sure the correct result is returned
5612 // (e.g. by setting the resulting string as the text of a QLabel and visually verifying
5613 // that it looks the same as what you entered in Linguist :-) )
5614 // - Generate the expectedTranslation column data using toUtf8().toHex()
5615 void tst_QJSEngine::translateScriptUnicode_data()
5617 QTest::addColumn<QString>("expression");
5618 QTest::addColumn<QString>("fileName");
5619 QTest::addColumn<QString>("expectedTranslation");
5621 QString fileName = QString::fromLatin1("translatable-unicode.js");
5622 QTest::newRow("qsTr('H\\u2082O')@translatable-unicode.js")
5623 << QString::fromLatin1("qsTr('H\\u2082O')") << fileName << QString::fromUtf8("\xcd\xbb\xcd\xbc\xcd\xbd");
5624 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')@translatable-unicode.js")
5625 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << fileName << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5626 QTest::newRow("qsTr('\\u0391\\u0392\\u0393')@translatable-unicode.js")
5627 << QString::fromLatin1("qsTr('\\u0391\\u0392\\u0393')") << fileName << QString::fromUtf8("\xd3\x9c\xd2\xb4\xd1\xbc");
5628 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')@translatable-unicode.js")
5629 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')") << fileName << QString::fromUtf8("\xd8\xae\xd8\xb3\xd8\xb3");
5630 QTest::newRow("qsTr('H\\u2082O', 'not the same H\\u2082O')@translatable-unicode.js")
5631 << QString::fromLatin1("qsTr('H\\u2082O', 'not the same H\\u2082O')") << fileName << QString::fromUtf8("\xd4\xb6\xd5\x8a\xd5\x92");
5632 QTest::newRow("qsTr('H\\u2082O')")
5633 << QString::fromLatin1("qsTr('H\\u2082O')") << QString() << QString::fromUtf8("\x48\xe2\x82\x82\x4f");
5634 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')")
5635 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << QString() << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5638 void tst_QJSEngine::translateScriptUnicode()
5640 QFETCH(QString, expression);
5641 QFETCH(QString, fileName);
5642 QFETCH(QString, expectedTranslation);
5644 QScriptEngine engine;
5646 TranslationScope tranScope(":/translations/translatable-unicode");
5647 engine.installTranslatorFunctions();
5649 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5650 QVERIFY(!engine.hasUncaughtException());
5653 void tst_QJSEngine::translateScriptUnicodeIdBased_data()
5655 QTest::addColumn<QString>("expression");
5656 QTest::addColumn<QString>("expectedTranslation");
5658 QTest::newRow("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1'')")
5659 << QString::fromLatin1("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1')") << QString::fromUtf8("\xc6\xa7\xc6\xb0\xc6\x88\xc8\xbc\xc8\x9d\xc8\xbf\xc8\x99");
5660 QTest::newRow("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')")
5661 << QString::fromLatin1("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')") << QString::fromUtf8("\xc7\xa0\xc8\xa1\xc8\x8b\xc8\x85\xc8\x95");
5662 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)")
5663 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)") << QString::fromUtf8("\x31\x30\x20\xc6\x92\xc6\xa1\xc7\x92\x28\xc8\x99\x29");
5664 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')")
5665 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')") << QString::fromUtf8("\xc6\x91\xc6\xb0\xc7\xb9");
5666 QTest::newRow("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')")
5667 << QString::fromLatin1("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')") << QString::fromUtf8("\xc7\x8d\xc6\x80\xc6\xa8\xc6\x90\xc6\x9e\xc6\xab");
5670 void tst_QJSEngine::translateScriptUnicodeIdBased()
5672 QFETCH(QString, expression);
5673 QFETCH(QString, expectedTranslation);
5675 QScriptEngine engine;
5677 TranslationScope tranScope(":/translations/idtranslatable-unicode");
5678 engine.installTranslatorFunctions();
5680 QCOMPARE(engine.evaluate(expression).toString(), expectedTranslation);
5681 QVERIFY(!engine.hasUncaughtException());
5684 void tst_QJSEngine::translateFromBuiltinCallback()
5687 eng.installTranslatorFunctions();
5689 // Callback has no translation context.
5690 eng.evaluate("function foo() { qsTr('foo'); }");
5692 // Stack at translation time will be:
5693 // qsTr, foo, forEach, global
5694 // qsTr() needs to walk to the outer-most (global) frame before it finds
5695 // a translation context, and this should not crash.
5696 eng.evaluate("[10,20].forEach(foo)", "script.js");
5700 #if 0 // ###FIXME: No QScriptValue::scope API
5701 void tst_QJSEngine::functionScopes()
5705 // top-level functions have only the global object in their scope
5706 QScriptValue fun = eng.evaluate("(function() {})");
5707 QVERIFY(fun.isFunction());
5708 QEXPECT_FAIL("", "QScriptValue::scope() is internal, not implemented", Abort);
5709 QVERIFY(fun.scope().isObject());
5710 QVERIFY(fun.scope().strictlyEquals(eng.globalObject()));
5711 QVERIFY(!eng.globalObject().scope().isValid());
5714 QScriptValue fun = eng.globalObject().property("Object");
5715 QVERIFY(fun.isFunction());
5716 // native built-in functions don't have scope
5717 QVERIFY(!fun.scope().isValid());
5721 QScriptValue fun = eng.evaluate("(function(arg) { var foo = arg; return function() { return foo; }; })(123)");
5722 QVERIFY(fun.isFunction());
5724 QScriptValue ret = fun.call();
5725 QVERIFY(ret.isNumber());
5726 QCOMPARE(ret.toInt32(), 123);
5728 QScriptValue scope = fun.scope();
5729 QVERIFY(scope.isObject());
5731 QScriptValue ret = scope.property("foo");
5732 QVERIFY(ret.isNumber());
5733 QCOMPARE(ret.toInt32(), 123);
5734 QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
5737 QScriptValue ret = scope.property("arg");
5738 QVERIFY(ret.isNumber());
5739 QCOMPARE(ret.toInt32(), 123);
5740 QCOMPARE(scope.propertyFlags("arg"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
5743 scope.setProperty("foo", 456);
5745 QScriptValue ret = fun.call();
5746 QVERIFY(ret.isNumber());
5747 QCOMPARE(ret.toInt32(), 456);
5750 scope = scope.scope();
5751 QVERIFY(scope.isObject());
5752 QVERIFY(scope.strictlyEquals(eng.globalObject()));
5757 #if 0 // ###FIXME: No QScriptContext API
5758 static QScriptValue counter_inner(QScriptContext *ctx, QScriptEngine *)
5760 QScriptValue outerAct = ctx->callee().scope();
5761 double count = outerAct.property("count").toNumber();
5762 outerAct.setProperty("count", count+1);
5766 static QScriptValue counter(QScriptContext *ctx, QScriptEngine *eng)
5768 QScriptValue act = ctx->activationObject();
5769 act.setProperty("count", ctx->argument(0).toInt32());
5770 QScriptValue result = eng->newFunction(counter_inner);
5771 result.setScope(act);
5775 static QScriptValue counter_hybrid(QScriptContext *ctx, QScriptEngine *eng)
5777 QScriptValue act = ctx->activationObject();
5778 act.setProperty("count", ctx->argument(0).toInt32());
5779 return eng->evaluate("(function() { return count++; })");
5782 void tst_QJSEngine::nativeFunctionScopes()
5786 QScriptValue fun = eng.newFunction(counter);
5787 QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123);
5788 QVERIFY(cnt.isFunction());
5790 QScriptValue ret = cnt.call();
5791 QVERIFY(ret.isNumber());
5792 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5793 QCOMPARE(ret.toInt32(), 123);
5797 QScriptValue fun = eng.newFunction(counter_hybrid);
5798 QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123);
5799 QVERIFY(cnt.isFunction());
5801 QScriptValue ret = cnt.call();
5802 QVERIFY(ret.isNumber());
5803 QCOMPARE(ret.toInt32(), 123);
5807 //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain
5810 eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n"
5811 "var c1 = counter(); var c2 = counter(); ");
5812 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5813 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5814 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5815 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5816 QVERIFY(!eng.hasUncaughtException());
5820 eng.globalObject().setProperty("counter", eng.newFunction(counter));
5821 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5822 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5823 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5824 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5825 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5826 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5827 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5828 QVERIFY(!eng.hasUncaughtException());
5832 eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid));
5833 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5834 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5835 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5836 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5837 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5838 QVERIFY(!eng.hasUncaughtException());
5843 #if 0 // ###FIXME: No QScriptProgram API
5844 static QScriptValue createProgram(QScriptContext *ctx, QScriptEngine *eng)
5846 QString code = ctx->argument(0).toString();
5847 QScriptProgram result(code);
5848 return qScriptValueFromValue(eng, result);
5851 void tst_QJSEngine::evaluateProgram()
5856 QString code("1 + 2");
5857 QString fileName("hello.js");
5858 int lineNumber(123);
5859 QScriptProgram program(code, fileName, lineNumber);
5860 QVERIFY(!program.isNull());
5861 QCOMPARE(program.sourceCode(), code);
5862 QCOMPARE(program.fileName(), fileName);
5863 QCOMPARE(program.firstLineNumber(), lineNumber);
5865 QScriptValue expected = eng.evaluate(code);
5866 for (int x = 0; x < 10; ++x) {
5867 QScriptValue ret = eng.evaluate(program);
5868 QVERIFY(ret.equals(expected));
5872 QScriptProgram sameProgram = program;
5873 QVERIFY(sameProgram == program);
5874 QVERIFY(eng.evaluate(sameProgram).equals(expected));
5877 QScriptProgram sameProgram2(program);
5878 QVERIFY(sameProgram2 == program);
5879 QVERIFY(eng.evaluate(sameProgram2).equals(expected));
5881 QScriptProgram differentProgram("2 + 3");
5882 QVERIFY(differentProgram != program);
5883 QVERIFY(!eng.evaluate(differentProgram).equals(expected));
5887 void tst_QJSEngine::evaluateProgram_customScope()
5891 QScriptProgram program("a");
5892 QVERIFY(!program.isNull());
5894 QScriptValue ret = eng.evaluate(program);
5895 QVERIFY(ret.isError());
5896 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined"));
5899 QScriptValue obj = eng.newObject();
5900 obj.setProperty("a", 123);
5901 QScriptContext *ctx = eng.currentContext();
5902 ctx->pushScope(obj);
5904 QScriptValue ret = eng.evaluate(program);
5905 QVERIFY(!ret.isError());
5906 QVERIFY(ret.equals(obj.property("a")));
5909 obj.setProperty("a", QScriptValue());
5911 QScriptValue ret = eng.evaluate(program);
5912 QVERIFY(ret.isError());
5915 QScriptValue obj2 = eng.newObject();
5916 obj2.setProperty("a", 456);
5917 ctx->pushScope(obj2);
5919 QScriptValue ret = eng.evaluate(program);
5920 QVERIFY(!ret.isError());
5921 QVERIFY(ret.equals(obj2.property("a")));
5928 void tst_QJSEngine::evaluateProgram_closure()
5932 QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })");
5933 QVERIFY(!program.isNull());
5934 QScriptValue createCounter = eng.evaluate(program);
5935 QVERIFY(createCounter.isFunction());
5936 QScriptValue counter = createCounter.call();
5937 QVERIFY(counter.isFunction());
5939 QScriptValue ret = counter.call();
5940 QVERIFY(ret.isNumber());
5942 QScriptValue counter2 = createCounter.call();
5943 QVERIFY(counter2.isFunction());
5944 QVERIFY(!counter2.equals(counter));
5946 QScriptValue ret = counter2.call();
5947 QVERIFY(ret.isNumber());
5952 void tst_QJSEngine::evaluateProgram_executeLater()
5955 // Program created in a function call, then executed later
5957 QScriptValue fun = eng.newFunction(createProgram);
5958 QScriptProgram program = qscriptvalue_cast<QScriptProgram>(
5959 fun.call(QScriptValue(), QScriptValueList() << "a + 1"));
5960 QVERIFY(!program.isNull());
5961 eng.globalObject().setProperty("a", QScriptValue());
5963 QScriptValue ret = eng.evaluate(program);
5964 QVERIFY(ret.isError());
5965 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined"));
5967 eng.globalObject().setProperty("a", 122);
5969 QScriptValue ret = eng.evaluate(program);
5970 QVERIFY(!ret.isError());
5971 QVERIFY(ret.isNumber());
5972 QCOMPARE(ret.toInt32(), 123);
5977 void tst_QJSEngine::evaluateProgram_multipleEngines()
5981 QString code("1 + 2");
5982 QScriptProgram program(code);
5983 QVERIFY(!program.isNull());
5984 double expected = eng.evaluate(program).toNumber();
5985 for (int x = 0; x < 2; ++x) {
5987 for (int y = 0; y < 2; ++y) {
5988 double ret = eng2.evaluate(program).toNumber();
5989 QCOMPARE(ret, expected);
5995 void tst_QJSEngine::evaluateProgram_empty()
5999 QScriptProgram program;
6000 QVERIFY(program.isNull());
6001 QScriptValue ret = eng.evaluate(program);
6002 QVERIFY(!ret.isValid());
6007 #if 0 // ###FIXME: No ScriptOwnership API
6008 void tst_QJSEngine::collectGarbageAfterConnect()
6011 QScriptEngine engine;
6012 QPointer<QWidget> widget = new QWidget;
6013 engine.globalObject().setProperty(
6014 "widget", engine.newQObject(widget, QScriptEngine::ScriptOwnership));
6015 QVERIFY(engine.evaluate("widget.customContextMenuRequested.connect(\n"
6016 " function() { print('hello'); }\n"
6019 QVERIFY(widget != 0);
6020 engine.evaluate("widget = null;");
6021 // The connection should not keep the widget alive.
6022 collectGarbage_helper(engine);
6023 QVERIFY(widget == 0);
6027 #if 0 // ###FIXME: No QScriptContext API
6028 void tst_QJSEngine::collectGarbageAfterNativeArguments()
6032 QScriptContext *ctx = eng.pushContext();
6033 QScriptValue arguments = ctx->argumentsObject();
6034 // Shouldn't crash when marking the arguments object.
6035 collectGarbage_helper(eng);
6038 static QScriptValue constructQObjectFromThisObject(QScriptContext *ctx, QScriptEngine *eng)
6040 if (!ctx->isCalledAsConstructor()) {
6041 qWarning("%s: ctx->isCalledAsConstructor() returned false", Q_FUNC_INFO);
6042 return QScriptValue();
6044 return eng->newQObject(ctx->thisObject(), new QObject, QScriptEngine::ScriptOwnership);
6047 void tst_QJSEngine::promoteThisObjectToQObjectInConstructor()
6049 QScriptEngine engine;
6050 QScriptValue ctor = engine.newFunction(constructQObjectFromThisObject);
6051 engine.globalObject().setProperty("Ctor", ctor);
6052 QScriptValue object = engine.evaluate("new Ctor");
6053 QVERIFY(!object.isError());
6054 QVERIFY(object.isQObject());
6055 QVERIFY(object.toQObject() != 0);
6056 QVERIFY(object.property("objectName").isString());
6057 QVERIFY(object.property("deleteLater").isFunction());
6061 static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
6063 void tst_QJSEngine::qRegExpInport_data()
6065 QTest::addColumn<QRegExp>("rx");
6066 QTest::addColumn<QString>("string");
6067 QTest::addColumn<QString>("matched");
6069 QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo";
6070 QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo";
6071 QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
6072 QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
6073 QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab";
6074 QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba";
6075 // this one will fail because we do not support the QRegExp::RegExp in JSC
6076 //QTest::newRow("not_greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp) << "aaaabaaba";
6077 QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt";
6078 QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt";
6079 QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string";
6080 QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string";
6081 QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba";
6082 QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
6083 QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
6084 QTest::newRow("html") << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>";
6085 QTest::newRow("html minimal") << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>";
6086 QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa";
6087 QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa";
6088 QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *";
6089 QTest::newRow(".? minimal") << minimal(QRegExp(".?")) << ".?";
6090 QTest::newRow(".+ minimal") << minimal(QRegExp(".+")) << ".+";
6091 QTest::newRow("[.?] minimal") << minimal(QRegExp("[.?]")) << ".?";
6092 QTest::newRow("[.+] minimal") << minimal(QRegExp("[.+]")) << ".+";
6095 void tst_QJSEngine::qRegExpInport()
6097 QFETCH(QRegExp, rx);
6098 QFETCH(QString, string);
6102 rexp = eng.newRegExp(rx);
6104 QCOMPARE(rexp.isValid(), true);
6105 QCOMPARE(rexp.isRegExp(), true);
6106 QVERIFY(rexp.isFunction());
6108 QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
6109 QJSValue result = func.call(QJSValue(), QJSValueList() << string << rexp);
6112 for (int i = 0; i <= rx.captureCount(); i++) {
6113 QCOMPARE(result.property(i).toString(), rx.cap(i));
6117 // QScriptValue::toDateTime() returns a local time, whereas JS dates
6118 // are always stored as UTC. QtScript must respect the current time
6119 // zone, and correctly adjust for daylight saving time that may be in
6120 // effect at a given date (QTBUG-9770).
6121 void tst_QJSEngine::dateRoundtripJSQtJS()
6123 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
6125 for (int i = 0; i < 8000; ++i) {
6126 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
6127 QDateTime qtDate = jsDate.toDateTime();
6128 QJSValue jsDate2 = eng.newDate(qtDate);
6129 if (jsDate2.toNumber() != jsDate.toNumber())
6130 QFAIL(qPrintable(jsDate.toString()));
6135 void tst_QJSEngine::dateRoundtripQtJSQt()
6137 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
6139 for (int i = 0; i < 8000; ++i) {
6140 QJSValue jsDate = eng.newDate(qtDate);
6141 QDateTime qtDate2 = jsDate.toDateTime();
6142 if (qtDate2 != qtDate)
6143 QFAIL(qPrintable(qtDate.toString()));
6144 qtDate = qtDate.addSecs(2*60*60);
6148 void tst_QJSEngine::dateConversionJSQt()
6150 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
6152 for (int i = 0; i < 8000; ++i) {
6153 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
6154 QDateTime qtDate = jsDate.toDateTime();
6155 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
6156 QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString();
6157 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
6158 if (qtUTCDateStr != jsUTCDateStr)
6159 QFAIL(qPrintable(jsDate.toString()));
6164 void tst_QJSEngine::dateConversionQtJS()
6166 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
6168 for (int i = 0; i < 8000; ++i) {
6169 QJSValue jsDate = eng.newDate(qtDate);
6170 QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString();
6171 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
6172 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
6173 if (jsUTCDateStr != qtUTCDateStr)
6174 QFAIL(qPrintable(qtDate.toString()));
6175 qtDate = qtDate.addSecs(2*60*60);
6179 #if 0 // ###FIXME: No QScriptContext API
6180 static QScriptValue createAnotherEngine(QScriptContext *, QScriptEngine *)
6183 eng.evaluate("function foo(x, y) { return x + y; }" );
6184 eng.evaluate("hello = 5; world = 6" );
6185 return eng.evaluate("foo(hello,world)").toInt32();
6189 void tst_QJSEngine::reentrency()
6192 eng.globalObject().setProperty("foo", eng.newFunction(createAnotherEngine));
6193 eng.evaluate("function bar() { return foo(); } hello = 9; function getHello() { return hello; }");
6194 QCOMPARE(eng.evaluate("foo() + getHello() + foo()").toInt32(), 5+6 + 9 + 5+6);
6195 QCOMPARE(eng.evaluate("foo").call().toInt32(), 5+6);
6196 QCOMPARE(eng.evaluate("hello").toInt32(), 9);
6197 QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9);
6201 #if 0 // ###FIXME: No QSCriptDeclarativeClass API
6202 void tst_QJSEngine::newFixedStaticScopeObject()
6204 // "Static scope objects" is an optimization we do for QML.
6205 // It enables the creation of JS objects that can guarantee to the
6206 // compiler that no properties will be added or removed. This enables
6207 // the compiler to generate a very simple (fast) property access, as
6208 // opposed to a full virtual lookup. Due to the inherent use of scope
6209 // chains in QML, this can make a huge difference (10x improvement for
6210 // benchmark in QTBUG-8576).
6211 // Ideally we would not need a special object type for this, and the
6212 // VM would dynamically optimize it to be fast...
6213 // See also QScriptEngine benchmark.
6216 static const int propertyCount = 4;
6217 QString names[] = { "foo", "bar", "baz", "Math" };
6218 QScriptValue values[] = { 123, "ciao", true, false };
6219 QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable,
6220 QScriptValue::ReadOnly | QScriptValue::Undeletable,
6221 QScriptValue::SkipInEnumeration | QScriptValue::Undeletable,
6222 QScriptValue::Undeletable };
6223 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags);
6226 for (int i = 0; i < propertyCount; ++i) {
6227 for (int x = 0; x < 2; ++x) {
6229 // Properties can't be deleted.
6230 scope.setProperty(names[i], QScriptValue());
6232 QVERIFY(scope.property(names[i]).equals(values[i]));
6233 QCOMPARE(scope.propertyFlags(names[i]), flags[i]);
6237 // Property that doesn't exist.
6238 QVERIFY(!scope.property("noSuchProperty").isValid());
6239 QCOMPARE(scope.propertyFlags("noSuchProperty"), QScriptValue::PropertyFlags());
6241 // Write to writable property.
6243 QScriptValue oldValue = scope.property("foo");
6244 QVERIFY(oldValue.isNumber());
6245 QScriptValue newValue = oldValue.toNumber() * 2;
6246 scope.setProperty("foo", newValue);
6247 QVERIFY(scope.property("foo").equals(newValue));
6248 scope.setProperty("foo", oldValue);
6249 QVERIFY(scope.property("foo").equals(oldValue));
6252 // Write to read-only property.
6253 scope.setProperty("bar", 456);
6254 QVERIFY(scope.property("bar").equals("ciao"));
6258 QScriptValueIterator it(scope);
6259 QSet<QString> iteratedNames;
6260 while (it.hasNext()) {
6262 iteratedNames.insert(it.name());
6264 for (int i = 0; i < propertyCount; ++i)
6265 QVERIFY(iteratedNames.contains(names[i]));
6268 // Push it on the scope chain of a new context.
6269 QScriptContext *ctx = eng.pushContext();
6270 ctx->pushScope(scope);
6271 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6272 QEXPECT_FAIL("", "activationObject has not been implemented yet", Continue);
6273 QVERIFY(ctx->activationObject().equals(scope));
6275 // Read property from JS.
6276 for (int i = 0; i < propertyCount; ++i) {
6277 for (int x = 0; x < 2; ++x) {
6279 // Property can't be deleted from JS.
6280 QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i]));
6281 QVERIFY(ret.equals(false));
6283 QVERIFY(eng.evaluate(names[i]).equals(values[i]));
6287 // Property that doesn't exist.
6288 QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: noSuchProperty is not defined"));
6290 // Write property from JS.
6292 QScriptValue oldValue = eng.evaluate("foo");
6293 QVERIFY(oldValue.isNumber());
6294 QScriptValue newValue = oldValue.toNumber() * 2;
6295 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6296 scope.setProperty("foo", oldValue);
6297 QVERIFY(eng.evaluate("foo").equals(oldValue));
6300 // Write to read-only property.
6301 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6303 // Create a closure and return properties from there.
6305 QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()");
6306 QVERIFY(props.isArray());
6307 // "foo" and "bar" come from scope object.
6308 QVERIFY(props.property(0).equals(scope.property("foo")));
6309 QVERIFY(props.property(1).equals(scope.property("bar")));
6310 // "baz" shadows property in scope object.
6311 QVERIFY(props.property(2).equals("shadow"));
6312 // "Math" comes from scope object, and shadows Global Object's "Math".
6313 QVERIFY(props.property(3).equals(scope.property("Math")));
6314 QVERIFY(!props.property(3).equals(eng.globalObject().property("Math")));
6315 // "Array" comes from Global Object.
6316 QVERIFY(props.property(4).equals(eng.globalObject().property("Array")));
6319 // As with normal JS, assigning to an undefined variable will create
6320 // the property on the Global Object, not the inner scope.
6321 QVERIFY(!eng.globalObject().property("newProperty").isValid());
6322 QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined());
6323 QVERIFY(!scope.property("newProperty").isValid());
6324 QVERIFY(eng.globalObject().property("newProperty").isNumber());
6326 // Nested static scope.
6328 static const int propertyCount2 = 2;
6329 QString names2[] = { "foo", "hum" };
6330 QScriptValue values2[] = { 321, "hello" };
6331 QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable,
6332 QScriptValue::ReadOnly | QScriptValue::Undeletable };
6333 QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2);
6334 ctx->pushScope(scope2);
6336 // "foo" shadows scope.foo.
6337 QVERIFY(eng.evaluate("foo").equals(scope2.property("foo")));
6338 QVERIFY(!eng.evaluate("foo").equals(scope.property("foo")));
6339 // "hum" comes from scope2.
6340 QVERIFY(eng.evaluate("hum").equals(scope2.property("hum")));
6341 // "Array" comes from Global Object.
6342 QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array")));
6347 QScriptValue fun = eng.evaluate("(function() { return foo; })");
6348 QVERIFY(fun.isFunction());
6350 // Function's scope chain persists after popContext().
6351 QVERIFY(fun.call().equals(scope.property("foo")));
6354 void tst_QJSEngine::newGrowingStaticScopeObject()
6356 // The main use case for a growing static scope object is to set it as
6357 // the activation object of a QScriptContext, so that all JS variable
6358 // declarations end up in that object. It needs to be "growable" since
6359 // we don't know in advance how many variables a script will declare.
6362 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng);
6365 QVERIFY(!QScriptValueIterator(scope).hasNext());
6366 QVERIFY(!scope.property("foo").isValid());
6368 // Add a static property.
6369 scope.setProperty("foo", 123);
6370 QVERIFY(scope.property("foo").equals(123));
6371 QEXPECT_FAIL("", "FIXME: newStaticScopeObject not properly implemented", Abort);
6372 QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
6374 // Modify existing property.
6375 scope.setProperty("foo", 456);
6376 QVERIFY(scope.property("foo").equals(456));
6378 // Add a read-only property.
6379 scope.setProperty("bar", "ciao", QScriptValue::ReadOnly);
6380 QVERIFY(scope.property("bar").equals("ciao"));
6381 QCOMPARE(scope.propertyFlags("bar"), QScriptValue::ReadOnly | QScriptValue::Undeletable);
6383 // Attempt to modify read-only property.
6384 scope.setProperty("bar", "hello");
6385 QVERIFY(scope.property("bar").equals("ciao"));
6387 // Properties can't be deleted.
6388 scope.setProperty("foo", QScriptValue());
6389 QVERIFY(scope.property("foo").equals(456));
6390 scope.setProperty("bar", QScriptValue());
6391 QVERIFY(scope.property("bar").equals("ciao"));
6395 QScriptValueIterator it(scope);
6396 QSet<QString> iteratedNames;
6397 while (it.hasNext()) {
6399 iteratedNames.insert(it.name());
6401 QCOMPARE(iteratedNames.size(), 2);
6402 QVERIFY(iteratedNames.contains("foo"));
6403 QVERIFY(iteratedNames.contains("bar"));
6406 // Push it on the scope chain of a new context.
6407 QScriptContext *ctx = eng.pushContext();
6408 ctx->pushScope(scope);
6409 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6410 QVERIFY(ctx->activationObject().equals(scope));
6412 // Read property from JS.
6413 QVERIFY(eng.evaluate("foo").equals(scope.property("foo")));
6414 QVERIFY(eng.evaluate("bar").equals(scope.property("bar")));
6416 // Write property from JS.
6418 QScriptValue oldValue = eng.evaluate("foo");
6419 QVERIFY(oldValue.isNumber());
6420 QScriptValue newValue = oldValue.toNumber() * 2;
6421 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6422 scope.setProperty("foo", oldValue);
6423 QVERIFY(eng.evaluate("foo").equals(oldValue));
6426 // Write to read-only property.
6427 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6430 QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math")));
6431 scope.setProperty("Math", "fake Math");
6432 QVERIFY(eng.evaluate("Math").equals(scope.property("Math")));
6434 // Variable declarations will create properties on the scope.
6435 eng.evaluate("var baz = 456");
6436 QVERIFY(scope.property("baz").equals(456));
6438 // Function declarations will create properties on the scope.
6439 eng.evaluate("function fun() { return baz; }");
6440 QVERIFY(scope.property("fun").isFunction());
6441 QVERIFY(scope.property("fun").call().equals(scope.property("baz")));
6443 // Demonstrate the limitation of a growable static scope: Once a function that
6444 // uses the scope has been compiled, it won't pick up properties that are added
6445 // to the scope later.
6447 QScriptValue fun = eng.evaluate("(function() { return futureProperty; })");
6448 QVERIFY(fun.isFunction());
6449 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6450 scope.setProperty("futureProperty", "added after the function was compiled");
6451 // If scope were dynamic, this would return the new property.
6452 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6459 #if 0 // ###FIXME: No QScript MetaObject API
6461 Q_SCRIPT_DECLARE_QMETAOBJECT(QStandardItemModel, QObject*)
6464 void tst_QJSEngine::scriptValueFromQMetaObject()
6468 QScriptValue meta = eng.scriptValueFromQMetaObject<QScriptEngine>();
6469 QVERIFY(meta.isQMetaObject());
6470 QCOMPARE(meta.toQMetaObject(), &QScriptEngine::staticMetaObject);
6471 // Because of missing Q_SCRIPT_DECLARE_QMETAOBJECT() for QScriptEngine.
6472 QEXPECT_FAIL("", "FIXME: because construct never returns invalid values", Continue);
6473 QVERIFY(!meta.construct().isValid());
6476 QScriptValue meta = eng.scriptValueFromQMetaObject<QStandardItemModel>();
6477 QVERIFY(meta.isQMetaObject());
6478 QCOMPARE(meta.toQMetaObject(), &QStandardItemModel::staticMetaObject);
6479 QScriptValue obj = meta.construct(QScriptValueList() << eng.newQObject(&eng));
6480 QVERIFY(obj.isQObject());
6481 QStandardItemModel *model = qobject_cast<QStandardItemModel*>(obj.toQObject());
6482 QVERIFY(model != 0);
6483 QCOMPARE(model->parent(), (QObject*)&eng);
6488 QTEST_MAIN(tst_QJSEngine)
6490 #include "tst_qjsengine.moc"