tests/auto/qscriptengine/tst_qscriptengine.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
child 37 758a864f9613
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
    48 #include <qscriptvalueiterator.h>
    48 #include <qscriptvalueiterator.h>
    49 #include <qgraphicsitem.h>
    49 #include <qgraphicsitem.h>
    50 #include <qstandarditemmodel.h>
    50 #include <qstandarditemmodel.h>
    51 #include <QtCore/qnumeric.h>
    51 #include <QtCore/qnumeric.h>
    52 #include <stdlib.h>
    52 #include <stdlib.h>
       
    53 
       
    54 #include <QtScript/private/qscriptdeclarativeclass_p.h>
    53 
    55 
    54 Q_DECLARE_METATYPE(QList<int>)
    56 Q_DECLARE_METATYPE(QList<int>)
    55 Q_DECLARE_METATYPE(QObjectList)
    57 Q_DECLARE_METATYPE(QObjectList)
    56 Q_DECLARE_METATYPE(QScriptProgram)
    58 Q_DECLARE_METATYPE(QScriptProgram)
    57 
    59 
   167     void promoteThisObjectToQObjectInConstructor();
   169     void promoteThisObjectToQObjectInConstructor();
   168 
   170 
   169     void qRegExpInport_data();
   171     void qRegExpInport_data();
   170     void qRegExpInport();
   172     void qRegExpInport();
   171     void reentrency();
   173     void reentrency();
       
   174     void newFixedStaticScopeObject();
       
   175     void newGrowingStaticScopeObject();
   172 };
   176 };
   173 
   177 
   174 tst_QScriptEngine::tst_QScriptEngine()
   178 tst_QScriptEngine::tst_QScriptEngine()
   175 {
   179 {
   176 }
   180 }
  4259     }
  4263     }
  4260     {
  4264     {
  4261         QScriptEngine eng;
  4265         QScriptEngine eng;
  4262         QCOMPARE(eng.evaluate("Array()").toString(), QString());
  4266         QCOMPARE(eng.evaluate("Array()").toString(), QString());
  4263     }
  4267     }
       
  4268 
       
  4269     {
       
  4270         QScriptEngine eng1;
       
  4271         QScriptEngine eng2;
       
  4272         {
       
  4273             QScriptValue d1 = eng1.newDate(0);
       
  4274             QScriptValue d2 = eng2.newDate(0);
       
  4275             QCOMPARE(d1.toDateTime(), d2.toDateTime());
       
  4276             QCOMPARE(d2.toDateTime(), d1.toDateTime());
       
  4277         }
       
  4278         {
       
  4279             QScriptValue r1 = eng1.newRegExp("foo", "gim");
       
  4280             QScriptValue r2 = eng2.newRegExp("foo", "gim");
       
  4281             QCOMPARE(r1.toRegExp(), r2.toRegExp());
       
  4282             QCOMPARE(r2.toRegExp(), r1.toRegExp());
       
  4283         }
       
  4284         {
       
  4285             QScriptValue o1 = eng1.newQObject(this);
       
  4286             QScriptValue o2 = eng2.newQObject(this);
       
  4287             QCOMPARE(o1.toQObject(), o2.toQObject());
       
  4288             QCOMPARE(o2.toQObject(), o1.toQObject());
       
  4289         }
       
  4290         {
       
  4291             QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject);
       
  4292             QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject);
       
  4293             QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject());
       
  4294             QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject());
       
  4295         }
       
  4296     }
  4264 }
  4297 }
  4265 
  4298 
  4266 void tst_QScriptEngine:: incDecNonObjectProperty()
  4299 void tst_QScriptEngine:: incDecNonObjectProperty()
  4267 {
  4300 {
  4268     QScriptEngine eng;
  4301     QScriptEngine eng;
  4953     QCOMPARE(eng.evaluate("foo").call().toInt32(), 5+6);
  4986     QCOMPARE(eng.evaluate("foo").call().toInt32(), 5+6);
  4954     QCOMPARE(eng.evaluate("hello").toInt32(), 9);
  4987     QCOMPARE(eng.evaluate("hello").toInt32(), 9);
  4955     QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9);
  4988     QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9);
  4956 }
  4989 }
  4957 
  4990 
       
  4991 void tst_QScriptEngine::newFixedStaticScopeObject()
       
  4992 {
       
  4993     QScriptEngine eng;
       
  4994     static const int propertyCount = 4;
       
  4995     QString names[] = { "foo", "bar", "baz", "Math" };
       
  4996     QScriptValue values[] = { 123, "ciao", true, false };
       
  4997     QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable,
       
  4998                                             QScriptValue::ReadOnly | QScriptValue::Undeletable,
       
  4999                                             QScriptValue::SkipInEnumeration | QScriptValue::Undeletable,
       
  5000                                             QScriptValue::Undeletable };
       
  5001     QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags);
       
  5002 
       
  5003     // Query property.
       
  5004     for (int i = 0; i < propertyCount; ++i) {
       
  5005         for (int x = 0; x < 2; ++x) {
       
  5006             if (x) {
       
  5007                 // Properties can't be deleted.
       
  5008                 scope.setProperty(names[i], QScriptValue());
       
  5009             }
       
  5010             QVERIFY(scope.property(names[i]).equals(values[i]));
       
  5011             QCOMPARE(scope.propertyFlags(names[i]), flags[i]);
       
  5012         }
       
  5013     }
       
  5014 
       
  5015     // Property that doesn't exist.
       
  5016     QVERIFY(!scope.property("noSuchProperty").isValid());
       
  5017     QCOMPARE(scope.propertyFlags("noSuchProperty"), QScriptValue::PropertyFlags());
       
  5018 
       
  5019     // Write to writable property.
       
  5020     {
       
  5021         QScriptValue oldValue = scope.property("foo");
       
  5022         QVERIFY(oldValue.isNumber());
       
  5023         QScriptValue newValue = oldValue.toNumber() * 2;
       
  5024         scope.setProperty("foo", newValue);
       
  5025         QVERIFY(scope.property("foo").equals(newValue));
       
  5026         scope.setProperty("foo", oldValue);
       
  5027         QVERIFY(scope.property("foo").equals(oldValue));
       
  5028     }
       
  5029 
       
  5030     // Write to read-only property.
       
  5031     scope.setProperty("bar", 456);
       
  5032     QVERIFY(scope.property("bar").equals("ciao"));
       
  5033 
       
  5034     // Iterate.
       
  5035     {
       
  5036         QScriptValueIterator it(scope);
       
  5037         QSet<QString> iteratedNames;
       
  5038         while (it.hasNext()) {
       
  5039             it.next();
       
  5040             iteratedNames.insert(it.name());
       
  5041         }
       
  5042         for (int i = 0; i < propertyCount; ++i)
       
  5043             QVERIFY(iteratedNames.contains(names[i]));
       
  5044     }
       
  5045 
       
  5046     // Push it on the scope chain of a new context.
       
  5047     QScriptContext *ctx = eng.pushContext();
       
  5048     ctx->pushScope(scope);
       
  5049     QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
       
  5050     QVERIFY(ctx->activationObject().equals(scope));
       
  5051 
       
  5052     // Read property from JS.
       
  5053     for (int i = 0; i < propertyCount; ++i) {
       
  5054         for (int x = 0; x < 2; ++x) {
       
  5055             if (x) {
       
  5056                 // Property can't be deleted from JS.
       
  5057                 QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i]));
       
  5058                 QVERIFY(ret.equals(false));
       
  5059             }
       
  5060             QVERIFY(eng.evaluate(names[i]).equals(values[i]));
       
  5061         }
       
  5062     }
       
  5063 
       
  5064     // Property that doesn't exist.
       
  5065     QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: Can't find variable: noSuchProperty"));
       
  5066 
       
  5067     // Write property from JS.
       
  5068     {
       
  5069         QScriptValue oldValue = eng.evaluate("foo");
       
  5070         QVERIFY(oldValue.isNumber());
       
  5071         QScriptValue newValue = oldValue.toNumber() * 2;
       
  5072         QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
       
  5073         scope.setProperty("foo", oldValue);
       
  5074         QVERIFY(eng.evaluate("foo").equals(oldValue));
       
  5075     }
       
  5076 
       
  5077     // Write to read-only property.
       
  5078     QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
       
  5079 
       
  5080     // Create a closure and return properties from there.
       
  5081     {
       
  5082         QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()");
       
  5083         QVERIFY(props.isArray());
       
  5084         // "foo" and "bar" come from scope object.
       
  5085         QVERIFY(props.property(0).equals(scope.property("foo")));
       
  5086         QVERIFY(props.property(1).equals(scope.property("bar")));
       
  5087         // "baz" shadows property in scope object.
       
  5088         QVERIFY(props.property(2).equals("shadow"));
       
  5089         // "Math" comes from scope object, and shadows Global Object's "Math".
       
  5090         QVERIFY(props.property(3).equals(scope.property("Math")));
       
  5091         QVERIFY(!props.property(3).equals(eng.globalObject().property("Math")));
       
  5092         // "Array" comes from Global Object.
       
  5093         QVERIFY(props.property(4).equals(eng.globalObject().property("Array")));
       
  5094     }
       
  5095 
       
  5096     // As with normal JS, assigning to an undefined variable will create
       
  5097     // the property on the Global Object, not the inner scope.
       
  5098     QVERIFY(!eng.globalObject().property("newProperty").isValid());
       
  5099     QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined());
       
  5100     QVERIFY(!scope.property("newProperty").isValid());
       
  5101     QVERIFY(eng.globalObject().property("newProperty").isNumber());
       
  5102 
       
  5103     // Nested static scope.
       
  5104     {
       
  5105         static const int propertyCount2 = 2;
       
  5106         QString names2[] = { "foo", "hum" };
       
  5107         QScriptValue values2[] = { 321, "hello" };
       
  5108         QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable,
       
  5109                                                  QScriptValue::ReadOnly | QScriptValue::Undeletable };
       
  5110         QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2);
       
  5111         ctx->pushScope(scope2);
       
  5112 
       
  5113         // "foo" shadows scope.foo.
       
  5114         QVERIFY(eng.evaluate("foo").equals(scope2.property("foo")));
       
  5115         QVERIFY(!eng.evaluate("foo").equals(scope.property("foo")));
       
  5116         // "hum" comes from scope2.
       
  5117         QVERIFY(eng.evaluate("hum").equals(scope2.property("hum")));
       
  5118         // "Array" comes from Global Object.
       
  5119         QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array")));
       
  5120 
       
  5121         ctx->popScope();
       
  5122     }
       
  5123 
       
  5124     QScriptValue fun = eng.evaluate("(function() { return foo; })");
       
  5125     QVERIFY(fun.isFunction());
       
  5126     eng.popContext();
       
  5127     // Function's scope chain persists after popContext().
       
  5128     QVERIFY(fun.call().equals(scope.property("foo")));
       
  5129 }
       
  5130 
       
  5131 void tst_QScriptEngine::newGrowingStaticScopeObject()
       
  5132 {
       
  5133     QScriptEngine eng;
       
  5134     QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng);
       
  5135 
       
  5136     // Initially empty.
       
  5137     QVERIFY(!QScriptValueIterator(scope).hasNext());
       
  5138     QVERIFY(!scope.property("foo").isValid());
       
  5139 
       
  5140     // Add a static property.
       
  5141     scope.setProperty("foo", 123);
       
  5142     QVERIFY(scope.property("foo").equals(123));
       
  5143     QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable);
       
  5144 
       
  5145     // Modify existing property.
       
  5146     scope.setProperty("foo", 456);
       
  5147     QVERIFY(scope.property("foo").equals(456));
       
  5148 
       
  5149     // Add a read-only property.
       
  5150     scope.setProperty("bar", "ciao", QScriptValue::ReadOnly);
       
  5151     QVERIFY(scope.property("bar").equals("ciao"));
       
  5152     QCOMPARE(scope.propertyFlags("bar"), QScriptValue::ReadOnly | QScriptValue::Undeletable);
       
  5153 
       
  5154     // Attempt to modify read-only property.
       
  5155     scope.setProperty("bar", "hello");
       
  5156     QVERIFY(scope.property("bar").equals("ciao"));
       
  5157 
       
  5158     // Properties can't be deleted.
       
  5159     scope.setProperty("foo", QScriptValue());
       
  5160     QVERIFY(scope.property("foo").equals(456));
       
  5161     scope.setProperty("bar", QScriptValue());
       
  5162     QVERIFY(scope.property("bar").equals("ciao"));
       
  5163 
       
  5164     // Iterate.
       
  5165     {
       
  5166         QScriptValueIterator it(scope);
       
  5167         QSet<QString> iteratedNames;
       
  5168         while (it.hasNext()) {
       
  5169             it.next();
       
  5170             iteratedNames.insert(it.name());
       
  5171         }
       
  5172         QCOMPARE(iteratedNames.size(), 2);
       
  5173         QVERIFY(iteratedNames.contains("foo"));
       
  5174         QVERIFY(iteratedNames.contains("bar"));
       
  5175     }
       
  5176 
       
  5177     // Push it on the scope chain of a new context.
       
  5178     QScriptContext *ctx = eng.pushContext();
       
  5179     ctx->pushScope(scope);
       
  5180     QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope
       
  5181     QVERIFY(ctx->activationObject().equals(scope));
       
  5182 
       
  5183     // Read property from JS.
       
  5184     QVERIFY(eng.evaluate("foo").equals(scope.property("foo")));
       
  5185     QVERIFY(eng.evaluate("bar").equals(scope.property("bar")));
       
  5186 
       
  5187     // Write property from JS.
       
  5188     {
       
  5189         QScriptValue oldValue = eng.evaluate("foo");
       
  5190         QVERIFY(oldValue.isNumber());
       
  5191         QScriptValue newValue = oldValue.toNumber() * 2;
       
  5192         QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue));
       
  5193         scope.setProperty("foo", oldValue);
       
  5194         QVERIFY(eng.evaluate("foo").equals(oldValue));
       
  5195     }
       
  5196 
       
  5197     // Write to read-only property.
       
  5198     QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao"));
       
  5199 
       
  5200     // Shadow property.
       
  5201     QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math")));
       
  5202     scope.setProperty("Math", "fake Math");
       
  5203     QVERIFY(eng.evaluate("Math").equals(scope.property("Math")));
       
  5204 
       
  5205     // Variable declarations will create properties on the scope.
       
  5206     eng.evaluate("var baz = 456");
       
  5207     QVERIFY(scope.property("baz").equals(456));
       
  5208 
       
  5209     // Function declarations will create properties on the scope.
       
  5210     eng.evaluate("function fun() { return baz; }");
       
  5211     QVERIFY(scope.property("fun").isFunction());
       
  5212     QVERIFY(scope.property("fun").call().equals(scope.property("baz")));
       
  5213 
       
  5214     // Demonstrate the limitation of a growable static scope: Once a function that
       
  5215     // uses the scope has been compiled, it won't pick up properties that are added
       
  5216     // to the scope later.
       
  5217     {
       
  5218         QScriptValue fun = eng.evaluate("(function() { return futureProperty; })");
       
  5219         QVERIFY(fun.isFunction());
       
  5220         QCOMPARE(fun.call().toString(), QString::fromLatin1("ReferenceError: Can't find variable: futureProperty"));
       
  5221         scope.setProperty("futureProperty", "added after the function was compiled");
       
  5222         // If scope were dynamic, this would return the new property.
       
  5223         QCOMPARE(fun.call().toString(), QString::fromLatin1("ReferenceError: Can't find variable: futureProperty"));
       
  5224     }
       
  5225 
       
  5226     eng.popContext();
       
  5227 }
       
  5228 
  4958 QTEST_MAIN(tst_QScriptEngine)
  5229 QTEST_MAIN(tst_QScriptEngine)
  4959 #include "tst_qscriptengine.moc"
  5230 #include "tst_qscriptengine.moc"