Don't crash when creating backtrace for built-in JS function
[qt:qt.git] / src / script / api / qscriptcontextinfo.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtScript module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL-ONLY$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser
12 ** General Public License version 2.1 as published by the Free Software
13 ** Foundation and appearing in the file LICENSE.LGPL included in the
14 ** packaging of this file.  Please review the following information to
15 ** ensure the GNU Lesser General Public License version 2.1 requirements
16 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** If you have questions regarding the use of this file, please contact
19 ** Nokia at qt-info@nokia.com.
20 ** $QT_END_LICENSE$
21 **
22 ****************************************************************************/
23
24 #include "config.h"
25 #include "qscriptcontextinfo.h"
26
27 #include "qscriptcontext_p.h"
28 #include "qscriptengine.h"
29 #include "qscriptengine_p.h"
30 #include "../bridge/qscriptqobject_p.h"
31 #include <QtCore/qdatastream.h>
32 #include <QtCore/qmetaobject.h>
33 #include "CodeBlock.h"
34 #include "JSFunction.h"
35 #if ENABLE(JIT)
36 #include "MacroAssemblerCodeRef.h"
37 #endif
38
39 QT_BEGIN_NAMESPACE
40
41 /*!
42   \since 4.4
43   \class QScriptContextInfo
44
45   \brief The QScriptContextInfo class provides additional information about a QScriptContext.
46
47   \ingroup script
48
49
50   QScriptContextInfo is typically used for debugging purposes. It can
51   provide information about the code being executed, such as the type
52   of the called function, and the original source code location of the
53   current statement.
54
55   If the called function is executing Qt Script code, you can obtain
56   the script location with the functions fileName() and lineNumber().
57
58   You can obtain the starting line number and ending line number of a
59   Qt Script function definition with functionStartLineNumber() and
60   functionEndLineNumber(), respectively.
61
62   For Qt Script functions and Qt methods (e.g. slots), you can call
63   functionParameterNames() to get the names of the formal parameters of the
64   function.
65
66   For Qt methods and Qt property accessors, you can obtain the index
67   of the underlying QMetaMethod or QMetaProperty by calling
68   functionMetaIndex().
69
70   \sa QScriptContext, QScriptEngineAgent
71 */
72
73 /*!
74     \enum QScriptContextInfo::FunctionType
75
76     This enum specifies the type of function being called.
77
78     \value ScriptFunction The function is a Qt Script function, i.e. it was defined through a call to QScriptEngine::evaluate().
79     \value QtFunction The function is a Qt function (a signal, slot or method).
80     \value QtPropertyFunction The function is a Qt property getter or setter.
81     \value NativeFunction The function is a built-in Qt Script function, or it was defined through a call to QScriptEngine::newFunction().
82 */
83
84 class QScriptContextInfoPrivate
85 {
86     Q_DECLARE_PUBLIC(QScriptContextInfo)
87 public:
88     QScriptContextInfoPrivate();
89     QScriptContextInfoPrivate(const QScriptContext *context);
90     ~QScriptContextInfoPrivate();
91
92     qint64 scriptId;
93     int lineNumber;
94     int columnNumber;
95     QString fileName;
96
97     QString functionName;
98     QScriptContextInfo::FunctionType functionType;
99
100     int functionStartLineNumber;
101     int functionEndLineNumber;
102     int functionMetaIndex;
103
104     QStringList parameterNames;
105
106     QBasicAtomicInt ref;
107
108     QScriptContextInfo *q_ptr;
109 };
110
111 /*!
112   \internal
113 */
114 QScriptContextInfoPrivate::QScriptContextInfoPrivate()
115 {
116     ref = 0;
117     functionType = QScriptContextInfo::NativeFunction;
118     functionMetaIndex = -1;
119     functionStartLineNumber = -1;
120     functionEndLineNumber = -1;
121     scriptId = -1;
122     lineNumber = -1;
123     columnNumber = -1;
124 }
125
126 /*!
127   \internal
128 */
129 QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *context)
130 {
131     Q_ASSERT(context);
132     ref = 0;
133     functionType = QScriptContextInfo::NativeFunction;
134     functionMetaIndex = -1;
135     functionStartLineNumber = -1;
136     functionEndLineNumber = -1;
137     scriptId = -1;
138     lineNumber = -1;
139     columnNumber = -1;
140
141     JSC::CallFrame *frame = const_cast<JSC::CallFrame *>(QScriptEnginePrivate::frameForContext(context));
142
143     // Get the line number:
144
145     //We need to know the context directly up in the backtrace, in order to get the line number, and adjust the global context
146     JSC::CallFrame *rewindContext = QScriptEnginePrivate::get(context->engine())->currentFrame;
147     if (QScriptEnginePrivate::contextForFrame(rewindContext) == context) {  //top context
148         frame = rewindContext; //for retreiving the global context's "fake" frame
149         // An agent might have provided the line number.
150         lineNumber = QScript::scriptEngineFromExec(frame)->agentLineNumber;
151     } else {
152         // rewind the stack from the top in order to find the frame from the caller where the returnPC is stored
153         while (rewindContext && QScriptEnginePrivate::contextForFrame(rewindContext->callerFrame()->removeHostCallFrameFlag()) != context)
154             rewindContext = rewindContext->callerFrame()->removeHostCallFrameFlag();
155         if (rewindContext) {
156             frame = rewindContext->callerFrame()->removeHostCallFrameFlag(); //for retreiving the global context's "fake" frame
157
158             JSC::Instruction *returnPC = rewindContext->returnPC();
159             JSC::CodeBlock *codeBlock = frame->codeBlock();
160             if (returnPC && codeBlock) {
161 #if ENABLE(JIT)
162                 unsigned bytecodeOffset = codeBlock->getBytecodeIndex(frame, JSC::ReturnAddressPtr(returnPC));
163 #else
164                 unsigned bytecodeOffset = returnPC - codeBlock->instructions().begin();
165 #endif
166                 bytecodeOffset--; //because returnPC is on the next instruction. We want the current one
167                 lineNumber = codeBlock->lineNumberForBytecodeOffset(const_cast<JSC::ExecState *>(frame), bytecodeOffset);
168             }
169         }
170     }
171
172     // Get the filename and the scriptId:
173     JSC::CodeBlock *codeBlock = frame->codeBlock();
174     if (codeBlock) {
175            JSC::SourceProvider *source = codeBlock->source();
176            scriptId = source->asID();
177            fileName = source->url();
178     }
179
180     // Get the others information:
181     JSC::JSObject *callee = frame->callee();
182     if (callee && callee->inherits(&JSC::InternalFunction::info))
183         functionName = JSC::asInternalFunction(callee)->name(frame);
184     if (callee && callee->inherits(&JSC::JSFunction::info)
185         && !JSC::asFunction(callee)->isHostFunction()) {
186         functionType = QScriptContextInfo::ScriptFunction;
187         JSC::FunctionExecutable *body = JSC::asFunction(callee)->jsExecutable();
188         functionStartLineNumber = body->lineNo();
189         functionEndLineNumber = body->lastLine();
190         for (size_t i = 0; i < body->parameterCount(); ++i)
191             parameterNames.append(body->parameterName(i));
192         // ### get the function name from the AST
193     } else if (callee && callee->inherits(&QScript::QtFunction::info)) {
194         functionType = QScriptContextInfo::QtFunction;
195         // ### the slot can be overloaded -- need to get the particular overload from the context
196         functionMetaIndex = static_cast<QScript::QtFunction*>(callee)->initialIndex();
197         const QMetaObject *meta = static_cast<QScript::QtFunction*>(callee)->metaObject();
198         if (meta != 0) {
199             QMetaMethod method = meta->method(functionMetaIndex);
200             QList<QByteArray> formals = method.parameterNames();
201             for (int i = 0; i < formals.count(); ++i)
202                 parameterNames.append(QLatin1String(formals.at(i)));
203         }
204     }
205     else if (callee && callee->inherits(&QScript::QtPropertyFunction::info)) {
206         functionType = QScriptContextInfo::QtPropertyFunction;
207         functionMetaIndex = static_cast<QScript::QtPropertyFunction*>(callee)->propertyIndex();
208     }
209 }
210
211 /*!
212   \internal
213 */
214 QScriptContextInfoPrivate::~QScriptContextInfoPrivate()
215 {
216 }
217
218 /*!
219   Constructs a new QScriptContextInfo from the given \a context.
220
221   The relevant information is extracted from the \a context at
222   construction time; i.e. if you continue script execution in the \a
223   context, the new state of the context will not be reflected in a
224   previously created QScriptContextInfo.
225 */
226 QScriptContextInfo::QScriptContextInfo(const QScriptContext *context)
227     : d_ptr(0)
228 {
229     if (context) {
230         d_ptr = new QScriptContextInfoPrivate(context);
231         d_ptr->q_ptr = this;
232     }
233 }
234
235 /*!
236   Constructs a new QScriptContextInfo from the \a other info.
237 */
238 QScriptContextInfo::QScriptContextInfo(const QScriptContextInfo &other)
239     : d_ptr(other.d_ptr)
240 {
241 }
242
243 /*!
244   Constructs a null QScriptContextInfo.
245
246   \sa isNull()
247 */
248 QScriptContextInfo::QScriptContextInfo()
249     : d_ptr(0)
250 {
251 }
252
253 /*!
254   Destroys the QScriptContextInfo.
255 */
256 QScriptContextInfo::~QScriptContextInfo()
257 {
258 }
259
260 /*!
261   Assigns the \a other info to this QScriptContextInfo,
262   and returns a reference to this QScriptContextInfo.
263 */
264 QScriptContextInfo &QScriptContextInfo::operator=(const QScriptContextInfo &other)
265 {
266     d_ptr = other.d_ptr;
267     return *this;
268 }
269
270 /*!
271   Returns the ID of the script where the code being executed was
272   defined, or -1 if the ID is not available (i.e. a native function is
273   being executed).
274
275   \sa QScriptEngineAgent::scriptLoad()
276 */
277 qint64 QScriptContextInfo::scriptId() const
278 {
279     Q_D(const QScriptContextInfo);
280     if (!d)
281         return -1;
282     return d->scriptId;
283 }
284
285 /*!
286   Returns the name of the file where the code being executed was
287   defined, if available; otherwise returns an empty string.
288
289   For Qt Script code, this function returns the fileName argument
290   that was passed to QScriptEngine::evaluate().
291
292   \sa lineNumber(), functionName()
293 */
294 QString QScriptContextInfo::fileName() const
295 {
296     Q_D(const QScriptContextInfo);
297     if (!d)
298         return QString();
299     return d->fileName;
300 }
301
302 /*!
303   Returns the line number corresponding to the statement being
304   executed, or -1 if the line number is not available.
305
306   The line number is only available if Qt Script code is being
307   executed.
308
309   \sa columnNumber(), fileName()
310 */
311 int QScriptContextInfo::lineNumber() const
312 {
313     Q_D(const QScriptContextInfo);
314     if (!d)
315         return -1;
316     return d->lineNumber;
317 }
318
319 /*!
320   \obsolete
321 */
322 int QScriptContextInfo::columnNumber() const
323 {
324     Q_D(const QScriptContextInfo);
325     if (!d)
326         return -1;
327     return d->columnNumber;
328 }
329
330 /*!
331   Returns the name of the called function, or an empty string if
332   the name is not available.
333
334   For script functions of type QtPropertyFunction, this function
335   always returns the name of the property; you can use
336   QScriptContext::argumentCount() to differentiate between reads and
337   writes.
338
339   \sa fileName(), functionType()
340 */
341 QString QScriptContextInfo::functionName() const
342 {
343     Q_D(const QScriptContextInfo);
344     if (!d)
345         return QString();
346     return d->functionName;
347 }
348
349 /*!
350   Returns the type of the called function.
351
352   \sa functionName(), QScriptContext::callee()
353 */
354 QScriptContextInfo::FunctionType QScriptContextInfo::functionType() const
355 {
356     Q_D(const QScriptContextInfo);
357     if (!d)
358         return NativeFunction;
359     return d->functionType;
360 }
361
362 /*!
363   Returns the line number where the definition of the called function
364   starts, or -1 if the line number is not available.
365
366   The starting line number is only available if the functionType() is
367   ScriptFunction.
368
369   \sa functionEndLineNumber(), fileName()
370 */
371 int QScriptContextInfo::functionStartLineNumber() const
372 {
373     Q_D(const QScriptContextInfo);
374     if (!d)
375         return -1;
376     return d->functionStartLineNumber;
377 }
378
379 /*!
380   Returns the line number where the definition of the called function
381   ends, or -1 if the line number is not available.
382
383   The ending line number is only available if the functionType() is
384   ScriptFunction.
385
386   \sa functionStartLineNumber()
387 */
388 int QScriptContextInfo::functionEndLineNumber() const
389 {
390     Q_D(const QScriptContextInfo);
391     if (!d)
392         return -1;
393     return d->functionEndLineNumber;
394 }
395
396 /*!
397   Returns the names of the formal parameters of the called function,
398   or an empty QStringList if the parameter names are not available.
399
400   \sa QScriptContext::argument()
401 */
402 QStringList QScriptContextInfo::functionParameterNames() const
403 {
404     Q_D(const QScriptContextInfo);
405     if (!d)
406         return QStringList();
407     return d->parameterNames;
408 }
409
410 /*!
411   Returns the meta index of the called function, or -1 if the meta
412   index is not available.
413
414   The meta index is only available if the functionType() is QtFunction
415   or QtPropertyFunction. For QtFunction, the meta index can be passed
416   to QMetaObject::method() to obtain the corresponding method
417   definition; for QtPropertyFunction, the meta index can be passed to
418   QMetaObject::property() to obtain the corresponding property
419   definition.
420
421   \sa QScriptContext::thisObject()
422 */
423 int QScriptContextInfo::functionMetaIndex() const
424 {
425     Q_D(const QScriptContextInfo);
426     if (!d)
427         return -1;
428     return d->functionMetaIndex;
429 }
430
431 /*!
432   Returns true if this QScriptContextInfo is null, i.e. does not
433   contain any information.
434 */
435 bool QScriptContextInfo::isNull() const
436 {
437     Q_D(const QScriptContextInfo);
438     return (d == 0);
439 }
440
441 /*!
442   Returns true if this QScriptContextInfo is equal to the \a other
443   info, otherwise returns false.
444 */
445 bool QScriptContextInfo::operator==(const QScriptContextInfo &other) const
446 {
447     Q_D(const QScriptContextInfo);
448     const QScriptContextInfoPrivate *od = other.d_func();
449     if (d == od)
450         return true;
451     if (!d || !od)
452         return false;
453     return ((d->scriptId == od->scriptId)
454             && (d->lineNumber == od->lineNumber)
455             && (d->columnNumber == od->columnNumber)
456             && (d->fileName == od->fileName)
457             && (d->functionName == od->functionName)
458             && (d->functionType == od->functionType)
459             && (d->functionStartLineNumber == od->functionStartLineNumber)
460             && (d->functionEndLineNumber == od->functionEndLineNumber)
461             && (d->functionMetaIndex == od->functionMetaIndex)
462             && (d->parameterNames == od->parameterNames));
463 }
464
465 /*!
466   Returns true if this QScriptContextInfo is not equal to the \a other
467   info, otherwise returns false.
468 */
469 bool QScriptContextInfo::operator!=(const QScriptContextInfo &other) const
470 {
471     return !(*this == other);
472 }
473
474 #ifndef QT_NO_DATASTREAM
475 /*!
476   \fn QDataStream &operator<<(QDataStream &stream, const QScriptContextInfo &info)
477   \since 4.4
478   \relates QScriptContextInfo
479
480   Writes the given \a info to the specified \a stream.
481 */
482 QDataStream &operator<<(QDataStream &out, const QScriptContextInfo &info)
483 {
484     out << info.scriptId();
485     out << (qint32)info.lineNumber();
486     out << (qint32)info.columnNumber();
487
488     out << (quint32)info.functionType();
489     out << (qint32)info.functionStartLineNumber();
490     out << (qint32)info.functionEndLineNumber();
491     out << (qint32)info.functionMetaIndex();
492
493     out << info.fileName();
494     out << info.functionName();
495     out << info.functionParameterNames();
496
497     return out;
498 }
499
500 /*!
501   \fn QDataStream &operator>>(QDataStream &stream, QScriptContextInfo &info)
502   \since 4.4
503   \relates QScriptContextInfo
504
505   Reads a QScriptContextInfo from the specified \a stream into the
506   given \a info.
507 */
508 Q_SCRIPT_EXPORT QDataStream &operator>>(QDataStream &in, QScriptContextInfo &info)
509 {
510     if (!info.d_ptr) {
511         info.d_ptr = new QScriptContextInfoPrivate();
512     }
513
514     in >> info.d_ptr->scriptId;
515
516     qint32 line;
517     in >> line;
518     info.d_ptr->lineNumber = line;
519
520     qint32 column;
521     in >> column;
522     info.d_ptr->columnNumber = column;
523
524     quint32 ftype;
525     in >> ftype;
526     info.d_ptr->functionType = QScriptContextInfo::FunctionType(ftype);
527
528     qint32 startLine;
529     in >> startLine;
530     info.d_ptr->functionStartLineNumber = startLine;
531
532     qint32 endLine;
533     in >> endLine;
534     info.d_ptr->functionEndLineNumber = endLine;
535
536     qint32 metaIndex;
537     in >> metaIndex;
538     info.d_ptr->functionMetaIndex = metaIndex;
539
540     in >> info.d_ptr->fileName;
541     in >> info.d_ptr->functionName;
542     in >> info.d_ptr->parameterNames;
543
544     return in;
545 }
546 #endif
547
548 QT_END_NAMESPACE