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 | |
56 | QT_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 | |
101 | class QScriptContextInfoPrivate : public QSharedData |
102 | { |
103 | Q_DECLARE_PUBLIC(QScriptContextInfo) |
104 | public: |
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 | */ |
129 | QScriptContextInfoPrivate::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 | */ |
143 | QScriptContextInfoPrivate::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 | */ |
236 | QScriptContextInfoPrivate::~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 | */ |
248 | QScriptContextInfo::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 | */ |
260 | QScriptContextInfo::QScriptContextInfo(const QScriptContextInfo &other) |
261 | : d_ptr(other.d_ptr) |
262 | { |
263 | } |
264 | |
265 | /*! |
266 | Constructs a null QScriptContextInfo. |
267 | |
268 | \sa isNull() |
269 | */ |
270 | QScriptContextInfo::QScriptContextInfo() |
271 | : d_ptr(0) |
272 | { |
273 | } |
274 | |
275 | /*! |
276 | Destroys the QScriptContextInfo. |
277 | */ |
278 | QScriptContextInfo::~QScriptContextInfo() |
279 | { |
280 | } |
281 | |
282 | /*! |
283 | Assigns the \a other info to this QScriptContextInfo, |
284 | and returns a reference to this QScriptContextInfo. |
285 | */ |
286 | QScriptContextInfo &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 | */ |
299 | qint64 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 | */ |
316 | QString 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 | */ |
333 | int 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 | */ |
344 | int 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 | */ |
363 | QString 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 | */ |
376 | QScriptContextInfo::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 | */ |
393 | int 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 | */ |
410 | int 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 | */ |
424 | QStringList 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 | */ |
445 | int 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 | */ |
457 | bool 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 | */ |
467 | bool 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 | */ |
491 | bool 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 | */ |
504 | QDataStream &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 | */ |
530 | Q_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 | |
570 | QT_END_NAMESPACE |
571 | |