1
/****************************************************************************
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of the QtScript module of the Qt Toolkit.
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file. Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
** In addition, as a special exception, Nokia gives you certain additional
25
** rights. These rights are described in the Nokia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
40
****************************************************************************/
43
#include "qscriptcontext.h"
45
#include "qscriptcontext_p.h"
46
#include "qscriptcontextinfo.h"
47
#include "qscriptengine.h"
48
#include "qscriptengine_p.h"
49
#include "../bridge/qscriptactivationobject_p.h"
51
#include "Arguments.h"
52
#include "CodeBlock.h"
54
#include "JSFunction.h"
56
#include "JSGlobalObject.h"
58
#include <QtCore/qstringlist.h>
66
\brief The QScriptContext class represents a Qt Script function invocation.
71
A QScriptContext provides access to the `this' object and arguments
72
passed to a script function. You typically want to access this
73
information when you're writing a native (C++) function (see
74
QScriptEngine::newFunction()) that will be called from script
75
code. For example, when the script code
77
\snippet doc/src/snippets/code/src_script_qscriptcontext.cpp 0
79
is evaluated, a QScriptContext will be created, and the context will
80
carry the arguments as QScriptValues; in this particular case, the
81
arguments will be one QScriptValue containing the number 20.5, a second
82
QScriptValue containing the string \c{"hello"}, and a third QScriptValue
83
containing a Qt Script object.
85
Use argumentCount() to get the number of arguments passed to the
86
function, and argument() to get an argument at a certain index. The
87
argumentsObject() function returns a Qt Script array object
88
containing all the arguments; you can use the QScriptValueIterator
89
to iterate over its elements, or pass the array on as arguments to
90
another script function using QScriptValue::call().
92
Use thisObject() to get the `this' object associated with the function call,
93
and setThisObject() to set the `this' object. If you are implementing a
94
native "instance method", you typically fetch the thisObject() and access
95
one or more of its properties:
97
\snippet doc/src/snippets/code/src_script_qscriptcontext.cpp 1
99
Use isCalledAsConstructor() to determine if the function was called
100
as a constructor (e.g. \c{"new foo()"} (as constructor) or just
101
\c{"foo()"}). When a function is called as a constructor, the
102
thisObject() contains the newly constructed object that the function
103
is expected to initialize.
105
Use throwValue() or throwError() to throw an exception.
107
Use callee() to obtain the QScriptValue that represents the function being
108
called. This can for example be used to call the function recursively.
110
Use parentContext() to get a pointer to the context that precedes
111
this context in the activation stack. This is mostly useful for
112
debugging purposes (e.g. when constructing some form of backtrace).
114
The activationObject() function returns the object that is used to
115
hold the local variables associated with this function call. You can
116
replace the activation object by calling setActivationObject(). A
117
typical usage of these functions is when you want script code to be
118
evaluated in the context of the parent context, e.g. to implement an
121
\snippet doc/src/snippets/code/src_script_qscriptcontext.cpp 2
123
Use backtrace() to get a human-readable backtrace associated with
124
this context. This can be useful for debugging purposes when
125
implementing native functions. The toString() function provides a
126
string representation of the context. (QScriptContextInfo provides
127
more detailed debugging-related information about the
130
Use engine() to obtain a pointer to the QScriptEngine that this context
133
\sa QScriptContextInfo, QScriptEngine::newFunction(), QScriptable
137
\enum QScriptContext::ExecutionState
139
This enum specifies the frameution state of the context.
141
\value NormalState The context is in a normal state.
143
\value ExceptionState The context is in an exceptional state.
147
\enum QScriptContext::Error
149
This enum specifies types of error.
151
\value ReferenceError A reference error.
153
\value SyntaxError A syntax error.
155
\value TypeError A type error.
157
\value RangeError A range error.
159
\value URIError A URI error.
161
\value UnknownError An unknown error.
167
QScriptContext::QScriptContext()
169
//QScriptContext doesn't exist, pointer to QScriptContext are just pointer to JSC::CallFrame
174
Throws an exception with the given \a value.
175
Returns the value thrown (the same as the argument).
177
\sa throwError(), state()
179
QScriptValue QScriptContext::throwValue(const QScriptValue &value)
181
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
182
JSC::JSValue jscValue = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(value);
183
frame->setException(jscValue);
188
Throws an \a error with the given \a text.
189
Returns the created error object.
191
The \a text will be stored in the \c{message} property of the error
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 {QtScript Extensions to ECMAScript}.
199
\sa throwValue(), state()
201
QScriptValue QScriptContext::throwError(Error error, const QString &text)
203
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
204
JSC::ErrorType jscError = JSC::GeneralError;
209
jscError = JSC::ReferenceError;
212
jscError = JSC::SyntaxError;
215
jscError = JSC::TypeError;
218
jscError = JSC::RangeError;
221
jscError = JSC::URIError;
224
JSC::JSObject *result = JSC::throwError(frame, jscError, text);
225
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
231
Throws an error with the given \a text.
232
Returns the created error object.
234
\sa throwValue(), state()
236
QScriptValue QScriptContext::throwError(const QString &text)
238
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
239
JSC::JSObject *result = JSC::throwError(frame, JSC::GeneralError, text);
240
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
244
Destroys this QScriptContext.
246
QScriptContext::~QScriptContext()
248
//QScriptContext doesn't exist, pointer to QScriptContext are just pointer to JSC::CallFrame
253
Returns the QScriptEngine that this QScriptContext belongs to.
255
QScriptEngine *QScriptContext::engine() const
257
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
258
return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame));
262
Returns the function argument at the given \a index.
264
If \a index >= argumentCount(), a QScriptValue of
265
the primitive type Undefined is returned.
269
QScriptValue QScriptContext::argument(int index) const
272
return QScriptValue();
273
if (index >= argumentCount())
274
return QScriptValue(QScriptValue::UndefinedValue);
275
QScriptValue v = argumentsObject().property(index);
280
Returns the callee. The callee is the function object that this
281
QScriptContext represents an invocation of.
283
QScriptValue QScriptContext::callee() const
285
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
286
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(frame->callee());
290
Returns the arguments object of this QScriptContext.
292
The arguments object has properties \c callee (equal to callee())
293
and \c length (equal to argumentCount()), and properties \c 0, \c 1,
294
..., argumentCount() - 1 that provide access to the argument
295
values. Initially, property \c P (0 <= \c P < argumentCount()) has
296
the same value as argument(\c P). In the case when \c P is less
297
than the number of formal parameters of the function, \c P shares
298
its value with the corresponding property of the activation object
299
(activationObject()). This means that changing this property changes
300
the corresponding property of the activation object and vice versa.
302
\sa argument(), activationObject()
304
QScriptValue QScriptContext::argumentsObject() const
306
JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
308
if (frame == frame->lexicalGlobalObject()->globalExec()) {
309
// <global> context doesn't have arguments. return an empty object
310
return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame))->newObject();
314
if (frame->codeBlock() && frame->callee()) {
315
JSC::JSValue result = frame->interpreter()->retrieveArguments(frame, JSC::asFunction(frame->callee()));
316
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
319
if (frame->callerFrame()->hasHostCallFrameFlag()) {
320
// <eval> context doesn't have arguments. return an empty object
321
return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame))->newObject();
324
//for a native function
325
if (!frame->optionalCalleeArguments()) {
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);
330
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(frame->optionalCalleeArguments());
334
Returns true if the function was called as a constructor
335
(e.g. \c{"new foo()"}); otherwise returns false.
337
When a function is called as constructor, the thisObject()
338
contains the newly constructed object to be initialized.
340
bool QScriptContext::isCalledAsConstructor() const
342
JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
344
//For native functions, look up flags.
345
uint flags = QScriptEnginePrivate::contextFlags(frame);
346
if (flags & QScriptEnginePrivate::NativeContext)
347
return flags & QScriptEnginePrivate::CalledAsConstructorContext;
349
//Not a native function, try to look up in the bytecode if we where called from op_construct
350
JSC::Instruction* returnPC = frame->returnPC();
355
JSC::CallFrame *callerFrame = QScriptEnginePrivate::frameForContext(parentContext());
359
if (returnPC[-JSC::op_construct_length].u.opcode == frame->interpreter()->getOpcode(JSC::op_construct)) {
360
//We are maybe called from the op_construct opcode which has 6 opperands.
361
//But we need to check we are not called from op_call with 4 opperands
363
//we make sure that the returnPC[-1] (thisRegister) is smaller than the returnPC[-3] (registerOffset)
364
//as if it was an op_call, the returnPC[-1] would be the registerOffset, bigger than returnPC[-3] (funcRegister)
365
return returnPC[-1].u.operand < returnPC[-3].u.operand;
371
Returns the parent context of this QScriptContext.
373
QScriptContext *QScriptContext::parentContext() const
375
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
376
JSC::CallFrame *callerFrame = frame->callerFrame()->removeHostCallFrameFlag();
377
return QScriptEnginePrivate::contextForFrame(callerFrame);
381
Returns the number of arguments passed to the function
384
Note that the argument count can be different from the
385
formal number of arguments (the \c{length} property of
390
int QScriptContext::argumentCount() const
392
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
393
int argc = frame->argumentCount();
395
--argc; // -1 due to "this"
402
QScriptValue QScriptContext::returnValue() const
404
qWarning("QScriptContext::returnValue() not implemented");
405
return QScriptValue();
411
void QScriptContext::setReturnValue(const QScriptValue &result)
413
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
414
JSC::CallFrame *callerFrame = frame->callerFrame();
415
if (!callerFrame->codeBlock())
417
Q_ASSERT_X(false, Q_FUNC_INFO, "check me");
418
int dst = frame->registers()[JSC::RegisterFile::ReturnValueRegister].i(); // returnValueRegister() is private
419
callerFrame[dst] = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(result);
423
Returns the activation object of this QScriptContext. The activation
424
object provides access to the local variables associated with this
427
\sa argument(), argumentsObject()
430
QScriptValue QScriptContext::activationObject() const
432
JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
433
JSC::JSObject *result = 0;
435
uint flags = QScriptEnginePrivate::contextFlags(frame);
436
if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) {
437
//For native functions, lazily create it if needed
438
QScript::QScriptActivationObject *scope = new (frame) QScript::QScriptActivationObject(frame);
439
frame->setScopeChain(frame->scopeChain()->copy()->push(scope));
441
QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext);
443
// look in scope chain
444
JSC::ScopeChainNode *node = frame->scopeChain();
445
JSC::ScopeChainIterator it(node);
446
for (it = node->begin(); it != node->end(); ++it) {
447
if ((*it) && (*it)->isVariableObject()) {
454
if (!parentContext())
455
return engine()->globalObject();
457
qWarning("QScriptContext::activationObject: could not get activation object for frame");
458
return QScriptValue();
459
/*JSC::CodeBlock *codeBlock = frame->codeBlock();
461
// non-Qt native function
462
Q_ASSERT(true); //### this should in theorry not happen
463
result = new (frame)QScript::QScriptActivationObject(frame);
466
JSC::FunctionBodyNode *body = static_cast<JSC::FunctionBodyNode*>(codeBlock->ownerNode());
467
result = new (frame)JSC::JSActivation(frame, body);
471
if (result && result->inherits(&QScript::QScriptActivationObject::info)
472
&& (static_cast<QScript::QScriptActivationObject*>(result)->delegate() != 0)) {
473
// Return the object that property access is being delegated to
474
result = static_cast<QScript::QScriptActivationObject*>(result)->delegate();
477
return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
481
Sets the activation object of this QScriptContext to be the given \a
484
If \a activation is not an object, this function does nothing.
486
void QScriptContext::setActivationObject(const QScriptValue &activation)
488
if (!activation.isObject())
490
else if (activation.engine() != engine()) {
491
qWarning("QScriptContext::setActivationObject() failed: "
492
"cannot set an object created in "
493
"a different engine");
496
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
497
QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
498
JSC::JSObject *object = JSC::asObject(engine->scriptValueToJSCValue(activation));
499
if (object == engine->originalGlobalObjectProxy)
500
object = engine->originalGlobalObject();
502
uint flags = QScriptEnginePrivate::contextFlags(frame);
503
if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) {
504
//For native functions, we create a scope node
505
JSC::JSObject *scope = object;
506
if (!scope->isVariableObject()) {
507
// Create a QScriptActivationObject that acts as a proxy
508
scope = new (frame) QScript::QScriptActivationObject(frame, scope);
510
frame->setScopeChain(frame->scopeChain()->copy()->push(scope));
511
QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext);
515
// else replace the first activation object in the scope chain
516
JSC::ScopeChainNode *node = frame->scopeChain();
518
if (node->object && node->object->isVariableObject()) {
519
if (!object->isVariableObject()) {
520
if (node->object->inherits(&QScript::QScriptActivationObject::info)) {
521
static_cast<QScript::QScriptActivationObject*>(node->object)->setDelegate(object);
523
// Create a QScriptActivationObject that acts as a proxy
524
node->object = new (frame) QScript::QScriptActivationObject(frame, object);
527
node->object = object;
536
Returns the `this' object associated with this QScriptContext.
538
QScriptValue QScriptContext::thisObject() const
540
JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
541
QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
542
JSC::JSValue result = engine->thisForContext(frame);
543
if (!result || result.isNull())
544
result = frame->globalThisValue();
545
return engine->scriptValueFromJSCValue(result);
549
Sets the `this' object associated with this QScriptContext to be
552
If \a thisObject is not an object, this function does nothing.
554
void QScriptContext::setThisObject(const QScriptValue &thisObject)
556
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
557
if (!thisObject.isObject())
559
if (thisObject.engine() != engine()) {
560
qWarning("QScriptContext::setThisObject() failed: "
561
"cannot set an object created in "
562
"a different engine");
565
if (frame == frame->lexicalGlobalObject()->globalExec()) {
566
engine()->setGlobalObject(thisObject);
569
JSC::JSValue jscThisObject = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(thisObject);
570
JSC::CodeBlock *cb = frame->codeBlock();
572
frame[cb->thisRegister()] = jscThisObject;
574
JSC::Register* thisRegister = QScriptEnginePrivate::thisRegisterForFrame(frame);
575
thisRegister[0] = jscThisObject;
580
Returns the frameution state of this QScriptContext.
582
QScriptContext::ExecutionState QScriptContext::state() const
584
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
585
if (frame->hadException())
586
return QScriptContext::ExceptionState;
587
return QScriptContext::NormalState;
591
Returns a human-readable backtrace of this QScriptContext.
593
Each line is of the form \c{<function-name>(<arguments>)@<file-name>:<line-number>}.
595
To access individual pieces of debugging-related information (for
596
example, to construct your own backtrace representation), use
599
\sa QScriptEngine::uncaughtExceptionBacktrace(), QScriptContextInfo, toString()
601
QStringList QScriptContext::backtrace() const
604
const QScriptContext *ctx = this;
606
result.append(ctx->toString());
607
ctx = ctx->parentContext();
615
Returns a string representation of this context.
616
This is useful for debugging.
620
QString QScriptContext::toString() const
622
QScriptContextInfo info(this);
625
QString functionName = info.functionName();
626
if (functionName.isEmpty()) {
627
if (parentContext()) {
628
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
629
if (info.functionType() == QScriptContextInfo::ScriptFunction)
630
result.append(QLatin1String("<anonymous>"));
631
else if(frame->callerFrame()->hasHostCallFrameFlag())
632
result.append(QLatin1String("<eval>"));
634
result.append(QLatin1String("<native>"));
636
result.append(QLatin1String("<global>"));
639
result.append(functionName);
642
QStringList parameterNames = info.functionParameterNames();
643
result.append(QLatin1Char('('));
644
for (int i = 0; i < argumentCount(); ++i) {
646
result.append(QLatin1String(", "));
647
if (i < parameterNames.count()) {
648
result.append(parameterNames.at(i));
649
result.append(QLatin1String(" = "));
651
QScriptValue arg = argument(i);
653
result.append(QLatin1Char('\''));
654
result.append(arg.toString());
656
result.append(QLatin1Char('\''));
659
result.append(QLatin1Char(')'));
661
QString fileName = info.fileName();
662
int lineNumber = info.lineNumber();
663
result.append(QLatin1String(" at "));
664
if (!fileName.isEmpty()) {
665
result.append(fileName);
666
result.append(QLatin1Char(':'));
668
result.append(QString::number(lineNumber));
676
Returns the scope chain of this QScriptContext.
678
QScriptValueList QScriptContext::scopeChain() const
680
activationObject(); //ensure the creation of the normal scope for native context
681
const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
682
QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
683
QScriptValueList result;
684
JSC::ScopeChainNode *node = frame->scopeChain();
685
JSC::ScopeChainIterator it(node);
686
for (it = node->begin(); it != node->end(); ++it) {
687
JSC::JSObject *object = *it;
690
if (object->inherits(&QScript::QScriptActivationObject::info)
691
&& (static_cast<QScript::QScriptActivationObject*>(object)->delegate() != 0)) {
692
// Return the object that property access is being delegated to
693
object = static_cast<QScript::QScriptActivationObject*>(object)->delegate();
695
result.append(engine->scriptValueFromJSCValue(object));
704
Adds the given \a object to the front of this context's scope chain.
706
If \a object is not an object, this function does nothing.
708
void QScriptContext::pushScope(const QScriptValue &object)
710
activationObject(); //ensure the creation of the normal scope for native context
711
if (!object.isObject())
713
else if (object.engine() != engine()) {
714
qWarning("QScriptContext::pushScope() failed: "
715
"cannot push an object created in "
716
"a different engine");
719
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
720
QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
721
JSC::JSObject *jscObject = JSC::asObject(engine->scriptValueToJSCValue(object));
722
if (jscObject == engine->originalGlobalObjectProxy)
723
jscObject = engine->originalGlobalObject();
724
JSC::ScopeChainNode *scope = frame->scopeChain();
725
Q_ASSERT(scope != 0);
726
if (!scope->object) {
727
// pushing to an "empty" chain
728
if (!jscObject->isGlobalObject()) {
729
qWarning("QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object");
732
scope->object = jscObject;
735
frame->setScopeChain(scope->push(jscObject));
742
Removes the front object from this context's scope chain, and
743
returns the removed object.
745
If the scope chain is already empty, this function returns an
746
invalid QScriptValue.
748
QScriptValue QScriptContext::popScope()
750
JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
751
JSC::ScopeChainNode *scope = frame->scopeChain();
752
Q_ASSERT(scope != 0);
753
QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
754
QScriptValue result = engine->scriptValueFromJSCValue(scope->object);
756
// We cannot have a null scope chain, so just zap the object pointer.
759
frame->setScopeChain(scope->pop());