source: trunk/src/script/qscriptecmafunction.cpp@ 397

Last change on this file since 397 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 15.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtScript module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
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.
22**
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
26** package.
27**
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.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptecmafunction_p.h"
43
44#ifndef QT_NO_SCRIPT
45
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"
51
52#include <QtCore/QtDebug>
53
54#ifndef QT_NO_QOBJECT
55# include "qscriptextqobject_p.h"
56# include <QtCore/QMetaMethod>
57#endif
58
59QT_BEGIN_NAMESPACE
60
61namespace QScript { namespace Ecma {
62
63class FunctionClassData: public QScriptClassData
64{
65 QScriptClassInfo *m_classInfo;
66
67public:
68 FunctionClassData(QScriptClassInfo *classInfo);
69 virtual ~FunctionClassData();
70
71 inline QScriptClassInfo *classInfo() const
72 { return m_classInfo; }
73
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);
83};
84
85FunctionClassData::FunctionClassData(QScriptClassInfo *classInfo)
86 : m_classInfo(classInfo)
87{
88}
89
90FunctionClassData::~FunctionClassData()
91{
92}
93
94bool FunctionClassData::resolve(const QScriptValueImpl &object,
95 QScriptNameIdImpl *nameId,
96 QScript::Member *member, QScriptValueImpl *base,
97 QScript::AccessMode /*access*/)
98{
99 if (object.classInfo() != classInfo())
100 return false;
101
102 QScriptEnginePrivate *eng = object.engine();
103
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);
110 *base = object;
111 return true;
112 }
113
114 return false;
115}
116
117bool FunctionClassData::get(const QScriptValueImpl &object, const Member &member,
118 QScriptValueImpl *result)
119{
120 if (object.classInfo() != classInfo())
121 return false;
122
123 QScriptEnginePrivate *eng = object.engine();
124 if (! member.isNativeProperty())
125 return false;
126
127 if (member.nameId() == eng->idTable()->id_length) {
128 *result = QScriptValueImpl(object.toFunction()->length);
129 return true;
130 } else if (member.nameId() == eng->idTable()->id_arguments) {
131 *result = eng->nullValue();
132 return true;
133 }
134
135 return false;
136}
137
138bool FunctionClassData::put(QScriptValueImpl *, const QScript::Member &,
139 const QScriptValueImpl &)
140{
141 return false;
142}
143
144void FunctionClassData::mark(const QScriptValueImpl &object, int generation)
145{
146 if (object.classInfo() != classInfo())
147 return;
148 QScriptFunction *fun = object.toFunction();
149 QScriptEnginePrivate *eng = object.engine();
150 fun->mark(eng, generation);
151}
152
153Function::Function(QScriptEnginePrivate *eng, QScriptClassInfo *classInfo):
154 Core(eng, classInfo)
155{
156 publicPrototype = eng->createFunction(method_void, 0, classInfo); // public prototype
157}
158
159Function::~Function()
160{
161}
162
163void Function::initialize()
164{
165 QScriptEnginePrivate *eng = engine();
166 eng->newConstructor(&ctor, this, publicPrototype);
167
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);
173
174 classInfo()->setData(new FunctionClassData(classInfo()));
175}
176
177void Function::execute(QScriptContextPrivate *context)
178{
179#ifndef Q_SCRIPT_NO_EVENT_NOTIFY
180 engine()->notifyFunctionEntry(context);
181#endif
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);
187#endif
188}
189
190QString Function::buildFunction(QScriptContextPrivate *context)
191{
192 int argc = context->argumentCount();
193
194 QString code;
195 code += QLatin1String("function(");
196
197 // the formals
198 for (int i = 0; i < argc - 1; ++i) {
199 if (i != 0)
200 code += QLatin1String(",");
201
202 code += context->argument(i).toString();
203 }
204
205 code += QLatin1String("){");
206
207 // the function body
208 if (argc != 0)
209 code += context->argument(argc - 1).toString();
210
211 code += QLatin1String("\n}");
212
213 return code;
214}
215
216void Function::newFunction(QScriptValueImpl *result, QScriptFunction *foo)
217{
218 engine()->newFunction(result, foo);
219}
220
221QScriptValueImpl Function::method_toString(QScriptContextPrivate *context, QScriptEnginePrivate *eng, QScriptClassInfo *)
222{
223 QScriptValueImpl self = context->thisObject();
224 if (QScriptFunction *foo = self.toFunction()) {
225 QString code = foo->toString(context);
226 return QScriptValueImpl(eng, code);
227 }
228
229 return throwThisObjectTypeError(
230 context, QLatin1String("Function.prototype.toString"));
231}
232
233QScriptValueImpl Function::method_call(QScriptContextPrivate *context, QScriptEnginePrivate *eng, QScriptClassInfo *)
234{
235 if (! context->thisObject().isFunction()) {
236 return throwThisObjectTypeError(
237 context, QLatin1String("Function.prototype.call"));
238 }
239
240 QScriptValueImpl thisObject = eng->toObject(context->argument(0));
241 if (! (thisObject.isValid () && thisObject.isObject()))
242 thisObject = eng->globalObject();
243
244 QScriptValueImplList args;
245 for (int i = 1; i < context->argumentCount(); ++i)
246 args << context->argument(i);
247
248 return context->thisObject().call(thisObject, args);
249}
250
251QScriptValueImpl Function::method_apply(QScriptContextPrivate *context, QScriptEnginePrivate *eng, QScriptClassInfo *)
252{
253 if (! context->thisObject().isFunction()) {
254 return throwThisObjectTypeError(
255 context, QLatin1String("Function.prototype.apply"));
256 }
257
258 QScriptValueImpl thisObject = eng->toObject(context->argument(0));
259 if (! (thisObject.isValid () && thisObject.isObject()))
260 thisObject = eng->globalObject();
261
262 QScriptValueImplList args;
263 QScriptValueImpl undefined = eng->undefinedValue();
264
265 QScriptValueImpl arg = context->argument(1);
266
267 if (Ecma::Array::Instance *arr = eng->arrayConstructor->get(arg)) {
268 QScript::Array actuals = arr->value;
269
270 for (quint32 i = 0; i < actuals.count(); ++i) {
271 QScriptValueImpl a = actuals.at(i);
272 if (! a.isValid())
273 args << undefined;
274 else
275 args << a;
276 }
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"));
286 }
287
288 return context->thisObject().call(thisObject, args);
289}
290
291QScriptValueImpl Function::method_void(QScriptContextPrivate *, QScriptEnginePrivate *eng, QScriptClassInfo *)
292{
293 return eng->undefinedValue();
294}
295
296QScriptValueImpl Function::method_disconnect(QScriptContextPrivate *context, QScriptEnginePrivate *eng, QScriptClassInfo *)
297{
298#ifndef QT_NO_QOBJECT
299 if (context->argumentCount() == 0) {
300 return context->throwError(
301 QLatin1String("Function.prototype.disconnect: no arguments given"));
302 }
303
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"));
310 }
311
312 QtFunction *qtSignal = static_cast<QtFunction*>(fun);
313
314 const QMetaObject *meta = qtSignal->metaObject();
315 if (!meta) {
316 return context->throwError(
317 QScriptContext::TypeError,
318 QString::fromLatin1("Function.prototype.disconnect: cannot disconnect from deleted QObject"));
319 }
320
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())));
327 }
328
329 QScriptValueImpl receiver;
330 QScriptValueImpl slot;
331 QScriptValueImpl arg0 = context->argument(0);
332 if (context->argumentCount() < 2) {
333 receiver = QScriptValueImpl();
334 slot = arg0;
335 } else {
336 receiver = arg0;
337 QScriptValueImpl arg1 = context->argument(1);
338 if (arg1.isFunction())
339 slot = arg1;
340 else
341 slot = receiver.property(arg1.toString(), QScriptValue::ResolvePrototype);
342 }
343
344 if (!slot.isFunction()) {
345 return context->throwError(
346 QScriptContext::TypeError,
347 QLatin1String("Function.prototype.disconnect: target is not a function"));
348 }
349
350 bool ok = eng->scriptDisconnect(self, receiver, slot);
351 if (!ok) {
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())));
356 }
357 return eng->undefinedValue();
358#else
359 Q_UNUSED(eng);
360 return context->throwError(QScriptContext::TypeError,
361 QLatin1String("Function.prototype.disconnect"));
362#endif // QT_NO_QOBJECT
363}
364
365QScriptValueImpl Function::method_connect(QScriptContextPrivate *context, QScriptEnginePrivate *eng, QScriptClassInfo *classInfo)
366{
367 Q_UNUSED(classInfo);
368
369#ifndef QT_NO_QOBJECT
370 if (context->argumentCount() == 0) {
371 return context->throwError(
372 QLatin1String("Function.prototype.connect: no arguments given"));
373 }
374
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"));
381 }
382
383 QtFunction *qtSignal = static_cast<QtFunction*>(fun);
384
385 const QMetaObject *meta = qtSignal->metaObject();
386 if (!meta) {
387 return context->throwError(
388 QScriptContext::TypeError,
389 QString::fromLatin1("Function.prototype.connect: cannot connect to deleted QObject"));
390 }
391
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())));
398 }
399
400 {
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())));
411 }
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);
415 }
416 }
417
418 QScriptValueImpl receiver;
419 QScriptValueImpl slot;
420 QScriptValueImpl arg0 = context->argument(0);
421 if (context->argumentCount() < 2) {
422 receiver = QScriptValueImpl();
423 slot = arg0;
424 } else {
425 receiver = arg0;
426 QScriptValueImpl arg1 = context->argument(1);
427 if (arg1.isFunction())
428 slot = arg1;
429 else
430 slot = receiver.property(arg1.toString(), QScriptValue::ResolvePrototype);
431 }
432
433 if (!slot.isFunction()) {
434 return context->throwError(
435 QScriptContext::TypeError,
436 QLatin1String("Function.prototype.connect: target is not a function"));
437 }
438
439 bool ok = eng->scriptConnect(self, receiver, slot);
440 if (!ok) {
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())));
445 }
446 return eng->undefinedValue();
447#else
448 Q_UNUSED(eng);
449 Q_UNUSED(classInfo);
450 return context->throwError(QScriptContext::TypeError,
451 QLatin1String("Function.prototype.connect"));
452#endif // QT_NO_QOBJECT
453}
454
455} } // namespace QScript::Ecma
456
457QT_END_NAMESPACE
458
459#endif // QT_NO_SCRIPT
Note: See TracBrowser for help on using the repository browser.