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