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" |