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 "qscriptcontext.h" |
42 | |
43 | #include "qscriptcontext_p.h" |
44 | #include "qscriptcontextinfo.h" |
45 | #include "qscriptengine.h" |
46 | #include "qscriptengine_p.h" |
47 | #include "../bridge/qscriptactivationobject_p.h" |
48 | |
49 | #include "Arguments.h" |
50 | #include "CodeBlock.h" |
51 | #include "Error.h" |
52 | #include "JSFunction.h" |
53 | #include "JSObject.h" |
54 | #include "JSGlobalObject.h" |
55 | |
56 | #include <QtCore/qstringlist.h> |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | /*! |
61 | \since 4.3 |
62 | \class QScriptContext |
63 | \inmodule QtScript |
64 | \brief The QScriptContext class represents a Qt Script function invocation. |
65 | |
66 | \ingroup script |
67 | |
68 | A QScriptContext provides access to the `this' object and arguments |
69 | passed to a script function. You typically want to access this |
70 | information when you're writing a native (C++) function (see |
71 | QScriptEngine::newFunction()) that will be called from script |
72 | code. For example, when the script code |
73 | |
74 | \snippet code/src_script_qscriptcontext.cpp 0 |
75 | |
76 | is evaluated, a QScriptContext will be created, and the context will |
77 | carry the arguments as QScriptValues; in this particular case, the |
78 | arguments will be one QScriptValue containing the number 20.5, a second |
79 | QScriptValue containing the string \c{"hello"}, and a third QScriptValue |
80 | containing a Qt Script object. |
81 | |
82 | Use argumentCount() to get the number of arguments passed to the |
83 | function, and argument() to get an argument at a certain index. The |
84 | argumentsObject() function returns a Qt Script array object |
85 | containing all the arguments; you can use the QScriptValueIterator |
86 | to iterate over its elements, or pass the array on as arguments to |
87 | another script function using QScriptValue::call(). |
88 | |
89 | Use thisObject() to get the `this' object associated with the function call, |
90 | and setThisObject() to set the `this' object. If you are implementing a |
91 | native "instance method", you typically fetch the thisObject() and access |
92 | one or more of its properties: |
93 | |
94 | \snippet code/src_script_qscriptcontext.cpp 1 |
95 | |
96 | Use isCalledAsConstructor() to determine if the function was called |
97 | as a constructor (e.g. \c{"new foo()"} (as constructor) or just |
98 | \c{"foo()"}). When a function is called as a constructor, the |
99 | thisObject() contains the newly constructed object that the function |
100 | is expected to initialize. |
101 | |
102 | Use throwValue() or throwError() to throw an exception. |
103 | |
104 | Use callee() to obtain the QScriptValue that represents the function being |
105 | called. This can for example be used to call the function recursively. |
106 | |
107 | Use parentContext() to get a pointer to the context that precedes |
108 | this context in the activation stack. This is mostly useful for |
109 | debugging purposes (e.g. when constructing some form of backtrace). |
110 | |
111 | The activationObject() function returns the object that is used to |
112 | hold the local variables associated with this function call. You can |
113 | replace the activation object by calling setActivationObject(). A |
114 | typical usage of these functions is when you want script code to be |
115 | evaluated in the context of the parent context, e.g. to implement an |
116 | include() function: |
117 | |
118 | \snippet code/src_script_qscriptcontext.cpp 2 |
119 | |
120 | Use backtrace() to get a human-readable backtrace associated with |
121 | this context. This can be useful for debugging purposes when |
122 | implementing native functions. The toString() function provides a |
123 | string representation of the context. (QScriptContextInfo provides |
124 | more detailed debugging-related information about the |
125 | QScriptContext.) |
126 | |
127 | Use engine() to obtain a pointer to the QScriptEngine that this context |
128 | resides in. |
129 | |
130 | \sa QScriptContextInfo, QScriptEngine::newFunction(), QScriptable |
131 | */ |
132 | |
133 | /*! |
134 | \enum QScriptContext::ExecutionState |
135 | |
136 | This enum specifies the frameution state of the context. |
137 | |
138 | \value NormalState The context is in a normal state. |
139 | |
140 | \value ExceptionState The context is in an exceptional state. |
141 | */ |
142 | |
143 | /*! |
144 | \enum QScriptContext::Error |
145 | |
146 | This enum specifies types of error. |
147 | |
148 | \value ReferenceError A reference error. |
149 | |
150 | \value SyntaxError A syntax error. |
151 | |
152 | \value TypeError A type error. |
153 | |
154 | \value RangeError A range error. |
155 | |
156 | \value URIError A URI error. |
157 | |
158 | \value UnknownError An unknown error. |
159 | */ |
160 | |
161 | /*! |
162 | \internal |
163 | */ |
164 | QScriptContext::QScriptContext() |
165 | { |
166 | //QScriptContext doesn't exist, pointer to QScriptContext are just pointer to JSC::CallFrame |
167 | Q_ASSERT(false); |
168 | } |
169 | |
170 | /*! |
171 | Throws an exception with the given \a value. |
172 | Returns the value thrown (the same as the argument). |
173 | |
174 | \sa throwError(), state() |
175 | */ |
176 | QScriptValue QScriptContext::throwValue(const QScriptValue &value) |
177 | { |
178 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
179 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
180 | QScript::APIShim shim(engine); |
181 | JSC::JSValue jscValue = engine->scriptValueToJSCValue(value); |
182 | engine->clearCurrentException(); |
183 | frame->setException(jscValue); |
184 | return value; |
185 | } |
186 | |
187 | /*! |
188 | Throws an \a error with the given \a text. |
189 | Returns the created error object. |
190 | |
191 | The \a text will be stored in the \c{message} property of the error |
192 | object. |
193 | |
194 | The error object will be initialized to contain information about |
195 | the location where the error occurred; specifically, it will have |
196 | properties \c{lineNumber}, \c{fileName} and \c{stack}. These |
197 | properties are described in \l {Qt Script Extensions to ECMAScript}. |
198 | |
199 | \sa throwValue(), state() |
200 | */ |
201 | QScriptValue QScriptContext::throwError(Error error, const QString &text) |
202 | { |
203 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
204 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
205 | QScript::APIShim shim(engine); |
206 | JSC::ErrorType jscError = JSC::GeneralError; |
207 | switch (error) { |
208 | case UnknownError: |
209 | break; |
210 | case ReferenceError: |
211 | jscError = JSC::ReferenceError; |
212 | break; |
213 | case SyntaxError: |
214 | jscError = JSC::SyntaxError; |
215 | break; |
216 | case TypeError: |
217 | jscError = JSC::TypeError; |
218 | break; |
219 | case RangeError: |
220 | jscError = JSC::RangeError; |
221 | break; |
222 | case URIError: |
223 | jscError = JSC::URIError; |
224 | break; |
225 | } |
226 | JSC::JSObject *result = JSC::throwError(frame, jscError, message: text); |
227 | engine->clearCurrentException(); |
228 | return engine->scriptValueFromJSCValue(value: result); |
229 | } |
230 | |
231 | /*! |
232 | \overload |
233 | |
234 | Throws an error with the given \a text. |
235 | Returns the created error object. |
236 | |
237 | \sa throwValue(), state() |
238 | */ |
239 | QScriptValue QScriptContext::throwError(const QString &text) |
240 | { |
241 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
242 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
243 | QScript::APIShim shim(engine); |
244 | engine->clearCurrentException(); |
245 | JSC::JSObject *result = JSC::throwError(frame, JSC::GeneralError, message: text); |
246 | return engine->scriptValueFromJSCValue(value: result); |
247 | } |
248 | |
249 | /*! |
250 | Destroys this QScriptContext. |
251 | */ |
252 | QScriptContext::~QScriptContext() |
253 | { |
254 | //QScriptContext doesn't exist, pointer to QScriptContext are just pointer to JSC::CallFrame |
255 | Q_ASSERT(false); |
256 | } |
257 | |
258 | /*! |
259 | Returns the QScriptEngine that this QScriptContext belongs to. |
260 | */ |
261 | QScriptEngine *QScriptContext::engine() const |
262 | { |
263 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
264 | return QScriptEnginePrivate::get(d: QScript::scriptEngineFromExec(exec: frame)); |
265 | } |
266 | |
267 | /*! |
268 | Returns the function argument at the given \a index. |
269 | |
270 | If \a index >= argumentCount(), a QScriptValue of |
271 | the primitive type Undefined is returned. |
272 | |
273 | \sa argumentCount() |
274 | */ |
275 | QScriptValue QScriptContext::argument(int index) const |
276 | { |
277 | if (index < 0) |
278 | return QScriptValue(); |
279 | if (index >= argumentCount()) |
280 | return QScriptValue(QScriptValue::UndefinedValue); |
281 | QScriptValue v = argumentsObject().property(arrayIndex: index); |
282 | return v; |
283 | } |
284 | |
285 | /*! |
286 | Returns the callee. The callee is the function object that this |
287 | QScriptContext represents an invocation of. |
288 | */ |
289 | QScriptValue QScriptContext::callee() const |
290 | { |
291 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
292 | QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec: frame); |
293 | QScript::APIShim shim(eng); |
294 | if (frame->callee() == eng->originalGlobalObject()) { |
295 | // This is a pushContext()-created context; the callee is a lie. |
296 | Q_ASSERT(QScriptEnginePrivate::contextFlags(const_cast<JSC::CallFrame*>(frame)) & QScriptEnginePrivate::NativeContext); |
297 | return QScriptValue(); |
298 | } |
299 | return eng->scriptValueFromJSCValue(value: frame->callee()); |
300 | } |
301 | |
302 | /*! |
303 | Returns the arguments object of this QScriptContext. |
304 | |
305 | The arguments object has properties \c callee (equal to callee()) |
306 | and \c length (equal to argumentCount()), and properties \c 0, \c 1, |
307 | ..., argumentCount() - 1 that provide access to the argument |
308 | values. Initially, property \c P (0 <= \c P < argumentCount()) has |
309 | the same value as argument(\c P). In the case when \c P is less |
310 | than the number of formal parameters of the function, \c P shares |
311 | its value with the corresponding property of the activation object |
312 | (activationObject()). This means that changing this property changes |
313 | the corresponding property of the activation object and vice versa. |
314 | |
315 | \sa argument(), activationObject() |
316 | */ |
317 | QScriptValue QScriptContext::argumentsObject() const |
318 | { |
319 | JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(context: this)); |
320 | QScript::APIShim shim(QScript::scriptEngineFromExec(exec: frame)); |
321 | |
322 | if (frame == frame->lexicalGlobalObject()->globalExec()) { |
323 | // <global> context doesn't have arguments. return an empty object |
324 | return QScriptEnginePrivate::get(d: QScript::scriptEngineFromExec(exec: frame))->newObject(); |
325 | } |
326 | |
327 | //for a js function |
328 | if (frame->codeBlock() && frame->callee()) { |
329 | if (!QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { |
330 | // We have a built-in JS host call. |
331 | // codeBlock is needed by retrieveArguments(), but since it |
332 | // contains junk, we would crash. Return an invalid value for now. |
333 | return QScriptValue(); |
334 | } |
335 | JSC::JSValue result = frame->interpreter()->retrieveArguments(frame, JSC::asFunction(value: frame->callee())); |
336 | return QScript::scriptEngineFromExec(exec: frame)->scriptValueFromJSCValue(value: result); |
337 | } |
338 | |
339 | if (frame->callerFrame()->hasHostCallFrameFlag()) { |
340 | // <eval> context doesn't have arguments. return an empty object |
341 | return QScriptEnginePrivate::get(d: QScript::scriptEngineFromExec(exec: frame))->newObject(); |
342 | } |
343 | |
344 | //for a native function |
345 | if (!frame->optionalCalleeArguments() |
346 | && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { // Make sure we don't go here for host JSFunctions |
347 | Q_ASSERT(frame->argumentCount() > 0); //we need at least 'this' otherwise we'll crash later |
348 | JSC::Arguments* arguments = new (&frame->globalData())JSC::Arguments(frame, JSC::Arguments::NoParameters); |
349 | frame->setCalleeArguments(arguments); |
350 | } |
351 | return QScript::scriptEngineFromExec(exec: frame)->scriptValueFromJSCValue(value: frame->optionalCalleeArguments()); |
352 | } |
353 | |
354 | /*! |
355 | Returns true if the function was called as a constructor |
356 | (e.g. \c{"new foo()"}); otherwise returns false. |
357 | |
358 | When a function is called as constructor, the thisObject() |
359 | contains the newly constructed object to be initialized. |
360 | |
361 | \note This function is only guaranteed to work for a context |
362 | corresponding to native functions. |
363 | */ |
364 | bool QScriptContext::isCalledAsConstructor() const |
365 | { |
366 | JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(context: this)); |
367 | QScript::APIShim shim(QScript::scriptEngineFromExec(exec: frame)); |
368 | |
369 | //For native functions, look up flags. |
370 | uint flags = QScriptEnginePrivate::contextFlags(frame); |
371 | if (flags & QScriptEnginePrivate::NativeContext) |
372 | return flags & QScriptEnginePrivate::CalledAsConstructorContext; |
373 | |
374 | //Not a native function, try to look up in the bytecode if we where called from op_construct |
375 | JSC::Instruction* returnPC = frame->returnPC(); |
376 | |
377 | if (!returnPC) |
378 | return false; |
379 | |
380 | JSC::CallFrame *callerFrame = QScriptEnginePrivate::frameForContext(context: parentContext()); |
381 | if (!callerFrame) |
382 | return false; |
383 | |
384 | if (returnPC[-JSC::op_construct_length].u.opcode == frame->interpreter()->getOpcode(JSC::op_construct)) { |
385 | //We are maybe called from the op_construct opcode which has 6 opperands. |
386 | //But we need to check we are not called from op_call with 4 opperands |
387 | |
388 | //we make sure that the returnPC[-1] (thisRegister) is smaller than the returnPC[-3] (registerOffset) |
389 | //as if it was an op_call, the returnPC[-1] would be the registerOffset, bigger than returnPC[-3] (funcRegister) |
390 | return returnPC[-1].u.operand < returnPC[-3].u.operand; |
391 | } |
392 | return false; |
393 | } |
394 | |
395 | /*! |
396 | Returns the parent context of this QScriptContext. |
397 | */ |
398 | QScriptContext *QScriptContext::parentContext() const |
399 | { |
400 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
401 | QScript::APIShim shim(QScript::scriptEngineFromExec(exec: frame)); |
402 | JSC::CallFrame *callerFrame = frame->callerFrame()->removeHostCallFrameFlag(); |
403 | return QScriptEnginePrivate::contextForFrame(frame: callerFrame); |
404 | } |
405 | |
406 | /*! |
407 | Returns the number of arguments passed to the function |
408 | in this invocation. |
409 | |
410 | Note that the argument count can be different from the |
411 | formal number of arguments (the \c{length} property of |
412 | callee()). |
413 | |
414 | \sa argument() |
415 | */ |
416 | int QScriptContext::argumentCount() const |
417 | { |
418 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
419 | int argc = frame->argumentCount(); |
420 | if (argc != 0) |
421 | --argc; // -1 due to "this" |
422 | return argc; |
423 | } |
424 | |
425 | /*! |
426 | \internal |
427 | */ |
428 | QScriptValue QScriptContext::returnValue() const |
429 | { |
430 | qWarning(msg: "QScriptContext::returnValue() not implemented" ); |
431 | return QScriptValue(); |
432 | } |
433 | |
434 | /*! |
435 | \internal |
436 | */ |
437 | void QScriptContext::setReturnValue(const QScriptValue &result) |
438 | { |
439 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
440 | JSC::CallFrame *callerFrame = frame->callerFrame(); |
441 | if (!callerFrame->codeBlock()) |
442 | return; |
443 | Q_ASSERT_X(false, Q_FUNC_INFO, "check me" ); |
444 | int dst = frame->registers()[JSC::RegisterFile::ReturnValueRegister].i(); // returnValueRegister() is private |
445 | callerFrame[dst] = QScript::scriptEngineFromExec(exec: frame)->scriptValueToJSCValue(value: result); |
446 | } |
447 | |
448 | /*! |
449 | Returns the activation object of this QScriptContext. The activation |
450 | object provides access to the local variables associated with this |
451 | context. |
452 | |
453 | \note The activation object might not be available if there is no |
454 | active QScriptEngineAgent, as it might be optimized. |
455 | |
456 | \sa argument(), argumentsObject() |
457 | */ |
458 | |
459 | QScriptValue QScriptContext::activationObject() const |
460 | { |
461 | JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(context: this)); |
462 | QScript::APIShim shim(QScript::scriptEngineFromExec(exec: frame)); |
463 | JSC::JSObject *result = 0; |
464 | |
465 | uint flags = QScriptEnginePrivate::contextFlags(frame); |
466 | if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) { |
467 | //For native functions, lazily create it if needed |
468 | QScript::QScriptActivationObject *scope = new (frame) QScript::QScriptActivationObject(frame); |
469 | frame->setScopeChain(frame->scopeChain()->copy()->push(o: scope)); |
470 | result = scope; |
471 | QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext); |
472 | } else { |
473 | // look in scope chain |
474 | JSC::ScopeChainNode *node = frame->scopeChain(); |
475 | JSC::ScopeChainIterator it(node); |
476 | for (it = node->begin(); it != node->end(); ++it) { |
477 | if ((*it) && (*it)->isVariableObject()) { |
478 | result = *it; |
479 | break; |
480 | } |
481 | } |
482 | } |
483 | if (!result) { |
484 | if (!parentContext()) |
485 | return engine()->globalObject(); |
486 | |
487 | qWarning(msg: "QScriptContext::activationObject: could not get activation object for frame" ); |
488 | return QScriptValue(); |
489 | /*JSC::CodeBlock *codeBlock = frame->codeBlock(); |
490 | if (!codeBlock) { |
491 | // non-Qt native function |
492 | Q_ASSERT(true); //### this should in theorry not happen |
493 | result = new (frame)QScript::QScriptActivationObject(frame); |
494 | } else { |
495 | // ### this is wrong |
496 | JSC::FunctionBodyNode *body = static_cast<JSC::FunctionBodyNode*>(codeBlock->ownerNode()); |
497 | result = new (frame)JSC::JSActivation(frame, body); |
498 | }*/ |
499 | } |
500 | |
501 | if (result && result->inherits(info: &QScript::QScriptActivationObject::info) |
502 | && (static_cast<QScript::QScriptActivationObject*>(result)->delegate() != 0)) { |
503 | // Return the object that property access is being delegated to |
504 | result = static_cast<QScript::QScriptActivationObject*>(result)->delegate(); |
505 | } |
506 | |
507 | return QScript::scriptEngineFromExec(exec: frame)->scriptValueFromJSCValue(value: result); |
508 | } |
509 | |
510 | /*! |
511 | Sets the activation object of this QScriptContext to be the given \a |
512 | activation. |
513 | |
514 | If \a activation is not an object, this function does nothing. |
515 | |
516 | \note For a context corresponding to a JavaScript function, this is only |
517 | guaranteed to work if there was an QScriptEngineAgent active on the |
518 | engine while the function was evaluated. |
519 | */ |
520 | void QScriptContext::setActivationObject(const QScriptValue &activation) |
521 | { |
522 | if (!activation.isObject()) |
523 | return; |
524 | else if (activation.engine() != engine()) { |
525 | qWarning(msg: "QScriptContext::setActivationObject() failed: " |
526 | "cannot set an object created in " |
527 | "a different engine" ); |
528 | return; |
529 | } |
530 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
531 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
532 | QScript::APIShim shim(engine); |
533 | JSC::JSObject *object = JSC::asObject(value: engine->scriptValueToJSCValue(value: activation)); |
534 | if (object == engine->originalGlobalObjectProxy) |
535 | object = engine->originalGlobalObject(); |
536 | |
537 | uint flags = QScriptEnginePrivate::contextFlags(frame); |
538 | if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) { |
539 | //For native functions, we create a scope node |
540 | JSC::JSObject *scope = object; |
541 | if (!scope->isVariableObject()) { |
542 | // Create a QScriptActivationObject that acts as a proxy |
543 | scope = new (frame) QScript::QScriptActivationObject(frame, scope); |
544 | } |
545 | frame->setScopeChain(frame->scopeChain()->copy()->push(o: scope)); |
546 | QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext); |
547 | return; |
548 | } |
549 | |
550 | // else replace the first activation object in the scope chain |
551 | JSC::ScopeChainNode *node = frame->scopeChain(); |
552 | while (node != 0) { |
553 | if (node->object && node->object->isVariableObject()) { |
554 | if (!object->isVariableObject()) { |
555 | if (node->object->inherits(info: &QScript::QScriptActivationObject::info)) { |
556 | static_cast<QScript::QScriptActivationObject*>(node->object)->setDelegate(object); |
557 | } else { |
558 | // Create a QScriptActivationObject that acts as a proxy |
559 | node->object = new (frame) QScript::QScriptActivationObject(frame, object); |
560 | } |
561 | } else { |
562 | node->object = object; |
563 | } |
564 | break; |
565 | } |
566 | node = node->next; |
567 | } |
568 | } |
569 | |
570 | /*! |
571 | Returns the `this' object associated with this QScriptContext. |
572 | */ |
573 | QScriptValue QScriptContext::thisObject() const |
574 | { |
575 | JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(context: this)); |
576 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
577 | QScript::APIShim shim(engine); |
578 | JSC::JSValue result = engine->thisForContext(frame); |
579 | if (!result || result.isNull()) |
580 | result = frame->globalThisValue(); |
581 | return engine->scriptValueFromJSCValue(value: result); |
582 | } |
583 | |
584 | /*! |
585 | Sets the `this' object associated with this QScriptContext to be |
586 | \a thisObject. |
587 | |
588 | If \a thisObject is not an object, this function does nothing. |
589 | */ |
590 | void QScriptContext::setThisObject(const QScriptValue &thisObject) |
591 | { |
592 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
593 | QScript::APIShim shim(QScript::scriptEngineFromExec(exec: frame)); |
594 | if (!thisObject.isObject()) |
595 | return; |
596 | if (thisObject.engine() != engine()) { |
597 | qWarning(msg: "QScriptContext::setThisObject() failed: " |
598 | "cannot set an object created in " |
599 | "a different engine" ); |
600 | return; |
601 | } |
602 | if (frame == frame->lexicalGlobalObject()->globalExec()) { |
603 | engine()->setGlobalObject(thisObject); |
604 | return; |
605 | } |
606 | JSC::JSValue jscThisObject = QScript::scriptEngineFromExec(exec: frame)->scriptValueToJSCValue(value: thisObject); |
607 | JSC::CodeBlock *cb = frame->codeBlock(); |
608 | if (cb != 0) { |
609 | frame[cb->thisRegister()] = jscThisObject; |
610 | } else { |
611 | JSC::Register* thisRegister = QScriptEnginePrivate::thisRegisterForFrame(frame); |
612 | thisRegister[0] = jscThisObject; |
613 | } |
614 | } |
615 | |
616 | /*! |
617 | Returns the frameution state of this QScriptContext. |
618 | */ |
619 | QScriptContext::ExecutionState QScriptContext::state() const |
620 | { |
621 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
622 | if (frame->hadException()) |
623 | return QScriptContext::ExceptionState; |
624 | return QScriptContext::NormalState; |
625 | } |
626 | |
627 | /*! |
628 | Returns a human-readable backtrace of this QScriptContext. |
629 | |
630 | Each line is of the form \c{<function-name>(<arguments>)@<file-name>:<line-number>}. |
631 | |
632 | To access individual pieces of debugging-related information (for |
633 | example, to construct your own backtrace representation), use |
634 | QScriptContextInfo. |
635 | |
636 | \sa QScriptEngine::uncaughtExceptionBacktrace(), QScriptContextInfo, toString() |
637 | */ |
638 | QStringList QScriptContext::backtrace() const |
639 | { |
640 | QStringList result; |
641 | const QScriptContext *ctx = this; |
642 | while (ctx) { |
643 | result.append(t: ctx->toString()); |
644 | ctx = ctx->parentContext(); |
645 | } |
646 | return result; |
647 | } |
648 | |
649 | /*! |
650 | \since 4.4 |
651 | |
652 | Returns a string representation of this context. |
653 | This is useful for debugging. |
654 | |
655 | \sa backtrace() |
656 | */ |
657 | QString QScriptContext::toString() const |
658 | { |
659 | QScriptContextInfo info(this); |
660 | QString result; |
661 | |
662 | QString functionName = info.functionName(); |
663 | if (functionName.isEmpty()) { |
664 | if (parentContext()) { |
665 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
666 | if (info.functionType() == QScriptContextInfo::ScriptFunction) |
667 | result.append(s: QLatin1String("<anonymous>" )); |
668 | else if(frame->callerFrame()->hasHostCallFrameFlag()) |
669 | result.append(s: QLatin1String("<eval>" )); |
670 | else |
671 | result.append(s: QLatin1String("<native>" )); |
672 | } else { |
673 | result.append(s: QLatin1String("<global>" )); |
674 | } |
675 | } else { |
676 | result.append(s: functionName); |
677 | } |
678 | |
679 | QStringList parameterNames = info.functionParameterNames(); |
680 | result.append(c: QLatin1Char('(')); |
681 | for (int i = 0; i < argumentCount(); ++i) { |
682 | if (i > 0) |
683 | result.append(s: QLatin1String(", " )); |
684 | if (i < parameterNames.count()) { |
685 | result.append(s: parameterNames.at(i)); |
686 | result.append(s: QLatin1String(" = " )); |
687 | } |
688 | QScriptValue arg = argument(index: i); |
689 | if (arg.isString()) |
690 | result.append(c: QLatin1Char('\'')); |
691 | result.append(s: arg.toString()); |
692 | if (arg.isString()) |
693 | result.append(c: QLatin1Char('\'')); |
694 | |
695 | } |
696 | result.append(c: QLatin1Char(')')); |
697 | |
698 | QString fileName = info.fileName(); |
699 | int lineNumber = info.lineNumber(); |
700 | result.append(s: QLatin1String(" at " )); |
701 | if (!fileName.isEmpty()) { |
702 | result.append(s: fileName); |
703 | result.append(c: QLatin1Char(':')); |
704 | } |
705 | result.append(s: QString::number(lineNumber)); |
706 | return result; |
707 | } |
708 | |
709 | /*! |
710 | \internal |
711 | \since 4.5 |
712 | |
713 | Returns the scope chain of this QScriptContext. |
714 | */ |
715 | QScriptValueList QScriptContext::scopeChain() const |
716 | { |
717 | activationObject(); //ensure the creation of the normal scope for native context |
718 | const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
719 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
720 | QScript::APIShim shim(engine); |
721 | QScriptValueList result; |
722 | JSC::ScopeChainNode *node = frame->scopeChain(); |
723 | JSC::ScopeChainIterator it(node); |
724 | for (it = node->begin(); it != node->end(); ++it) { |
725 | JSC::JSObject *object = *it; |
726 | if (!object) |
727 | continue; |
728 | if (object->inherits(info: &QScript::QScriptActivationObject::info) |
729 | && (static_cast<QScript::QScriptActivationObject*>(object)->delegate() != 0)) { |
730 | // Return the object that property access is being delegated to |
731 | object = static_cast<QScript::QScriptActivationObject*>(object)->delegate(); |
732 | } |
733 | result.append(t: engine->scriptValueFromJSCValue(value: object)); |
734 | } |
735 | return result; |
736 | } |
737 | |
738 | /*! |
739 | \internal |
740 | \since 4.5 |
741 | |
742 | Adds the given \a object to the front of this context's scope chain. |
743 | |
744 | If \a object is not an object, this function does nothing. |
745 | */ |
746 | void QScriptContext::pushScope(const QScriptValue &object) |
747 | { |
748 | activationObject(); //ensure the creation of the normal scope for native context |
749 | if (!object.isObject()) |
750 | return; |
751 | else if (object.engine() != engine()) { |
752 | qWarning(msg: "QScriptContext::pushScope() failed: " |
753 | "cannot push an object created in " |
754 | "a different engine" ); |
755 | return; |
756 | } |
757 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
758 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
759 | QScript::APIShim shim(engine); |
760 | JSC::JSObject *jscObject = JSC::asObject(value: engine->scriptValueToJSCValue(value: object)); |
761 | if (jscObject == engine->originalGlobalObjectProxy) |
762 | jscObject = engine->originalGlobalObject(); |
763 | JSC::ScopeChainNode *scope = frame->scopeChain(); |
764 | Q_ASSERT(scope != 0); |
765 | if (!scope->object) { |
766 | // pushing to an "empty" chain |
767 | if (!jscObject->isGlobalObject()) { |
768 | qWarning(msg: "QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object" ); |
769 | return; |
770 | } |
771 | scope->object = jscObject; |
772 | } |
773 | else |
774 | frame->setScopeChain(scope->push(o: jscObject)); |
775 | } |
776 | |
777 | /*! |
778 | \internal |
779 | \since 4.5 |
780 | |
781 | Removes the front object from this context's scope chain, and |
782 | returns the removed object. |
783 | |
784 | If the scope chain is already empty, this function returns an |
785 | invalid QScriptValue. |
786 | */ |
787 | QScriptValue QScriptContext::popScope() |
788 | { |
789 | activationObject(); //ensure the creation of the normal scope for native context |
790 | JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context: this); |
791 | JSC::ScopeChainNode *scope = frame->scopeChain(); |
792 | Q_ASSERT(scope != 0); |
793 | QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec: frame); |
794 | QScript::APIShim shim(engine); |
795 | QScriptValue result = engine->scriptValueFromJSCValue(value: scope->object); |
796 | if (!scope->next) { |
797 | // We cannot have a null scope chain, so just zap the object pointer. |
798 | scope->object = 0; |
799 | } else { |
800 | frame->setScopeChain(scope->pop()); |
801 | } |
802 | return result; |
803 | } |
804 | |
805 | QT_END_NAMESPACE |
806 | |