1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qjsengine.h>
46 #include <qjsvalueiterator.h>
47 #include <qgraphicsitem.h>
48 #include <qstandarditemmodel.h>
49 #include <QtCore/qnumeric.h>
52 #include <private/v8.h>
54 Q_DECLARE_METATYPE(QList<int>)
55 Q_DECLARE_METATYPE(QObjectList)
57 // The JavaScriptCore GC marks the C stack. To try to ensure that there is
58 // no JSObject* left in stack memory by the compiler, we call this function
59 // to zap some bytes of memory before calling collectGarbage().
60 static void zapSomeStack()
63 memset(buf, 0, sizeof(buf));
66 static void collectGarbage_helper(QJSEngine &eng)
73 extern Q_QML_EXPORT v8::Local<v8::Context> qt_QJSEngineV8Context(QJSEngine *);
74 extern Q_QML_EXPORT v8::Local<v8::Value> qt_QJSValueV8Value(const QJSValue &);
77 class tst_QJSEngine : public QObject
83 virtual ~tst_QJSEngine();
86 void constructWithParent();
87 #if 0 // FIXME: no QScriptContext
88 void currentContext();
89 void pushPopContext();
91 #if 0 // FIXME: No prototype API in QScriptEngine
92 void getSetDefaultPrototype_int();
93 void getSetDefaultPrototype_customType();
95 #if 0 // FIXME: no QScriptContext
97 void newFunctionWithArg();
98 void newFunctionWithProto();
102 void newArray_HooliganTask218092();
103 void newArray_HooliganTask233836();
105 #if 0 // FIXME: No prototype API in QScriptEngine
106 void newVariant_defaultPrototype();
108 #if 0 // ###FIXME: No QVariant object promotion API
109 void newVariant_promoteObject();
110 void newVariant_replaceValue();
112 void newVariant_valueOfToString();
113 #if 0 // ###FIXME: No QVariant object promotion API
114 void newVariant_promoteNonObject();
115 void newVariant_promoteNonQScriptObject();
122 void newQObject_ownership();
123 void newQObject_promoteObject();
124 void newQObject_sameQObject();
125 #if 0 // FIXME: No prototype API in QScriptEngine
126 void newQObject_defaultPrototype();
128 void newQObject_promoteNonObject();
129 void newQObject_promoteNonQScriptObject();
130 void newQObject_deletedEngine();
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 void globalObjectProperties_enumerate();
142 void createGlobalObjectProperty();
143 void globalObjectGetterSetterProperty();
144 #if 0 // ###FIXME: No support for setting the global object
145 void customGlobalObjectWithPrototype();
147 void globalObjectWithCustomPrototype();
148 void builtinFunctionNames_data();
149 void builtinFunctionNames();
150 #if 0 // ###FIXME: No syntax checking result
151 void checkSyntax_data();
154 #if 0 // ###FIXME: No support for canEvaluate
155 void canEvaluate_data();
158 void evaluate_data();
160 #if 0 // ###FIXME: no support for c-style callbacks
161 void nestedEvaluate();
163 #if 0 // ### FIXME: No c-style callbacks
164 void uncaughtException();
166 void errorMessage_QT679();
167 void valueConversion_basic();
168 #if 0 // FIXME: No API for custom types
169 void valueConversion_customType();
170 void valueConversion_sequence();
172 void valueConversion_QVariant();
173 #if 0 // FIXME: No support for custom types
174 void valueConversion_hooliganTask248802();
176 void valueConversion_basic2();
177 void valueConversion_dateTime();
178 void valueConversion_regExp();
179 #if 0 // FIXME: No qScriptValueFromValue
180 void qScriptValueFromValue_noEngine();
182 #if 0 // ###FIXME: No QScriptContext
183 void importExtension();
184 void infiniteRecursion();
186 #if 0 // FIXME: No support for default prototypes
187 void castWithPrototypeChain();
189 void castWithMultipleInheritance();
190 void collectGarbage();
191 #if 0 // ###FIXME: no reportAdditionalMemoryCost API
192 void reportAdditionalMemoryCost();
194 void gcWithNestedDataStructure();
195 #if 0 // ###FIXME: No processEvents handling
196 void processEventsWhileRunning();
197 void processEventsWhileRunning_function();
198 void throwErrorFromProcessEvents_data();
199 void throwErrorFromProcessEvents();
200 void disableProcessEventsInterval();
203 void numberParsing_data();
204 void numberParsing();
205 void automaticSemicolonInsertion();
206 #if 0 // ###FIXME: no abortEvaluation API
207 void abortEvaluation_notEvaluating();
208 void abortEvaluation_data();
209 void abortEvaluation();
210 void abortEvaluation_tryCatch();
211 void abortEvaluation_fromNative();
212 void abortEvaluation_QTBUG9433();
214 #if 0 // ###FIXME: no QScriptEngine::isEvaluating
215 void isEvaluating_notEvaluating();
216 void isEvaluating_fromNative();
217 void isEvaluating_fromEvent();
219 #if 0 // ###FIXME: depracated
220 void printFunctionWithCustomHandler();
221 void printThrowsException();
223 void errorConstructors();
224 void argumentsProperty_globalContext();
225 void argumentsProperty_JS();
226 #if 0 // ###FIXME: no QScriptContext API
227 void argumentsProperty_evaluateInNativeFunction();
229 void jsNumberClass();
230 void jsForInStatement_simple();
231 void jsForInStatement_prototypeProperties();
232 void jsForInStatement_mutateWhileIterating();
233 void jsForInStatement_arrays();
234 void jsForInStatement_nullAndUndefined();
235 void jsFunctionDeclarationAsStatement();
236 void stringObjects();
237 void jsStringPrototypeReplaceBugs();
238 void getterSetterThisObject_global();
239 void getterSetterThisObject_plain();
240 void getterSetterThisObject_prototypeChain();
241 #if 0 // ###FIXME: no QScriptContext API
242 void getterSetterThisObject_activation();
244 void jsContinueInSwitch();
245 void jsShadowReadOnlyPrototypeProperty();
246 void jsReservedWords_data();
247 void jsReservedWords();
248 void jsFutureReservedWords_data();
249 void jsFutureReservedWords();
250 void jsThrowInsideWithStatement();
251 #if 0 // ###FIXME: No QScriptEngineAgent API
252 void getSetAgent_ownership();
253 void getSetAgent_deleteAgent();
254 void getSetAgent_differentEngine();
256 #if 0 // ###FIXME: No QScriptString API
257 void reentrancy_stringHandles();
259 #if 0 // ###FIXME: No processEventsInterval API
260 void reentrancy_processEventsInterval();
262 #if 0 // FIXME: No support for custom types
263 void reentrancy_typeConversion();
265 void reentrancy_globalObjectProperties();
266 void reentrancy_Array();
267 void reentrancy_objectCreation();
268 void jsIncDecNonObjectProperty();
269 #if 0 // ###FIXME: no installTranslatorFunctions API
270 void installTranslatorFunctions();
271 void translateScript_data();
272 void translateScript();
273 void translateScript_crossScript();
274 void translateScript_callQsTrFromNative();
275 void translateScript_trNoOp();
276 void translateScript_callQsTrFromCpp();
277 void translateWithInvalidArgs_data();
278 void translateWithInvalidArgs();
279 void translationContext_data();
280 void translationContext();
281 void translateScriptIdBased();
282 void translateScriptUnicode_data();
283 void translateScriptUnicode();
284 void translateScriptUnicodeIdBased_data();
285 void translateScriptUnicodeIdBased();
286 void translateFromBuiltinCallback();
288 #if 0 // ###FIXME: No QScriptValue::scope API
289 void functionScopes();
291 #if 0 // ###FIXME: No QScriptContext API
292 void nativeFunctionScopes();
294 #if 0 // ###FIXME: No QScriptProgram API
295 void evaluateProgram();
296 void evaluateProgram_customScope();
297 void evaluateProgram_closure();
298 void evaluateProgram_executeLater();
299 void evaluateProgram_multipleEngines();
300 void evaluateProgram_empty();
302 #if 0 // ###FIXME: No QScriptContext API
303 void collectGarbageAfterConnect();
304 void collectGarbageAfterNativeArguments();
305 void promoteThisObjectToQObjectInConstructor();
307 #if 0 // ###FIXME: No QScript MetaObject API
308 void scriptValueFromQMetaObject();
311 void qRegExpInport_data();
312 void qRegExpInport();
313 #if 0 // ###FIXME: No QScriptContext API
316 #if 0 // ###FIXME: No QSCriptDeclarativeClass API
317 void newFixedStaticScopeObject();
318 void newGrowingStaticScopeObject();
320 void dateRoundtripJSQtJS();
321 void dateRoundtripQtJSQt();
322 void dateConversionJSQt();
323 void dateConversionQtJS();
324 void functionPrototypeExtensions();
325 void threadedEngine();
327 void v8Context_simple();
328 void v8Context_exception();
329 void v8Context_mixAPIs();
332 tst_QJSEngine::tst_QJSEngine()
336 tst_QJSEngine::~tst_QJSEngine()
340 void tst_QJSEngine::constructWithParent()
342 QPointer<QJSEngine> ptr;
345 QJSEngine *engine = new QJSEngine(&obj);
351 #if 0 // FIXME: no QScriptContext
352 void tst_QJSEngine::currentContext()
355 QScriptContext *globalCtx = eng.currentContext();
356 QVERIFY(globalCtx != 0);
357 QVERIFY(globalCtx->parentContext() == 0);
358 QCOMPARE(globalCtx->engine(), &eng);
359 QCOMPARE(globalCtx->argumentCount(), 0);
360 QCOMPARE(globalCtx->backtrace().size(), 1);
361 QVERIFY(!globalCtx->isCalledAsConstructor());
362 QVERIFY(globalCtx->callee().isUndefined());
363 QCOMPARE(globalCtx->state(), QScriptContext::NormalState);
364 QVERIFY(globalCtx->thisObject().strictlyEquals(eng.globalObject()));
365 QVERIFY(globalCtx->activationObject().strictlyEquals(eng.globalObject()));
366 QVERIFY(globalCtx->argumentsObject().isObject());
369 void tst_QJSEngine::pushPopContext()
372 QScriptContext *globalCtx = eng.currentContext();
373 QScriptContext *ctx = eng.pushContext();
375 QCOMPARE(ctx->parentContext(), globalCtx);
376 QVERIFY(!ctx->isCalledAsConstructor());
377 QVERIFY(ctx->callee().isUndefined());
378 QVERIFY(ctx->thisObject().strictlyEquals(eng.globalObject()));
379 QCOMPARE(ctx->argumentCount(), 0);
380 QCOMPARE(ctx->backtrace().size(), 2);
381 QCOMPARE(ctx->engine(), &eng);
382 QCOMPARE(ctx->state(), QScriptContext::NormalState);
383 QVERIFY(ctx->activationObject().isObject());
384 QVERIFY(ctx->argumentsObject().isObject());
386 QScriptContext *ctx2 = eng.pushContext();
388 QCOMPARE(ctx2->parentContext(), ctx);
389 QVERIFY(!ctx2->activationObject().strictlyEquals(ctx->activationObject()));
390 QVERIFY(!ctx2->argumentsObject().strictlyEquals(ctx->argumentsObject()));
394 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
395 eng.popContext(); // ignored
396 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()");
397 eng.popContext(); // ignored
400 static QScriptValue myFunction(QScriptContext *, QScriptEngine *eng)
402 return eng->nullValue();
405 static QScriptValue myFunctionWithVoidArg(QScriptContext *, QScriptEngine *eng, void *)
407 return eng->nullValue();
410 static QScriptValue myThrowingFunction(QScriptContext *ctx, QScriptEngine *)
412 return ctx->throwError("foo");
415 static QScriptValue myFunctionThatReturns(QScriptContext *, QScriptEngine *eng)
417 return QScriptValue(eng, 42);
420 static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext *, QScriptEngine *)
422 return QScriptValue(1024);
425 static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext *, QScriptEngine *, void *arg)
427 QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg);
428 return QScriptValue(wrongEngine, 42);
431 static QScriptValue sumFunction(QScriptContext *context, QScriptEngine *engine)
435 for (int i = 0; i < context->argumentCount(); i++) {
436 QScriptValue n = context->argument(i);
438 sum += n.toInteger();
441 return QScriptValue(engine, sum);
444 void tst_QJSEngine::newFunction()
448 QScriptValue fun = eng.newFunction(myFunction);
449 QVERIFY(!fun.isUndefined());
450 QCOMPARE(fun.isCallable(), true);
451 QCOMPARE(fun.isObject(), true);
452 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
453 // a prototype property is automatically constructed
455 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
456 QVERIFY(prot.isObject());
457 QVERIFY(prot.property("constructor").strictlyEquals(fun));
459 // prototype should be Function.prototype
460 QVERIFY(!fun.prototype().isUndefined());
461 QCOMPARE(fun.prototype().isCallable(), true);
462 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
464 QCOMPARE(fun.call().isNull(), true);
465 QCOMPARE(fun.callAsConstructor().isObject(), true);
469 void tst_QJSEngine::newFunctionWithArg()
473 QScriptValue fun = eng.newFunction(myFunctionWithVoidArg, (void*)this);
474 QVERIFY(fun.isCallable());
475 QCOMPARE(fun.scriptClass(), (QScriptClass*)0);
476 // a prototype property is automatically constructed
478 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal);
479 QVERIFY(prot.isObject());
480 QVERIFY(prot.property("constructor").strictlyEquals(fun));
482 // prototype should be Function.prototype
483 QVERIFY(!fun.prototype().isUndefined());
484 QCOMPARE(fun.prototype().isCallable(), true);
485 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
487 QCOMPARE(fun.call().isNull(), true);
488 QCOMPARE(fun.callAsConstructor().isObject(), true);
492 void tst_QJSEngine::newFunctionWithProto()
496 QScriptValue proto = eng.newObject();
497 QScriptValue fun = eng.newFunction(myFunction, proto);
498 QVERIFY(!fun.isUndefined());
499 QCOMPARE(fun.isCallable(), true);
500 QCOMPARE(fun.isObject(), true);
501 // internal prototype should be Function.prototype
502 QVERIFY(!fun.prototype().isUndefined());
503 QCOMPARE(fun.prototype().isCallable(), true);
504 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);
505 // public prototype should be the one we passed
506 QCOMPARE(fun.property("prototype").strictlyEquals(proto), true);
507 QCOMPARE(proto.property("constructor").strictlyEquals(fun), true);
509 QCOMPARE(fun.call().isNull(), true);
510 QCOMPARE(fun.callAsConstructor().isObject(), true);
512 // whether the return value is correct
514 QScriptValue fun = eng.newFunction(myFunctionThatReturns);
515 QVERIFY(!fun.isUndefined());
516 QCOMPARE(fun.isCallable(), true);
517 QCOMPARE(fun.isObject(), true);
519 QScriptValue result = fun.call();
520 QCOMPARE(result.isNumber(), true);
521 QCOMPARE(result.toInt(), 42);
523 // whether the return value is assigned to the correct engine
525 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine);
526 QVERIFY(!fun.isUndefined());
527 QCOMPARE(fun.isCallable(), true);
528 QCOMPARE(fun.isObject(), true);
530 QScriptValue result = fun.call();
531 QCOMPARE(result.engine(), &eng);
532 QCOMPARE(result.isNumber(), true);
533 QCOMPARE(result.toInt(), 1024);
535 // whether the return value is undefined when returning a value with wrong engine
537 QScriptEngine wrongEngine;
539 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void *>(&wrongEngine));
540 QVERIFY(!fun.isUndefined());
541 QCOMPARE(fun.isCallable(), true);
542 QCOMPARE(fun.isObject(), true);
544 QTest::ignoreMessage(QtWarningMsg, "QScriptValue::call(): Value from different engine returned from native function, returning undefined value instead.");
545 QScriptValue result = fun.call();
546 QCOMPARE(result.isUndefined(), true);
548 // checking if arguments are passed correctly
550 QScriptEngine wrongEngine;
552 QScriptValue fun = eng.newFunction(sumFunction);
553 QVERIFY(!fun.isUndefined());
554 QCOMPARE(fun.isCallable(), true);
555 QCOMPARE(fun.isObject(), true);
557 QScriptValue result = fun.call();
558 QCOMPARE(result.isNumber(), true);
559 QCOMPARE(result.toInt(), 0);
561 result = fun.call(QScriptValue(), QScriptValueList() << 1);
562 QCOMPARE(result.isNumber(), true);
563 QCOMPARE(result.toInt(), 1);
565 result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3);
566 QCOMPARE(result.isNumber(), true);
567 QCOMPARE(result.toInt(), 6);
569 result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3 << 4);
570 QCOMPARE(result.isNumber(), true);
571 QCOMPARE(result.toInt(), 10);
576 void tst_QJSEngine::newObject()
579 QJSValue object = eng.newObject();
580 QVERIFY(!object.isUndefined());
581 QCOMPARE(object.isObject(), true);
582 QCOMPARE(object.isCallable(), false);
583 // ###FIXME: No QScriptClass QCOMPARE(object.scriptClass(), (QScriptClass*)0);
584 // prototype should be Object.prototype
585 QVERIFY(!object.prototype().isUndefined());
586 QCOMPARE(object.prototype().isObject(), true);
587 QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
590 void tst_QJSEngine::newArray()
593 QJSValue array = eng.newArray();
594 QVERIFY(!array.isUndefined());
595 QCOMPARE(array.isArray(), true);
596 QCOMPARE(array.isObject(), true);
597 QVERIFY(!array.isCallable());
598 // ###FIXME: No QScriptClass QCOMPARE(array.scriptClass(), (QScriptClass*)0);
599 // prototype should be Array.prototype
600 QVERIFY(!array.prototype().isUndefined());
601 QCOMPARE(array.prototype().isArray(), true);
602 QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true);
605 void tst_QJSEngine::newArray_HooliganTask218092()
609 QJSValue ret = eng.evaluate("[].splice(0, 0, 'a')");
610 QVERIFY(ret.isArray());
611 QCOMPARE(ret.property("length").toInt(), 0);
614 QJSValue ret = eng.evaluate("['a'].splice(0, 1, 'b')");
615 QVERIFY(ret.isArray());
616 QCOMPARE(ret.property("length").toInt(), 1);
619 QJSValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')");
620 QVERIFY(ret.isArray());
621 QCOMPARE(ret.property("length").toInt(), 1);
624 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')");
625 QVERIFY(ret.isArray());
626 QCOMPARE(ret.property("length").toInt(), 2);
629 QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')");
630 QVERIFY(ret.isArray());
631 QCOMPARE(ret.property("length").toInt(), 2);
635 void tst_QJSEngine::newArray_HooliganTask233836()
639 // According to ECMA-262, this should cause a RangeError.
640 QJSValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')");
641 QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError")));
644 QJSValue ret = eng.newArray(0xFFFFFFFF);
645 QEXPECT_FAIL("", "The maximum length of arrays is defined by v8 currently and differs from QtScript", Abort);
646 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
647 ret.setProperty(0xFFFFFFFF, 123);
648 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
649 QVERIFY(ret.property(0xFFFFFFFF).isNumber());
650 QCOMPARE(ret.property(0xFFFFFFFF).toInt(), 123);
651 ret.setProperty(123, 456);
652 QCOMPARE(ret.property("length").toUInt(), uint(0xFFFFFFFF));
653 QVERIFY(ret.property(123).isNumber());
654 QCOMPARE(ret.property(123).toInt(), 456);
658 void tst_QJSEngine::newVariant()
662 QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2)));
663 QVERIFY(!opaque.isUndefined());
664 QCOMPARE(opaque.isVariant(), true);
665 QVERIFY(!opaque.isCallable());
666 QCOMPARE(opaque.isObject(), true);
667 QVERIFY(!opaque.prototype().isUndefined());
668 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
669 QCOMPARE(opaque.prototype().isVariant(), true);
670 QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque));
674 #if 0 // FIXME: No prototype API in QScriptEngine
675 void tst_QJSEngine::newVariant_defaultPrototype()
677 // default prototype should be set automatically
680 QScriptValue proto = eng.newObject();
681 eng.setDefaultPrototype(qMetaTypeId<QString>(), proto);
682 QScriptValue ret = eng.newVariant(QVariant(QString::fromLatin1("hello")));
683 QVERIFY(ret.isVariant());
684 // ###FIXME: No QScriptClass QCOMPARE(ret.scriptClass(), (QScriptClass*)0);
685 QVERIFY(ret.prototype().strictlyEquals(proto));
686 eng.setDefaultPrototype(qMetaTypeId<QString>(), QScriptValue());
687 QScriptValue ret2 = eng.newVariant(QVariant(QString::fromLatin1("hello")));
688 QVERIFY(ret2.isVariant());
689 QVERIFY(!ret2.prototype().strictlyEquals(proto));
694 #if 0 // ###FIXME: No QVariant object promotion API
695 void tst_QJSEngine::newVariant_promoteObject()
697 // "promote" plain object to variant
700 QScriptValue object = eng.newObject();
701 object.setProperty("foo", eng.newObject());
702 object.setProperty("bar", object.property("foo"));
703 QVERIFY(object.property("foo").isObject());
704 QVERIFY(!object.property("foo").isVariant());
705 QScriptValue originalProto = object.property("foo").prototype();
706 QSKIP("It is not possible to promote plain object to a wrapper");
707 QScriptValue ret = eng.newVariant(object.property("foo"), QVariant(123));
708 QVERIFY(ret.isObject());
709 QVERIFY(ret.strictlyEquals(object.property("foo")));
710 QVERIFY(ret.isVariant());
711 QVERIFY(object.property("foo").isVariant());
712 QVERIFY(object.property("bar").isVariant());
713 QCOMPARE(ret.toVariant(), QVariant(123));
714 QVERIFY(ret.prototype().strictlyEquals(originalProto));
718 void tst_QJSEngine::newVariant_replaceValue()
720 // replace value of existing object
723 QScriptValue object = eng.newVariant(QVariant(123));
724 for (int x = 0; x < 2; ++x) {
725 QScriptValue ret = eng.newVariant(object, QVariant(456));
726 QVERIFY(!ret.isUndefined());
727 QVERIFY(ret.strictlyEquals(object));
728 QVERIFY(ret.isVariant());
729 QCOMPARE(ret.toVariant(), QVariant(456));
735 void tst_QJSEngine::newVariant_valueOfToString()
737 // valueOf() and toString()
740 QJSValue object = eng.toScriptValue(QVariant(QPoint(10, 20)));
741 QJSValue value = object.property("valueOf").callWithInstance(object);
742 QVERIFY(value.isObject());
743 QVERIFY(value.strictlyEquals(object));
744 QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)"));
748 #if 0 // ###FIXME: No QVariant object promotion API
749 void tst_QJSEngine::newVariant_promoteNonObject()
754 QScriptValue ret = eng.newVariant(123, var);
755 QVERIFY(ret.isVariant());
756 QCOMPARE(ret.toVariant(), var);
760 void tst_QJSEngine::newVariant_promoteNonQScriptObject()
762 QSKIP("This test relay on limitation of QtScript JSC implementation");
765 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newVariant(): changing class of non-QScriptObject not supported");
766 QScriptValue ret = eng.newVariant(eng.newArray(), 123);
767 QVERIFY(ret.isUndefined());
772 void tst_QJSEngine::newRegExp()
774 QSKIP("Test failing - QTBUG-22238");
776 QJSValue rexp = eng.toScriptValue(QRegExp("foo"));
777 QVERIFY(!rexp.isUndefined());
778 QCOMPARE(rexp.isRegExp(), true);
779 QCOMPARE(rexp.isObject(), true);
780 QVERIFY(rexp.isCallable()); // in JSC, RegExp objects are callable
781 // prototype should be RegExp.prototype
782 QVERIFY(!rexp.prototype().isUndefined());
783 QCOMPARE(rexp.prototype().isObject(), true);
784 QCOMPARE(rexp.prototype().isRegExp(), false);
785 QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
787 QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
790 void tst_QJSEngine::jsRegExp()
792 QSKIP("Test failing - QTBUG-22238");
794 // See ECMA-262 Section 15.10, "RegExp Objects".
795 // These should really be JS-only tests, as they test the implementation's
796 // ECMA-compliance, not the C++ API. Compliance should already be covered
797 // by the Mozilla tests (qscriptjstestsuite).
798 // We can consider updating the expected results of this test if the
799 // RegExp implementation changes.
802 QJSValue r = eng.evaluate("/foo/gim");
803 QVERIFY(r.isRegExp());
804 QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim"));
806 QJSValue rxCtor = eng.globalObject().property("RegExp");
807 QJSValue r2 = rxCtor.call(QJSValueList() << r);
808 QVERIFY(r2.isRegExp());
809 QVERIFY(r2.strictlyEquals(r));
811 QJSValue r3 = rxCtor.call(QJSValueList() << r << "gim");
812 QVERIFY(r3.isError());
813 QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another
815 QJSValue r4 = rxCtor.call(QJSValueList() << "foo" << "gim");
816 QVERIFY(r4.isRegExp());
818 QJSValue r5 = rxCtor.callAsConstructor(QJSValueList() << r);
819 QVERIFY(r5.isRegExp());
820 QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
821 // In JSC, constructing a RegExp from another produces the same identical object.
822 // This is different from SpiderMonkey and old back-end.
823 QVERIFY(!r5.strictlyEquals(r));
825 QEXPECT_FAIL("", "V8 and jsc ignores invalid flags", Continue); //https://bugs.webkit.org/show_bug.cgi?id=41614
826 QJSValue r6 = rxCtor.callAsConstructor(QJSValueList() << "foo" << "bar");
827 QVERIFY(r6.isError());
828 // QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
831 QJSValue r7 = eng.evaluate("/foo/gimp");
832 /* v8 and jsc ignores invalid flags
833 QVERIFY(r7.isError());
834 QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag
837 // JSC doesn't complain about duplicate flags.
838 QJSValue r8 = eng.evaluate("/foo/migmigmig");
839 QVERIFY(r8.isRegExp());
840 QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim"));
842 QJSValue r9 = rxCtor.callAsConstructor();
843 QVERIFY(r9.isRegExp());
844 QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));
846 QJSValue r10 = rxCtor.callAsConstructor(QJSValueList() << "" << "gim");
847 QVERIFY(r10.isRegExp());
848 QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));
850 QJSValue r11 = rxCtor.callAsConstructor(QJSValueList() << "{1.*}" << "g");
851 QVERIFY(r11.isRegExp());
852 QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
855 void tst_QJSEngine::newDate()
860 QJSValue date = eng.evaluate("new Date(0)");
861 QVERIFY(!date.isUndefined());
862 QCOMPARE(date.isDate(), true);
863 QCOMPARE(date.isObject(), true);
864 QVERIFY(!date.isCallable());
865 // prototype should be Date.prototype
866 QVERIFY(!date.prototype().isUndefined());
867 QCOMPARE(date.prototype().isDate(), true);
868 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
872 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime);
873 QJSValue date = eng.toScriptValue(dt);
874 QVERIFY(!date.isUndefined());
875 QCOMPARE(date.isDate(), true);
876 QCOMPARE(date.isObject(), true);
877 // prototype should be Date.prototype
878 QVERIFY(!date.prototype().isUndefined());
879 QCOMPARE(date.prototype().isDate(), true);
880 QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true);
882 QCOMPARE(date.toDateTime(), dt);
886 QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC);
887 QJSValue date = eng.toScriptValue(dt);
888 // toDateTime() result should be in local time
889 QCOMPARE(date.toDateTime(), dt.toLocalTime());
893 void tst_QJSEngine::jsParseDate()
896 // Date.parse() should return NaN when it fails
898 QJSValue ret = eng.evaluate("Date.parse()");
899 QVERIFY(ret.isNumber());
900 QVERIFY(qIsNaN(ret.toNumber()));
903 // Date.parse() should be able to parse the output of Date().toString()
905 QJSValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()");
906 QVERIFY(ret.isBool());
907 QCOMPARE(ret.toBool(), true);
911 void tst_QJSEngine::newQObject()
917 QJSValue qobject = eng.newQObject(0);
918 QCOMPARE(qobject.isNull(), true);
919 QCOMPARE(qobject.isObject(), false);
920 QCOMPARE(qobject.toQObject(), (QObject *)0);
923 QJSValue qobject = eng.newQObject(&temp);
924 QVERIFY(!qobject.isUndefined());
925 QCOMPARE(qobject.isQObject(), true);
926 QCOMPARE(qobject.isObject(), true);
927 QCOMPARE(qobject.toQObject(), (QObject *)&temp);
928 QVERIFY(!qobject.isCallable());
929 // prototype should be QObject.prototype
930 QCOMPARE(qobject.prototype().isObject(), true);
931 QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue);
932 QCOMPARE(qobject.prototype().isQObject(), true);
933 // ###FIXME: No QScriptClass QCOMPARE(qobject.scriptClass(), (QScriptClass*)0);
937 void tst_QJSEngine::newQObject_ownership()
941 QPointer<QObject> ptr = new QObject();
944 QJSValue v = eng.newQObject(ptr);
946 collectGarbage_helper(eng);
948 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
952 QPointer<QObject> ptr = new QObject(this);
955 QJSValue v = eng.newQObject(ptr);
957 QObject *before = ptr;
958 collectGarbage_helper(eng);
959 QVERIFY(ptr == before);
963 QObject *parent = new QObject();
964 QObject *child = new QObject(parent);
965 QJSValue v = eng.newQObject(child);
966 QCOMPARE(v.toQObject(), child);
968 QCOMPARE(v.toQObject(), (QObject *)0);
971 QPointer<QObject> ptr = new QObject();
974 QJSValue v = eng.newQObject(ptr);
976 collectGarbage_helper(eng);
977 // no parent, so it should be like ScriptOwnership
979 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
983 QObject *parent = new QObject();
984 QPointer<QObject> child = new QObject(parent);
987 QJSValue v = eng.newQObject(child);
989 collectGarbage_helper(eng);
990 // has parent, so it should be like QtOwnership
996 void tst_QJSEngine::newQObject_promoteObject()
998 #if 0 // ### FIXME: object promotion is not supported
1000 // "promote" plain object to QObject
1002 QScriptValue obj = eng.newObject();
1003 QScriptValue originalProto = obj.prototype();
1004 QScriptValue ret = eng.newQObject(obj, this);
1005 QVERIFY(!ret.isUndefined());
1006 QVERIFY(ret.isQObject());
1007 QVERIFY(ret.strictlyEquals(obj));
1008 QVERIFY(obj.isQObject());
1009 QCOMPARE(ret.toQObject(), (QObject *)this);
1010 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1011 QScriptValue val = ret.property("objectName");
1012 QVERIFY(val.isString());
1014 // "promote" variant object to QObject
1016 QScriptValue obj = eng.newVariant(123);
1017 QVERIFY(obj.isVariant());
1018 QScriptValue originalProto = obj.prototype();
1019 QScriptValue ret = eng.newQObject(obj, this);
1020 QVERIFY(ret.isQObject());
1021 QVERIFY(ret.strictlyEquals(obj));
1022 QVERIFY(obj.isQObject());
1023 QCOMPARE(ret.toQObject(), (QObject *)this);
1024 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1026 // replace QObject* of existing object
1028 QScriptValue object = eng.newVariant(123);
1029 QScriptValue originalProto = object.prototype();
1030 QObject otherQObject;
1031 for (int x = 0; x < 2; ++x) {
1032 QScriptValue ret = eng.newQObject(object, &otherQObject);
1033 QVERIFY(!ret.isUndefined());
1034 QVERIFY(ret.isQObject());
1035 QVERIFY(ret.strictlyEquals(object));
1036 QCOMPARE(ret.toQObject(), (QObject *)&otherQObject);
1037 QVERIFY(ret.prototype().strictlyEquals(originalProto));
1043 void tst_QJSEngine::newQObject_sameQObject()
1045 #if 0 // ###FIXME: No QObjectWrapOptions API
1046 QSKIP("This test strongly relies on strictlyEquals feature that would change in near future");
1048 // calling newQObject() several times with same object
1049 for (int x = 0; x < 2; ++x) {
1051 // the default is to create a new wrapper object
1052 QScriptValue obj1 = eng.newQObject(&qobj);
1053 QScriptValue obj2 = eng.newQObject(&qobj);
1054 QVERIFY(!obj2.strictlyEquals(obj1));
1056 QScriptEngine::QObjectWrapOptions opt = 0;
1057 bool preferExisting = (x != 0);
1059 opt |= QScriptEngine::PreferExistingWrapperObject;
1061 QScriptValue obj3 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
1062 QVERIFY(!obj3.strictlyEquals(obj2));
1063 QScriptValue obj4 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt);
1064 QCOMPARE(obj4.strictlyEquals(obj3), preferExisting);
1066 QScriptValue obj5 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
1067 QVERIFY(!obj5.strictlyEquals(obj4));
1068 QScriptValue obj6 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt);
1069 QCOMPARE(obj6.strictlyEquals(obj5), preferExisting);
1071 QScriptValue obj7 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
1072 QScriptEngine::ExcludeSuperClassMethods | opt);
1073 QVERIFY(!obj7.strictlyEquals(obj6));
1074 QScriptValue obj8 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership,
1075 QScriptEngine::ExcludeSuperClassMethods | opt);
1076 QCOMPARE(obj8.strictlyEquals(obj7), preferExisting);
1081 #if 0 // FIXME: No prototype API in QScriptEngine
1082 void tst_QJSEngine::newQObject_defaultPrototype()
1086 // newQObject() should set the default prototype, if one has been registered
1088 QScriptValue oldQObjectProto = eng.defaultPrototype(qMetaTypeId<QObject*>());
1090 QScriptValue qobjectProto = eng.newObject();
1091 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), qobjectProto);
1093 QScriptValue ret = eng.newQObject(&temp);
1094 QVERIFY(ret.prototype().equals(qobjectProto));
1096 QScriptValue tstProto = eng.newObject();
1097 int typeId = qRegisterMetaType<tst_QJSEngine*>("tst_QJSEngine*");
1098 eng.setDefaultPrototype(typeId, tstProto);
1100 QScriptValue ret = eng.newQObject(temp);
1101 QVERIFY(ret.prototype().equals(tstProto));
1104 eng.setDefaultPrototype(qMetaTypeId<QObject*>(), oldQObjectProto);
1105 eng.setDefaultPrototype(typeId, QScriptValue());
1110 void tst_QJSEngine::newQObject_promoteNonObject()
1112 #if 0 // ### FIXME: object promotion is not supported
1115 QScriptValue ret = eng.newQObject(123, this);
1116 QVERIFY(ret.isQObject());
1117 QCOMPARE(ret.toQObject(), this);
1122 void tst_QJSEngine::newQObject_promoteNonQScriptObject()
1124 #if 0 // ### FIXME: object promotion is not supported
1125 QSKIP("Promotion of non QScriptObjects kind of works (there is not difference between Object and Array, look at comments in newQObject implementation).");
1128 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newQObject(): changing class of non-QScriptObject not supported");
1129 QScriptValue ret = eng.newQObject(eng.newArray(), this);
1130 QVERIFY(ret.isUndefined());
1135 void tst_QJSEngine::newQObject_deletedEngine()
1138 QObject *ptr = new QObject();
1139 QSignalSpy spy(ptr, SIGNAL(destroyed()));
1142 object = engine.newQObject(ptr);
1143 engine.globalObject().setProperty("obj", object);
1145 QTRY_VERIFY(spy.count());
1148 #if 0 // ### FIXME: No QScript Metaobject support right now
1150 Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*)
1151 Q_SCRIPT_DECLARE_QMETAOBJECT(QWidget, QWidget*)
1154 static QScriptValue myConstructor(QScriptContext *ctx, QScriptEngine *eng)
1157 if (ctx->isCalledAsConstructor()) {
1158 obj = ctx->thisObject();
1160 obj = eng->newObject();
1161 obj.setPrototype(ctx->callee().property("prototype"));
1163 obj.setProperty("isCalledAsConstructor", QScriptValue(eng, ctx->isCalledAsConstructor()));
1167 static QScriptValue instanceofJS(const QScriptValue &inst, const QScriptValue &ctor)
1169 return inst.engine()->evaluate("(function(inst, ctor) { return inst instanceof ctor; })")
1170 .call(QScriptValueList() << inst << ctor);
1173 void tst_QJSEngine::newQMetaObject()
1177 QScriptValue qclass = eng.newQMetaObject<QObject>();
1178 QScriptValue qclass2 = eng.newQMetaObject<QWidget>();
1180 QScriptValue qclass = qScriptValueFromQMetaObject<QObject>(&eng);
1181 QScriptValue qclass2 = qScriptValueFromQMetaObject<QWidget>(&eng);
1183 QVERIFY(!qclass.isUndefined());
1184 QCOMPARE(qclass.isQMetaObject(), true);
1185 QCOMPARE(qclass.toQMetaObject(), &QObject::staticMetaObject);
1186 QCOMPARE(qclass.isCallable(), true);
1187 QVERIFY(qclass.property("prototype").isObject());
1189 QVERIFY(!qclass2.isUndefined());
1190 QCOMPARE(qclass2.isQMetaObject(), true);
1191 QCOMPARE(qclass2.toQMetaObject(), &QWidget::staticMetaObject);
1192 QCOMPARE(qclass2.isCallable(), true);
1193 QVERIFY(qclass2.property("prototype").isObject());
1195 // prototype should be QMetaObject.prototype
1196 QCOMPARE(qclass.prototype().isObject(), true);
1197 QCOMPARE(qclass2.prototype().isObject(), true);
1199 QScriptValue instance = qclass.callAsConstructor();
1200 QCOMPARE(instance.isQObject(), true);
1201 QCOMPARE(instance.toQObject()->metaObject(), qclass.toQMetaObject());
1202 QEXPECT_FAIL("", "FIXME: newQMetaObject not implemented properly yet", Abort);
1203 QVERIFY(instance.instanceOf(qclass));
1204 QVERIFY(instanceofJS(instance, qclass).strictlyEquals(true));
1206 QScriptValue instance2 = qclass2.callAsConstructor();
1207 QCOMPARE(instance2.isQObject(), true);
1208 QCOMPARE(instance2.toQObject()->metaObject(), qclass2.toQMetaObject());
1209 QVERIFY(instance2.instanceOf(qclass2));
1210 QVERIFY(instanceofJS(instance2, qclass2).strictlyEquals(true));
1211 QVERIFY(!instance2.instanceOf(qclass));
1212 QVERIFY(instanceofJS(instance2, qclass).strictlyEquals(false));
1214 QScriptValueList args;
1216 QScriptValue instance3 = qclass.callAsConstructor(args);
1217 QCOMPARE(instance3.isQObject(), true);
1218 QCOMPARE(instance3.toQObject()->parent(), instance.toQObject());
1219 QVERIFY(instance3.instanceOf(qclass));
1220 QVERIFY(instanceofJS(instance3, qclass).strictlyEquals(true));
1221 QVERIFY(!instance3.instanceOf(qclass2));
1222 QVERIFY(instanceofJS(instance3, qclass2).strictlyEquals(false));
1225 QPointer<QObject> qpointer1 = instance.toQObject();
1226 QPointer<QObject> qpointer2 = instance2.toQObject();
1227 QPointer<QObject> qpointer3 = instance3.toQObject();
1233 // verify that AutoOwnership is in effect
1234 instance = QScriptValue();
1235 collectGarbage_helper(eng);
1237 QVERIFY(!qpointer1);
1239 QVERIFY(!qpointer3); // was child of instance
1241 QVERIFY(instance.toQObject() == 0);
1242 QVERIFY(instance3.toQObject() == 0); // was child of instance
1243 QVERIFY(instance2.toQObject() != 0);
1244 instance2 = QScriptValue();
1245 collectGarbage_helper(eng);
1246 QVERIFY(instance2.toQObject() == 0);
1248 // with custom constructor
1249 QScriptValue ctor = eng.newFunction(myConstructor);
1250 QScriptValue qclass3 = eng.newQMetaObject(&QObject::staticMetaObject, ctor);
1251 QVERIFY(qclass3.property("prototype").equals(ctor.property("prototype")));
1253 QScriptValue ret = qclass3.call();
1254 QVERIFY(ret.isObject());
1255 QVERIFY(ret.property("isCalledAsConstructor").isBool());
1256 QVERIFY(!ret.property("isCalledAsConstructor").toBool());
1257 QVERIFY(ret.instanceOf(qclass3));
1258 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1259 QVERIFY(!ret.instanceOf(qclass));
1260 QVERIFY(instanceofJS(ret, qclass).strictlyEquals(false));
1263 QScriptValue ret = qclass3.callAsConstructor();
1264 QVERIFY(ret.isObject());
1265 QVERIFY(ret.property("isCalledAsConstructor").isBool());
1266 QVERIFY(ret.property("isCalledAsConstructor").toBool());
1267 QVERIFY(ret.instanceOf(qclass3));
1268 QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true));
1269 QVERIFY(!ret.instanceOf(qclass2));
1270 QVERIFY(instanceofJS(ret, qclass2).strictlyEquals(false));
1274 qclass2.setProperty("prototype", qclass.callAsConstructor());
1275 QVERIFY(qclass2.callAsConstructor().instanceOf(qclass));
1276 QVERIFY(instanceofJS(qclass2.callAsConstructor(), qclass).strictlyEquals(true));
1278 // with meta-constructor
1279 QScriptValue qclass4 = eng.newQMetaObject(&QObject::staticMetaObject);
1281 QScriptValue inst = qclass4.callAsConstructor();
1282 QVERIFY(inst.isQObject());
1283 QVERIFY(inst.toQObject() != 0);
1284 QCOMPARE(inst.toQObject()->parent(), (QObject*)0);
1285 QVERIFY(inst.instanceOf(qclass4));
1286 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1287 QVERIFY(!inst.instanceOf(qclass3));
1288 QVERIFY(instanceofJS(inst, qclass3).strictlyEquals(false));
1292 QScriptValue inst = qclass4.callAsConstructor(QScriptValueList() << eng.newQObject(&temp));
1293 QVERIFY(inst.isQObject());
1294 QVERIFY(inst.toQObject() != 0);
1295 QCOMPARE(inst.toQObject()->parent(), (QObject*)this);
1296 QVERIFY(inst.instanceOf(qclass4));
1297 QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true));
1298 QVERIFY(!inst.instanceOf(qclass2));
1299 QVERIFY(instanceofJS(inst, qclass2).strictlyEquals(false));
1304 #if 0 // ###FIXME: No activation object support
1305 void tst_QJSEngine::newActivationObject()
1307 QSKIP("internal function not implemented in JSC-based back-end");
1309 QScriptValue act = eng.newActivationObject();
1310 QEXPECT_FAIL("", "", Continue);
1311 QVERIFY(!act.isUndefined());
1312 QEXPECT_FAIL("", "", Continue);
1313 QCOMPARE(act.isObject(), true);
1314 QVERIFY(!act.isCallable());
1315 QScriptValue v(&eng, 123);
1316 act.setProperty("prop", v);
1317 QEXPECT_FAIL("", "", Continue);
1318 QCOMPARE(act.property("prop").strictlyEquals(v), true);
1319 QVERIFY(act.scope().isUndefined());
1320 QEXPECT_FAIL("", "", Continue);
1321 QVERIFY(act.prototype().isNull());
1325 #if 0 // ###FIXME: No setGlobalObject support - yay
1326 void tst_QJSEngine::getSetGlobalObjectSimple()
1328 QScriptEngine engine;
1329 QScriptValue object = engine.newObject();
1330 object.setProperty("foo", 123);
1331 engine.evaluate("var bar = 100");
1332 engine.setGlobalObject(object);
1333 engine.evaluate("rab = 100");
1334 QVERIFY(!engine.globalObject().property("rab").isUndefined());
1335 QVERIFY(!engine.globalObject().property("foo").isUndefined());
1336 QVERIFY(engine.globalObject().property("bar").isUndefined());
1339 void tst_QJSEngine::getSetGlobalObject()
1342 QScriptValue glob = eng.globalObject();
1343 glob = QScriptValue(); // kill reference to old global object
1344 collectGarbage_helper(eng);
1346 glob = eng.globalObject();
1347 QVERIFY(!glob.isUndefined());
1348 QCOMPARE(glob.isObject(), true);
1349 QVERIFY(!glob.isCallable());
1350 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(glob));
1351 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(glob));
1352 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1353 QCOMPARE(glob.toString(), QString::fromLatin1("[object global]"));
1354 // prototype should be Object.prototype
1355 QVERIFY(!glob.prototype().isUndefined());
1356 QCOMPARE(glob.prototype().isObject(), true);
1357 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1358 QCOMPARE(glob.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true);
1360 eng.setGlobalObject(glob);
1361 QVERIFY(eng.globalObject().equals(glob));
1362 eng.setGlobalObject(123);
1363 QVERIFY(eng.globalObject().equals(glob));
1365 QScriptValue obj = eng.newObject();
1366 eng.setGlobalObject(obj);
1367 QVERIFY(eng.globalObject().strictlyEquals(obj));
1368 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1369 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1370 QVERIFY(eng.evaluate("this").strictlyEquals(obj));
1371 QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue);
1372 QCOMPARE(eng.globalObject().toString(), QString::fromLatin1("[object global]"));
1374 collectGarbage_helper(eng);
1375 glob = QScriptValue(); // kill reference to old global object
1376 collectGarbage_helper(eng);
1377 obj = eng.newObject();
1378 eng.setGlobalObject(obj);
1379 QVERIFY(eng.globalObject().strictlyEquals(obj));
1380 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1381 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1383 collectGarbage_helper(eng);
1384 QVERIFY(eng.globalObject().strictlyEquals(obj));
1385 QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj));
1386 QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj));
1388 QVERIFY(obj.property("foo").isUndefined());
1389 eng.evaluate("var foo = 123");
1391 QScriptValue ret = obj.property("foo");
1392 QVERIFY(ret.isNumber());
1393 QCOMPARE(ret.toInt(), 123);
1396 QVERIFY(obj.property("bar").isUndefined());
1397 eng.evaluate("bar = 456");
1399 QScriptValue ret = obj.property("bar");
1400 QVERIFY(ret.isNumber());
1401 QCOMPARE(ret.toInt(), 456);
1404 QVERIFY(obj.property("baz").isUndefined());
1405 eng.evaluate("this['baz'] = 789");
1407 QScriptValue ret = obj.property("baz");
1408 QVERIFY(ret.isNumber());
1409 QCOMPARE(ret.toInt(), 789);
1413 QScriptValue ret = eng.evaluate("(function() { return this; })()");
1414 QVERIFY(ret.strictlyEquals(obj));
1419 QScriptValue ret = eng.evaluate("delete foo");
1420 QVERIFY(ret.isBool());
1421 QVERIFY(ret.toBool());
1422 QVERIFY(obj.property("foo").isUndefined());
1425 // Getter/setter property.
1426 //the custom global object have an interceptor
1427 QVERIFY(eng.evaluate("this.__defineGetter__('oof', function() { return this.bar; })").isUndefined());
1428 QVERIFY(eng.evaluate("this.__defineSetter__('oof', function(v) { this.bar = v; })").isUndefined());
1429 QVERIFY(eng.evaluate("this.__lookupGetter__('oof')").isCallable());
1430 QVERIFY(eng.evaluate("this.__lookupSetter__('oof')").isCallable());
1431 eng.evaluate("oof = 123");
1432 QVERIFY(eng.evaluate("oof").equals(obj.property("bar")));
1436 QScriptValue ret = eng.evaluate("a = []; for (var p in this) a.push(p); a");
1437 QCOMPARE(ret.toString(), QString::fromLatin1("bar,baz,oof,p,a"));
1442 #if 0 // ###FIXME: no c-style callbacks
1443 static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *)
1445 if (ctx->argumentCount() > 0)
1446 ctx->thisObject().setProperty("foo", ctx->argument(0));
1447 return ctx->thisObject().property("foo");
1451 void tst_QJSEngine::globalObjectProperties()
1453 QSKIP("Test failing - QTBUG-22238");
1454 // See ECMA-262 Section 15.1, "The Global Object".
1457 QJSValue global = eng.globalObject();
1459 QVERIFY(global.property("NaN").isNumber());
1460 QVERIFY(qIsNaN(global.property("NaN").toNumber()));
1462 QVERIFY(global.property("Infinity").isNumber());
1463 QVERIFY(qIsInf(global.property("Infinity").toNumber()));
1465 QVERIFY(global.property("undefined").isUndefined());
1467 QVERIFY(global.property("eval").isCallable());
1469 QVERIFY(global.property("parseInt").isCallable());
1471 QVERIFY(global.property("parseFloat").isCallable());
1473 QVERIFY(global.property("isNaN").isCallable());
1475 QVERIFY(global.property("isFinite").isCallable());
1477 QVERIFY(global.property("decodeURI").isCallable());
1479 QVERIFY(global.property("decodeURIComponent").isCallable());
1481 QVERIFY(global.property("encodeURI").isCallable());
1483 QVERIFY(global.property("encodeURIComponent").isCallable());
1485 QVERIFY(global.property("Object").isCallable());
1486 QVERIFY(global.property("Function").isCallable());
1487 QVERIFY(global.property("Array").isCallable());
1488 QVERIFY(global.property("String").isCallable());
1489 QVERIFY(global.property("Boolean").isCallable());
1490 QVERIFY(global.property("Number").isCallable());
1491 QVERIFY(global.property("Date").isCallable());
1492 QVERIFY(global.property("RegExp").isCallable());
1493 QVERIFY(global.property("Error").isCallable());
1494 QVERIFY(global.property("EvalError").isCallable());
1495 QVERIFY(global.property("RangeError").isCallable());
1496 QVERIFY(global.property("ReferenceError").isCallable());
1497 QVERIFY(global.property("SyntaxError").isCallable());
1498 QVERIFY(global.property("TypeError").isCallable());
1499 QVERIFY(global.property("URIError").isCallable());
1500 QVERIFY(global.property("Math").isObject());
1501 QVERIFY(!global.property("Math").isCallable());
1504 void tst_QJSEngine::globalObjectEquals()
1507 QJSValue o = eng.globalObject();
1508 QVERIFY(o.strictlyEquals(eng.globalObject()));
1509 QVERIFY(o.equals(eng.globalObject()));
1512 void tst_QJSEngine::globalObjectProperties_enumerate()
1514 QSKIP("Test failing - QTBUG-22238");
1516 QJSValue global = eng.globalObject();
1518 QSet<QString> expectedNames;
1526 << "encodeURIComponent"
1543 << "decodeURIComponent"
1553 << "execScript" //execScript for IE compatibility.
1555 QSet<QString> actualNames;
1557 QJSValueIterator it(global);
1558 while (it.hasNext()) {
1560 actualNames.insert(it.name());
1564 QSet<QString> remainingNames = actualNames;
1566 QSet<QString>::const_iterator it;
1567 for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) {
1569 QVERIFY(actualNames.contains(name));
1570 remainingNames.remove(name);
1573 QVERIFY(remainingNames.isEmpty());
1576 void tst_QJSEngine::createGlobalObjectProperty()
1579 QJSValue global = eng.globalObject();
1580 // create property with no attributes
1582 QString name = QString::fromLatin1("foo");
1583 QVERIFY(global.property(name).isUndefined());
1585 global.setProperty(name, val);
1586 QVERIFY(global.property(name).equals(val));
1587 global.deleteProperty(name);
1588 QVERIFY(global.property(name).isUndefined());
1590 // create property with attributes
1591 #if 0 // ###FIXME: setProperty with flags is not supported
1593 QString name = QString::fromLatin1("bar");
1594 QVERIFY(global.property(name).isUndefined());
1595 QScriptValue val(QString::fromLatin1("ciao"));
1596 QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration;
1597 global.setProperty(name, val, flags);
1598 QVERIFY(global.property(name).equals(val));
1599 //QEXPECT_FAIL("", "QTBUG-6134: custom Global Object properties don't retain attributes", Continue);
1600 global.setProperty(name, QScriptValue());
1601 QVERIFY(global.property(name).isUndefined());
1606 void tst_QJSEngine::globalObjectGetterSetterProperty()
1608 #if 0 // ###FIXME: No c-style callbacks
1609 QScriptEngine engine;
1610 QScriptValue global = engine.globalObject();
1611 global.setProperty("bar", engine.newFunction(getSetFoo),
1612 QScriptValue::PropertySetter | QScriptValue::PropertyGetter);
1613 global.setProperty("foo", 123);
1614 QVERIFY(global.property("bar").equals(global.property("foo")));
1615 QVERIFY(engine.evaluate("bar").equals(global.property("foo")));
1616 global.setProperty("bar", 456);
1617 QVERIFY(global.property("bar").equals(global.property("foo")));
1619 engine.evaluate("__defineGetter__('baz', function() { return 789; })");
1620 QVERIFY(engine.evaluate("baz").equals(789));
1621 QVERIFY(global.property("baz").equals(789));
1625 #if 0 // ###FIXME: No support for setting the global object
1626 void tst_QJSEngine::customGlobalObjectWithPrototype()
1628 for (int x = 0; x < 2; ++x) {
1629 QScriptEngine engine;
1630 QScriptValue wrap = engine.newObject();
1631 QScriptValue global = engine.globalObject();
1632 QScriptValue originalGlobalProto = global.prototype();
1634 // Set prototype before setting global object
1635 wrap.setPrototype(global);
1636 QVERIFY(wrap.prototype().strictlyEquals(global));
1637 engine.setGlobalObject(wrap);
1639 // Set prototype after setting global object
1640 engine.setGlobalObject(wrap);
1641 wrap.setPrototype(global);
1642 QVERIFY(wrap.prototype().strictlyEquals(global));
1645 QScriptValue ret = engine.evaluate("print");
1646 QVERIFY(ret.isCallable());
1647 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1650 QScriptValue ret = engine.evaluate("this.print");
1651 QVERIFY(ret.isCallable());
1652 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1655 QScriptValue ret = engine.evaluate("hasOwnProperty('print')");
1656 QVERIFY(ret.isBool());
1657 if (x) QEXPECT_FAIL("", "Why?", Continue);
1658 QVERIFY(!ret.toBool());
1661 QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')");
1662 QVERIFY(ret.isBool());
1663 if (x) QEXPECT_FAIL("", "Why?", Continue);
1664 QVERIFY(!ret.toBool());
1667 QScriptValue anotherProto = engine.newObject();
1668 anotherProto.setProperty("anotherProtoProperty", 123);
1669 global.setPrototype(anotherProto);
1671 QScriptValue ret = engine.evaluate("print");
1672 QVERIFY(ret.isCallable());
1673 QVERIFY(ret.strictlyEquals(wrap.property("print")));
1676 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1677 QVERIFY(ret.isNumber());
1678 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1681 QScriptValue ret = engine.evaluate("this.anotherProtoProperty");
1682 QVERIFY(ret.isNumber());
1683 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1686 wrap.setPrototype(anotherProto);
1688 QScriptValue ret = engine.evaluate("print");
1689 QVERIFY(ret.isError());
1690 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: print is not defined"));
1693 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1694 QVERIFY(ret.isNumber());
1695 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
1697 QVERIFY(global.prototype().strictlyEquals(anotherProto));
1699 global.setPrototype(originalGlobalProto);
1700 engine.setGlobalObject(global);
1702 QScriptValue ret = engine.evaluate("anotherProtoProperty");
1703 QVERIFY(ret.isError());
1704 QVERIFY(ret.toString().startsWith("ReferenceError: "));
1707 QScriptValue ret = engine.evaluate("print");
1708 QVERIFY(ret.isCallable());
1709 QVERIFY(ret.strictlyEquals(global.property("print")));
1711 QVERIFY(anotherProto.property("print").isUndefined());
1716 void tst_QJSEngine::globalObjectWithCustomPrototype()
1719 QJSValue proto = engine.newObject();
1720 proto.setProperty("protoProperty", 123);
1721 QJSValue global = engine.globalObject();
1722 QJSValue originalProto = global.prototype();
1723 global.setPrototype(proto);
1725 QJSValue ret = engine.evaluate("protoProperty");
1726 QEXPECT_FAIL("", "Replacing the prototype of the global object is currently unsupported (see also v8 issue 1078)", Abort);
1727 QVERIFY(ret.isNumber());
1728 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1731 QJSValue ret = engine.evaluate("this.protoProperty");
1732 QVERIFY(ret.isNumber());
1733 QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
1736 QJSValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
1737 QVERIFY(ret.isBool());
1738 QVERIFY(!ret.toBool());
1741 QJSValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
1742 QVERIFY(ret.isBool());
1743 QVERIFY(!ret.toBool());
1746 // Custom prototype set from JS
1748 QJSValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
1749 QVERIFY(ret.isNumber());
1750 QVERIFY(ret.strictlyEquals(global.property("a")));
1754 void tst_QJSEngine::builtinFunctionNames_data()
1756 QTest::addColumn<QString>("expression");
1757 QTest::addColumn<QString>("expectedName");
1759 // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects".
1761 QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt");
1762 QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat");
1763 QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN");
1764 QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite");
1765 QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI");
1766 QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent");
1767 QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI");
1768 QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent");
1769 QTest::newRow("escape") << QString("escape") << QString("escape");
1770 QTest::newRow("unescape") << QString("unescape") << QString("unescape");
1772 QTest::newRow("Array") << QString("Array") << QString("Array");
1773 QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString");
1774 QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString");
1775 QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat");
1776 QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join");
1777 QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop");
1778 QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push");
1779 QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse");
1780 QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift");
1781 QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice");
1782 QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort");
1783 QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice");
1784 QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift");
1786 QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean");
1787 QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString");
1789 QTest::newRow("Date") << QString("Date") << QString("Date");
1790 QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString");
1791 QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString");
1792 QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString");
1793 QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString");
1794 QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString");
1795 QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString");
1796 QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf");
1797 QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime");
1798 QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear");
1799 QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear");
1800 QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear");
1801 QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth");
1802 QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth");
1803 QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate");
1804 QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate");
1805 QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay");
1806 QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay");
1807 QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours");
1808 QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours");
1809 QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes");
1810 QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes");
1811 QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds");
1812 QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds");
1813 QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds");
1814 QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds");
1815 QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset");
1816 QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime");
1817 QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds");
1818 QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds");
1819 QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds");
1820 QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds");
1821 QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes");
1822 QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes");
1823 QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours");
1824 QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours");
1825 QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate");
1826 QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate");
1827 QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth");
1828 QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth");
1829 QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear");
1830 QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear");
1831 QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear");
1832 QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString");
1833 QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString");
1835 QTest::newRow("Error") << QString("Error") << QString("Error");
1836 // QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace");
1837 QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString");
1839 QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError");
1840 QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError");
1841 QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError");
1842 QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError");
1843 QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError");
1844 QTest::newRow("URIError") << QString("URIError") << QString("URIError");
1846 QTest::newRow("Function") << QString("Function") << QString("Function");
1847 QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString");
1848 QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply");
1849 QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call");
1850 /* In V8, those function are only there for signals
1851 QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect");
1852 QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");*/
1854 QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs");
1855 QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos");
1856 QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin");
1857 QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan");
1858 QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2");
1859 QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil");
1860 QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos");
1861 QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp");
1862 QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor");
1863 QTest::newRow("Math.log") << QString("Math.log") << QString("log");
1864 QTest::newRow("Math.max") << QString("Math.max") << QString("max");
1865 QTest::newRow("Math.min") << QString("Math.min") << QString("min");
1866 QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
1867 QTest::newRow("Math.random") << QString("Math.random") << QString("random");
1868 QTest::newRow("Math.round") << QString("Math.round") << QString("round");
1869 QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
1870 QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
1871 QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
1873 QTest::newRow("Number") << QString("Number") << QString("Number");
1874 QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
1875 QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
1876 QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
1877 QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed");
1878 QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential");
1879 QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision");
1881 QTest::newRow("Object") << QString("Object") << QString("Object");
1882 QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString");
1883 QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString");
1884 QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf");
1885 QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty");
1886 QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf");
1887 QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable");
1888 QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__");
1889 QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__");
1891 QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp");
1892 QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec");
1893 QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test");
1894 QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString");
1896 QTest::newRow("String") << QString("String") << QString("String");
1897 QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString");
1898 QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf");
1899 QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
1900 QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
1901 QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
1902 QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
1903 QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
1904 QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
1905 QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match");
1906 QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace");
1907 QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
1908 QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
1909 QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
1910 QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
1911 QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
1912 QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
1913 QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase");
1914 QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase");
1917 void tst_QJSEngine::builtinFunctionNames()
1919 QFETCH(QString, expression);
1920 QFETCH(QString, expectedName);
1922 // The "name" property is actually non-standard, but JSC supports it.
1923 QJSValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression));
1924 QVERIFY(ret.isString());
1925 QCOMPARE(ret.toString(), expectedName);
1928 #if 0 // ###FIXME: No syntax checking result
1929 void tst_QJSEngine::checkSyntax_data()
1931 QTest::addColumn<QString>("code");
1932 QTest::addColumn<int>("expectedState");
1933 QTest::addColumn<int>("errorLineNumber");
1934 QTest::addColumn<int>("errorColumnNumber");
1935 QTest::addColumn<QString>("errorMessage");
1938 << QString("0") << int(QScriptSyntaxCheckResult::Valid)
1940 QTest::newRow("if (")
1941 << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate)
1942 << 0 << -1 << "Uncaught SyntaxError: Unexpected end of input";
1943 QTest::newRow("if else")
1944 << QString("\nif else") << int(QScriptSyntaxCheckResult::Error)
1945 << 2 << 3 << "Uncaught SyntaxError: Unexpected token else";
1946 QTest::newRow("foo[")
1947 << QString("foo[") << int(QScriptSyntaxCheckResult::Intermediate)
1948 << 1 << 4 << "Uncaught SyntaxError: Unexpected end of input";
1949 QTest::newRow("foo['bar']")
1950 << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid)
1954 << QString("/*") << int(QScriptSyntaxCheckResult::Error)
1955 << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
1956 QTest::newRow("/*\nMy comment")
1957 << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Error)
1958 << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
1959 QTest::newRow("/*\nMy comment */\nfoo = 10")
1960 << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid)
1962 QTest::newRow("foo = 10 /*")
1963 << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Error)
1964 << 1 << 9 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
1965 QTest::newRow("foo = 10; /*")
1966 << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Error)
1967 << 1 << 10 << "Uncaught SyntaxError: Unexpected token ILLEGAL";
1968 QTest::newRow("foo = 10 /* My comment */")
1969 << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid)
1972 QTest::newRow("/=/")
1973 << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1974 QTest::newRow("/=/g")
1975 << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1976 QTest::newRow("/a/")
1977 << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1978 QTest::newRow("/a/g")
1979 << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << "";
1982 void tst_QJSEngine::checkSyntax()
1984 QFETCH(QString, code);
1985 QFETCH(int, expectedState);
1986 QFETCH(int, errorLineNumber);
1987 QFETCH(int, errorColumnNumber);
1988 QFETCH(QString, errorMessage);
1990 QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code);
1991 QCOMPARE(int(result.state()), expectedState);
1992 QCOMPARE(result.errorLineNumber(), errorLineNumber);
1993 QCOMPARE(result.errorColumnNumber(), errorColumnNumber);
1994 QCOMPARE(result.errorMessage(), errorMessage);
1998 QScriptSyntaxCheckResult copy = result;
1999 QCOMPARE(copy.state(), result.state());
2000 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
2001 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
2002 QCOMPARE(copy.errorMessage(), result.errorMessage());
2005 QScriptSyntaxCheckResult copy(result);
2006 QCOMPARE(copy.state(), result.state());
2007 QCOMPARE(copy.errorLineNumber(), result.errorLineNumber());
2008 QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber());
2009 QCOMPARE(copy.errorMessage(), result.errorMessage());
2014 #if 0 // ###FIXME: No support for canEvaluate
2015 void tst_QJSEngine::canEvaluate_data()
2017 QTest::addColumn<QString>("code");
2018 QTest::addColumn<bool>("expectSuccess");
2020 QTest::newRow("") << QString("") << true;
2021 QTest::newRow("0") << QString("0") << true;
2022 QTest::newRow("!") << QString("!\n") << false;
2023 QTest::newRow("if (") << QString("if (\n") << false;
2024 QTest::newRow("if (10) //") << QString("if (10) //\n") << false;
2025 QTest::newRow("a = 1; if (") << QString("a = 1;\nif (\n") << false;
2026 QTest::newRow("./test.js") << QString("./test.js\n") << true;
2027 QTest::newRow("if (0) print(1)") << QString("if (0)\nprint(1)\n") << true;
2028 QTest::newRow("0 = ") << QString("0 = \n") << false;
2029 QTest::newRow("0 = 0") << QString("0 = 0\n") << true;
2030 QTest::newRow("foo[") << QString("foo[") << false;
2031 QTest::newRow("foo[") << QString("foo[\n") << false;
2032 QTest::newRow("foo['bar']") << QString("foo['bar']") << true;
2034 //v8 does thinks unterminated comments are error rather than unfinished
2035 // QTest::newRow("/*") << QString("/*") << false;
2036 // QTest::newRow("/*\nMy comment") << QString("/*\nMy comment") << false;
2037 QTest::newRow("/*\nMy comment */\nfoo = 10") << QString("/*\nMy comment */\nfoo = 10") << true;
2038 // QTest::newRow("foo = 10 /*") << QString("foo = 10 /*") << false;
2039 // QTest::newRow("foo = 10; /*") << QString("foo = 10; /*") << false;
2040 QTest::newRow("foo = 10 /* My comment */") << QString("foo = 10 /* My comment */") << true;
2042 QTest::newRow("/=/") << QString("/=/") << true;
2043 QTest::newRow("/=/g") << QString("/=/g") << true;
2044 QTest::newRow("/a/") << QString("/a/") << true;
2045 QTest::newRow("/a/g") << QString("/a/g") << true;
2048 void tst_QJSEngine::canEvaluate()
2050 QFETCH(QString, code);
2051 QFETCH(bool, expectSuccess);
2054 QCOMPARE(eng.canEvaluate(code), expectSuccess);
2058 void tst_QJSEngine::evaluate_data()
2060 QTest::addColumn<QString>("code");
2061 QTest::addColumn<int>("lineNumber");
2062 QTest::addColumn<bool>("expectHadError");
2063 QTest::addColumn<int>("expectErrorLineNumber");
2065 QTest::newRow("(newline)") << QString("\n") << -1 << false << -1;
2066 QTest::newRow("0 //") << QString("0 //") << -1 << false << -1;
2067 QTest::newRow("/* */") << QString("/* */") << -1 << false << -1;
2068 QTest::newRow("//") << QString("//") << -1 << false << -1;
2069 QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1;
2070 QTest::newRow("(empty)") << QString("") << -1 << false << -1;
2071 QTest::newRow("0") << QString("0") << -1 << false << -1;
2072 QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2;
2073 QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1;
2074 QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2;
2076 QTest::newRow("f()") << QString("function f()\n"
2079 " var b=\";\n" // here's the error
2084 QTest::newRow("0") << QString("0") << 10 << false << -1;
2085 QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 12;
2086 QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1;
2087 QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12;
2089 QTest::newRow("f()") << QString("function f()\n"
2093 " var b=\";\n" // here's the error
2096 << 10 << true << 15;
2097 QTest::newRow("functionThatDoesntExist()")
2098 << QString(";\n;\n;\nfunctionThatDoesntExist()")
2100 QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }")
2101 << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }")
2103 QTest::newRow("duplicateLabel: { duplicateLabel: ; }")
2104 << QString("duplicateLabel: { duplicateLabel: ; }")
2105 << 12 << true << 12;
2107 QTest::newRow("/=/") << QString("/=/") << -1 << false << -1;
2108 QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1;
2109 QTest::newRow("/a/") << QString("/a/") << -1 << false << -1;
2110 QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1;
2111 QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1;
2112 QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1;
2115 void tst_QJSEngine::evaluate()
2117 QFETCH(QString, code);
2118 QFETCH(int, lineNumber);
2119 QFETCH(bool, expectHadError);
2120 QFETCH(int, expectErrorLineNumber);
2124 if (lineNumber != -1)
2125 ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber);
2127 ret = eng.evaluate(code);
2128 QCOMPARE(ret.isError(), expectHadError);
2129 #if 0 // ###FIXME: No support for the line number of an uncaught exception
2130 QEXPECT_FAIL("f()", "SyntaxError do not report line number", Continue);
2131 QEXPECT_FAIL("duplicateLabel: { duplicateLabel: ; }", "SyntaxError do not report line number", Continue);
2132 QCOMPARE(eng.uncaughtExceptionLineNumber(), expectErrorLineNumber);
2134 if (ret.isError()) {
2135 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
2136 QVERIFY(ret.property("lineNumber").strictlyEquals(eng.toScriptValue(expectErrorLineNumber)));
2138 #if 0 // ###FIXME: No support for the backtrace of an uncaught exception
2139 QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
2144 #if 0 // ###FIXME: no support for c-style callbacks
2145 static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng)
2147 QScriptValue result = eng->newObject();
2148 eng->evaluate("var bar = 'local';");
2149 result.setProperty("thisObjectIdBefore", ctx->thisObject().property("id"));
2150 QScriptValue evaluatedThisObject = eng->evaluate("this");
2151 result.setProperty("thisObjectIdAfter", ctx->thisObject().property("id"));
2152 result.setProperty("evaluatedThisObjectId", evaluatedThisObject.property("id"));
2153 result.setProperty("local_bar", eng->evaluate("bar"));
2158 // Tests that nested evaluate uses the "this" that was passed.
2159 void tst_QJSEngine::nestedEvaluate()
2162 QScriptValue fun = eng.newFunction(eval_nested);
2163 eng.globalObject().setProperty("fun", fun);
2164 // From JS function call
2166 QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()");
2167 QCOMPARE(result.property("local_bar").toString(), QString("local"));
2168 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
2169 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
2170 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
2171 QScriptValue bar = eng.evaluate("bar"); // Was introduced in local scope.
2172 QVERIFY(bar.isError());
2173 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
2175 // From QScriptValue::call()
2177 QScriptValue result = fun.callWithInstance(eng.evaluate("p = { id:'foo' }") , QScriptValueList() );
2178 QCOMPARE(result.property("local_bar").toString(), QString("local"));
2179 QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo"));
2180 QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo"));
2181 QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo"));
2182 QScriptValue bar = eng.evaluate("bar");
2183 QVERIFY(bar.isError());
2184 QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError")));
2189 #if 0 // ### FIXME: No c-style callbacks
2190 void tst_QJSEngine::uncaughtException()
2193 QScriptValue fun = eng.newFunction(myFunction);
2194 QScriptValue throwFun = eng.newFunction(myThrowingFunction);
2195 for (int x = -1; x < 2; ++x) {
2197 QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", /*fileName=*/QString(), /*lineNumber=*/x);
2198 QVERIFY(eng.hasUncaughtException());
2199 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2200 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2201 (void)ret.toString();
2202 QVERIFY(eng.hasUncaughtException());
2203 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2204 QVERIFY(fun.call().isNull());
2205 QVERIFY(eng.hasUncaughtException());
2206 QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2);
2207 QVERIFY(eng.uncaughtException().strictlyEquals(ret));
2208 eng.clearExceptions();
2209 QVERIFY(!eng.hasUncaughtException());
2210 QCOMPARE(eng.uncaughtExceptionLineNumber(), -1);
2211 QVERIFY(eng.uncaughtException().isUndefined());
2213 eng.evaluate("2 = 3");
2214 QVERIFY(eng.hasUncaughtException());
2215 QScriptValue ret2 = throwFun.call();
2216 QVERIFY(ret2.isError());
2217 QVERIFY(eng.hasUncaughtException());
2218 QVERIFY(eng.uncaughtException().strictlyEquals(ret2));
2219 QCOMPARE(eng.uncaughtExceptionLineNumber(), 0);
2220 eng.clearExceptions();
2221 QVERIFY(!eng.hasUncaughtException());
2222 eng.evaluate("1 + 2");
2223 QVERIFY(!eng.hasUncaughtException());
2226 QScriptValue ret = eng.evaluate("a = 10");
2227 QVERIFY(!eng.hasUncaughtException());
2228 QVERIFY(eng.uncaughtException().isUndefined());
2231 QScriptValue ret = eng.evaluate("1 = 2");
2232 QVERIFY(eng.hasUncaughtException());
2233 eng.clearExceptions();
2234 QVERIFY(!eng.hasUncaughtException());
2237 eng.globalObject().setProperty("throwFun", throwFun);
2238 eng.evaluate("1;\nthrowFun();");
2239 QVERIFY(eng.hasUncaughtException());
2240 QCOMPARE(eng.uncaughtExceptionLineNumber(), 2);
2241 eng.clearExceptions();
2242 QVERIFY(!eng.hasUncaughtException());
2248 void tst_QJSEngine::errorMessage_QT679()
2251 engine.globalObject().setProperty("foo", 15);
2252 QJSValue error = engine.evaluate("'hello world';\nfoo.bar.blah");
2253 QVERIFY(error.isError());
2254 QVERIFY(error.toString().startsWith(QString::fromLatin1("TypeError: ")));
2260 Foo() : x(-1), y(-1) { }
2263 Q_DECLARE_METATYPE(Foo)
2264 Q_DECLARE_METATYPE(Foo*)
2266 #if 0 // FIXME: No prototype API in QScriptEngine
2267 void tst_QJSEngine::getSetDefaultPrototype_int()
2271 QScriptValue object = eng.newObject();
2272 QVERIFY(eng.defaultPrototype(qMetaTypeId<int>()).isUndefined());
2273 eng.setDefaultPrototype(qMetaTypeId<int>(), object);
2274 QCOMPARE(eng.defaultPrototype(qMetaTypeId<int>()).strictlyEquals(object), true);
2275 QScriptValue value = eng.newVariant(int(123));
2276 QCOMPARE(value.prototype().isObject(), true);
2277 QCOMPARE(value.prototype().strictlyEquals(object), true);
2279 eng.setDefaultPrototype(qMetaTypeId<int>(), QScriptValue());
2280 QVERIFY(eng.defaultPrototype(qMetaTypeId<int>()).isUndefined());
2281 QScriptValue value2 = eng.newVariant(int(123));
2282 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2285 void tst_QJSEngine::getSetDefaultPrototype_customType()
2289 QScriptValue object = eng.newObject();
2290 QVERIFY(eng.defaultPrototype(qMetaTypeId<Foo>()).isUndefined());
2291 eng.setDefaultPrototype(qMetaTypeId<Foo>(), object);
2292 QCOMPARE(eng.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(object), true);
2293 QScriptValue value = eng.newVariant(qVariantFromValue(Foo()));
2294 QCOMPARE(value.prototype().isObject(), true);
2295 QCOMPARE(value.prototype().strictlyEquals(object), true);
2297 eng.setDefaultPrototype(qMetaTypeId<Foo>(), QScriptValue());
2298 QVERIFY(eng.defaultPrototype(qMetaTypeId<Foo>()).isUndefined());
2299 QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo()));
2300 QCOMPARE(value2.prototype().strictlyEquals(object), false);
2304 static QJSValue fooToScriptValue(QJSEngine *eng, const Foo &foo)
2306 QJSValue obj = eng->newObject();
2307 obj.setProperty("x", eng->toScriptValue(foo.x));
2308 obj.setProperty("y", eng->toScriptValue(foo.y));
2312 static void fooFromScriptValue(const QJSValue &value, Foo &foo)
2314 foo.x = value.property("x").toInt();
2315 foo.y = value.property("y").toInt();
2318 static QJSValue fooToScriptValueV2(QJSEngine *eng, const Foo &foo)
2320 return eng->toScriptValue(foo.x);
2323 static void fooFromScriptValueV2(const QJSValue &value, Foo &foo)
2325 foo.x = value.toInt();
2328 Q_DECLARE_METATYPE(QLinkedList<QString>)
2329 Q_DECLARE_METATYPE(QList<Foo>)
2330 Q_DECLARE_METATYPE(QVector<QChar>)
2331 Q_DECLARE_METATYPE(QStack<int>)
2332 Q_DECLARE_METATYPE(QQueue<char>)
2333 Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
2335 void tst_QJSEngine::valueConversion_basic()
2339 QJSValue num = eng.toScriptValue(123);
2340 QCOMPARE(num.isNumber(), true);
2341 QCOMPARE(num.strictlyEquals(eng.toScriptValue(123)), true);
2343 int inum = eng.fromScriptValue<int>(num);
2344 QCOMPARE(inum, 123);
2346 QString snum = eng.fromScriptValue<QString>(num);
2347 QCOMPARE(snum, QLatin1String("123"));
2350 QJSValue num = eng.toScriptValue(123);
2351 QCOMPARE(num.isNumber(), true);
2352 QCOMPARE(num.strictlyEquals(eng.toScriptValue(123)), true);
2354 int inum = eng.fromScriptValue<int>(num);
2355 QCOMPARE(inum, 123);
2357 QString snum = eng.fromScriptValue<QString>(num);
2358 QCOMPARE(snum, QLatin1String("123"));
2361 QJSValue num = eng.toScriptValue(123);
2362 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
2363 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
2364 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
2365 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
2366 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
2367 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
2368 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
2369 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
2373 QCOMPARE(eng.fromScriptValue<char>(num), char(123));
2374 QCOMPARE(eng.fromScriptValue<unsigned char>(num), (unsigned char)(123));
2375 QCOMPARE(eng.fromScriptValue<short>(num), short(123));
2376 QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
2377 QCOMPARE(eng.fromScriptValue<float>(num), float(123));
2378 QCOMPARE(eng.fromScriptValue<double>(num), double(123));
2379 QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
2380 QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
2384 QJSValue num = eng.toScriptValue(Q_INT64_C(0x100000000));
2385 QCOMPARE(eng.fromScriptValue<qlonglong>(num), Q_INT64_C(0x100000000));
2386 QCOMPARE(eng.fromScriptValue<qulonglong>(num), Q_UINT64_C(0x100000000));
2390 QChar c = QLatin1Char('c');
2391 QJSValue str = eng.toScriptValue(QString::fromLatin1("ciao"));
2392 QCOMPARE(eng.fromScriptValue<QChar>(str), c);
2393 QJSValue code = eng.toScriptValue(c.unicode());
2394 QCOMPARE(eng.fromScriptValue<QChar>(code), c);
2395 QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c);
2398 QVERIFY(eng.toScriptValue(static_cast<void *>(0)).isNull());
2401 #if 0 // FIXME: No API for custom types
2402 void tst_QJSEngine::valueConversion_customType()
2406 // a type that we don't have built-in conversion of
2407 // (it's stored as a variant)
2408 QTime tm(1, 2, 3, 4);
2409 QScriptValue val = eng.toScriptValue(tm);
2410 QCOMPARE(eng.fromScriptValue<QTime>(val), tm);
2417 QScriptValue fooVal = eng.toScriptValue(foo);
2418 QCOMPARE(fooVal.isVariant(), true);
2420 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
2421 QCOMPARE(foo2.x, foo.x);
2422 QCOMPARE(foo2.y, foo.y);
2425 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValue, fooFromScriptValue);
2431 QScriptValue fooVal = eng.toScriptValue(foo);
2432 QCOMPARE(fooVal.isObject(), true);
2433 QVERIFY(fooVal.prototype().strictlyEquals(eng.evaluate("Object.prototype")));
2434 QCOMPARE(fooVal.property("x").strictlyEquals(QScriptValue(&eng, 12)), true);
2435 QCOMPARE(fooVal.property("y").strictlyEquals(QScriptValue(&eng, 34)), true);
2436 fooVal.setProperty("x", QScriptValue(&eng, 56));
2437 fooVal.setProperty("y", QScriptValue(&eng, 78));
2439 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
2440 QCOMPARE(foo2.x, 56);
2441 QCOMPARE(foo2.y, 78);
2443 QScriptValue fooProto = eng.newObject();
2444 eng.setDefaultPrototype(qMetaTypeId<Foo>(), fooProto);
2445 QScriptValue fooVal2 = eng.toScriptValue(foo2);
2446 QVERIFY(fooVal2.prototype().strictlyEquals(fooProto));
2447 QVERIFY(fooVal2.property("x").strictlyEquals(QScriptValue(&eng, 56)));
2448 QVERIFY(fooVal2.property("y").strictlyEquals(QScriptValue(&eng, 78)));
2452 void tst_QJSEngine::valueConversion_sequence()
2455 qScriptRegisterSequenceMetaType<QLinkedList<QString> >(&eng);
2458 QLinkedList<QString> lst;
2459 lst << QLatin1String("foo") << QLatin1String("bar");
2460 QScriptValue lstVal = eng.toScriptValue(lst);
2461 QCOMPARE(lstVal.isArray(), true);
2462 QCOMPARE(lstVal.property("length").toInt(), 2);
2463 QCOMPARE(lstVal.property("0").isString(), true);
2464 QCOMPARE(lstVal.property("0").toString(), QLatin1String("foo"));
2465 QCOMPARE(lstVal.property("1").isString(), true);
2466 QCOMPARE(lstVal.property("1").toString(), QLatin1String("bar"));
2469 qScriptRegisterSequenceMetaType<QList<Foo> >(&eng);
2470 qScriptRegisterSequenceMetaType<QStack<int> >(&eng);
2471 qScriptRegisterSequenceMetaType<QVector<QChar> >(&eng);
2472 qScriptRegisterSequenceMetaType<QQueue<char> >(&eng);
2473 qScriptRegisterSequenceMetaType<QLinkedList<QStack<int> > >(&eng);
2476 QLinkedList<QStack<int> > lst;
2477 QStack<int> first; first << 13 << 49; lst << first;
2478 QStack<int> second; second << 99999;lst << second;
2479 QScriptValue lstVal = eng.toScriptValue(lst);
2480 QCOMPARE(lstVal.isArray(), true);
2481 QCOMPARE(lstVal.property("length").toInt(), 2);
2482 QCOMPARE(lstVal.property("0").isArray(), true);
2483 QCOMPARE(lstVal.property("0").property("length").toInt(), 2);
2484 QCOMPARE(lstVal.property("0").property("0").toInt(), first.at(0));
2485 QCOMPARE(lstVal.property("0").property("1").toInt(), first.at(1));
2486 QCOMPARE(lstVal.property("1").isArray(), true);
2487 QCOMPARE(lstVal.property("1").property("length").toInt(), 1);
2488 QCOMPARE(lstVal.property("1").property("0").toInt(), second.at(0));
2489 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("0")), first);
2490 QCOMPARE(qscriptvalue_cast<QStack<int> >(lstVal.property("1")), second);
2491 QCOMPARE(qscriptvalue_cast<QLinkedList<QStack<int> > >(lstVal), lst);
2498 QScriptValue v = eng.toScriptValue(&foo);
2499 Foo *pfoo = qscriptvalue_cast<Foo*>(v);
2500 QCOMPARE(pfoo, &foo);
2504 QScriptValue v = eng.toScriptValue(pfoo);
2505 QCOMPARE(v.isNull(), true);
2506 QVERIFY(qscriptvalue_cast<Foo*>(v) == 0);
2510 // QList<int> and QObjectList should be converted from/to arrays by default
2514 QScriptValue val = eng.toScriptValue(lst);
2515 QVERIFY(val.isArray());
2516 QCOMPARE(val.property("length").toInt(), lst.size());
2517 QCOMPARE(val.property(0).toInt(), lst.at(0));
2518 QCOMPARE(val.property(1).toInt(), lst.at(1));
2519 QCOMPARE(val.property(2).toInt(), lst.at(2));
2521 QCOMPARE(qscriptvalue_cast<QList<int> >(val), lst);
2526 QScriptValue val = eng.toScriptValue(lst);
2527 QVERIFY(val.isArray());
2528 QCOMPARE(val.property("length").toInt(), lst.size());
2529 QCOMPARE(val.property(0).toQObject(), (QObject *)this);
2531 QCOMPARE(qscriptvalue_cast<QObjectList>(val), lst);
2536 void tst_QJSEngine::valueConversion_QVariant()
2539 // qScriptValueFromValue() should be "smart" when the argument is a QVariant
2541 QJSValue val = eng.toScriptValue(QVariant());
2542 QVERIFY(!val.isVariant());
2543 QVERIFY(val.isUndefined());
2545 // Checking nested QVariants
2548 QVariant tmp2(QMetaType::QVariant, &tmp1);
2549 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2551 QJSValue val1 = eng.toScriptValue(tmp1);
2552 QJSValue val2 = eng.toScriptValue(tmp2);
2553 QVERIFY(val1.isUndefined());
2554 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2555 QVERIFY(!val2.isUndefined());
2556 QVERIFY(!val1.isVariant());
2557 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2558 QVERIFY(val2.isVariant());
2562 QVariant tmp2(QMetaType::QVariant, &tmp1);
2563 QVariant tmp3(QMetaType::QVariant, &tmp2);
2564 QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int);
2565 QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant);
2566 QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant);
2568 QJSValue val1 = eng.toScriptValue(tmp2);
2569 QJSValue val2 = eng.toScriptValue(tmp3);
2570 QVERIFY(!val1.isUndefined());
2571 QVERIFY(!val2.isUndefined());
2572 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2573 QVERIFY(val1.isVariant());
2574 QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue);
2575 QVERIFY(val2.isVariant());
2576 QVERIFY(val1.toVariant().toInt() == 123);
2577 QVERIFY(eng.toScriptValue(val2.toVariant()).toVariant().toInt() == 123);
2580 QJSValue val = eng.toScriptValue(QVariant(true));
2581 QVERIFY(!val.isVariant());
2582 QVERIFY(val.isBool());
2583 QCOMPARE(val.toBool(), true);
2586 QJSValue val = eng.toScriptValue(QVariant(int(123)));
2587 QVERIFY(!val.isVariant());
2588 QVERIFY(val.isNumber());
2589 QCOMPARE(val.toNumber(), qreal(123));
2592 QJSValue val = eng.toScriptValue(QVariant(qreal(1.25)));
2593 QVERIFY(!val.isVariant());
2594 QVERIFY(val.isNumber());
2595 QCOMPARE(val.toNumber(), qreal(1.25));
2598 QString str = QString::fromLatin1("ciao");
2599 QJSValue val = eng.toScriptValue(QVariant(str));
2600 QVERIFY(!val.isVariant());
2601 QVERIFY(val.isString());
2602 QCOMPARE(val.toString(), str);
2605 QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
2606 QVERIFY(!val.isVariant());
2607 QVERIFY(val.isQObject());
2608 QCOMPARE(val.toQObject(), (QObject*)this);
2611 QVariant var = qVariantFromValue(QPoint(123, 456));
2612 QJSValue val = eng.toScriptValue(var);
2613 QVERIFY(val.isVariant());
2614 QCOMPARE(val.toVariant(), var);
2617 QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123));
2619 QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull());
2622 #if 0 // FIXME: No support for custom types
2623 void tst_QJSEngine::valueConversion_hooliganTask248802()
2626 qScriptRegisterMetaType<Foo>(&eng, fooToScriptValueV2, fooFromScriptValueV2);
2628 QScriptValue num(&eng, 123);
2629 Foo foo = eng.fromScriptValue<Foo>(num);
2630 QCOMPARE(foo.x, 123);
2633 QScriptValue num(123);
2634 Foo foo = eng.fromScriptValue<Foo>(num);
2635 QCOMPARE(foo.x, -1);
2638 QScriptValue str(&eng, QLatin1String("123"));
2639 Foo foo = eng.fromScriptValue<Foo>(str);
2640 QCOMPARE(foo.x, 123);
2646 void tst_QJSEngine::valueConversion_basic2()
2649 // more built-in types
2651 QJSValue val = eng.toScriptValue(uint(123));
2652 QVERIFY(val.isNumber());
2653 QCOMPARE(val.toInt(), 123);
2656 QJSValue val = eng.toScriptValue(qulonglong(123));
2657 QVERIFY(val.isNumber());
2658 QCOMPARE(val.toInt(), 123);
2661 QJSValue val = eng.toScriptValue(float(123));
2662 QVERIFY(val.isNumber());
2663 QCOMPARE(val.toInt(), 123);
2666 QJSValue val = eng.toScriptValue(short(123));
2667 QVERIFY(val.isNumber());
2668 QCOMPARE(val.toInt(), 123);
2671 QJSValue val = eng.toScriptValue(ushort(123));
2672 QVERIFY(val.isNumber());
2673 QCOMPARE(val.toInt(), 123);
2676 QJSValue val = eng.toScriptValue(char(123));
2677 QVERIFY(val.isNumber());
2678 QCOMPARE(val.toInt(), 123);
2681 QJSValue val = eng.toScriptValue(uchar(123));
2682 QVERIFY(val.isNumber());
2683 QCOMPARE(val.toInt(), 123);
2687 void tst_QJSEngine::valueConversion_dateTime()
2691 QDateTime in = QDateTime::currentDateTime();
2692 QJSValue val = eng.toScriptValue(in);
2693 QVERIFY(val.isDate());
2694 QCOMPARE(val.toDateTime(), in);
2697 QDate in = QDate::currentDate();
2698 QJSValue val = eng.toScriptValue(in);
2699 QVERIFY(val.isDate());
2700 QCOMPARE(val.toDateTime().date(), in);
2704 void tst_QJSEngine::valueConversion_regExp()
2708 QRegExp in = QRegExp("foo");
2709 QJSValue val = eng.toScriptValue(in);
2710 QVERIFY(val.isRegExp());
2711 QRegExp out = qjsvalue_cast<QRegExp>(val);
2712 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue);
2713 QCOMPARE(out.patternSyntax(), in.patternSyntax());
2714 QCOMPARE(out.pattern(), in.pattern());
2715 QCOMPARE(out.caseSensitivity(), in.caseSensitivity());
2716 QCOMPARE(out.isMinimal(), in.isMinimal());
2719 QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2);
2720 QJSValue val = eng.toScriptValue(in);
2721 QVERIFY(val.isRegExp());
2722 QCOMPARE(qjsvalue_cast<QRegExp>(val), in);
2725 QRegExp in = QRegExp("foo");
2726 in.setMinimal(true);
2727 QJSValue val = eng.toScriptValue(in);
2728 QVERIFY(val.isRegExp());
2729 QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue);
2730 QCOMPARE(qjsvalue_cast<QRegExp>(val).isMinimal(), in.isMinimal());
2734 #if 0 // FIXME: No qScriptValueFromValue
2735 void tst_QJSEngine::qScriptValueFromValue_noEngine()
2737 QVERIFY(qScriptValueFromValue(0, 123).isUndefined());
2738 QVERIFY(qScriptValueFromValue(0, QVariant(123)).isUndefined());
2742 #if 0 // ###FIXME: No QScriptContext
2743 static QScriptValue __import__(QScriptContext *ctx, QScriptEngine *eng)
2745 return eng->importExtension(ctx->argument(0).toString());
2748 void tst_QJSEngine::importExtension()
2750 QStringList libPaths = QCoreApplication::instance()->libraryPaths();
2751 QCoreApplication::instance()->setLibraryPaths(QStringList() << SRCDIR);
2753 QStringList availableExtensions;
2756 QVERIFY(eng.importedExtensions().isEmpty());
2757 QStringList ret = eng.availableExtensions();
2758 QCOMPARE(ret.size(), 4);
2759 QCOMPARE(ret.at(0), QString::fromLatin1("com"));
2760 QCOMPARE(ret.at(1), QString::fromLatin1("com.trolltech"));
2761 QCOMPARE(ret.at(2), QString::fromLatin1("com.trolltech.recursive"));
2762 QCOMPARE(ret.at(3), QString::fromLatin1("com.trolltech.syntaxerror"));
2763 availableExtensions = ret;
2766 // try to import something that doesn't exist
2769 QScriptValue ret = eng.importExtension("this.extension.does.not.exist");
2770 QCOMPARE(eng.hasUncaughtException(), true);
2771 QCOMPARE(ret.isError(), true);
2772 QCOMPARE(ret.toString(), QString::fromLatin1("Error: Unable to import this.extension.does.not.exist: no such extension"));
2777 for (int x = 0; x < 2; ++x) {
2778 QCOMPARE(!eng.globalObject().property("com").isUndefined(), x == 1);
2779 QScriptValue ret = eng.importExtension("com.trolltech");
2780 QCOMPARE(eng.hasUncaughtException(), false);
2781 QVERIFY(ret.isUndefined());
2783 QScriptValue com = eng.globalObject().property("com");
2784 QCOMPARE(com.isObject(), true);
2785 QCOMPARE(com.property("wasDefinedAlready")
2786 .strictlyEquals(QScriptValue(&eng, false)), true);
2787 QCOMPARE(com.property("name")
2788 .strictlyEquals(QScriptValue(&eng, "com")), true);
2789 QCOMPARE(com.property("level")
2790 .strictlyEquals(QScriptValue(&eng, 1)), true);
2791 QVERIFY(com.property("originalPostInit").isUndefined());
2792 QVERIFY(com.property("postInitCallCount").strictlyEquals(1));
2794 QScriptValue trolltech = com.property("trolltech");
2795 QCOMPARE(trolltech.isObject(), true);
2796 QCOMPARE(trolltech.property("wasDefinedAlready")
2797 .strictlyEquals(QScriptValue(&eng, false)), true);
2798 QCOMPARE(trolltech.property("name")
2799 .strictlyEquals(QScriptValue(&eng, "com.trolltech")), true);
2800 QCOMPARE(trolltech.property("level")
2801 .strictlyEquals(QScriptValue(&eng, 2)), true);
2802 QVERIFY(trolltech.property("originalPostInit").isUndefined());
2803 QVERIFY(trolltech.property("postInitCallCount").strictlyEquals(1));
2805 QStringList imp = eng.importedExtensions();
2806 QCOMPARE(imp.size(), 2);
2807 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2808 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2809 QCOMPARE(eng.availableExtensions(), availableExtensions);
2812 // recursive import should throw an error
2815 QVERIFY(eng.importedExtensions().isEmpty());
2816 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2817 QScriptValue ret = eng.importExtension("com.trolltech.recursive");
2818 QCOMPARE(eng.hasUncaughtException(), true);
2819 QVERIFY(ret.isError());
2820 QCOMPARE(ret.toString(), QString::fromLatin1("Error: recursive import of com.trolltech.recursive"));
2821 QStringList imp = eng.importedExtensions();
2822 QCOMPARE(imp.size(), 2);
2823 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2824 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2825 QCOMPARE(eng.availableExtensions(), availableExtensions);
2830 eng.globalObject().setProperty("__import__", eng.newFunction(__import__));
2831 for (int x = 0; x < 2; ++x) {
2833 QVERIFY(eng.importedExtensions().isEmpty());
2834 QScriptValue ret = eng.importExtension("com.trolltech.syntaxerror");
2835 QVERIFY(eng.hasUncaughtException());
2836 QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue);
2837 QCOMPARE(eng.uncaughtExceptionLineNumber(), 4);
2838 QVERIFY(ret.isError());
2839 QVERIFY(ret.toString().contains(QLatin1String("SyntaxError")));
2841 QStringList imp = eng.importedExtensions();
2842 QCOMPARE(imp.size(), 2);
2843 QCOMPARE(imp.at(0), QString::fromLatin1("com"));
2844 QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech"));
2845 QCOMPARE(eng.availableExtensions(), availableExtensions);
2848 QCoreApplication::instance()->setLibraryPaths(libPaths);
2851 static QScriptValue recurse(QScriptContext *ctx, QScriptEngine *eng)
2854 return ctx->callee().call();
2857 static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng)
2860 return ctx->callee().callAsConstructor();
2863 void tst_QJSEngine::infiniteRecursion()
2865 // Infinite recursion in JS should cause the VM to throw an error
2866 // when the JS stack is exhausted.
2867 // The exact error is back-end specific and subject to change.
2868 const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded");
2871 QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();");
2872 QCOMPARE(ret.isError(), true);
2873 QVERIFY(ret.toString().startsWith(stackOverflowError));
2875 #if 0 //The native C++ stack overflow before the JS stack
2877 QScriptValue fun = eng.newFunction(recurse);
2878 QScriptValue ret = fun.call();
2879 QCOMPARE(ret.isError(), true);
2880 QCOMPARE(ret.toString(), stackOverflowError);
2883 QScriptValue fun = eng.newFunction(recurse2);
2884 QScriptValue ret = fun.callAsConstructor();
2885 QCOMPARE(ret.isError(), true);
2886 QCOMPARE(ret.toString(), stackOverflowError);
2896 struct Baz : public Bar {
2900 Q_DECLARE_METATYPE(Bar*)
2901 Q_DECLARE_METATYPE(Baz*)
2903 Q_DECLARE_METATYPE(QGradient)
2904 Q_DECLARE_METATYPE(QGradient*)
2905 Q_DECLARE_METATYPE(QLinearGradient)
2907 #if 0 // FIXME: No support for default prototypes
2908 class Zoo : public QObject
2914 Baz *toBaz(Bar *b) { return reinterpret_cast<Baz*>(b); }
2917 void tst_QJSEngine::castWithPrototypeChain()
2923 QScriptValue barProto = eng.toScriptValue(&bar);
2924 QScriptValue bazProto = eng.toScriptValue(&baz);
2925 eng.setDefaultPrototype(qMetaTypeId<Bar*>(), barProto);
2926 eng.setDefaultPrototype(qMetaTypeId<Baz*>(), bazProto);
2931 QScriptValue baz2Value = eng.toScriptValue(&baz2);
2933 // qscriptvalue_cast() does magic; if the QScriptValue contains
2934 // t of type T, and the target type is T*, &t is returned.
2935 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2937 QCOMPARE(pbaz->b, baz2.b);
2940 QScriptValue scriptZoo = eng.newQObject(&zoo);
2941 QScriptValue toBaz = scriptZoo.property("toBaz");
2942 QVERIFY(toBaz.isCallable());
2944 // no relation between Bar and Baz's proto --> casting fails
2946 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2951 QScriptValue ret = toBaz.callWithInstance(scriptZoo, QScriptValueList() << baz2Value);
2952 QVERIFY(ret.isError());
2953 QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to toBaz(); candidates were\n toBaz(Bar*)"));
2956 // establish chain -- now casting should work
2957 // Why? because qscriptvalue_cast() does magic again.
2958 // It the instance itself is not of type T, qscriptvalue_cast()
2959 // searches the prototype chain for T, and if it finds one, it infers
2960 // that the instance can also be casted to that type. This cast is
2961 // _not_ safe and thus relies on the developer doing the right thing.
2962 // This is an undocumented feature to enable qscriptvalue_cast() to
2963 // be used by prototype functions to cast the JS this-object to C++.
2964 bazProto.setPrototype(barProto);
2967 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2969 QCOMPARE(pbar->a, baz2.a);
2973 QScriptValue ret = toBaz.callWithInstance(scriptZoo, QScriptValueList() << baz2Value);
2974 QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue);
2975 QVERIFY(!ret.isError());
2976 QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue);
2977 QCOMPARE(qscriptvalue_cast<Baz*>(ret), pbaz);
2981 bazProto.setPrototype(barProto.prototype()); // kill chain
2983 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2985 // should not work anymore
2986 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
2990 bazProto.setPrototype(eng.newQObject(&temp));
2992 Baz *pbaz = qscriptvalue_cast<Baz*>(baz2Value);
2994 // should not work now either
2995 Bar *pbar = qscriptvalue_cast<Bar*>(baz2Value);
3000 QScriptValue b = eng.toScriptValue(QBrush());
3001 b.setPrototype(barProto);
3002 // this shows that a "wrong" cast is possible, if you
3003 // don't play by the rules (the pointer is actually a QBrush*)...
3004 Bar *pbar = qscriptvalue_cast<Bar*>(b);
3009 QScriptValue gradientProto = eng.toScriptValue(QGradient());
3010 QScriptValue linearGradientProto = eng.toScriptValue(QLinearGradient());
3011 linearGradientProto.setPrototype(gradientProto);
3012 QLinearGradient lg(10, 20, 30, 40);
3013 QScriptValue linearGradient = eng.toScriptValue(lg);
3015 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
3016 QVERIFY(pgrad == 0);
3018 linearGradient.setPrototype(linearGradientProto);
3020 QGradient *pgrad = qscriptvalue_cast<QGradient*>(linearGradient);
3021 QVERIFY(pgrad != 0);
3022 QCOMPARE(pgrad->type(), QGradient::LinearGradient);
3023 QLinearGradient *plingrad = static_cast<QLinearGradient*>(pgrad);
3024 QCOMPARE(plingrad->start(), lg.start());
3025 QCOMPARE(plingrad->finalStop(), lg.finalStop());
3031 class Klazz : public QWidget,
3032 public QStandardItem,
3033 public QGraphicsItem
3035 Q_INTERFACES(QGraphicsItem)
3038 Klazz(QWidget *parent = 0) : QWidget(parent) { }
3039 virtual QRectF boundingRect() const { return QRectF(); }
3040 virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { }
3043 Q_DECLARE_METATYPE(Klazz*)
3044 Q_DECLARE_METATYPE(QStandardItem*)
3046 void tst_QJSEngine::castWithMultipleInheritance()
3050 QJSValue v = eng.newQObject(&klz);
3052 QCOMPARE(qjsvalue_cast<Klazz*>(v), &klz);
3053 QCOMPARE(qjsvalue_cast<QWidget*>(v), (QWidget *)&klz);
3054 QCOMPARE(qjsvalue_cast<QObject*>(v), (QObject *)&klz);
3055 QCOMPARE(qjsvalue_cast<QStandardItem*>(v), (QStandardItem *)&klz);
3056 QCOMPARE(qjsvalue_cast<QGraphicsItem*>(v), (QGraphicsItem *)&klz);
3059 void tst_QJSEngine::collectGarbage()
3062 eng.evaluate("a = new Object(); a = new Object(); a = new Object()");
3063 QJSValue a = eng.newObject();
3064 a = eng.newObject();
3065 a = eng.newObject();
3066 QPointer<QObject> ptr = new QObject();
3068 (void)eng.newQObject(ptr);
3069 collectGarbage_helper(eng);
3071 QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
3075 #if 0 // ###FIXME: no reportAdditionalMemoryCost API
3076 void tst_QJSEngine::reportAdditionalMemoryCost()
3079 // There isn't any reliable way to test whether calling
3080 // this function affects garbage collection responsiveness;
3081 // the best we can do is call it with a few different values.
3082 for (int x = 0; x < 100; ++x) {
3083 eng.reportAdditionalMemoryCost(0);
3084 eng.reportAdditionalMemoryCost(10);
3085 eng.reportAdditionalMemoryCost(1000);
3086 eng.reportAdditionalMemoryCost(10000);
3087 eng.reportAdditionalMemoryCost(100000);
3088 eng.reportAdditionalMemoryCost(1000000);
3089 eng.reportAdditionalMemoryCost(10000000);
3090 eng.reportAdditionalMemoryCost(-1);
3091 eng.reportAdditionalMemoryCost(-1000);
3092 QScriptValue obj = eng.newObject();
3093 eng.collectGarbage();
3098 void tst_QJSEngine::gcWithNestedDataStructure()
3100 // The GC must be able to traverse deeply nested objects, otherwise this
3101 // test would crash.
3103 QJSValue ret = eng.evaluate(
3104 "function makeList(size)"
3108 " for (var i = 0; i < size; ++i) {"
3109 " l.data = i + \"\";"
3110 " l.next = { }; l = l.next;"
3115 QVERIFY(!ret.isError());
3116 const int size = 200;
3117 QJSValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size));
3118 QVERIFY(!head.isError());
3119 for (int x = 0; x < 2; ++x) {
3121 eng.evaluate("gc()");
3123 // Make sure all the nodes are still alive.
3124 for (int i = 0; i < 200; ++i) {
3125 QCOMPARE(l.property("data").toString(), QString::number(i));
3126 l = l.property("next");
3131 #if 0 // ###FIXME: No processEvents handling
3132 class EventReceiver : public QObject
3139 bool event(QEvent *e) {
3140 received |= (e->type() == QEvent::User + 1);
3141 return QObject::event(e);
3147 void tst_QJSEngine::processEventsWhileRunning()
3149 for (int x = 0; x < 2; ++x) {
3154 // This is running for a silly amount of time just to make sure
3155 // the script doesn't finish before event processing is triggered.
3156 QString script = QString::fromLatin1(
3157 "var end = Number(new Date()) + 2000;"
3159 "while (Number(new Date()) < end) {"
3163 EventReceiver receiver;
3164 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3166 eng.evaluate(script);
3167 QVERIFY(!eng.hasUncaughtException());
3168 QVERIFY(!receiver.received);
3170 QCOMPARE(eng.processEventsInterval(), -1);
3171 eng.setProcessEventsInterval(100);
3172 eng.evaluate(script);
3173 QVERIFY(!eng.hasUncaughtException());
3174 QVERIFY(receiver.received);
3181 void tst_QJSEngine::processEventsWhileRunning_function()
3184 QScriptValue script = eng.evaluate(QString::fromLatin1(
3185 "(function() { var end = Number(new Date()) + 2000;"
3187 "while (Number(new Date()) < end) {"
3191 eng.setProcessEventsInterval(100);
3193 for (int x = 0; x < 2; ++x) {
3194 EventReceiver receiver;
3195 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3196 QVERIFY(!eng.hasUncaughtException());
3197 QVERIFY(!receiver.received);
3198 QCOMPARE(eng.processEventsInterval(), 100);
3200 if (x) script.call();
3201 else script.callAsConstructor();
3203 QVERIFY(!eng.hasUncaughtException());
3204 QVERIFY(receiver.received);
3209 class EventReceiver2 : public QObject
3212 EventReceiver2(QScriptEngine *eng) {
3216 bool event(QEvent *e) {
3217 if (e->type() == QEvent::User + 1) {
3218 engine->currentContext()->throwError("Killed");
3220 return QObject::event(e);
3223 QScriptEngine *engine;
3226 void tst_QJSEngine::throwErrorFromProcessEvents_data()
3228 QTest::addColumn<QString>("script");
3229 QTest::addColumn<QString>("error");
3231 QTest::newRow("while (1)")
3232 << QString::fromLatin1("while (1) { }")
3233 << QString::fromLatin1("Error: Killed");
3234 QTest::newRow("while (1) i++")
3235 << QString::fromLatin1("i = 0; while (1) { i++; }")
3236 << QString::fromLatin1("Error: Killed");
3237 // Unlike abortEvaluation(), scripts should be able to catch the
3239 QTest::newRow("try catch")
3240 << QString::fromLatin1("try {"
3243 " throw new Error('Caught');"
3245 << QString::fromLatin1("Error: Caught");
3248 void tst_QJSEngine::throwErrorFromProcessEvents()
3250 QFETCH(QString, script);
3251 QFETCH(QString, error);
3255 EventReceiver2 receiver(&eng);
3256 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3258 eng.setProcessEventsInterval(100);
3259 QScriptValue ret = eng.evaluate(script);
3260 QVERIFY(ret.isError());
3261 QCOMPARE(ret.toString(), error);
3264 void tst_QJSEngine::disableProcessEventsInterval()
3267 eng.setProcessEventsInterval(100);
3268 QCOMPARE(eng.processEventsInterval(), 100);
3269 eng.setProcessEventsInterval(0);
3270 QCOMPARE(eng.processEventsInterval(), 0);
3271 eng.setProcessEventsInterval(-1);
3272 QCOMPARE(eng.processEventsInterval(), -1);
3273 eng.setProcessEventsInterval(-100);
3274 QCOMPARE(eng.processEventsInterval(), -100);
3279 void tst_QJSEngine::stacktrace()
3281 QString script = QString::fromLatin1(
3282 "function foo(counter) {\n"
3283 " switch (counter) {\n"
3284 " case 0: foo(counter+1); break;\n"
3285 " case 1: foo(counter+1); break;\n"
3286 " case 2: foo(counter+1); break;\n"
3287 " case 3: foo(counter+1); break;\n"
3288 " case 4: foo(counter+1); break;\n"
3290 " throw new Error('blah');\n"
3295 const QString fileName("testfile");
3297 QStringList backtrace;
3298 backtrace << "foo(5)@testfile:9"
3299 << "foo(4)@testfile:7"
3300 << "foo(3)@testfile:6"
3301 << "foo(2)@testfile:5"
3302 << "foo(1)@testfile:4"
3303 << "foo(0)@testfile:3"
3304 << "<global>()@testfile:12";
3307 QJSValue result = eng.evaluate(script, fileName);
3308 QVERIFY(result.isError());
3310 // FIXME? it is not standard.
3311 //QCOMPARE(result.property("fileName").toString(), fileName);
3312 //QCOMPARE(result.property("lineNumber").toInt(), 9);
3314 QJSValue stack = result.property("stack");
3316 // FIXME? it is not standard.
3317 // QVERIFY(stack.isArray());
3318 //QCOMPARE(stack.property("length").toInt(), 7);
3320 QJSValueIterator it(stack);
3322 while (it.hasNext()) {
3324 QJSValue obj = it.value();
3325 QJSValue frame = obj.property("frame");
3327 QCOMPARE(obj.property("fileName").toString(), fileName);
3329 QJSValue callee = frame.property("arguments").property("callee");
3330 QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo")));
3331 QCOMPARE(obj.property("functionName").toString(), QString("foo"));
3332 int line = obj.property("lineNumber").toInt();
3336 QCOMPARE(line, 3 + counter);
3338 QVERIFY(frame.strictlyEquals(eng.globalObject()));
3339 QVERIFY(obj.property("functionName").toString().isEmpty());
3345 // FIXME? it is not standard.
3347 // QJSValue bt = result.property("backtrace").call(result);
3348 // QCOMPARE(qjsvalue_cast<QStringList>(bt), backtrace);
3351 // throw something that isn't an Error object
3352 // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty());
3353 QString script2 = QString::fromLatin1(
3354 "function foo(counter) {\n"
3355 " switch (counter) {\n"
3356 " case 0: foo(counter+1); break;\n"
3357 " case 1: foo(counter+1); break;\n"
3358 " case 2: foo(counter+1); break;\n"
3359 " case 3: foo(counter+1); break;\n"
3360 " case 4: foo(counter+1); break;\n"
3362 " throw 'just a string';\n"
3367 QJSValue result2 = eng.evaluate(script2, fileName);
3368 QVERIFY(!result2.isError());
3369 QVERIFY(result2.isString());
3372 void tst_QJSEngine::numberParsing_data()
3374 QTest::addColumn<QString>("string");
3375 QTest::addColumn<qreal>("expect");
3377 QTest::newRow("decimal 0") << QString("0") << qreal(0);
3378 QTest::newRow("octal 0") << QString("00") << qreal(00);
3379 QTest::newRow("hex 0") << QString("0x0") << qreal(0x0);
3380 QTest::newRow("decimal 100") << QString("100") << qreal(100);
3381 QTest::newRow("hex 100") << QString("0x100") << qreal(0x100);
3382 QTest::newRow("octal 100") << QString("0100") << qreal(0100);
3383 QTest::newRow("decimal 4G") << QString("4294967296") << qreal(Q_UINT64_C(4294967296));
3384 QTest::newRow("hex 4G") << QString("0x100000000") << qreal(Q_UINT64_C(0x100000000));
3385 QTest::newRow("octal 4G") << QString("040000000000") << qreal(Q_UINT64_C(040000000000));
3386 QTest::newRow("0.5") << QString("0.5") << qreal(0.5);
3387 QTest::newRow("1.5") << QString("1.5") << qreal(1.5);
3388 QTest::newRow("1e2") << QString("1e2") << qreal(100);
3391 void tst_QJSEngine::numberParsing()
3393 QFETCH(QString, string);
3394 QFETCH(qreal, expect);
3397 QJSValue ret = eng.evaluate(string);
3398 QVERIFY(ret.isNumber());
3399 qreal actual = ret.toNumber();
3400 QCOMPARE(actual, expect);
3403 // see ECMA-262, section 7.9
3404 // This is testing ECMA compliance, not our C++ API, but it's important that
3405 // the back-end is conformant in this regard.
3406 void tst_QJSEngine::automaticSemicolonInsertion()
3410 QJSValue ret = eng.evaluate("{ 1 2 } 3");
3411 QVERIFY(ret.isError());
3412 QVERIFY(ret.toString().contains("SyntaxError"));
3415 QJSValue ret = eng.evaluate("{ 1\n2 } 3");
3416 QVERIFY(ret.isNumber());
3417 QCOMPARE(ret.toInt(), 3);
3420 QJSValue ret = eng.evaluate("for (a; b\n)");
3421 QVERIFY(ret.isError());
3422 QVERIFY(ret.toString().contains("SyntaxError"));
3425 QJSValue ret = eng.evaluate("(function() { return\n1 + 2 })()");
3426 QVERIFY(ret.isUndefined());
3429 eng.evaluate("c = 2; b = 1");
3430 QJSValue ret = eng.evaluate("a = b\n++c");
3431 QVERIFY(ret.isNumber());
3432 QCOMPARE(ret.toInt(), 3);
3435 QJSValue ret = eng.evaluate("if (a > b)\nelse c = d");
3436 QVERIFY(ret.isError());
3437 QVERIFY(ret.toString().contains("SyntaxError"));
3440 eng.evaluate("function c() { return { foo: function() { return 5; } } }");
3441 eng.evaluate("b = 1; d = 2; e = 3");
3442 QJSValue ret = eng.evaluate("a = b + c\n(d + e).foo()");
3443 QVERIFY(ret.isNumber());
3444 QCOMPARE(ret.toInt(), 6);
3447 QJSValue ret = eng.evaluate("throw\n1");
3448 QVERIFY(ret.isError());
3449 QVERIFY(ret.toString().contains("SyntaxError"));
3452 QJSValue ret = eng.evaluate("a = Number(1)\n++a");
3453 QVERIFY(ret.isNumber());
3454 QCOMPARE(ret.toInt(), 2);
3457 // "a semicolon is never inserted automatically if the semicolon
3458 // would then be parsed as an empty statement"
3460 eng.evaluate("a = 123");
3461 QJSValue ret = eng.evaluate("if (0)\n ++a; a");
3462 QVERIFY(ret.isNumber());
3463 QCOMPARE(ret.toInt(), 123);
3466 eng.evaluate("a = 123");
3467 QJSValue ret = eng.evaluate("if (0)\n --a; a");
3468 QVERIFY(ret.isNumber());
3469 QCOMPARE(ret.toInt(), 123);
3472 eng.evaluate("a = 123");
3473 QJSValue ret = eng.evaluate("if ((0))\n ++a; a");
3474 QVERIFY(ret.isNumber());
3475 QCOMPARE(ret.toInt(), 123);
3478 eng.evaluate("a = 123");
3479 QJSValue ret = eng.evaluate("if ((0))\n --a; a");
3480 QVERIFY(ret.isNumber());
3481 QCOMPARE(ret.toInt(), 123);
3484 eng.evaluate("a = 123");
3485 QJSValue ret = eng.evaluate("if (0\n)\n ++a; a");
3486 QVERIFY(ret.isNumber());
3487 QCOMPARE(ret.toInt(), 123);
3490 eng.evaluate("a = 123");
3491 QJSValue ret = eng.evaluate("if (0\n ++a; a");
3492 QVERIFY(ret.isError());
3495 eng.evaluate("a = 123");
3496 QJSValue ret = eng.evaluate("if (0))\n ++a; a");
3497 QVERIFY(ret.isError());
3500 QJSValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n");
3501 QVERIFY(ret.isNumber());
3502 QCOMPARE(ret.toInt(), 10);
3505 QJSValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n");
3506 QVERIFY(ret.isNumber());
3507 QCOMPARE(ret.toInt(), 20);
3510 QJSValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n");
3511 QVERIFY(ret.isNumber());
3512 QCOMPARE(ret.toInt(), 10);
3515 QJSValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n");
3516 QVERIFY(ret.isNumber());
3517 QCOMPARE(ret.toInt(), 20);
3520 QJSValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n");
3521 QVERIFY(ret.isNumber());
3522 QCOMPARE(ret.toInt(), 10);
3525 QJSValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n");
3526 QVERIFY(ret.isNumber());
3527 QCOMPARE(ret.toInt(), 20);
3530 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n");
3531 QVERIFY(ret.isNumber());
3532 QCOMPARE(ret.toInt(), 3);
3535 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n");
3536 QVERIFY(ret.isNumber());
3537 QCOMPARE(ret.toInt(), 6);
3540 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n");
3541 QVERIFY(ret.isNumber());
3542 QCOMPARE(ret.toInt(), 3);
3545 QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n");
3546 QVERIFY(ret.isNumber());
3547 QCOMPARE(ret.toInt(), 6);
3550 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n");
3551 QVERIFY(ret.isNumber());
3552 QCOMPARE(ret.toInt(), 5);
3555 QJSValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n");
3556 QVERIFY(ret.isNumber());
3557 QCOMPARE(ret.toInt(), 10);
3560 QJSValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n");
3561 QVERIFY(ret.isNumber());
3562 QCOMPARE(ret.toInt(), 15);
3565 QJSValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n");
3566 QVERIFY(ret.isNumber());
3567 QCOMPARE(ret.toInt(), 10);
3571 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n");
3572 QVERIFY(ret.isNumber());
3573 QCOMPARE(ret.toInt(), 2);
3576 QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n");
3577 QVERIFY(ret.isNumber());
3578 QCOMPARE(ret.toInt(), 0);
3582 QJSValue ret = eng.evaluate("if (0)");
3583 QVERIFY(ret.isError());
3586 QJSValue ret = eng.evaluate("while (0)");
3587 QVERIFY(ret.isError());
3590 QJSValue ret = eng.evaluate("for (;;)");
3591 QVERIFY(ret.isError());
3594 QJSValue ret = eng.evaluate("for (p in this)");
3595 QVERIFY(ret.isError());
3598 QJSValue ret = eng.evaluate("with (this)");
3599 QVERIFY(ret.isError());
3602 QJSValue ret = eng.evaluate("do");
3603 QVERIFY(ret.isError());
3607 #if 0 // ###FIXME: no abortEvaluation API
3608 class EventReceiver3 : public QObject
3611 enum AbortionResult {
3618 EventReceiver3(QScriptEngine *eng) {
3623 bool event(QEvent *e) {
3624 if (e->type() == QEvent::User + 1) {
3625 switch (resultType) {
3627 engine->abortEvaluation();
3630 engine->abortEvaluation(QScriptValue(engine, QString::fromLatin1("Aborted")));
3633 engine->abortEvaluation(engine->currentContext()->throwError("AbortedWithError"));
3636 engine->abortEvaluation(QScriptValue(1234));
3639 return QObject::event(e);
3642 AbortionResult resultType;
3643 QScriptEngine *engine;
3646 static QScriptValue myFunctionAbortingEvaluation(QScriptContext *, QScriptEngine *eng)
3648 eng->abortEvaluation();
3649 return eng->nullValue(); // should be ignored
3652 void tst_QJSEngine::abortEvaluation_notEvaluating()
3656 eng.abortEvaluation();
3657 QVERIFY(!eng.hasUncaughtException());
3659 eng.abortEvaluation(123);
3661 QScriptValue ret = eng.evaluate("'ciao'");
3662 QVERIFY(ret.isString());
3663 QCOMPARE(ret.toString(), QString::fromLatin1("ciao"));
3667 void tst_QJSEngine::abortEvaluation_data()
3669 QTest::addColumn<QString>("script");
3671 QTest::newRow("while (1)")
3672 << QString::fromLatin1("while (1) { }");
3673 QTest::newRow("while (1) i++")
3674 << QString::fromLatin1("i = 0; while (1) { i++; }");
3675 QTest::newRow("try catch")
3676 << QString::fromLatin1("try {"
3679 " throw new Error('Caught');"
3683 void tst_QJSEngine::abortEvaluation()
3685 QFETCH(QString, script);
3688 EventReceiver3 receiver(&eng);
3690 eng.setProcessEventsInterval(100);
3691 for (int x = 0; x < 4; ++x) {
3692 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3693 receiver.resultType = EventReceiver3::AbortionResult(x);
3694 QScriptValue ret = eng.evaluate(script);
3695 switch (receiver.resultType) {
3696 case EventReceiver3::None:
3697 QVERIFY(!eng.hasUncaughtException());
3698 QVERIFY(ret.isUndefined());
3700 case EventReceiver3::Number:
3701 QVERIFY(!eng.hasUncaughtException());
3702 QVERIFY(ret.isNumber());
3703 QCOMPARE(ret.toInt(), 1234);
3705 case EventReceiver3::String:
3706 QVERIFY(!eng.hasUncaughtException());
3707 QVERIFY(ret.isString());
3708 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3710 case EventReceiver3::Error:
3711 QVERIFY(eng.hasUncaughtException());
3712 QVERIFY(ret.isError());
3713 QCOMPARE(ret.toString(), QString::fromLatin1("Error: AbortedWithError"));
3720 void tst_QJSEngine::abortEvaluation_tryCatch()
3722 QSKIP("It crashes");
3724 EventReceiver3 receiver(&eng);
3725 eng.setProcessEventsInterval(100);
3726 // scripts cannot intercept the abortion with try/catch
3727 for (int y = 0; y < 4; ++y) {
3728 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3729 receiver.resultType = EventReceiver3::AbortionResult(y);
3730 QScriptValue ret = eng.evaluate(QString::fromLatin1(
3733 " (function() { while (1) { } })();\n"
3738 switch (receiver.resultType) {
3739 case EventReceiver3::None:
3740 QVERIFY(!eng.hasUncaughtException());
3741 QVERIFY(ret.isUndefined());
3743 case EventReceiver3::Number:
3744 QVERIFY(!eng.hasUncaughtException());
3745 QVERIFY(ret.isNumber());
3746 QCOMPARE(ret.toInt(), 1234);
3748 case EventReceiver3::String:
3749 QVERIFY(!eng.hasUncaughtException());
3750 QVERIFY(ret.isString());
3751 QCOMPARE(ret.toString(), QString::fromLatin1("Aborted"));
3753 case EventReceiver3::Error:
3754 QVERIFY(eng.hasUncaughtException());
3755 QVERIFY(ret.isError());
3761 void tst_QJSEngine::abortEvaluation_fromNative()
3764 QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation);
3765 eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun);
3766 QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()");
3767 QVERIFY(ret.isUndefined());
3770 class ThreadedEngine : public QThread {
3774 QScriptEngine* m_engine;
3778 m_engine = new QScriptEngine();
3779 m_engine->setGlobalObject(m_engine->newQObject(&temp));
3780 m_engine->evaluate("while (1) { sleep(); }");
3788 m_engine->abortEvaluation();
3792 void tst_QJSEngine::abortEvaluation_QTBUG9433()
3794 ThreadedEngine engine;
3796 QVERIFY(engine.isRunning());
3798 for (uint i = 0; i < 50; ++i) { // up to ~2500 ms
3799 if (engine.isFinished())
3803 if (!engine.isFinished()) {
3806 QFAIL("abortEvaluation doesn't work");
3812 #if 0 // ###FIXME: no QScriptEngine::isEvaluating
3813 static QScriptValue myFunctionReturningIsEvaluating(QScriptContext *, QScriptEngine *eng)
3815 return QScriptValue(eng, eng->isEvaluating());
3818 class EventReceiver4 : public QObject
3821 EventReceiver4(QScriptEngine *eng) {
3823 wasEvaluating = false;
3826 bool event(QEvent *e) {
3827 if (e->type() == QEvent::User + 1) {
3828 wasEvaluating = engine->isEvaluating();
3830 return QObject::event(e);
3833 QScriptEngine *engine;
3837 void tst_QJSEngine::isEvaluating_notEvaluating()
3841 QVERIFY(!eng.isEvaluating());
3844 QVERIFY(!eng.isEvaluating());
3845 eng.evaluate("123");
3846 QVERIFY(!eng.isEvaluating());
3847 eng.evaluate("0 = 0");
3848 QVERIFY(!eng.isEvaluating());
3851 void tst_QJSEngine::isEvaluating_fromNative()
3854 QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating);
3855 eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun);
3856 QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()");
3857 QVERIFY(ret.isBool());
3858 QVERIFY(ret.toBool());
3860 QVERIFY(ret.isBool());
3861 QVERIFY(ret.toBool());
3862 ret = myFunctionReturningIsEvaluating(eng.currentContext(), &eng);
3863 QVERIFY(ret.isBool());
3864 QVERIFY(!ret.toBool());
3867 void tst_QJSEngine::isEvaluating_fromEvent()
3870 EventReceiver4 receiver(&eng);
3871 QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1)));
3873 QString script = QString::fromLatin1(
3874 "var end = Number(new Date()) + 1000;"
3876 "while (Number(new Date()) < end) {"
3880 eng.setProcessEventsInterval(100);
3881 eng.evaluate(script);
3882 QVERIFY(receiver.wasEvaluating);
3886 static QtMsgType theMessageType;
3887 static QString theMessage;
3889 static void myMsgHandler(QtMsgType type, const char *msg)
3891 theMessageType = type;
3892 theMessage = QString::fromLatin1(msg);
3896 void tst_QJSEngine::printFunctionWithCustomHandler()
3898 // The built-in print() function passes the output to Qt's message
3899 // handler. By installing a custom message handler, the output can be
3900 // redirected without changing the print() function itself.
3901 // This behavior is not documented.
3903 QtMsgHandler oldHandler = qInstallMsgHandler(myMsgHandler);
3904 QVERIFY(eng.globalObject().property("print").isCallable());
3906 theMessageType = QtSystemMsg;
3907 QVERIFY(theMessage.isEmpty());
3908 QVERIFY(eng.evaluate("print('test')").isUndefined());
3909 QCOMPARE(theMessageType, QtDebugMsg);
3910 QCOMPARE(theMessage, QString::fromLatin1("test"));
3912 theMessageType = QtSystemMsg;
3914 QVERIFY(eng.evaluate("print(3, true, 'little pigs')").isUndefined());
3915 QCOMPARE(theMessageType, QtDebugMsg);
3916 QCOMPARE(theMessage, QString::fromLatin1("3 true little pigs"));
3918 qInstallMsgHandler(oldHandler);
3921 void tst_QJSEngine::printThrowsException()
3923 // If an argument to print() causes an exception to be thrown when
3924 // it's converted to a string, print() should propagate the exception.
3926 QJSValue ret = eng.evaluate("print({ toString: function() { throw 'foo'; } });");
3927 QVERIFY(eng.hasUncaughtException());
3928 QVERIFY(ret.strictlyEquals(eng.toScriptValue(QLatin1String("foo"))));
3932 void tst_QJSEngine::errorConstructors()
3935 QStringList prefixes;
3936 prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI";
3937 for (int x = 0; x < 3; ++x) {
3938 for (int i = 0; i < prefixes.size(); ++i) {
3939 QString name = prefixes.at(i) + QLatin1String("Error");
3940 QString code = QString(i+1, QLatin1Char('\n'));
3942 code += QLatin1String("throw ");
3944 code += QLatin1String("new ");
3945 code += name + QLatin1String("()");
3946 QJSValue ret = eng.evaluate(code);
3947 QVERIFY(ret.isError());
3948 QVERIFY(ret.toString().startsWith(name));
3949 //QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown
3950 QEXPECT_FAIL("", "we have no more lineNumber property ", Continue);
3951 QCOMPARE(ret.property("lineNumber").toInt(), i+2);
3956 void tst_QJSEngine::argumentsProperty_globalContext()
3960 // Unlike function calls, the global context doesn't have an
3961 // arguments property.
3962 QJSValue ret = eng.evaluate("arguments");
3963 QVERIFY(ret.isError());
3964 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
3966 eng.evaluate("arguments = 10");
3968 QJSValue ret = eng.evaluate("arguments");
3969 QVERIFY(ret.isNumber());
3970 QCOMPARE(ret.toInt(), 10);
3972 QVERIFY(eng.evaluate("delete arguments").toBool());
3973 QVERIFY(eng.globalObject().property("arguments").isUndefined());
3976 void tst_QJSEngine::argumentsProperty_JS()
3980 eng.evaluate("o = { arguments: 123 }");
3981 QJSValue ret = eng.evaluate("with (o) { arguments; }");
3982 QVERIFY(ret.isNumber());
3983 QCOMPARE(ret.toInt(), 123);
3987 QVERIFY(eng.globalObject().property("arguments").isUndefined());
3988 // This is testing ECMA-262 compliance. In function calls, "arguments"
3989 // appears like a local variable, and it can be replaced.
3990 QJSValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()");
3991 QVERIFY(ret.isNumber());
3992 QCOMPARE(ret.toInt(), 456);
3993 QVERIFY(eng.globalObject().property("arguments").isUndefined());
3997 #if 0 // ###FIXME: no QScriptContext API
3998 static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng)
4000 // Since evaluate() is done in the current context, "arguments" should
4001 // refer to currentContext()->argumentsObject().
4002 // This is for consistency with the built-in JS eval() function.
4003 eng->evaluate("var a = arguments[0];");
4004 eng->evaluate("arguments[0] = 200;");
4005 return eng->evaluate("a + arguments[0]");
4008 void tst_QJSEngine::argumentsProperty_evaluateInNativeFunction()
4011 QScriptValue fun = eng.newFunction(argumentsProperty_fun);
4012 eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun));
4013 QScriptValue result = eng.evaluate("fun(18)");
4014 QVERIFY(result.isNumber());
4015 QCOMPARE(result.toInt(), 200+18);
4019 void tst_QJSEngine::jsNumberClass()
4021 // See ECMA-262 Section 15.7, "Number Objects".
4025 QJSValue ctor = eng.globalObject().property("Number");
4026 QVERIFY(ctor.property("length").isNumber());
4027 QCOMPARE(ctor.property("length").toNumber(), qreal(1));
4028 QJSValue proto = ctor.property("prototype");
4029 QVERIFY(proto.isObject());
4031 QVERIFY(ctor.property("MAX_VALUE").isNumber());
4032 QVERIFY(ctor.property("MIN_VALUE").isNumber());
4033 QVERIFY(ctor.property("NaN").isNumber());
4034 QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
4035 QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
4037 QCOMPARE(proto.toNumber(), qreal(0));
4038 QVERIFY(proto.property("constructor").strictlyEquals(ctor));
4041 QJSValue ret = eng.evaluate("Number()");
4042 QVERIFY(ret.isNumber());
4043 QCOMPARE(ret.toNumber(), qreal(0));
4046 QJSValue ret = eng.evaluate("Number(123)");
4047 QVERIFY(ret.isNumber());
4048 QCOMPARE(ret.toNumber(), qreal(123));
4051 QJSValue ret = eng.evaluate("Number('456')");
4052 QVERIFY(ret.isNumber());
4053 QCOMPARE(ret.toNumber(), qreal(456));
4056 QJSValue ret = eng.evaluate("new Number()");
4057 QVERIFY(!ret.isNumber());
4058 QVERIFY(ret.isObject());
4059 QCOMPARE(ret.toNumber(), qreal(0));
4062 QJSValue ret = eng.evaluate("new Number(123)");
4063 QVERIFY(!ret.isNumber());
4064 QVERIFY(ret.isObject());
4065 QCOMPARE(ret.toNumber(), qreal(123));
4068 QJSValue ret = eng.evaluate("new Number('456')");
4069 QVERIFY(!ret.isNumber());
4070 QVERIFY(ret.isObject());
4071 QCOMPARE(ret.toNumber(), qreal(456));
4074 QVERIFY(proto.property("toString").isCallable());
4076 QJSValue ret = eng.evaluate("new Number(123).toString()");
4077 QVERIFY(ret.isString());
4078 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4081 QJSValue ret = eng.evaluate("new Number(123).toString(8)");
4082 QVERIFY(ret.isString());
4083 QCOMPARE(ret.toString(), QString::fromLatin1("173"));
4086 QJSValue ret = eng.evaluate("new Number(123).toString(16)");
4087 QVERIFY(ret.isString());
4088 QCOMPARE(ret.toString(), QString::fromLatin1("7b"));
4090 QVERIFY(proto.property("toLocaleString").isCallable());
4092 QJSValue ret = eng.evaluate("new Number(123).toLocaleString()");
4093 QVERIFY(ret.isString());
4094 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4096 QVERIFY(proto.property("valueOf").isCallable());
4098 QJSValue ret = eng.evaluate("new Number(123).valueOf()");
4099 QVERIFY(ret.isNumber());
4100 QCOMPARE(ret.toNumber(), qreal(123));
4102 QVERIFY(proto.property("toExponential").isCallable());
4104 QJSValue ret = eng.evaluate("new Number(123).toExponential()");
4105 QVERIFY(ret.isString());
4106 QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2"));
4108 QVERIFY(proto.property("toFixed").isCallable());
4110 QJSValue ret = eng.evaluate("new Number(123).toFixed()");
4111 QVERIFY(ret.isString());
4112 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4114 QVERIFY(proto.property("toPrecision").isCallable());
4116 QJSValue ret = eng.evaluate("new Number(123).toPrecision()");
4117 QVERIFY(ret.isString());
4118 QCOMPARE(ret.toString(), QString::fromLatin1("123"));
4122 void tst_QJSEngine::jsForInStatement_simple()
4126 QJSValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r");
4127 QStringList lst = qjsvalue_cast<QStringList>(ret);
4128 QVERIFY(lst.isEmpty());
4131 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4132 "for (var p in o) r[r.length] = p; r");
4133 QStringList lst = qjsvalue_cast<QStringList>(ret);
4134 QCOMPARE(lst.size(), 1);
4135 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4138 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
4139 "for (var p in o) r[r.length] = p; r");
4140 QStringList lst = qjsvalue_cast<QStringList>(ret);
4141 QCOMPARE(lst.size(), 2);
4142 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4143 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
4147 void tst_QJSEngine::jsForInStatement_prototypeProperties()
4151 QJSValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];"
4152 "for (var p in o) r[r.length] = p; r");
4153 QStringList lst = qjsvalue_cast<QStringList>(ret);
4154 QCOMPARE(lst.size(), 1);
4155 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4158 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];"
4159 "for (var p in o) r[r.length] = p; r");
4160 QStringList lst = qjsvalue_cast<QStringList>(ret);
4161 QCOMPARE(lst.size(), 2);
4162 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4163 QCOMPARE(lst.at(1), QString::fromLatin1("q"));
4166 // shadowed property
4167 QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];"
4168 "for (var p in o) r[r.length] = p; r");
4169 QStringList lst = qjsvalue_cast<QStringList>(ret);
4170 QCOMPARE(lst.size(), 1);
4171 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4176 void tst_QJSEngine::jsForInStatement_mutateWhileIterating()
4179 // deleting property during enumeration
4181 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4182 "for (var p in o) { r[r.length] = p; delete r[p]; } r");
4183 QStringList lst = qjsvalue_cast<QStringList>(ret);
4184 QCOMPARE(lst.size(), 1);
4185 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4188 QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];"
4189 "for (var p in o) { r[r.length] = p; delete o.q; } r");
4190 QStringList lst = qjsvalue_cast<QStringList>(ret);
4191 QCOMPARE(lst.size(), 1);
4192 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4195 // adding property during enumeration
4197 QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];"
4198 "for (var p in o) { r[r.length] = p; o.q = 456; } r");
4199 QStringList lst = qjsvalue_cast<QStringList>(ret);
4200 QCOMPARE(lst.size(), 1);
4201 QCOMPARE(lst.at(0), QString::fromLatin1("p"));
4206 void tst_QJSEngine::jsForInStatement_arrays()
4210 QJSValue ret = eng.evaluate("a = [123, 456]; r = [];"
4211 "for (var p in a) r[r.length] = p; r");
4212 QStringList lst = qjsvalue_cast<QStringList>(ret);
4213 QCOMPARE(lst.size(), 2);
4214 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4215 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4218 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];"
4219 "for (var p in a) r[r.length] = p; r");
4220 QStringList lst = qjsvalue_cast<QStringList>(ret);
4221 QCOMPARE(lst.size(), 3);
4222 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4223 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4224 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
4227 QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';"
4228 "b = [111, 222, 333]; b.bar = 'baz';"
4229 "a.__proto__ = b; r = [];"
4230 "for (var p in a) r[r.length] = p; r");
4231 QStringList lst = qjsvalue_cast<QStringList>(ret);
4232 QCOMPARE(lst.size(), 5);
4233 QCOMPARE(lst.at(0), QString::fromLatin1("0"));
4234 QCOMPARE(lst.at(1), QString::fromLatin1("1"));
4235 QCOMPARE(lst.at(2), QString::fromLatin1("foo"));
4236 QCOMPARE(lst.at(3), QString::fromLatin1("2"));
4237 QCOMPARE(lst.at(4), QString::fromLatin1("bar"));
4241 void tst_QJSEngine::jsForInStatement_nullAndUndefined()
4245 QJSValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r");
4246 QVERIFY(ret.isBool());
4247 QVERIFY(ret.toBool());
4250 QJSValue ret = eng.evaluate("r = true; for (var p in null) r = false; r");
4251 QVERIFY(ret.isBool());
4252 QVERIFY(ret.toBool());
4256 void tst_QJSEngine::jsFunctionDeclarationAsStatement()
4258 // ECMA-262 does not allow function declarations to be used as statements,
4259 // but several popular implementations (including JSC) do. See the NOTE
4260 // at the beginning of chapter 12 in ECMA-262 5th edition, where it's
4261 // recommended that implementations either disallow this usage or issue
4263 // Since we had a bug report long ago about QtScript not supporting this
4264 // "feature" (and thus deviating from other implementations), we still
4265 // check this behavior.
4268 QVERIFY(eng.globalObject().property("bar").isUndefined());
4269 eng.evaluate("function foo(arg) {\n"
4270 " if (arg == 'bar')\n"
4271 " function bar() { return 'bar'; }\n"
4273 " function baz() { return 'baz'; }\n"
4274 " return (arg == 'bar') ? bar : baz;\n"
4276 QVERIFY(eng.globalObject().property("bar").isUndefined());
4277 QVERIFY(eng.globalObject().property("baz").isUndefined());
4278 QVERIFY(eng.evaluate("foo").isCallable());
4280 QJSValue ret = eng.evaluate("foo('bar')");
4281 QVERIFY(ret.isCallable());
4282 QJSValue ret2 = ret.call();
4283 QCOMPARE(ret2.toString(), QString::fromLatin1("bar"));
4284 QVERIFY(eng.globalObject().property("bar").isUndefined());
4285 QVERIFY(eng.globalObject().property("baz").isUndefined());
4288 QJSValue ret = eng.evaluate("foo('baz')");
4289 QVERIFY(ret.isCallable());
4290 QJSValue ret2 = ret.call();
4291 QCOMPARE(ret2.toString(), QString::fromLatin1("baz"));
4292 QVERIFY(eng.globalObject().property("bar").isUndefined());
4293 QVERIFY(eng.globalObject().property("baz").isUndefined());
4297 void tst_QJSEngine::stringObjects()
4299 // See ECMA-262 Section 15.5, "String Objects".
4302 QString str("ciao");
4305 QJSValue obj = eng.evaluate(QString::fromLatin1("new String('%0')").arg(str));
4306 QCOMPARE(obj.property("length").toInt(), str.length());
4307 for (int i = 0; i < str.length(); ++i) {
4308 QString pname = QString::number(i);
4309 QVERIFY(obj.property(pname).isString());
4310 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4311 QEXPECT_FAIL("", "FIXME: This is V8 issue 862. ECMA script standard 15.5.5.2 compliance.", Continue);
4312 QVERIFY(!obj.deleteProperty(pname));
4313 obj.setProperty(pname, 123);
4314 QVERIFY(obj.property(pname).isString());
4315 QCOMPARE(obj.property(pname).toString(), QString(str.at(i)));
4317 QVERIFY(obj.property("-1").isUndefined());
4318 QVERIFY(obj.property(QString::number(str.length())).isUndefined());
4320 QJSValue val = eng.toScriptValue(123);
4321 obj.setProperty("-1", val);
4322 QVERIFY(obj.property("-1").strictlyEquals(val));
4323 obj.setProperty("100", val);
4324 QVERIFY(obj.property("100").strictlyEquals(val));
4328 QJSValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r");
4329 QVERIFY(ret.isArray());
4330 QStringList lst = qjsvalue_cast<QStringList>(ret);
4331 QCOMPARE(lst.size(), str.length());
4332 for (int i = 0; i < str.length(); ++i)
4333 QCOMPARE(lst.at(i), QString::number(i));
4335 QJSValue ret2 = eng.evaluate("s[0] = 123; s[0]");
4336 QVERIFY(ret2.isString());
4337 QCOMPARE(ret2.toString().length(), 1);
4338 QCOMPARE(ret2.toString().at(0), str.at(0));
4340 QJSValue ret3 = eng.evaluate("s[-1] = 123; s[-1]");
4341 QVERIFY(ret3.isNumber());
4342 QCOMPARE(ret3.toInt(), 123);
4344 QJSValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]");
4345 QVERIFY(ret4.isNumber());
4346 QCOMPARE(ret4.toInt(), 456);
4348 QJSValue ret5 = eng.evaluate("delete s[0]");
4349 QVERIFY(ret5.isBool());
4350 QEXPECT_FAIL("", "FIXME: This is V8 bug, please report it! ECMA script standard 15.5.5.2", Abort);
4351 QVERIFY(!ret5.toBool());
4353 QJSValue ret6 = eng.evaluate("delete s[-1]");
4354 QVERIFY(ret6.isBool());
4355 QVERIFY(ret6.toBool());
4357 QJSValue ret7 = eng.evaluate("delete s[s.length]");
4358 QVERIFY(ret7.isBool());
4359 QVERIFY(ret7.toBool());
4363 void tst_QJSEngine::jsStringPrototypeReplaceBugs()
4368 QJSValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args");
4369 QVERIFY(ret.isArray());
4370 int len = ret.property("length").toInt();
4372 for (int i = 0; i < len; ++i) {
4373 QJSValue args = ret.property(i);
4374 QCOMPARE(args.property("length").toInt(), 4);
4375 QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string
4376 QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture
4377 QVERIFY(args.property(2).isNumber());
4378 QCOMPARE(args.property(2).toInt(), i*2); // index of match
4379 QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a"));
4384 QJSValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})");
4385 QVERIFY(ret.isString());
4386 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4390 void tst_QJSEngine::getterSetterThisObject_global()
4395 eng.evaluate("__defineGetter__('x', function() { return this; });");
4397 QJSValue ret = eng.evaluate("x");
4398 QVERIFY(ret.equals(eng.globalObject()));
4401 QJSValue ret = eng.evaluate("(function() { return x; })()");
4402 QVERIFY(ret.equals(eng.globalObject()));
4405 QJSValue ret = eng.evaluate("with (this) x");
4406 QVERIFY(ret.equals(eng.globalObject()));
4409 QJSValue ret = eng.evaluate("with ({}) x");
4410 QVERIFY(ret.equals(eng.globalObject()));
4413 QJSValue ret = eng.evaluate("(function() { with ({}) return x; })()");
4414 QVERIFY(ret.equals(eng.globalObject()));
4417 eng.evaluate("__defineSetter__('x', function() { return this; });");
4419 QJSValue ret = eng.evaluate("x = 'foo'");
4420 // SpiderMonkey says setter return value, JSC says RHS.
4421 QVERIFY(ret.isString());
4422 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4425 QJSValue ret = eng.evaluate("(function() { return x = 'foo'; })()");
4426 // SpiderMonkey says setter return value, JSC says RHS.
4427 QVERIFY(ret.isString());
4428 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4431 QJSValue ret = eng.evaluate("with (this) x = 'foo'");
4432 // SpiderMonkey says setter return value, JSC says RHS.
4433 QVERIFY(ret.isString());
4434 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4437 QJSValue ret = eng.evaluate("with ({}) x = 'foo'");
4438 // SpiderMonkey says setter return value, JSC says RHS.
4439 QVERIFY(ret.isString());
4440 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4443 QJSValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()");
4444 // SpiderMonkey says setter return value, JSC says RHS.
4445 QVERIFY(ret.isString());
4446 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
4451 void tst_QJSEngine::getterSetterThisObject_plain()
4455 eng.evaluate("o = {}");
4457 eng.evaluate("o.__defineGetter__('x', function() { return this; })");
4458 QVERIFY(eng.evaluate("o.x === o").toBool());
4459 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4460 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
4461 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4463 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4464 // SpiderMonkey says setter return value, JSC says RHS.
4465 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
4466 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4467 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4471 void tst_QJSEngine::getterSetterThisObject_prototypeChain()
4475 eng.evaluate("o = {}; p = {}; o.__proto__ = p");
4477 eng.evaluate("p.__defineGetter__('x', function() { return this; })");
4478 QVERIFY(eng.evaluate("o.x === o").toBool());
4479 QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o")));
4480 QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBool());
4481 eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o"));
4482 eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o"));
4484 eng.evaluate("o.__defineSetter__('x', function() { return this; });");
4485 // SpiderMonkey says setter return value, JSC says RHS.
4486 QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBool());
4487 QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo"));
4488 QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo"));
4492 #if 0 // ###FIXME: no QScriptContext API
4493 void tst_QJSEngine::getterSetterThisObject_activation()
4497 QScriptContext *ctx = eng.pushContext();
4499 QScriptValue act = ctx->activationObject();
4500 act.setProperty("act", act);
4502 eng.evaluate("act.__defineGetter__('x', function() { return this; })");
4503 QVERIFY(eng.evaluate("x === act").toBool());
4504 QEXPECT_FAIL("", "QTBUG-17605: Not possible to implement local variables as getter/setter properties", Abort);
4505 QVERIFY(!eng.hasUncaughtException());
4506 QVERIFY(eng.evaluate("with (act) x").equals("foo"));
4507 QVERIFY(eng.evaluate("(function() { with (act) return x; })() === act").toBool());
4508 eng.evaluate("q = {}; with (act) with (q) x").equals(eng.evaluate("act"));
4509 eng.evaluate("with (q) with (act) x").equals(eng.evaluate("act"));
4511 eng.evaluate("act.__defineSetter__('x', function() { return this; });");
4512 QVERIFY(eng.evaluate("(x = 'foo') === 'foo'").toBool());
4513 QVERIFY(eng.evaluate("with (act) x = 'foo'").equals("foo"));
4514 QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals("foo"));
4520 void tst_QJSEngine::jsContinueInSwitch()
4522 // This is testing ECMA-262 compliance, not C++ API.
4525 // switch - continue
4527 QJSValue ret = eng.evaluate("switch (1) { default: continue; }");
4528 QVERIFY(ret.isError());
4530 // for - switch - case - continue
4532 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4534 " case 1: ++j; continue;\n"
4535 " case 100: ++j; continue;\n"
4536 " case 1000: ++j; continue;\n"
4539 QVERIFY(ret.isNumber());
4540 QCOMPARE(ret.toInt(), 3);
4542 // for - switch - case - default - continue
4544 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4546 " case 1: ++j; continue;\n"
4547 " case 100: ++j; continue;\n"
4548 " case 1000: ++j; continue;\n"
4549 " default: if (i < 50000) break; else continue;\n"
4552 QVERIFY(ret.isNumber());
4553 QCOMPARE(ret.toInt(), 3);
4555 // switch - for - continue
4557 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
4559 " for (i = 0; i < 100000; ++i) {\n"
4563 QVERIFY(ret.isNumber());
4564 QCOMPARE(ret.toInt(), 100000);
4566 // switch - switch - continue
4568 QJSValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }");
4569 QVERIFY(ret.isError());
4571 // for - switch - switch - continue
4573 QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n"
4577 " case 1: ++j; continue;\n"
4581 QVERIFY(ret.isNumber());
4582 QCOMPARE(ret.toInt(), 1);
4584 // switch - for - switch - continue
4586 QJSValue ret = eng.evaluate("j = 123; switch (j) {\n"
4588 " for (i = 0; i < 100000; ++i) {\n"
4595 QVERIFY(ret.isNumber());
4596 QCOMPARE(ret.toInt(), 100000);
4600 void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty()
4602 // SpiderMonkey has different behavior than JSC and V8; it disallows
4603 // creating a property on the instance if there's a property with the
4604 // same name in the prototype, and that property is read-only. We
4605 // adopted that behavior in the old (4.5) QtScript back-end, but it
4606 // just seems weird -- and non-compliant. Adopt the JSC behavior instead.
4608 QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber());
4609 QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt(), 123);
4610 QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBool());
4613 void tst_QJSEngine::jsReservedWords_data()
4615 QTest::addColumn<QString>("word");
4616 QTest::newRow("break") << QString("break");
4617 QTest::newRow("case") << QString("case");
4618 QTest::newRow("catch") << QString("catch");
4619 QTest::newRow("continue") << QString("continue");
4620 QTest::newRow("default") << QString("default");
4621 QTest::newRow("delete") << QString("delete");
4622 QTest::newRow("do") << QString("do");
4623 QTest::newRow("else") << QString("else");
4624 QTest::newRow("false") << QString("false");
4625 QTest::newRow("finally") << QString("finally");
4626 QTest::newRow("for") << QString("for");
4627 QTest::newRow("function") << QString("function");
4628 QTest::newRow("if") << QString("if");
4629 QTest::newRow("in") << QString("in");
4630 QTest::newRow("instanceof") << QString("instanceof");
4631 QTest::newRow("new") << QString("new");
4632 QTest::newRow("null") << QString("null");
4633 QTest::newRow("return") << QString("return");
4634 QTest::newRow("switch") << QString("switch");
4635 QTest::newRow("this") << QString("this");
4636 QTest::newRow("throw") << QString("throw");
4637 QTest::newRow("true") << QString("true");
4638 QTest::newRow("try") << QString("try");
4639 QTest::newRow("typeof") << QString("typeof");
4640 QTest::newRow("var") << QString("var");
4641 QTest::newRow("void") << QString("void");
4642 QTest::newRow("while") << QString("while");
4643 QTest::newRow("with") << QString("with");
4646 void tst_QJSEngine::jsReservedWords()
4648 // See ECMA-262 Section 7.6.1, "Reserved Words".
4649 // We prefer that the implementation is less strict than the spec; e.g.
4650 // it's good to allow reserved words as identifiers in object literals,
4651 // and when accessing properties using dot notation.
4653 QFETCH(QString, word);
4656 QJSValue ret = eng.evaluate(word + " = 123");
4657 QVERIFY(ret.isError());
4658 QString str = ret.toString();
4659 QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError"));
4663 QJSValue ret = eng.evaluate("var " + word + " = 123");
4664 QVERIFY(ret.isError());
4665 QVERIFY(ret.toString().startsWith("SyntaxError"));
4669 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4670 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
4671 QVERIFY(!ret.isError());
4672 QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word)));
4676 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
4677 // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC
4678 QVERIFY(!ret.isError());
4679 QVERIFY(ret.property(word).isNumber());
4682 // SpiderMonkey allows this, but we don't
4684 QJSValue ret = eng.evaluate("function " + word + "() {}");
4685 QVERIFY(ret.isError());
4686 QVERIFY(ret.toString().startsWith("SyntaxError"));
4690 void tst_QJSEngine::jsFutureReservedWords_data()
4692 QTest::addColumn<QString>("word");
4693 QTest::addColumn<bool>("allowed");
4694 QTest::newRow("abstract") << QString("abstract") << true;
4695 QTest::newRow("boolean") << QString("boolean") << true;
4696 QTest::newRow("byte") << QString("byte") << true;
4697 QTest::newRow("char") << QString("char") << true;
4698 QTest::newRow("class") << QString("class") << false;
4699 QTest::newRow("const") << QString("const") << false;
4700 QTest::newRow("debugger") << QString("debugger") << false;
4701 QTest::newRow("double") << QString("double") << true;
4702 QTest::newRow("enum") << QString("enum") << false;
4703 QTest::newRow("export") << QString("export") << false;
4704 QTest::newRow("extends") << QString("extends") << false;
4705 QTest::newRow("final") << QString("final") << true;
4706 QTest::newRow("float") << QString("float") << true;
4707 QTest::newRow("goto") << QString("goto") << true;
4708 QTest::newRow("implements") << QString("implements") << true;
4709 QTest::newRow("import") << QString("import") << false;
4710 QTest::newRow("int") << QString("int") << true;
4711 QTest::newRow("interface") << QString("interface") << true;
4712 QTest::newRow("long") << QString("long") << true;
4713 QTest::newRow("native") << QString("native") << true;
4714 QTest::newRow("package") << QString("package") << true;
4715 QTest::newRow("private") << QString("private") << true;
4716 QTest::newRow("protected") << QString("protected") << true;
4717 QTest::newRow("public") << QString("public") << true;
4718 QTest::newRow("short") << QString("short") << true;
4719 QTest::newRow("static") << QString("static") << true;
4720 QTest::newRow("super") << QString("super") << false;
4721 QTest::newRow("synchronized") << QString("synchronized") << true;
4722 QTest::newRow("throws") << QString("throws") << true;
4723 QTest::newRow("transient") << QString("transient") << true;
4724 QTest::newRow("volatile") << QString("volatile") << true;
4727 void tst_QJSEngine::jsFutureReservedWords()
4730 // See ECMA-262 Section 7.6.1.2, "Future Reserved Words".
4731 // In real-world implementations, most of these words are
4732 // actually allowed as normal identifiers.
4734 QFETCH(QString, word);
4735 QFETCH(bool, allowed);
4738 QJSValue ret = eng.evaluate(word + " = 123");
4739 QCOMPARE(!ret.isError(), allowed);
4743 QJSValue ret = eng.evaluate("var " + word + " = 123");
4744 QCOMPARE(!ret.isError(), allowed);
4747 // this should probably be allowed (see task 162567)
4749 QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123");
4750 QCOMPARE(ret.isNumber(), allowed);
4751 QCOMPARE(!ret.isError(), allowed);
4754 // this should probably be allowed (see task 162567)
4756 QJSValue ret = eng.evaluate("o = { " + word + ": 123 }");
4757 QCOMPARE(!ret.isError(), allowed);
4761 void tst_QJSEngine::jsThrowInsideWithStatement()
4763 // This is testing ECMA-262 compliance, not C++ API.
4768 QJSValue ret = eng.evaluate(
4770 " o = { bad : \"bug\" };"
4777 QVERIFY(ret.isError());
4778 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4781 QJSValue ret = eng.evaluate(
4783 " o = { bad : \"bug\" };"
4790 QVERIFY(ret.isError());
4791 QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError")));
4794 QJSValue ret = eng.evaluate(
4795 "o = { bug : \"no bug\" };"
4803 QVERIFY(!ret.isError());
4804 QVERIFY(ret.isNumber());
4805 QCOMPARE(ret.toInt(), 123);
4808 QJSValue ret = eng.evaluate(
4809 "o = { bug : \"no bug\" };"
4813 QVERIFY(ret.isNumber());
4814 QJSValue ret2 = eng.evaluate("bug");
4815 QVERIFY(ret2.isError());
4816 QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError")));
4820 #if 0 // ###FIXME: No QScriptEngineAgent API
4821 class TestAgent : public QScriptEngineAgent
4824 TestAgent(QScriptEngine *engine) : QScriptEngineAgent(engine) {}
4827 void tst_QJSEngine::getSetAgent_ownership()
4829 // engine deleted before agent --> agent deleted too
4830 QScriptEngine *eng = new QScriptEngine;
4831 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4832 TestAgent *agent = new TestAgent(eng);
4833 eng->setAgent(agent);
4834 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4835 eng->setAgent(0); // the engine maintains ownership of the old agent
4836 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4840 void tst_QJSEngine::getSetAgent_deleteAgent()
4842 // agent deleted before engine --> engine's agent should become 0
4843 QScriptEngine *eng = new QScriptEngine;
4844 TestAgent *agent = new TestAgent(eng);
4845 eng->setAgent(agent);
4846 QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent);
4848 QCOMPARE(eng->agent(), (QScriptEngineAgent*)0);
4849 eng->evaluate("(function(){ return 123; })()");
4853 void tst_QJSEngine::getSetAgent_differentEngine()
4857 TestAgent *agent = new TestAgent(&eng);
4858 QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine");
4859 eng2.setAgent(agent);
4860 QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0);
4864 #if 0 // ###FIXME: No QScriptString API
4865 void tst_QJSEngine::reentrancy_stringHandles()
4869 QScriptString s1 = eng1.toStringHandle("foo");
4870 QScriptString s2 = eng2.toStringHandle("foo");
4875 #if 0 // ###FIXME: No processEventsInterval API
4876 void tst_QJSEngine::reentrancy_processEventsInterval()
4880 eng1.setProcessEventsInterval(123);
4881 QCOMPARE(eng2.processEventsInterval(), -1);
4882 eng2.setProcessEventsInterval(456);
4883 QCOMPARE(eng1.processEventsInterval(), 123);
4887 #if 0 // FIXME: No support for custom types
4888 void tst_QJSEngine::reentrancy_typeConversion()
4892 qScriptRegisterMetaType<Foo>(&eng1, fooToScriptValue, fooFromScriptValue);
4897 QScriptValue fooVal = qScriptValueFromValue(&eng1, foo);
4898 QVERIFY(fooVal.isObject());
4899 QVERIFY(!fooVal.isVariant());
4900 QCOMPARE(fooVal.property("x").toInt(), 12);
4901 QCOMPARE(fooVal.property("y").toInt(), 34);
4902 fooVal.setProperty("x", 56);
4903 fooVal.setProperty("y", 78);
4905 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
4906 QCOMPARE(foo2.x, 56);
4907 QCOMPARE(foo2.y, 78);
4910 QScriptValue fooVal = qScriptValueFromValue(&eng2, foo);
4911 QVERIFY(fooVal.isVariant());
4913 Foo foo2 = eng.fromScriptValue<Foo>(fooVal);
4914 QCOMPARE(foo2.x, 12);
4915 QCOMPARE(foo2.y, 34);
4917 QVERIFY(eng1.defaultPrototype(qMetaTypeId<Foo>()).isUndefined());
4918 QVERIFY(eng2.defaultPrototype(qMetaTypeId<Foo>()).isUndefined());
4919 QScriptValue proto1 = eng1.newObject();
4920 eng1.setDefaultPrototype(qMetaTypeId<Foo>(), proto1);
4921 QVERIFY(eng2.defaultPrototype(qMetaTypeId<Foo>()).isUndefined());
4922 QScriptValue proto2 = eng2.newObject();
4923 eng2.setDefaultPrototype(qMetaTypeId<Foo>(), proto2);
4924 QVERIFY(!eng2.defaultPrototype(qMetaTypeId<Foo>()).isUndefined());
4925 QVERIFY(eng1.defaultPrototype(qMetaTypeId<Foo>()).strictlyEquals(proto1));
4929 void tst_QJSEngine::reentrancy_globalObjectProperties()
4933 QVERIFY(eng2.globalObject().property("a").isUndefined());
4934 eng1.evaluate("a = 10");
4935 QVERIFY(eng1.globalObject().property("a").isNumber());
4936 QVERIFY(eng2.globalObject().property("a").isUndefined());
4937 eng2.evaluate("a = 20");
4938 QVERIFY(eng2.globalObject().property("a").isNumber());
4939 QCOMPARE(eng1.globalObject().property("a").toInt(), 10);
4942 void tst_QJSEngine::reentrancy_Array()
4944 // weird bug with JSC backend
4947 QCOMPARE(eng.evaluate("Array()").toString(), QString());
4948 eng.evaluate("Array.prototype.toString");
4949 QCOMPARE(eng.evaluate("Array()").toString(), QString());
4953 QCOMPARE(eng.evaluate("Array()").toString(), QString());
4957 void tst_QJSEngine::reentrancy_objectCreation()
4963 QDateTime dt = QDateTime::currentDateTime();
4964 QJSValue d1 = eng1.toScriptValue(dt);
4965 QJSValue d2 = eng2.toScriptValue(dt);
4966 QCOMPARE(d1.toDateTime(), d2.toDateTime());
4967 QCOMPARE(d2.toDateTime(), d1.toDateTime());
4970 QJSValue r1 = eng1.evaluate("new RegExp('foo', 'gim')");
4971 QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')");
4972 QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2));
4973 QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1));
4976 QJSValue o1 = eng1.newQObject(&temp);
4977 QJSValue o2 = eng2.newQObject(&temp);
4978 QCOMPARE(o1.toQObject(), o2.toQObject());
4979 QCOMPARE(o2.toQObject(), o1.toQObject());
4981 #if 0 // ###FIXME: No QScriptEngine::newQMetaObject API
4983 QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject);
4984 QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject);
4985 QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject());
4986 QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject());
4991 void tst_QJSEngine::jsIncDecNonObjectProperty()
4993 // This is testing ECMA-262 compliance, not C++ API.
4997 QJSValue ret = eng.evaluate("var a; a.n++");
4998 QVERIFY(ret.isError());
4999 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5002 QJSValue ret = eng.evaluate("var a; a.n--");
5003 QVERIFY(ret.isError());
5004 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5007 QJSValue ret = eng.evaluate("var a = null; a.n++");
5008 QVERIFY(ret.isError());
5009 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5012 QJSValue ret = eng.evaluate("var a = null; a.n--");
5013 QVERIFY(ret.isError());
5014 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5017 QJSValue ret = eng.evaluate("var a; ++a.n");
5018 QVERIFY(ret.isError());
5019 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5022 QJSValue ret = eng.evaluate("var a; --a.n");
5023 QVERIFY(ret.isError());
5024 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5027 QJSValue ret = eng.evaluate("var a; a.n += 1");
5028 QVERIFY(ret.isError());
5029 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5032 QJSValue ret = eng.evaluate("var a; a.n -= 1");
5033 QVERIFY(ret.isError());
5034 QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError")));
5037 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length++");
5038 QVERIFY(ret.isNumber());
5039 QCOMPARE(ret.toInt(), 4);
5042 QJSValue ret = eng.evaluate("var a = 'ciao'; a.length--");
5043 QVERIFY(ret.isNumber());
5044 QCOMPARE(ret.toInt(), 4);
5047 QJSValue ret = eng.evaluate("var a = 'ciao'; ++a.length");
5048 QVERIFY(ret.isNumber());
5049 QCOMPARE(ret.toInt(), 5);
5052 QJSValue ret = eng.evaluate("var a = 'ciao'; --a.length");
5053 QVERIFY(ret.isNumber());
5054 QCOMPARE(ret.toInt(), 3);
5058 #if 0 // ###FIXME: no installTranslatorFunctions API
5059 void tst_QJSEngine::installTranslatorFunctions()
5062 QScriptValue global = eng.globalObject();
5063 QVERIFY(global.property("qsTranslate").isUndefined());
5064 QVERIFY(global.property("QT_TRANSLATE_NOOP").isUndefined());
5065 QVERIFY(global.property("qsTr").isUndefined());
5066 QVERIFY(global.property("QT_TR_NOOP").isUndefined());
5067 QVERIFY(global.property("qsTrId").isUndefined());
5068 QVERIFY(global.property("QT_TRID_NOOP").isUndefined());
5069 QVERIFY(global.property("String").property("prototype").property("arg").isUndefined());
5071 eng.installTranslatorFunctions();
5072 QVERIFY(global.property("qsTranslate").isCallable());
5073 QVERIFY(global.property("QT_TRANSLATE_NOOP").isCallable());
5074 QVERIFY(global.property("qsTr").isCallable());
5075 QVERIFY(global.property("QT_TR_NOOP").isCallable());
5076 QVERIFY(global.property("qsTrId").isCallable());
5077 QVERIFY(global.property("QT_TRID_NOOP").isCallable());
5078 QVERIFY(global.property("String").property("prototype").property("arg").isCallable());
5081 QScriptValue ret = eng.evaluate("qsTr('foo')");
5082 QVERIFY(ret.isString());
5083 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5086 QScriptValue ret = eng.evaluate("qsTranslate('foo', 'bar')");
5087 QVERIFY(ret.isString());
5088 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
5091 QScriptValue ret = eng.evaluate("QT_TR_NOOP('foo')");
5092 QVERIFY(ret.isString());
5093 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5096 QScriptValue ret = eng.evaluate("QT_TRANSLATE_NOOP('foo', 'bar')");
5097 QVERIFY(ret.isString());
5098 QCOMPARE(ret.toString(), QString::fromLatin1("bar"));
5101 QScriptValue ret = eng.evaluate("'foo%0'.arg('bar')");
5102 QEXPECT_FAIL("Custom global object", "FIXME: why we expect that String prototype exists?", Abort);
5103 QVERIFY(ret.isString());
5104 QCOMPARE(ret.toString(), QString::fromLatin1("foobar"));
5107 QScriptValue ret = eng.evaluate("'foo%0'.arg(123)");
5108 QVERIFY(ret.isString());
5109 QCOMPARE(ret.toString(), QString::fromLatin1("foo123"));
5112 // Maybe this should throw an error?
5113 QScriptValue ret = eng.evaluate("'foo%0'.arg()");
5114 QVERIFY(ret.isString());
5115 QCOMPARE(ret.toString(), QString());
5119 QScriptValue ret = eng.evaluate("qsTrId('foo')");
5120 QVERIFY(ret.isString());
5121 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5124 QScriptValue ret = eng.evaluate("QT_TRID_NOOP('foo')");
5125 QVERIFY(ret.isString());
5126 QCOMPARE(ret.toString(), QString::fromLatin1("foo"));
5128 QVERIFY(eng.evaluate("QT_TRID_NOOP()").isUndefined());
5131 class TranslationScope
5134 TranslationScope(const QString &fileName)
5136 translator.load(fileName);
5137 QCoreApplication::instance()->installTranslator(&translator);
5141 QCoreApplication::instance()->removeTranslator(&translator);
5145 QTranslator translator;
5148 void tst_QJSEngine::translateScript_data()
5150 QTest::addColumn<QString>("expression");
5151 QTest::addColumn<QString>("fileName");
5152 QTest::addColumn<QString>("expectedTranslation");
5154 QString fileName = QString::fromLatin1("translatable.js");
5156 QTest::newRow("qsTr('One')@translatable.js")
5157 << QString::fromLatin1("qsTr('One')") << fileName << QString::fromLatin1("En");
5158 QTest::newRow("qsTr('Hello')@translatable.js")
5159 << QString::fromLatin1("qsTr('Hello')") << fileName << QString::fromLatin1("Hallo");
5161 QTest::newRow("(function() { return qsTr('One'); })()@translatable.js")
5162 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName << QString::fromLatin1("En");
5163 QTest::newRow("(function() { return qsTr('Hello'); })()@translatable.js")
5164 << QString::fromLatin1("(function() { return qsTr('Hello'); })()") << fileName << QString::fromLatin1("Hallo");
5166 QTest::newRow("eval('qsTr(\\'One\\')')@translatable.js")
5167 << QString::fromLatin1("eval('qsTr(\\'One\\')')") << fileName << QString::fromLatin1("En");
5168 QTest::newRow("eval('qsTr(\\'Hello\\')')@translatable.js")
5169 << QString::fromLatin1("eval('qsTr(\\'Hello\\')')") << fileName << QString::fromLatin1("Hallo");
5171 QTest::newRow("qsTr('%n message(s) saved', '', 1)@translatable.js")
5172 << QString::fromLatin1("qsTr('%n message(s) saved', '', 1)") << fileName << QString::fromLatin1("1 melding lagret");
5173 QTest::newRow("qsTr('%n message(s) saved', '', 3).arg@translatable.js")
5174 << QString::fromLatin1("qsTr('%n message(s) saved', '', 3)") << fileName << QString::fromLatin1("3 meldinger lagret");
5177 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable.js")
5178 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName << QString::fromLatin1("To");
5179 QTest::newRow("qsTranslate('FooContext', 'Goodbye')@translatable.js")
5180 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye')") << fileName << QString::fromLatin1("Farvel");
5182 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')@translatable.js")
5183 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')") << fileName << QString::fromLatin1("To");
5184 QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')@translatable.js")
5185 << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')") << fileName << QString::fromLatin1("Farvel");
5187 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')@translatable.js")
5188 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')") << fileName << QString::fromLatin1("Farvel");
5189 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')@translatable.js")
5190 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')") << fileName << QString::fromLatin1("Farvel");
5192 QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)@translatable.js")
5193 << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)") << fileName << QString::fromLatin1("Goodbye");
5195 QTest::newRow("qsTr('One', 'not the same one')@translatable.js")
5196 << QString::fromLatin1("qsTr('One', 'not the same one')") << fileName << QString::fromLatin1("Enda en");
5198 QTest::newRow("qsTr('One', 'not the same one', 42)@translatable.js")
5199 << QString::fromLatin1("qsTr('One', 'not the same one', 42)") << fileName << QString::fromLatin1("One");
5202 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)@translatable.js")
5203 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)") << fileName << QString::fromLatin1("1 fooaktig bar funnet");
5204 QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)@translatable.js")
5205 << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)") << fileName << QString::fromLatin1("2 fooaktige barer funnet");
5207 // Don't exist in translation
5208 QTest::newRow("qsTr('Three')@translatable.js")
5209 << QString::fromLatin1("qsTr('Three')") << fileName << QString::fromLatin1("Three");
5210 QTest::newRow("qsTranslate('FooContext', 'So long')@translatable.js")
5211 << QString::fromLatin1("qsTranslate('FooContext', 'So long')") << fileName << QString::fromLatin1("So long");
5212 QTest::newRow("qsTranslate('BarContext', 'Goodbye')@translatable.js")
5213 << QString::fromLatin1("qsTranslate('BarContext', 'Goodbye')") << fileName << QString::fromLatin1("Goodbye");
5215 // Translate strings from the second script (translatable2.js)
5217 QString fileName2 = QString::fromLatin1("translatable2.js");
5218 QTest::newRow("qsTr('Three')@translatable2.js")
5219 << QString::fromLatin1("qsTr('Three')") << fileName2 << QString::fromLatin1("Tre");
5220 QTest::newRow("qsTr('Happy birthday!')@translatable2.js")
5221 << QString::fromLatin1("qsTr('Happy birthday!')") << fileName2 << QString::fromLatin1("Gratulerer med dagen!");
5223 // Not translated because translation is only in translatable.js
5224 QTest::newRow("qsTr('One')@translatable2.js")
5225 << QString::fromLatin1("qsTr('One')") << fileName2 << QString::fromLatin1("One");
5226 QTest::newRow("(function() { return qsTr('One'); })()@translatable2.js")
5227 << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName2 << QString::fromLatin1("One");
5229 // For qsTranslate() the filename shouldn't matter
5230 QTest::newRow("qsTranslate('FooContext', 'Two')@translatable2.js")
5231 << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName2 << QString::fromLatin1("To");
5232 QTest::newRow("qsTranslate('BarContext', 'Congratulations!')@translatable.js")
5233 << QString::fromLatin1("qsTranslate('BarContext', 'Congratulations!')") << fileName << QString::fromLatin1("Gratulerer!");
5236 void tst_QJSEngine::translateScript()
5238 QFETCH(QString, expression);
5239 QFETCH(QString, fileName);
5240 QFETCH(QString, expectedTranslation);
5242 QScriptEngine engine;
5244 TranslationScope tranScope(":/translations/translatable_la");
5245 engine.installTranslatorFunctions();
5247 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5248 QVERIFY(!engine.hasUncaughtException());
5251 void tst_QJSEngine::translateScript_crossScript()
5253 QScriptEngine engine;
5254 TranslationScope tranScope(":/translations/translatable_la");
5255 engine.installTranslatorFunctions();
5257 QString fileName = QString::fromLatin1("translatable.js");
5258 QString fileName2 = QString::fromLatin1("translatable2.js");
5259 // qsTr() should use the innermost filename as context
5260 engine.evaluate("function foo(s) { return bar(s); }", fileName);
5261 engine.evaluate("function bar(s) { return qsTr(s); }", fileName2);
5262 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5263 QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre"));
5264 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One"));
5266 engine.evaluate("function foo(s) { return bar(s); }", fileName2);
5267 engine.evaluate("function bar(s) { return qsTr(s); }", fileName);
5268 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three"));
5269 QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En"));
5270 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En"));
5273 static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng)
5275 return eng->globalObject().property("qsTr").callWithInstance(ctx->thisObject(), ctx->argumentsObject());
5278 void tst_QJSEngine::translateScript_callQsTrFromNative()
5280 QScriptEngine engine;
5281 TranslationScope tranScope(":/translations/translatable_la");
5282 engine.installTranslatorFunctions();
5284 QString fileName = QString::fromLatin1("translatable.js");
5285 QString fileName2 = QString::fromLatin1("translatable2.js");
5286 // Calling qsTr() from a native function
5287 engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr));
5288 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En"));
5289 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One"));
5290 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three"));
5291 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
5294 void tst_QJSEngine::translateScript_trNoOp()
5296 QScriptEngine engine;
5297 TranslationScope tranScope(":/translations/translatable_la");
5298 engine.installTranslatorFunctions();
5300 QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined());
5301 QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One"));
5303 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined());
5304 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined());
5305 QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two"));
5308 void tst_QJSEngine::translateScript_callQsTrFromCpp()
5310 QScriptEngine engine;
5311 TranslationScope tranScope(":/translations/translatable_la");
5312 engine.installTranslatorFunctions();
5314 // There is no context, but it shouldn't crash
5315 QCOMPARE(engine.globalObject().property("qsTr").call(
5316 QScriptValueList() << "One").toString(), QString::fromLatin1("One"));
5319 void tst_QJSEngine::translateWithInvalidArgs_data()
5321 QTest::addColumn<QString>("expression");
5322 QTest::addColumn<QString>("expectedError");
5324 QTest::newRow("qsTr()") << "qsTr()" << "Error: qsTr() requires at least one argument";
5325 QTest::newRow("qsTr(123)") << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string";
5326 QTest::newRow("qsTr('foo', 123)") << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string";
5327 QTest::newRow("qsTr('foo', 'bar', 'baz')") << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number";
5328 QTest::newRow("qsTr('foo', 'bar', true)") << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number";
5330 QTest::newRow("qsTranslate()") << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments";
5331 QTest::newRow("qsTranslate('foo')") << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments";
5332 QTest::newRow("qsTranslate(123, 'foo')") << "qsTranslate(123, 'foo')" << "Error: qsTranslate(): first argument (context) must be a string";
5333 QTest::newRow("qsTranslate('foo', 123)") << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string";
5334 QTest::newRow("qsTranslate('foo', 'bar', 123)") << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string";
5335 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string";
5336 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number";
5337 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'";
5339 QTest::newRow("qsTrId()") << "qsTrId()" << "Error: qsTrId() requires at least one argument";
5340 QTest::newRow("qsTrId(123)") << "qsTrId(123)" << "TypeError: qsTrId(): first argument (id) must be a string";
5341 QTest::newRow("qsTrId('foo', 'bar')") << "qsTrId('foo', 'bar')" << "TypeError: qsTrId(): second argument (n) must be a number";
5344 void tst_QJSEngine::translateWithInvalidArgs()
5346 QFETCH(QString, expression);
5347 QFETCH(QString, expectedError);
5348 QScriptEngine engine;
5349 engine.installTranslatorFunctions();
5350 QScriptValue result = engine.evaluate(expression);
5351 QVERIFY(result.isError());
5352 QCOMPARE(result.toString(), expectedError);
5355 void tst_QJSEngine::translationContext_data()
5357 QTest::addColumn<QString>("path");
5358 QTest::addColumn<QString>("text");
5359 QTest::addColumn<QString>("expectedTranslation");
5361 QTest::newRow("translatable.js") << "translatable.js" << "One" << "En";
5362 QTest::newRow("/translatable.js") << "/translatable.js" << "One" << "En";
5363 QTest::newRow("/foo/translatable.js") << "/foo/translatable.js" << "One" << "En";
5364 QTest::newRow("/foo/bar/translatable.js") << "/foo/bar/translatable.js" << "One" << "En";
5365 QTest::newRow("./translatable.js") << "./translatable.js" << "One" << "En";
5366 QTest::newRow("../translatable.js") << "../translatable.js" << "One" << "En";
5367 QTest::newRow("foo/translatable.js") << "foo/translatable.js" << "One" << "En";
5368 QTest::newRow("file:///home/qt/translatable.js") << "file:///home/qt/translatable.js" << "One" << "En";
5369 QTest::newRow(":/resources/translatable.js") << ":/resources/translatable.js" << "One" << "En";
5370 QTest::newRow("/translatable.js.foo") << "/translatable.js.foo" << "One" << "En";
5371 QTest::newRow("/translatable.txt") << "/translatable.txt" << "One" << "En";
5372 QTest::newRow("translatable") << "translatable" << "One" << "En";
5373 QTest::newRow("foo/translatable") << "foo/translatable" << "One" << "En";
5375 QTest::newRow("native separators")
5376 << (QDir::toNativeSeparators(QDir::currentPath()) + QDir::separator() + "translatable.js")
5379 QTest::newRow("translatable.js/") << "translatable.js/" << "One" << "One";
5380 QTest::newRow("nosuchscript.js") << "" << "One" << "One";
5381 QTest::newRow("(empty)") << "" << "One" << "One";
5384 void tst_QJSEngine::translationContext()
5386 TranslationScope tranScope(":/translations/translatable_la");
5388 QScriptEngine engine;
5389 engine.installTranslatorFunctions();
5391 QFETCH(QString, path);
5392 QFETCH(QString, text);
5393 QFETCH(QString, expectedTranslation);
5394 QScriptValue ret = engine.evaluate(QString::fromLatin1("qsTr('%0')").arg(text), path);
5395 QVERIFY(ret.isString());
5396 QCOMPARE(ret.toString(), expectedTranslation);
5399 void tst_QJSEngine::translateScriptIdBased()
5401 QScriptEngine engine;
5403 TranslationScope tranScope(":/translations/idtranslatable_la");
5404 engine.installTranslatorFunctions();
5406 QString fileName = QString::fromLatin1("idtranslatable.js");
5408 QHash<QString, QString> expectedTranslations;
5409 expectedTranslations["qtn_foo_bar"] = "First string";
5410 expectedTranslations["qtn_needle"] = "Second string";
5411 expectedTranslations["qtn_haystack"] = "Third string";
5412 expectedTranslations["qtn_bar_baz"] = "Fourth string";
5414 QHash<QString, QString>::const_iterator it;
5415 for (it = expectedTranslations.constBegin(); it != expectedTranslations.constEnd(); ++it) {
5416 for (int x = 0; x < 2; ++x) {
5421 QCOMPARE(engine.evaluate(QString::fromLatin1("qsTrId('%0')")
5422 .arg(it.key()), fn).toString(),
5424 QCOMPARE(engine.evaluate(QString::fromLatin1("QT_TRID_NOOP('%0')")
5425 .arg(it.key()), fn).toString(),
5428 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return qsTrId('%0'); })()")
5429 .arg(it.key()), fn).toString(),
5431 QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return QT_TRID_NOOP('%0'); })()")
5432 .arg(it.key()), fn).toString(),
5438 QCOMPARE(engine.evaluate("qsTrId('qtn_bar_baz', 10)").toString(),
5439 QString::fromLatin1("10 fooish bar(s) found"));
5440 QCOMPARE(engine.evaluate("qsTrId('qtn_foo_bar', 10)").toString(),
5441 QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural
5444 // How to add a new test row:
5445 // - Find a nice list of Unicode characters to choose from
5446 // - Write source string/context/comment in .js using Unicode escape sequences (\uABCD)
5447 // - Update corresponding .ts file (e.g. lupdate foo.js -ts foo.ts -codecfortr UTF-8)
5448 // - Enter translation in Linguist
5449 // - Update corresponding .qm file (e.g. lrelease foo.ts)
5450 // - Evaluate script that performs translation; make sure the correct result is returned
5451 // (e.g. by setting the resulting string as the text of a QLabel and visually verifying
5452 // that it looks the same as what you entered in Linguist :-) )
5453 // - Generate the expectedTranslation column data using toUtf8().toHex()
5454 void tst_QJSEngine::translateScriptUnicode_data()
5456 QTest::addColumn<QString>("expression");
5457 QTest::addColumn<QString>("fileName");
5458 QTest::addColumn<QString>("expectedTranslation");
5460 QString fileName = QString::fromLatin1("translatable-unicode.js");
5461 QTest::newRow("qsTr('H\\u2082O')@translatable-unicode.js")
5462 << QString::fromLatin1("qsTr('H\\u2082O')") << fileName << QString::fromUtf8("\xcd\xbb\xcd\xbc\xcd\xbd");
5463 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')@translatable-unicode.js")
5464 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << fileName << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5465 QTest::newRow("qsTr('\\u0391\\u0392\\u0393')@translatable-unicode.js")
5466 << QString::fromLatin1("qsTr('\\u0391\\u0392\\u0393')") << fileName << QString::fromUtf8("\xd3\x9c\xd2\xb4\xd1\xbc");
5467 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')@translatable-unicode.js")
5468 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')") << fileName << QString::fromUtf8("\xd8\xae\xd8\xb3\xd8\xb3");
5469 QTest::newRow("qsTr('H\\u2082O', 'not the same H\\u2082O')@translatable-unicode.js")
5470 << QString::fromLatin1("qsTr('H\\u2082O', 'not the same H\\u2082O')") << fileName << QString::fromUtf8("\xd4\xb6\xd5\x8a\xd5\x92");
5471 QTest::newRow("qsTr('H\\u2082O')")
5472 << QString::fromLatin1("qsTr('H\\u2082O')") << QString() << QString::fromUtf8("\x48\xe2\x82\x82\x4f");
5473 QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')")
5474 << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << QString() << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2");
5477 void tst_QJSEngine::translateScriptUnicode()
5479 QFETCH(QString, expression);
5480 QFETCH(QString, fileName);
5481 QFETCH(QString, expectedTranslation);
5483 QScriptEngine engine;
5485 TranslationScope tranScope(":/translations/translatable-unicode");
5486 engine.installTranslatorFunctions();
5488 QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation);
5489 QVERIFY(!engine.hasUncaughtException());
5492 void tst_QJSEngine::translateScriptUnicodeIdBased_data()
5494 QTest::addColumn<QString>("expression");
5495 QTest::addColumn<QString>("expectedTranslation");
5497 QTest::newRow("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1'')")
5498 << QString::fromLatin1("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1')") << QString::fromUtf8("\xc6\xa7\xc6\xb0\xc6\x88\xc8\xbc\xc8\x9d\xc8\xbf\xc8\x99");
5499 QTest::newRow("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')")
5500 << QString::fromLatin1("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')") << QString::fromUtf8("\xc7\xa0\xc8\xa1\xc8\x8b\xc8\x85\xc8\x95");
5501 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)")
5502 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)") << QString::fromUtf8("\x31\x30\x20\xc6\x92\xc6\xa1\xc7\x92\x28\xc8\x99\x29");
5503 QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')")
5504 << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')") << QString::fromUtf8("\xc6\x91\xc6\xb0\xc7\xb9");
5505 QTest::newRow("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')")
5506 << QString::fromLatin1("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')") << QString::fromUtf8("\xc7\x8d\xc6\x80\xc6\xa8\xc6\x90\xc6\x9e\xc6\xab");
5509 void tst_QJSEngine::translateScriptUnicodeIdBased()
5511 QFETCH(QString, expression);
5512 QFETCH(QString, expectedTranslation);
5514 QScriptEngine engine;
5516 TranslationScope tranScope(":/translations/idtranslatable-unicode");
5517 engine.installTranslatorFunctions();
5519 QCOMPARE(engine.evaluate(expression).toString(), expectedTranslation);
5520 QVERIFY(!engine.hasUncaughtException());
5523 void tst_QJSEngine::translateFromBuiltinCallback()
5526 eng.installTranslatorFunctions();
5528 // Callback has no translation context.
5529 eng.evaluate("function foo() { qsTr('foo'); }");
5531 // Stack at translation time will be:
5532 // qsTr, foo, forEach, global
5533 // qsTr() needs to walk to the outer-most (global) frame before it finds
5534 // a translation context, and this should not crash.
5535 eng.evaluate("[10,20].forEach(foo)", "script.js");
5539 #if 0 // ###FIXME: No QScriptValue::scope API
5540 void tst_QJSEngine::functionScopes()
5544 // top-level functions have only the global object in their scope
5545 QScriptValue fun = eng.evaluate("(function() {})");
5546 QVERIFY(fun.isCallable());
5547 QEXPECT_FAIL("", "QScriptValue::scope() is internal, not implemented", Abort);
5548 QVERIFY(fun.scope().isObject());
5549 QVERIFY(fun.scope().strictlyEquals(eng.globalObject()));
5550 QVERIFY(eng.globalObject().scope().isUndefined());
5553 QScriptValue fun = eng.globalObject().property("Object");
5554 QVERIFY(fun.isCallable());
5555 // native built-in functions don't have scope
5556 QVERIFY(fun.scope().isUndefined());
5560 QScriptValue fun = eng.evaluate("(function(arg) { var foo = arg; return function() { return foo; }; })(123)");
5561 QVERIFY(fun.isCallable());
5563 QScriptValue ret = fun.call();
5564 QVERIFY(ret.isNumber());
5565 QCOMPARE(ret.toInt(), 123);
5567 QScriptValue scope = fun.scope();
5568 QVERIFY(scope.isObject());
5570 QScriptValue ret = scope.property("foo");
5571 QVERIFY(ret.isNumber());
5572 QCOMPARE(ret.toInt(), 123);
5575 QScriptValue ret = scope.property("arg");
5576 QVERIFY(ret.isNumber());
5577 QCOMPARE(ret.toInt(), 123);
5580 scope.setProperty("foo", 456);
5582 QScriptValue ret = fun.call();
5583 QVERIFY(ret.isNumber());
5584 QCOMPARE(ret.toInt(), 456);
5587 scope = scope.scope();
5588 QVERIFY(scope.isObject());
5589 QVERIFY(scope.strictlyEquals(eng.globalObject()));
5594 #if 0 // ###FIXME: No QScriptContext API
5595 static QScriptValue counter_inner(QScriptContext *ctx, QScriptEngine *)
5597 QScriptValue outerAct = ctx->callee().scope();
5598 double count = outerAct.property("count").toNumber();
5599 outerAct.setProperty("count", count+1);
5603 static QScriptValue counter(QScriptContext *ctx, QScriptEngine *eng)
5605 QScriptValue act = ctx->activationObject();
5606 act.setProperty("count", ctx->argument(0).toInt());
5607 QScriptValue result = eng->newFunction(counter_inner);
5608 result.setScope(act);
5612 static QScriptValue counter_hybrid(QScriptContext *ctx, QScriptEngine *eng)
5614 QScriptValue act = ctx->activationObject();
5615 act.setProperty("count", ctx->argument(0).toInt());
5616 return eng->evaluate("(function() { return count++; })");
5619 void tst_QJSEngine::nativeFunctionScopes()
5623 QScriptValue fun = eng.newFunction(counter);
5624 QScriptValue cnt = fun.call(QScriptValueList() << 123);
5625 QVERIFY(cnt.isCallable());
5627 QScriptValue ret = cnt.call();
5628 QVERIFY(ret.isNumber());
5629 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5630 QCOMPARE(ret.toInt(), 123);
5634 QScriptValue fun = eng.newFunction(counter_hybrid);
5635 QScriptValue cnt = fun.call(QScriptValueList() << 123);
5636 QVERIFY(cnt.isCallable());
5638 QScriptValue ret = cnt.call();
5639 QVERIFY(ret.isNumber());
5640 QCOMPARE(ret.toInt(), 123);
5644 //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain
5647 eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n"
5648 "var c1 = counter(); var c2 = counter(); ");
5649 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5650 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5651 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5652 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5653 QVERIFY(!eng.hasUncaughtException());
5657 eng.globalObject().setProperty("counter", eng.newFunction(counter));
5658 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5659 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5660 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5661 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5662 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5663 QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue);
5664 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5665 QVERIFY(!eng.hasUncaughtException());
5669 eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid));
5670 eng.evaluate("var c1 = counter(); var c2 = counter(); ");
5671 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0"));
5672 QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1"));
5673 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0"));
5674 QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1"));
5675 QVERIFY(!eng.hasUncaughtException());
5680 #if 0 // ###FIXME: No QScriptProgram API
5681 static QScriptValue createProgram(QScriptContext *ctx, QScriptEngine *eng)
5683 QString code = ctx->argument(0).toString();
5684 QScriptProgram result(code);
5685 return qScriptValueFromValue(eng, result);
5688 void tst_QJSEngine::evaluateProgram()
5693 QString code("1 + 2");
5694 QString fileName("hello.js");
5695 int lineNumber(123);
5696 QScriptProgram program(code, fileName, lineNumber);
5697 QVERIFY(!program.isNull());
5698 QCOMPARE(program.sourceCode(), code);
5699 QCOMPARE(program.fileName(), fileName);
5700 QCOMPARE(program.firstLineNumber(), lineNumber);
5702 QScriptValue expected = eng.evaluate(code);
5703 for (int x = 0; x < 10; ++x) {
5704 QScriptValue ret = eng.evaluate(program);
5705 QVERIFY(ret.equals(expected));
5709 QScriptProgram sameProgram = program;
5710 QVERIFY(sameProgram == program);
5711 QVERIFY(eng.evaluate(sameProgram).equals(expected));
5714 QScriptProgram sameProgram2(program);
5715 QVERIFY(sameProgram2 == program);
5716 QVERIFY(eng.evaluate(sameProgram2).equals(expected));
5718 QScriptProgram differentProgram("2 + 3");
5719 QVERIFY(differentProgram != program);
5720 QVERIFY(!eng.evaluate(differentProgram).equals(expected));
5724 void tst_QJSEngine::evaluateProgram_customScope()
5728 QScriptProgram program("a");
5729 QVERIFY(!program.isNull());
5731 QScriptValue ret = eng.evaluate(program);
5732 QVERIFY(ret.isError());
5733 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined"));
5736 QScriptValue obj = eng.newObject();
5737 obj.setProperty("a", 123);
5738 QScriptContext *ctx = eng.currentContext();
5739 ctx->pushScope(obj);
5741 QScriptValue ret = eng.evaluate(program);
5742 QVERIFY(!ret.isError());
5743 QVERIFY(ret.equals(obj.property("a")));
5746 obj.setProperty("a", QScriptValue());
5748 QScriptValue ret = eng.evaluate(program);
5749 QVERIFY(ret.isError());
5752 QScriptValue obj2 = eng.newObject();
5753 obj2.setProperty("a", 456);
5754 ctx->pushScope(obj2);
5756 QScriptValue ret = eng.evaluate(program);
5757 QVERIFY(!ret.isError());
5758 QVERIFY(ret.equals(obj2.property("a")));
5765 void tst_QJSEngine::evaluateProgram_closure()
5769 QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })");
5770 QVERIFY(!program.isNull());
5771 QScriptValue createCounter = eng.evaluate(program);
5772 QVERIFY(createCounter.isCallable());
5773 QScriptValue counter = createCounter.call();
5774 QVERIFY(counter.isCallable());
5776 QScriptValue ret = counter.call();
5777 QVERIFY(ret.isNumber());
5779 QScriptValue counter2 = createCounter.call();
5780 QVERIFY(counter2.isCallable());
5781 QVERIFY(!counter2.equals(counter));
5783 QScriptValue ret = counter2.call();
5784 QVERIFY(ret.isNumber());
5789 void tst_QJSEngine::evaluateProgram_executeLater()
5792 // Program created in a function call, then executed later
5794 QScriptValue fun = eng.newFunction(createProgram);
5795 QScriptProgram program = qscriptvalue_cast<QScriptProgram>(
5796 fun.call(QScriptValueList() << "a + 1"));
5797 QVERIFY(!program.isNull());
5798 eng.globalObject().setProperty("a", QScriptValue());
5800 QScriptValue ret = eng.evaluate(program);
5801 QVERIFY(ret.isError());
5802 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined"));
5804 eng.globalObject().setProperty("a", 122);
5806 QScriptValue ret = eng.evaluate(program);
5807 QVERIFY(!ret.isError());
5808 QVERIFY(ret.isNumber());
5809 QCOMPARE(ret.toInt(), 123);
5814 void tst_QJSEngine::evaluateProgram_multipleEngines()
5818 QString code("1 + 2");
5819 QScriptProgram program(code);
5820 QVERIFY(!program.isNull());
5821 double expected = eng.evaluate(program).toNumber();
5822 for (int x = 0; x < 2; ++x) {
5824 for (int y = 0; y < 2; ++y) {
5825 double ret = eng2.evaluate(program).toNumber();
5826 QCOMPARE(ret, expected);
5832 void tst_QJSEngine::evaluateProgram_empty()
5836 QScriptProgram program;
5837 QVERIFY(program.isNull());
5838 QScriptValue ret = eng.evaluate(program);
5839 QVERIFY(ret.isUndefined());
5844 #if 0 // ###FIXME: No ScriptOwnership API
5845 void tst_QJSEngine::collectGarbageAfterConnect()
5848 QScriptEngine engine;
5849 QPointer<QWidget> widget = new QWidget;
5850 engine.globalObject().setProperty(
5851 "widget", engine.newQObject(widget, QScriptEngine::ScriptOwnership));
5852 QVERIFY(engine.evaluate("widget.customContextMenuRequested.connect(\n"
5853 " function() { print('hello'); }\n"
5856 QVERIFY(widget != 0);
5857 engine.evaluate("widget = null;");
5858 // The connection should not keep the widget alive.
5859 collectGarbage_helper(engine);
5860 QVERIFY(widget == 0);
5864 #if 0 // ###FIXME: No QScriptContext API
5865 void tst_QJSEngine::collectGarbageAfterNativeArguments()
5869 QScriptContext *ctx = eng.pushContext();
5870 QScriptValue arguments = ctx->argumentsObject();
5871 // Shouldn't crash when marking the arguments object.
5872 collectGarbage_helper(eng);
5875 static QScriptValue constructQObjectFromThisObject(QScriptContext *ctx, QScriptEngine *eng)
5877 if (!ctx->isCalledAsConstructor()) {
5878 qWarning("%s: ctx->isCalledAsConstructor() returned false", Q_FUNC_INFO);
5879 return QScriptValue();
5881 return eng->newQObject(ctx->thisObject(), new QObject, QScriptEngine::ScriptOwnership);
5884 void tst_QJSEngine::promoteThisObjectToQObjectInConstructor()
5886 QScriptEngine engine;
5887 QScriptValue ctor = engine.newFunction(constructQObjectFromThisObject);
5888 engine.globalObject().setProperty("Ctor", ctor);
5889 QScriptValue object = engine.evaluate("new Ctor");
5890 QVERIFY(!object.isError());
5891 QVERIFY(object.isQObject());
5892 QVERIFY(object.toQObject() != 0);
5893 QVERIFY(object.property("objectName").isString());
5894 QVERIFY(object.property("deleteLater").isCallable());
5898 static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
5900 void tst_QJSEngine::qRegExpInport_data()
5902 QTest::addColumn<QRegExp>("rx");
5903 QTest::addColumn<QString>("string");
5904 QTest::addColumn<QString>("matched");
5906 QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo";
5907 QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo";
5908 QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
5909 QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo";
5910 QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab";
5911 QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba";
5912 // this one will fail because we do not support the QRegExp::RegExp in JSC
5913 //QTest::newRow("not_greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp) << "aaaabaaba";
5914 QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt";
5915 QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt";
5916 QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string";
5917 QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string";
5918 QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba";
5919 QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
5920 QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a";
5921 QTest::newRow("html") << QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2) << "<b>bold</b><i>italic</i><b>bold</b>";
5922 QTest::newRow("html minimal") << minimal(QRegExp("<b>(.*)</b>", Qt::CaseSensitive, QRegExp::RegExp2)) << "<b>bold</b><i>italic</i><b>bold</b>";
5923 QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa";
5924 QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa";
5925 QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *";
5926 QTest::newRow(".? minimal") << minimal(QRegExp(".?")) << ".?";
5927 QTest::newRow(".+ minimal") << minimal(QRegExp(".+")) << ".+";
5928 QTest::newRow("[.?] minimal") << minimal(QRegExp("[.?]")) << ".?";
5929 QTest::newRow("[.+] minimal") << minimal(QRegExp("[.+]")) << ".+";
5932 void tst_QJSEngine::qRegExpInport()
5934 QSKIP("Test failing - QTBUG-22238");
5935 QFETCH(QRegExp, rx);
5936 QFETCH(QString, string);
5940 rexp = eng.toScriptValue(rx);
5942 QCOMPARE(rexp.isRegExp(), true);
5943 QVERIFY(rexp.isCallable());
5945 QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
5946 QJSValue result = func.call(QJSValueList() << string << rexp);
5949 for (int i = 0; i <= rx.captureCount(); i++) {
5950 QCOMPARE(result.property(i).toString(), rx.cap(i));
5954 // QScriptValue::toDateTime() returns a local time, whereas JS dates
5955 // are always stored as UTC. QtScript must respect the current time
5956 // zone, and correctly adjust for daylight saving time that may be in
5957 // effect at a given date (QTBUG-9770).
5958 void tst_QJSEngine::dateRoundtripJSQtJS()
5960 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
5962 for (int i = 0; i < 8000; ++i) {
5963 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
5964 QDateTime qtDate = jsDate.toDateTime();
5965 QJSValue jsDate2 = eng.toScriptValue(qtDate);
5966 if (jsDate2.toNumber() != jsDate.toNumber())
5967 QFAIL(qPrintable(jsDate.toString()));
5972 void tst_QJSEngine::dateRoundtripQtJSQt()
5974 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
5976 for (int i = 0; i < 8000; ++i) {
5977 QJSValue jsDate = eng.toScriptValue(qtDate);
5978 QDateTime qtDate2 = jsDate.toDateTime();
5979 if (qtDate2 != qtDate)
5980 QFAIL(qPrintable(qtDate.toString()));
5981 qtDate = qtDate.addSecs(2*60*60);
5985 void tst_QJSEngine::dateConversionJSQt()
5987 uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t();
5989 for (int i = 0; i < 8000; ++i) {
5990 QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0));
5991 QDateTime qtDate = jsDate.toDateTime();
5992 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
5993 QString jsUTCDateStr = jsDate.property("toISOString").callWithInstance(jsDate).toString();
5994 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
5995 if (qtUTCDateStr != jsUTCDateStr)
5996 QFAIL(qPrintable(jsDate.toString()));
6001 void tst_QJSEngine::dateConversionQtJS()
6003 QDateTime qtDate = QDateTime(QDate(2009, 1, 1));
6005 for (int i = 0; i < 8000; ++i) {
6006 QJSValue jsDate = eng.toScriptValue(qtDate);
6007 QString jsUTCDateStr = jsDate.property("toISOString").callWithInstance(jsDate).toString();
6008 jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000")
6009 QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate);
6010 if (jsUTCDateStr != qtUTCDateStr)
6011 QFAIL(qPrintable(qtDate.toString()));
6012 qtDate = qtDate.addSecs(2*60*60);
6016 #if 0 // ###FIXME: No QScriptContext API
6017 static QScriptValue createAnotherEngine(QScriptContext *, QScriptEngine *)
6020 eng.evaluate("function foo(x, y) { return x + y; }" );
6021 eng.evaluate("hello = 5; world = 6" );
6022 return eng.evaluate("foo(hello,world)").toInt();
6026 void tst_QJSEngine::reentrency()
6029 eng.globalObject().setProperty("foo", eng.newFunction(createAnotherEngine));
6030 eng.evaluate("function bar() { return foo(); } hello = 9; function getHello() { return hello; }");
6031 QCOMPARE(eng.evaluate("foo() + getHello() + foo()").toInt(), 5+6 + 9 + 5+6);
6032 QCOMPARE(eng.evaluate("foo").call().toInt(), 5+6);
6033 QCOMPARE(eng.evaluate("hello").toInt(), 9);
6034 QCOMPARE(eng.evaluate("foo() + hello").toInt(), 5+6+9);
6038 #if 0 // ###FIXME: No QSCriptDeclarativeClass API
6039 void tst_QJSEngine::newFixedStaticScopeObject()
6041 // "Static scope objects" is an optimization we do for QML.
6042 // It enables the creation of JS objects that can guarantee to the
6043 // compiler that no properties will be added or removed. This enables
6044 // the compiler to generate a very simple (fast) property access, as
6045 // opposed to a full virtual lookup. Due to the inherent use of scope
6046 // chains in QML, this can make a huge difference (10x improvement for
6047 // benchmark in QTBUG-8576).
6048 // Ideally we would not need a special object type for this, and the
6049 // VM would dynamically optimize it to be fast...
6050 // See also QScriptEngine benchmark.
6053 static const int propertyCount = 4;
6054 QString names[] = { "foo", "bar", "baz", "Math" };
6055 QScriptValue values[] = { 123, "ciao", true, false };
6056 QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable,
6057 QScriptValue::ReadOnly | QScriptValue::Undeletable,
6058 QScriptValue::SkipInEnumeration | QScriptValue::Undeletable,
6059 QScriptValue::Undeletable };
6060 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags);
6063 for (int i = 0; i < propertyCount; ++i) {
6064 for (int x = 0; x < 2; ++x) {
6066 // Properties can't be deleted.
6067 scope.setProperty(names[i], QScriptValue());
6069 QVERIFY(scope.property(names[i]).equals(values[i]));
6073 // Property that doesn't exist.
6074 QVERIFY(scope.property("noSuchProperty").isUndefined());
6076 // Write to writable property.
6078 QScriptValue oldValue = scope.property("foo");
6079 QVERIFY(oldValue.isNumber());
6080 QScriptValue newValue = oldValue.toNumber() * 2;
6081 scope.setProperty("foo", newValue);
6082 QVERIFY(scope.property("foo").equals(newValue));
6083 scope.setProperty("foo", oldValue);
6084 QVERIFY(scope.property("foo").equals(oldValue));
6087 // Write to read-only property.
6088 scope.setProperty("bar", 456);
6089 QVERIFY(scope.property("bar").equals("ciao"));
6093 QScriptValueIterator it(scope);
6094 QSet<QString> iteratedNames;
6095 while (it.hasNext()) {
6097 iteratedNames.insert(it.name());
6099 for (int i = 0; i < propertyCount; ++i)
6100 QVERIFY(iteratedNames.contains(names[i]));
6103 // Push it on the scope chain of a new context.
6104 QScriptContext *ctx = eng.pushContext();
6105 ctx->pushScope(scope);
6106 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6107 QEXPECT_FAIL("", "activationObject has not been implemented yet", Continue);
6108 QVERIFY(ctx->activationObject().equals(scope));
6110 // Read property from JS.
6111 for (int i = 0; i < propertyCount; ++i) {
6112 for (int x = 0; x < 2; ++x) {
6114 // Property can't be deleted from JS.
6115 QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i]));
6116 QVERIFY(ret.equals(false));
6118 QVERIFY(eng.evaluate(names[i]).equals(values[i]));
6122 // Property that doesn't exist.
6123 QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: noSuchProperty is not defined"));
6125 // Write property from JS.
6127 QScriptValue oldValue = eng.evaluate("foo");
6128 QVERIFY(oldValue.isNumber());
6129 QScriptValue newValue = oldValue.toNumber() * 2;
6130 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6131 scope.setProperty("foo", oldValue);
6132 QVERIFY(eng.evaluate("foo").equals(oldValue));
6135 // Write to read-only property.
6136 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6138 // Create a closure and return properties from there.
6140 QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()");
6141 QVERIFY(props.isArray());
6142 // "foo" and "bar" come from scope object.
6143 QVERIFY(props.property(0).equals(scope.property("foo")));
6144 QVERIFY(props.property(1).equals(scope.property("bar")));
6145 // "baz" shadows property in scope object.
6146 QVERIFY(props.property(2).equals("shadow"));
6147 // "Math" comes from scope object, and shadows Global Object's "Math".
6148 QVERIFY(props.property(3).equals(scope.property("Math")));
6149 QVERIFY(!props.property(3).equals(eng.globalObject().property("Math")));
6150 // "Array" comes from Global Object.
6151 QVERIFY(props.property(4).equals(eng.globalObject().property("Array")));
6154 // As with normal JS, assigning to an undefined variable will create
6155 // the property on the Global Object, not the inner scope.
6156 QVERIFY(eng.globalObject().property("newProperty").isUndefined());
6157 QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined());
6158 QVERIFY(!scope.property("newProperty").isUndefined());
6159 QVERIFY(eng.globalObject().property("newProperty").isNumber());
6161 // Nested static scope.
6163 static const int propertyCount2 = 2;
6164 QString names2[] = { "foo", "hum" };
6165 QScriptValue values2[] = { 321, "hello" };
6166 QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable,
6167 QScriptValue::ReadOnly | QScriptValue::Undeletable };
6168 QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2);
6169 ctx->pushScope(scope2);
6171 // "foo" shadows scope.foo.
6172 QVERIFY(eng.evaluate("foo").equals(scope2.property("foo")));
6173 QVERIFY(!eng.evaluate("foo").equals(scope.property("foo")));
6174 // "hum" comes from scope2.
6175 QVERIFY(eng.evaluate("hum").equals(scope2.property("hum")));
6176 // "Array" comes from Global Object.
6177 QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array")));
6182 QScriptValue fun = eng.evaluate("(function() { return foo; })");
6183 QVERIFY(fun.isCallable());
6185 // Function's scope chain persists after popContext().
6186 QVERIFY(fun.call().equals(scope.property("foo")));
6189 void tst_QJSEngine::newGrowingStaticScopeObject()
6191 // The main use case for a growing static scope object is to set it as
6192 // the activation object of a QScriptContext, so that all JS variable
6193 // declarations end up in that object. It needs to be "growable" since
6194 // we don't know in advance how many variables a script will declare.
6197 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng);
6200 QVERIFY(!QScriptValueIterator(scope).hasNext());
6201 QVERIFY(scope.property("foo").isUndefined());
6203 // Add a static property.
6204 scope.setProperty("foo", 123);
6205 QVERIFY(scope.property("foo").equals(123));
6206 QEXPECT_FAIL("", "FIXME: newStaticScopeObject not properly implemented", Abort);
6208 // Modify existing property.
6209 scope.setProperty("foo", 456);
6210 QVERIFY(scope.property("foo").equals(456));
6212 // Add a read-only property.
6213 scope.setProperty("bar", "ciao", QScriptValue::ReadOnly);
6214 QVERIFY(scope.property("bar").equals("ciao"));
6216 // Attempt to modify read-only property.
6217 scope.setProperty("bar", "hello");
6218 QVERIFY(scope.property("bar").equals("ciao"));
6220 // Properties can't be deleted.
6221 scope.setProperty("foo", QScriptValue());
6222 QVERIFY(scope.property("foo").equals(456));
6223 scope.setProperty("bar", QScriptValue());
6224 QVERIFY(scope.property("bar").equals("ciao"));
6228 QScriptValueIterator it(scope);
6229 QSet<QString> iteratedNames;
6230 while (it.hasNext()) {
6232 iteratedNames.insert(it.name());
6234 QCOMPARE(iteratedNames.size(), 2);
6235 QVERIFY(iteratedNames.contains("foo"));
6236 QVERIFY(iteratedNames.contains("bar"));
6239 // Push it on the scope chain of a new context.
6240 QScriptContext *ctx = eng.pushContext();
6241 ctx->pushScope(scope);
6242 QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
6243 QVERIFY(ctx->activationObject().equals(scope));
6245 // Read property from JS.
6246 QVERIFY(eng.evaluate("foo").equals(scope.property("foo")));
6247 QVERIFY(eng.evaluate("bar").equals(scope.property("bar")));
6249 // Write property from JS.
6251 QScriptValue oldValue = eng.evaluate("foo");
6252 QVERIFY(oldValue.isNumber());
6253 QScriptValue newValue = oldValue.toNumber() * 2;
6254 QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
6255 scope.setProperty("foo", oldValue);
6256 QVERIFY(eng.evaluate("foo").equals(oldValue));
6259 // Write to read-only property.
6260 QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
6263 QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math")));
6264 scope.setProperty("Math", "fake Math");
6265 QVERIFY(eng.evaluate("Math").equals(scope.property("Math")));
6267 // Variable declarations will create properties on the scope.
6268 eng.evaluate("var baz = 456");
6269 QVERIFY(scope.property("baz").equals(456));
6271 // Function declarations will create properties on the scope.
6272 eng.evaluate("function fun() { return baz; }");
6273 QVERIFY(scope.property("fun").isCallable());
6274 QVERIFY(scope.property("fun").call().equals(scope.property("baz")));
6276 // Demonstrate the limitation of a growable static scope: Once a function that
6277 // uses the scope has been compiled, it won't pick up properties that are added
6278 // to the scope later.
6280 QScriptValue fun = eng.evaluate("(function() { return futureProperty; })");
6281 QVERIFY(fun.isCallable());
6282 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6283 scope.setProperty("futureProperty", "added after the function was compiled");
6284 // If scope were dynamic, this would return the new property.
6285 QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError")));
6292 #if 0 // ###FIXME: No QScript MetaObject API
6294 Q_SCRIPT_DECLARE_QMETAOBJECT(QStandardItemModel, QObject*)
6297 void tst_QJSEngine::scriptValueFromQMetaObject()
6301 QScriptValue meta = eng.scriptValueFromQMetaObject<QScriptEngine>();
6302 QVERIFY(meta.isQMetaObject());
6303 QCOMPARE(meta.toQMetaObject(), &QScriptEngine::staticMetaObject);
6304 // Because of missing Q_SCRIPT_DECLARE_QMETAOBJECT() for QScriptEngine.
6305 QEXPECT_FAIL("", "FIXME: because construct never returns invalid values", Continue);
6306 QVERIFY(meta.callAsConstructor().isUndefined());
6309 QScriptValue meta = eng.scriptValueFromQMetaObject<QStandardItemModel>();
6310 QVERIFY(meta.isQMetaObject());
6311 QCOMPARE(meta.toQMetaObject(), &QStandardItemModel::staticMetaObject);
6312 QScriptValue obj = meta.callAsConstructor(QScriptValueList() << eng.newQObject(&eng));
6313 QVERIFY(obj.isQObject());
6314 QStandardItemModel *model = qobject_cast<QStandardItemModel*>(obj.toQObject());
6315 QVERIFY(model != 0);
6316 QCOMPARE(model->parent(), (QObject*)&eng);
6321 void tst_QJSEngine::functionPrototypeExtensions()
6323 // QJS adds connect and disconnect properties to Function.prototype.
6325 QJSValue funProto = eng.globalObject().property("Function").property("prototype");
6326 QVERIFY(funProto.isCallable());
6327 QVERIFY(funProto.property("connect").isCallable());
6328 QVERIFY(funProto.property("disconnect").isCallable());
6330 // No properties should appear in for-in statements.
6331 QJSValue props = eng.evaluate("props = []; for (var p in Function.prototype) props.push(p); props");
6332 QVERIFY(props.isArray());
6333 QCOMPARE(props.property("length").toInt(), 0);
6336 class ThreadedTestEngine : public QThread {
6342 ThreadedTestEngine()
6346 QJSEngine firstEngine;
6347 QJSEngine secondEngine;
6348 QJSValue value = firstEngine.evaluate("1");
6349 result = secondEngine.evaluate("1 + " + QString::number(value.toInt())).toInt();
6353 void tst_QJSEngine::threadedEngine()
6355 ThreadedTestEngine thread1;
6356 ThreadedTestEngine thread2;
6361 QCOMPARE(thread1.result, 2);
6362 QCOMPARE(thread2.result, 2);
6365 void tst_QJSEngine::v8Context_simple()
6369 v8::HandleScope handleScope;
6370 v8::Local<v8::Context> context = QT_PREPEND_NAMESPACE(qt_QJSEngineV8Context(&eng));
6371 v8::Context::Scope contextScope(context);
6373 v8::Local<v8::Script> script = v8::Script::Compile(
6374 v8::String::New("({ foo: 123, bar: 'ciao', baz: true })"));
6377 v8::Local<v8::Value> result = script->Run();
6379 QVERIFY(!tc.HasCaught());
6380 QVERIFY(result->IsObject());
6382 v8::Local<v8::Object> object = result.As<v8::Object>();
6383 QVERIFY(object->Get(v8::String::New("foo"))->Equals(v8::Number::New(123)));
6384 QVERIFY(object->Get(v8::String::New("bar"))->Equals(v8::String::New("ciao")));
6385 QVERIFY(object->Get(v8::String::New("baz"))->IsTrue());
6388 void tst_QJSEngine::v8Context_exception()
6392 v8::HandleScope handleScope;
6393 v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng);
6394 v8::Context::Scope contextScope(context);
6396 int startLineNumber = 42;
6397 v8::ScriptOrigin origin(v8::String::New("test.js"), v8::Integer::New(startLineNumber));
6398 v8::Local<v8::Script> script = v8::Script::Compile(
6400 "function foo(i) {\n"
6402 " throw Error('Catch me if you can');\n"
6408 // QJS does this for us:
6409 // v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
6412 v8::Local<v8::Value> result = script->Run();
6414 QVERIFY(tc.HasCaught());
6415 QVERIFY(result.IsEmpty());
6417 v8::Local<v8::Message> message = tc.Message();
6418 QVERIFY(!message.IsEmpty());
6419 QCOMPARE(*v8::String::AsciiValue(message->Get()), "Uncaught Error: Catch me if you can");
6420 QCOMPARE(*v8::String::AsciiValue(message->GetScriptResourceName()), "test.js");
6421 QCOMPARE(message->GetLineNumber(), startLineNumber + 3);
6424 void tst_QJSEngine::v8Context_mixAPIs()
6428 v8::HandleScope handleScope;
6429 v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng);
6430 v8::Context::Scope contextScope(context);
6432 QJSValue globalQJS = eng.globalObject();
6433 v8::Local<v8::Value> globalV8Value = qt_QJSValueV8Value(globalQJS);
6434 QVERIFY(!globalV8Value.IsEmpty());
6435 QVERIFY(globalV8Value->IsObject());
6436 v8::Local<v8::Object> globalV8 = globalV8Value.As<v8::Object>();
6438 QVERIFY(globalQJS.property("foo").isUndefined());
6439 QVERIFY(globalV8->Get(v8::String::New("foo"))->IsUndefined());
6441 globalQJS.setProperty("foo", 123);
6442 QVERIFY(globalV8->Get(v8::String::New("foo"))->Equals(v8::Number::New(123)));
6444 globalV8->Set(v8::String::New("bar"), v8::String::New("ciao"));
6445 QVERIFY(globalQJS.property("bar").equals("ciao"));
6447 QJSValue arrayQJS = eng.newArray(10);
6448 v8::Local<v8::Value> arrayV8Value = qt_QJSValueV8Value(arrayQJS);
6449 QVERIFY(!arrayV8Value.IsEmpty());
6450 QVERIFY(arrayV8Value->IsArray());
6451 v8::Local<v8::Array> arrayV8 = arrayV8Value.As<v8::Array>();
6453 QCOMPARE(int(arrayV8->Length()), 10);
6454 arrayV8->Set(5, v8::Null());
6455 QVERIFY(arrayQJS.property(5).isNull());
6458 QTEST_MAIN(tst_QJSEngine)
6460 #include "tst_qjsengine.moc"