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