Changeset 64130 in webkit
- Timestamp:
- Jul 27, 2010 8:37:25 AM (14 years ago)
- Location:
- trunk/JavaScriptCore/qt
- Files:
-
- 2 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/qt/ChangeLog
r63980 r64130 1 2010-07-27 Caio Marcelo de Oliveira Filho <caio.oliveira@openbossa.org> 2 3 Reviewed by Kenneth Rohde Christiansen. 4 5 [Qt] Implement QScriptEngine::newFunction() parts that doesn't depend on QScriptContext 6 https://bugs.webkit.org/show_bug.cgi?id=42174 7 8 Since our function can be called in Javascript both as a function 9 and as a constructor, we couldn't use the existing 10 JSObjectMakeFunctionWithCallback() and JSObjectMakeConstructor(). 11 12 Instead, a JSClassRef was created, implementing the needed 13 callbacks (the callAsConstructor is not there yet because its 14 behaviour depends on QScriptContext). 15 16 For the moment, QScriptContext is defined as a void type, since we 17 still don't use it. 18 19 The variant of newFunction() that also takes an external argument 20 was also implemented. The details of implementation were added to 21 the qscriptfunction{.c,_p.h} files. 22 23 This commit also adds tests, some of them from Qt's upstream. 24 25 * api/QtScript.pro: 26 * api/qscriptengine.cpp: 27 (QScriptEngine::newFunction): 28 * api/qscriptengine.h: 29 * api/qscriptengine_p.cpp: 30 (QScriptEnginePrivate::QScriptEnginePrivate): 31 (QScriptEnginePrivate::~QScriptEnginePrivate): 32 (QScriptEnginePrivate::newFunction): 33 * api/qscriptengine_p.h: 34 * api/qscriptfunction.cpp: Added. 35 (qt_NativeFunction_finalize): 36 (qt_NativeFunction_callAsFunction): 37 (qt_NativeFunctionWithArg_finalize): 38 (qt_NativeFunctionWithArg_callAsFunction): 39 * api/qscriptfunction_p.h: Added. 40 (QNativeFunctionData::QNativeFunctionData): 41 (QNativeFunctionWithArgData::QNativeFunctionWithArgData): 42 * api/qscriptoriginalglobalobject_p.h: 43 (QScriptOriginalGlobalObject::QScriptOriginalGlobalObject): 44 (QScriptOriginalGlobalObject::~QScriptOriginalGlobalObject): 45 (QScriptOriginalGlobalObject::functionPrototype): 46 * tests/qscriptengine/tst_qscriptengine.cpp: 47 (myFunction): 48 (myFunctionWithArg): 49 (myFunctionThatReturns): 50 (myFunctionThatReturnsWithoutEngine): 51 (myFunctionThatReturnsWrongEngine): 52 (tst_QScriptEngine::newFunction): 53 1 54 2010-07-23 Jedrzej Nowacki <jedrzej.nowacki@nokia.com> 2 55 -
trunk/JavaScriptCore/qt/api/QtScript.pro
r63318 r64130 29 29 $$PWD/qscriptprogram.cpp \ 30 30 $$PWD/qscriptsyntaxcheckresult.cpp \ 31 $$PWD/qscriptfunction.cpp 31 32 32 33 HEADERS += $$PWD/qtscriptglobal.h \ … … 44 45 $$PWD/qscriptsyntaxcheckresult.h \ 45 46 $$PWD/qscriptoriginalglobalobject_p.h \ 46 47 $$PWD/qscriptfunction_p.h 47 48 48 49 !static: DEFINES += QT_MAKEDLL -
trunk/JavaScriptCore/qt/api/qscriptengine.cpp
r62377 r64130 259 259 260 260 /*! 261 Creates a QScriptValue that wraps a native (C++) function. \a fun 262 must be a C++ function with signature QScriptEngine::FunctionSignature. 263 \a length is the number of arguments that \a fun expects; this becomes 264 the \c{length} property of the created QScriptValue. 265 266 Note that \a length only gives an indication of the number of 267 arguments that the function expects; an actual invocation of a 268 function can include any number of arguments. You can check the 269 \l{QScriptContext::argumentCount()}{argumentCount()} of the 270 QScriptContext associated with the invocation to determine the 271 actual number of arguments passed. 272 273 A \c{prototype} property is automatically created for the resulting 274 function object, to provide for the possibility that the function 275 will be used as a constructor. 276 277 By combining newFunction() and the property flags 278 QScriptValue::PropertyGetter and QScriptValue::PropertySetter, you 279 can create script object properties that behave like normal 280 properties in script code, but are in fact accessed through 281 functions (analogous to how properties work in \l{Qt's Property 282 System}). Example: 283 284 \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 11 285 286 When the property \c{foo} of the script object is subsequently 287 accessed in script code, \c{getSetFoo()} will be invoked to handle 288 the access. In this particular case, we chose to store the "real" 289 value of \c{foo} as a property of the accessor function itself; you 290 are of course free to do whatever you like in this function. 291 292 In the above example, a single native function was used to handle 293 both reads and writes to the property; the argument count is used to 294 determine if we are handling a read or write. You can also use two 295 separate functions; just specify the relevant flag 296 (QScriptValue::PropertyGetter or QScriptValue::PropertySetter) when 297 setting the property, e.g.: 298 299 \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 12 300 301 \sa QScriptValue::call() 302 */ 303 QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionSignature fun, int length) 304 { 305 return QScriptValuePrivate::get(d_ptr->newFunction(fun, 0, length)); 306 } 307 308 /*! 309 Creates a constructor function from \a fun, with the given \a length. 310 The \c{prototype} property of the resulting function is set to be the 311 given \a prototype. The \c{constructor} property of \a prototype is 312 set to be the resulting function. 313 314 When a function is called as a constructor (e.g. \c{new Foo()}), the 315 `this' object associated with the function call is the new object 316 that the function is expected to initialize; the prototype of this 317 default constructed object will be the function's public 318 \c{prototype} property. If you always want the function to behave as 319 a constructor (e.g. \c{Foo()} should also create a new object), or 320 if you need to create your own object rather than using the default 321 `this' object, you should make sure that the prototype of your 322 object is set correctly; either by setting it manually, or, when 323 wrapping a custom type, by having registered the defaultPrototype() 324 of that type. Example: 325 326 \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 9 327 328 To wrap a custom type and provide a constructor for it, you'd typically 329 do something like this: 330 331 \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 10 332 */ 333 QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionSignature fun, const QScriptValue& prototype, int length) 334 { 335 return QScriptValuePrivate::get(d_ptr->newFunction(fun, QScriptValuePrivate::get(prototype), length)); 336 } 337 338 /*! 339 \internal 340 \since 4.4 341 */ 342 QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionWithArgSignature fun, void* arg) 343 { 344 return QScriptValuePrivate::get(d_ptr->newFunction(fun, arg)); 345 } 346 347 /*! 261 348 Creates a QtScript object of class Object. 262 349 … … 295 382 return QScriptValuePrivate::get(d_ptr->globalObject()); 296 383 } 384 385 /*! 386 \typedef QScriptEngine::FunctionSignature 387 \relates QScriptEngine 388 389 The function signature \c{QScriptValue f(QScriptContext *, QScriptEngine *)}. 390 391 A function with such a signature can be passed to 392 QScriptEngine::newFunction() to wrap the function. 393 */ 394 395 /*! 396 \typedef QScriptEngine::FunctionWithArgSignature 397 \relates QScriptEngine 398 399 The function signature \c{QScriptValue f(QScriptContext *, QScriptEngine *, void *)}. 400 401 A function with such a signature can be passed to 402 QScriptEngine::newFunction() to wrap the function. 403 */ -
trunk/JavaScriptCore/qt/api/qscriptengine.h
r62377 r64130 31 31 class QScriptEnginePrivate; 32 32 33 // FIXME: Remove this once QScriptContext is properly defined. 34 typedef void QScriptContext; 35 33 36 // Internal typedef 34 37 typedef QExplicitlySharedDataPointer<QScriptEnginePrivate> QScriptEnginePtr; … … 57 60 QScriptValue nullValue(); 58 61 QScriptValue undefinedValue(); 62 63 typedef QScriptValue (*FunctionSignature)(QScriptContext *, QScriptEngine *); 64 typedef QScriptValue (*FunctionWithArgSignature)(QScriptContext *, QScriptEngine *, void *); 65 66 QScriptValue newFunction(FunctionSignature fun, int length = 0); 67 QScriptValue newFunction(FunctionSignature fun, const QScriptValue& prototype, int length = 0); 68 QScriptValue newFunction(FunctionWithArgSignature fun, void* arg); 69 59 70 QScriptValue newObject(); 60 71 QScriptValue newArray(uint length = 0); -
trunk/JavaScriptCore/qt/api/qscriptengine_p.cpp
r63318 r64130 22 22 #include "qscriptengine_p.h" 23 23 24 #include "qscriptfunction_p.h" 24 25 #include "qscriptprogram_p.h" 25 26 #include "qscriptvalue_p.h" … … 34 35 , m_exception(0) 35 36 , m_originalGlobalObject(m_context) 37 , m_nativeFunctionClass(JSClassCreate(&qt_NativeFunctionClass)) 38 , m_nativeFunctionWithArgClass(JSClassCreate(&qt_NativeFunctionWithArgClass)) 36 39 { 37 40 } … … 39 42 QScriptEnginePrivate::~QScriptEnginePrivate() 40 43 { 44 JSClassRelease(m_nativeFunctionClass); 45 JSClassRelease(m_nativeFunctionWithArgClass); 41 46 if (m_exception) 42 47 JSValueUnprotect(m_context, m_exception); … … 87 92 } 88 93 94 QScriptValuePrivate* QScriptEnginePrivate::newFunction(QScriptEngine::FunctionSignature fun, QScriptValuePrivate* prototype, int length) 95 { 96 // Note that this private data will be deleted in the object finalize function. 97 QNativeFunctionData* data = new QNativeFunctionData(this, fun); 98 JSObjectRef funJS = JSObjectMake(m_context, m_nativeFunctionClass, reinterpret_cast<void*>(data)); 99 QScriptValuePrivate* proto = prototype ? prototype : newObject(); 100 return newFunction(funJS, proto); 101 } 102 103 QScriptValuePrivate* QScriptEnginePrivate::newFunction(QScriptEngine::FunctionWithArgSignature fun, void* arg) 104 { 105 // Note that this private data will be deleted in the object finalize function. 106 QNativeFunctionWithArgData* data = new QNativeFunctionWithArgData(this, fun, arg); 107 JSObjectRef funJS = JSObjectMake(m_context, m_nativeFunctionWithArgClass, reinterpret_cast<void*>(data)); 108 QScriptValuePrivate* proto = newObject(); 109 return newFunction(funJS, proto); 110 } 111 112 QScriptValuePrivate* QScriptEnginePrivate::newFunction(JSObjectRef funJS, QScriptValuePrivate* prototype) 113 { 114 JSObjectSetPrototype(m_context, funJS, m_originalGlobalObject.functionPrototype()); 115 116 QScriptValuePrivate* result = new QScriptValuePrivate(this, funJS); 117 static JSStringRef protoName = QScriptConverter::toString("prototype"); 118 static JSStringRef constructorName = QScriptConverter::toString("constructor"); 119 result->setProperty(protoName, prototype, QScriptValue::Undeletable); 120 prototype->setProperty(constructorName, result, QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); 121 122 return result; 123 } 124 89 125 QScriptValuePrivate* QScriptEnginePrivate::newObject() const 90 126 { -
trunk/JavaScriptCore/qt/api/qscriptengine_p.h
r63318 r64130 72 72 inline JSValueRef makeJSValue(QScriptValue::SpecialValue value) const; 73 73 74 QScriptValuePrivate* newFunction(QScriptEngine::FunctionSignature fun, QScriptValuePrivate* prototype, int length); 75 QScriptValuePrivate* newFunction(QScriptEngine::FunctionWithArgSignature fun, void* arg); 76 QScriptValuePrivate* newFunction(JSObjectRef funObject, QScriptValuePrivate* prototype); 77 74 78 QScriptValuePrivate* newObject() const; 75 79 QScriptValuePrivate* newArray(uint length); … … 90 94 91 95 QScriptOriginalGlobalObject m_originalGlobalObject; 96 97 JSClassRef m_nativeFunctionClass; 98 JSClassRef m_nativeFunctionWithArgClass; 92 99 }; 93 100 -
trunk/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h
r63318 r64130 46 46 inline bool isArray(JSValueRef value) const; 47 47 inline bool isError(JSValueRef value) const; 48 49 inline JSValueRef functionPrototype() const; 48 50 private: 49 51 inline bool isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const; … … 58 60 JSObjectRef m_errorConstructor; 59 61 JSValueRef m_errorPrototype; 62 JSObjectRef m_functionConstructor; 63 JSValueRef m_functionPrototype; 60 64 61 65 // Reference to standard JS functions that are not exposed by JSC C API. … … 74 78 initializeMember(globalObject, propertyName.get(), "Array", m_arrayConstructor, m_arrayPrototype); 75 79 initializeMember(globalObject, propertyName.get(), "Error", m_errorConstructor, m_errorPrototype); 80 initializeMember(globalObject, propertyName.get(), "Function", m_functionConstructor, m_functionPrototype); 76 81 77 82 propertyName.adopt(JSStringCreateWithUTF8CString("hasOwnProperty")); … … 120 125 JSValueUnprotect(m_context, m_errorConstructor); 121 126 JSValueUnprotect(m_context, m_errorPrototype); 127 JSValueUnprotect(m_context, m_functionConstructor); 128 JSValueUnprotect(m_context, m_functionPrototype); 122 129 JSValueUnprotect(m_context, m_hasOwnPropertyFunction); 123 130 JSValueUnprotect(m_context, m_getOwnPropertyNamesFunction); … … 174 181 } 175 182 183 inline JSValueRef QScriptOriginalGlobalObject::functionPrototype() const 184 { 185 return m_functionPrototype; 186 } 187 176 188 inline bool QScriptOriginalGlobalObject::isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const 177 189 { -
trunk/JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp
r62661 r64130 36 36 37 37 private slots: 38 void newFunction(); 38 39 void newObject(); 39 40 void globalObject(); … … 58 59 QVERIFY2(engine.evaluate("1+1").isValid(), "the expression should be evaluated and an valid result should be returned"); 59 60 QVERIFY2(engine.evaluate("ping").isValid(), "Script throwing an unhandled exception should return an exception value"); 61 } 62 63 static QScriptValue myFunction(QScriptContext*, QScriptEngine* eng) 64 { 65 return eng->nullValue(); 66 } 67 68 static QScriptValue myFunctionWithArg(QScriptContext*, QScriptEngine* eng, void* arg) 69 { 70 int* result = reinterpret_cast<int*>(arg); 71 return QScriptValue(eng, *result); 72 } 73 74 static QScriptValue myFunctionThatReturns(QScriptContext*, QScriptEngine* eng) 75 { 76 return QScriptValue(eng, 42); 77 } 78 79 static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext*, QScriptEngine*) 80 { 81 return QScriptValue(1024); 82 } 83 84 static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext*, QScriptEngine*, void* arg) 85 { 86 QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg); 87 return QScriptValue(wrongEngine, 42); 88 } 89 90 void tst_QScriptEngine::newFunction() 91 { 92 QScriptEngine eng; 93 { 94 QScriptValue fun = eng.newFunction(myFunction); 95 QCOMPARE(fun.isValid(), true); 96 QCOMPARE(fun.isFunction(), true); 97 QCOMPARE(fun.isObject(), true); 98 // QCOMPARE(fun.scriptClass(), (QScriptClass*)0); 99 // a prototype property is automatically constructed 100 { 101 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); 102 QVERIFY(prot.isObject()); 103 QVERIFY(prot.property("constructor").strictlyEquals(fun)); 104 QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); 105 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); 106 QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); 107 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); 108 } 109 // prototype should be Function.prototype 110 QCOMPARE(fun.prototype().isValid(), true); 111 QCOMPARE(fun.prototype().isFunction(), true); 112 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); 113 114 QCOMPARE(fun.call().isNull(), true); 115 // QCOMPARE(fun.construct().isObject(), true); 116 } 117 // the overload that takes an extra argument 118 { 119 int expectedResult = 42; 120 QScriptValue fun = eng.newFunction(myFunctionWithArg, reinterpret_cast<void*>(&expectedResult)); 121 QVERIFY(fun.isFunction()); 122 // QCOMPARE(fun.scriptClass(), (QScriptClass*)0); 123 // a prototype property is automatically constructed 124 { 125 QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); 126 QVERIFY(prot.isObject()); 127 QVERIFY(prot.property("constructor").strictlyEquals(fun)); 128 QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); 129 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); 130 QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); 131 QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); 132 } 133 // prototype should be Function.prototype 134 QCOMPARE(fun.prototype().isValid(), true); 135 QCOMPARE(fun.prototype().isFunction(), true); 136 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); 137 138 QScriptValue result = fun.call(); 139 QCOMPARE(result.isNumber(), true); 140 QCOMPARE(result.toInt32(), expectedResult); 141 } 142 // the overload that takes a prototype 143 { 144 QScriptValue proto = eng.newObject(); 145 QScriptValue fun = eng.newFunction(myFunction, proto); 146 QCOMPARE(fun.isValid(), true); 147 QCOMPARE(fun.isFunction(), true); 148 QCOMPARE(fun.isObject(), true); 149 // internal prototype should be Function.prototype 150 QCOMPARE(fun.prototype().isValid(), true); 151 QCOMPARE(fun.prototype().isFunction(), true); 152 QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); 153 // public prototype should be the one we passed 154 QCOMPARE(fun.property("prototype").strictlyEquals(proto), true); 155 QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); 156 QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); 157 QCOMPARE(proto.property("constructor").strictlyEquals(fun), true); 158 QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); 159 QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); 160 161 QCOMPARE(fun.call().isNull(), true); 162 // QCOMPARE(fun.construct().isObject(), true); 163 } 164 // whether the return value is correct 165 { 166 QScriptValue fun = eng.newFunction(myFunctionThatReturns); 167 QCOMPARE(fun.isValid(), true); 168 QCOMPARE(fun.isFunction(), true); 169 QCOMPARE(fun.isObject(), true); 170 171 QScriptValue result = fun.call(); 172 QCOMPARE(result.isNumber(), true); 173 QCOMPARE(result.toInt32(), 42); 174 } 175 // whether the return value is assigned to the correct engine 176 { 177 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine); 178 QCOMPARE(fun.isValid(), true); 179 QCOMPARE(fun.isFunction(), true); 180 QCOMPARE(fun.isObject(), true); 181 182 QScriptValue result = fun.call(); 183 QCOMPARE(result.engine(), &eng); 184 QCOMPARE(result.isNumber(), true); 185 QCOMPARE(result.toInt32(), 1024); 186 } 187 // whether the return value is undefined when returning a value with wrong engine 188 { 189 QScriptEngine wrongEngine; 190 191 QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void*>(&wrongEngine)); 192 QCOMPARE(fun.isValid(), true); 193 QCOMPARE(fun.isFunction(), true); 194 QCOMPARE(fun.isObject(), true); 195 196 QTest::ignoreMessage(QtWarningMsg, "Value from different engine returned from native function, returning undefined value instead."); 197 QScriptValue result = fun.call(); 198 QCOMPARE(result.isValid(), true); 199 QCOMPARE(result.isUndefined(), true); 200 } 60 201 } 61 202
Note: See TracChangeset
for help on using the changeset viewer.