1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 ** This file is part of the QtScript module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
10 ** This file contains pre-release code and may not be distributed.
11 ** You may use this file in accordance with the terms and conditions
12 ** contained in the either Technology Preview License Agreement or the
13 ** Beta Release License Agreement.
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file. Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you are unsure which license is appropriate for your use, please
37 ** contact the sales department at http://www.qtsoftware.com/contact.
40 ****************************************************************************/
42 #include "qscriptecmafunction_p.h"
46 #include "qscriptengine_p.h"
47 #include "qscriptvalueimpl_p.h"
48 #include "qscriptcontext_p.h"
49 #include "qscriptmember_p.h"
50 #include "qscriptobject_p.h"
52 #include <QtCore/QtDebug>
55 # include "qscriptextqobject_p.h"
56 # include <QtCore/QMetaMethod>
61 namespace QScript
{ namespace Ecma
{
63 class FunctionClassData
: public QScriptClassData
65 QScriptClassInfo
*m_classInfo
;
68 FunctionClassData(QScriptClassInfo
*classInfo
);
69 virtual ~FunctionClassData();
71 inline QScriptClassInfo
*classInfo() const
72 { return m_classInfo
; }
74 virtual bool resolve(const QScriptValueImpl
&object
,
75 QScriptNameIdImpl
*nameId
,
76 QScript::Member
*member
, QScriptValueImpl
*base
,
77 QScript::AccessMode access
);
78 virtual bool get(const QScriptValueImpl
&obj
, const Member
&m
,
79 QScriptValueImpl
*out_value
);
80 virtual bool put(QScriptValueImpl
*object
, const QScript::Member
&member
,
81 const QScriptValueImpl
&value
);
82 virtual void mark(const QScriptValueImpl
&object
, int generation
);
85 FunctionClassData::FunctionClassData(QScriptClassInfo
*classInfo
)
86 : m_classInfo(classInfo
)
90 FunctionClassData::~FunctionClassData()
94 bool FunctionClassData::resolve(const QScriptValueImpl
&object
,
95 QScriptNameIdImpl
*nameId
,
96 QScript::Member
*member
, QScriptValueImpl
*base
,
97 QScript::AccessMode
/*access*/)
99 if (object
.classInfo() != classInfo())
102 QScriptEnginePrivate
*eng
= object
.engine();
104 if ((nameId
== eng
->idTable()->id_length
)
105 || (nameId
== eng
->idTable()->id_arguments
)) {
106 member
->native(nameId
, /*id=*/ 0,
107 QScriptValue::Undeletable
108 | QScriptValue::ReadOnly
109 | QScriptValue::SkipInEnumeration
);
117 bool FunctionClassData::get(const QScriptValueImpl
&object
, const Member
&member
,
118 QScriptValueImpl
*result
)
120 if (object
.classInfo() != classInfo())
123 QScriptEnginePrivate
*eng
= object
.engine();
124 if (! member
.isNativeProperty())
127 if (member
.nameId() == eng
->idTable()->id_length
) {
128 *result
= QScriptValueImpl(object
.toFunction()->length
);
130 } else if (member
.nameId() == eng
->idTable()->id_arguments
) {
131 *result
= eng
->nullValue();
138 bool FunctionClassData::put(QScriptValueImpl
*, const QScript::Member
&,
139 const QScriptValueImpl
&)
144 void FunctionClassData::mark(const QScriptValueImpl
&object
, int generation
)
146 if (object
.classInfo() != classInfo())
148 QScriptFunction
*fun
= object
.toFunction();
149 QScriptEnginePrivate
*eng
= object
.engine();
150 fun
->mark(eng
, generation
);
153 Function::Function(QScriptEnginePrivate
*eng
, QScriptClassInfo
*classInfo
):
156 publicPrototype
= eng
->createFunction(method_void
, 0, classInfo
); // public prototype
159 Function::~Function()
163 void Function::initialize()
165 QScriptEnginePrivate
*eng
= engine();
166 eng
->newConstructor(&ctor
, this, publicPrototype
);
168 addPrototypeFunction(QLatin1String("toString"), method_toString
, 1);
169 addPrototypeFunction(QLatin1String("apply"), method_apply
, 2);
170 addPrototypeFunction(QLatin1String("call"), method_call
, 1);
171 addPrototypeFunction(QLatin1String("connect"), method_connect
, 1);
172 addPrototypeFunction(QLatin1String("disconnect"), method_disconnect
, 1);
174 classInfo()->setData(new FunctionClassData(classInfo()));
177 void Function::execute(QScriptContextPrivate
*context
)
179 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
180 engine()->notifyFunctionEntry(context
);
182 int lineNumber
= context
->currentLine
;
183 QString contents
= buildFunction(context
);
184 engine()->evaluate(context
, contents
, lineNumber
);
185 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
186 engine()->notifyFunctionExit(context
);
190 QString
Function::buildFunction(QScriptContextPrivate
*context
)
192 int argc
= context
->argumentCount();
195 code
+= QLatin1String("function(");
198 for (int i
= 0; i
< argc
- 1; ++i
) {
200 code
+= QLatin1Char(',');
202 code
+= context
->argument(i
).toString();
205 code
+= QLatin1String("){");
209 code
+= context
->argument(argc
- 1).toString();
211 code
+= QLatin1String("\n}");
216 void Function::newFunction(QScriptValueImpl
*result
, QScriptFunction
*foo
)
218 engine()->newFunction(result
, foo
);
221 QScriptValueImpl
Function::method_toString(QScriptContextPrivate
*context
, QScriptEnginePrivate
*eng
, QScriptClassInfo
*)
223 QScriptValueImpl self
= context
->thisObject();
224 if (QScriptFunction
*foo
= self
.toFunction()) {
225 QString code
= foo
->toString(context
);
226 return QScriptValueImpl(eng
, code
);
229 return throwThisObjectTypeError(
230 context
, QLatin1String("Function.prototype.toString"));
233 QScriptValueImpl
Function::method_call(QScriptContextPrivate
*context
, QScriptEnginePrivate
*eng
, QScriptClassInfo
*)
235 if (! context
->thisObject().isFunction()) {
236 return throwThisObjectTypeError(
237 context
, QLatin1String("Function.prototype.call"));
240 QScriptValueImpl thisObject
= eng
->toObject(context
->argument(0));
241 if (! (thisObject
.isValid () && thisObject
.isObject()))
242 thisObject
= eng
->globalObject();
244 QScriptValueImplList args
;
245 for (int i
= 1; i
< context
->argumentCount(); ++i
)
246 args
<< context
->argument(i
);
248 return context
->thisObject().call(thisObject
, args
);
251 QScriptValueImpl
Function::method_apply(QScriptContextPrivate
*context
, QScriptEnginePrivate
*eng
, QScriptClassInfo
*)
253 if (! context
->thisObject().isFunction()) {
254 return throwThisObjectTypeError(
255 context
, QLatin1String("Function.prototype.apply"));
258 QScriptValueImpl thisObject
= eng
->toObject(context
->argument(0));
259 if (! (thisObject
.isValid () && thisObject
.isObject()))
260 thisObject
= eng
->globalObject();
262 QScriptValueImplList args
;
263 QScriptValueImpl undefined
= eng
->undefinedValue();
265 QScriptValueImpl arg
= context
->argument(1);
267 if (Ecma::Array::Instance
*arr
= eng
->arrayConstructor
->get(arg
)) {
268 QScript::Array actuals
= arr
->value
;
270 for (quint32 i
= 0; i
< actuals
.count(); ++i
) {
271 QScriptValueImpl a
= actuals
.at(i
);
277 } else if (arg
.classInfo() == eng
->m_class_arguments
) {
278 QScript::ArgumentsObjectData
*arguments
;
279 arguments
= static_cast<QScript::ArgumentsObjectData
*> (arg
.objectData());
280 QScriptObject
*activation
= arguments
->activation
.objectValue();
281 for (uint i
= 0; i
< arguments
->length
; ++i
)
282 args
<< activation
->m_values
[i
];
283 } else if (!(arg
.isUndefined() || arg
.isNull())) {
284 return context
->throwError(QScriptContext::TypeError
,
285 QLatin1String("Function.prototype.apply: second argument is not an array"));
288 return context
->thisObject().call(thisObject
, args
);
291 QScriptValueImpl
Function::method_void(QScriptContextPrivate
*, QScriptEnginePrivate
*eng
, QScriptClassInfo
*)
293 return eng
->undefinedValue();
296 QScriptValueImpl
Function::method_disconnect(QScriptContextPrivate
*context
, QScriptEnginePrivate
*eng
, QScriptClassInfo
*)
298 #ifndef QT_NO_QOBJECT
299 if (context
->argumentCount() == 0) {
300 return context
->throwError(
301 QLatin1String("Function.prototype.disconnect: no arguments given"));
304 QScriptValueImpl self
= context
->thisObject();
305 QScriptFunction
*fun
= self
.toFunction();
306 if ((fun
== 0) || (fun
->type() != QScriptFunction::Qt
)) {
307 return context
->throwError(
308 QScriptContext::TypeError
,
309 QLatin1String("Function.prototype.disconnect: this object is not a signal"));
312 QtFunction
*qtSignal
= static_cast<QtFunction
*>(fun
);
314 const QMetaObject
*meta
= qtSignal
->metaObject();
316 return context
->throwError(
317 QScriptContext::TypeError
,
318 QString::fromLatin1("Function.prototype.disconnect: cannot disconnect from deleted QObject"));
321 QMetaMethod sig
= meta
->method(qtSignal
->initialIndex());
322 if (sig
.methodType() != QMetaMethod::Signal
) {
323 return context
->throwError(QScriptContext::TypeError
,
324 QString::fromLatin1("Function.prototype.disconnect: %0::%1 is not a signal")
325 .arg(QLatin1String(qtSignal
->metaObject()->className()))
326 .arg(QLatin1String(sig
.signature())));
329 QScriptValueImpl receiver
;
330 QScriptValueImpl slot
;
331 QScriptValueImpl arg0
= context
->argument(0);
332 if (context
->argumentCount() < 2) {
333 receiver
= QScriptValueImpl();
337 QScriptValueImpl arg1
= context
->argument(1);
338 if (arg1
.isFunction())
341 slot
= receiver
.property(arg1
.toString(), QScriptValue::ResolvePrototype
);
344 if (!slot
.isFunction()) {
345 return context
->throwError(
346 QScriptContext::TypeError
,
347 QLatin1String("Function.prototype.disconnect: target is not a function"));
350 bool ok
= eng
->scriptDisconnect(self
, receiver
, slot
);
352 return context
->throwError(
353 QString::fromLatin1("Function.prototype.disconnect: failed to disconnect from %0::%1")
354 .arg(QLatin1String(qtSignal
->metaObject()->className()))
355 .arg(QLatin1String(sig
.signature())));
357 return eng
->undefinedValue();
360 return context
->throwError(QScriptContext::TypeError
,
361 QLatin1String("Function.prototype.disconnect"));
362 #endif // QT_NO_QOBJECT
365 QScriptValueImpl
Function::method_connect(QScriptContextPrivate
*context
, QScriptEnginePrivate
*eng
, QScriptClassInfo
*classInfo
)
369 #ifndef QT_NO_QOBJECT
370 if (context
->argumentCount() == 0) {
371 return context
->throwError(
372 QLatin1String("Function.prototype.connect: no arguments given"));
375 QScriptValueImpl self
= context
->thisObject();
376 QScriptFunction
*fun
= self
.toFunction();
377 if ((fun
== 0) || (fun
->type() != QScriptFunction::Qt
)) {
378 return context
->throwError(
379 QScriptContext::TypeError
,
380 QLatin1String("Function.prototype.connect: this object is not a signal"));
383 QtFunction
*qtSignal
= static_cast<QtFunction
*>(fun
);
385 const QMetaObject
*meta
= qtSignal
->metaObject();
387 return context
->throwError(
388 QScriptContext::TypeError
,
389 QString::fromLatin1("Function.prototype.connect: cannot connect to deleted QObject"));
392 QMetaMethod sig
= meta
->method(qtSignal
->initialIndex());
393 if (sig
.methodType() != QMetaMethod::Signal
) {
394 return context
->throwError(QScriptContext::TypeError
,
395 QString::fromLatin1("Function.prototype.connect: %0::%1 is not a signal")
396 .arg(QLatin1String(qtSignal
->metaObject()->className()))
397 .arg(QLatin1String(sig
.signature())));
401 QList
<int> overloads
= qtSignal
->overloadedIndexes();
402 if (!overloads
.isEmpty()) {
403 overloads
.append(qtSignal
->initialIndex());
404 QByteArray signature
= sig
.signature();
405 QString message
= QString::fromLatin1("Function.prototype.connect: ambiguous connect to %0::%1(); candidates are\n")
406 .arg(QLatin1String(qtSignal
->metaObject()->className()))
407 .arg(QLatin1String(signature
.left(signature
.indexOf('('))));
408 for (int i
= 0; i
< overloads
.size(); ++i
) {
409 QMetaMethod mtd
= meta
->method(overloads
.at(i
));
410 message
.append(QString::fromLatin1(" %0\n").arg(QString::fromLatin1(mtd
.signature())));
412 message
.append(QString::fromLatin1("Use e.g. object['%0'].connect() to connect to a particular overload")
413 .arg(QLatin1String(signature
)));
414 return context
->throwError(message
);
418 QScriptValueImpl receiver
;
419 QScriptValueImpl slot
;
420 QScriptValueImpl arg0
= context
->argument(0);
421 if (context
->argumentCount() < 2) {
422 receiver
= QScriptValueImpl();
426 QScriptValueImpl arg1
= context
->argument(1);
427 if (arg1
.isFunction())
430 slot
= receiver
.property(arg1
.toString(), QScriptValue::ResolvePrototype
);
433 if (!slot
.isFunction()) {
434 return context
->throwError(
435 QScriptContext::TypeError
,
436 QLatin1String("Function.prototype.connect: target is not a function"));
439 bool ok
= eng
->scriptConnect(self
, receiver
, slot
, Qt::AutoConnection
);
441 return context
->throwError(
442 QString::fromLatin1("Function.prototype.connect: failed to connect to %0::%1")
443 .arg(QLatin1String(qtSignal
->metaObject()->className()))
444 .arg(QLatin1String(sig
.signature())));
446 return eng
->undefinedValue();
450 return context
->throwError(QScriptContext::TypeError
,
451 QLatin1String("Function.prototype.connect"));
452 #endif // QT_NO_QOBJECT
455 } } // namespace QScript::Ecma
459 #endif // QT_NO_SCRIPT