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