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

source code of qtscript/src/script/api/qscriptcontextinfo.cpp