source: trunk/src/script/api/qscriptcontext.cpp@ 1036

Last change on this file since 1036 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 27.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtScript module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL-ONLY$
10** GNU Lesser General Public License Usage
11** This file may be used under the terms of the GNU Lesser
12** General Public License version 2.1 as published by the Free Software
13** Foundation and appearing in the file LICENSE.LGPL included in the
14** packaging of this file. Please review the following information to
15** ensure the GNU Lesser General Public License version 2.1 requirements
16** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17**
18** If you have questions regarding the use of this file, please contact
19** Nokia at [email protected].
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
42QT_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*/
149QScriptContext::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*/
161QScriptValue 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*/
184QScriptValue 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*/
220QScriptValue 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*/
231QScriptContext::~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*/
240QScriptEngine *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*/
254QScriptValue 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*/
268QScriptValue QScriptContext::callee() const
269{
270 const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
271 QScript::APIShim shim(QScript::scriptEngineFromExec(frame));
272 return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(frame->callee());
273}
274
275/*!
276 Returns the arguments object of this QScriptContext.
277
278 The arguments object has properties \c callee (equal to callee())
279 and \c length (equal to argumentCount()), and properties \c 0, \c 1,
280 ..., argumentCount() - 1 that provide access to the argument
281 values. Initially, property \c P (0 <= \c P < argumentCount()) has
282 the same value as argument(\c P). In the case when \c P is less
283 than the number of formal parameters of the function, \c P shares
284 its value with the corresponding property of the activation object
285 (activationObject()). This means that changing this property changes
286 the corresponding property of the activation object and vice versa.
287
288 \sa argument(), activationObject()
289*/
290QScriptValue QScriptContext::argumentsObject() const
291{
292 JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
293 QScript::APIShim shim(QScript::scriptEngineFromExec(frame));
294
295 if (frame == frame->lexicalGlobalObject()->globalExec()) {
296 // <global> context doesn't have arguments. return an empty object
297 return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame))->newObject();
298 }
299
300 //for a js function
301 if (frame->codeBlock() && frame->callee()) {
302 if (!QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) {
303 // We have a built-in JS host call.
304 // codeBlock is needed by retrieveArguments(), but since it
305 // contains junk, we would crash. Return an invalid value for now.
306 return QScriptValue();
307 }
308 JSC::JSValue result = frame->interpreter()->retrieveArguments(frame, JSC::asFunction(frame->callee()));
309 return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
310 }
311
312 if (frame->callerFrame()->hasHostCallFrameFlag()) {
313 // <eval> context doesn't have arguments. return an empty object
314 return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame))->newObject();
315 }
316
317 //for a native function
318 if (!frame->optionalCalleeArguments()
319 && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { // Make sure we don't go here for host JSFunctions
320 Q_ASSERT(frame->argumentCount() > 0); //we need at least 'this' otherwise we'll crash later
321 JSC::Arguments* arguments = new (&frame->globalData())JSC::Arguments(frame, JSC::Arguments::NoParameters);
322 frame->setCalleeArguments(arguments);
323 }
324 return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(frame->optionalCalleeArguments());
325}
326
327/*!
328 Returns true if the function was called as a constructor
329 (e.g. \c{"new foo()"}); otherwise returns false.
330
331 When a function is called as constructor, the thisObject()
332 contains the newly constructed object to be initialized.
333
334 \note This function is only guaranteed to work for a context
335 corresponding to native functions.
336*/
337bool QScriptContext::isCalledAsConstructor() const
338{
339 JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
340 QScript::APIShim shim(QScript::scriptEngineFromExec(frame));
341
342 //For native functions, look up flags.
343 uint flags = QScriptEnginePrivate::contextFlags(frame);
344 if (flags & QScriptEnginePrivate::NativeContext)
345 return flags & QScriptEnginePrivate::CalledAsConstructorContext;
346
347 //Not a native function, try to look up in the bytecode if we where called from op_construct
348 JSC::Instruction* returnPC = frame->returnPC();
349
350 if (!returnPC)
351 return false;
352
353 JSC::CallFrame *callerFrame = QScriptEnginePrivate::frameForContext(parentContext());
354 if (!callerFrame)
355 return false;
356
357 if (returnPC[-JSC::op_construct_length].u.opcode == frame->interpreter()->getOpcode(JSC::op_construct)) {
358 //We are maybe called from the op_construct opcode which has 6 opperands.
359 //But we need to check we are not called from op_call with 4 opperands
360
361 //we make sure that the returnPC[-1] (thisRegister) is smaller than the returnPC[-3] (registerOffset)
362 //as if it was an op_call, the returnPC[-1] would be the registerOffset, bigger than returnPC[-3] (funcRegister)
363 return returnPC[-1].u.operand < returnPC[-3].u.operand;
364 }
365 return false;
366}
367
368/*!
369 Returns the parent context of this QScriptContext.
370*/
371QScriptContext *QScriptContext::parentContext() const
372{
373 const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
374 QScript::APIShim shim(QScript::scriptEngineFromExec(frame));
375 JSC::CallFrame *callerFrame = frame->callerFrame()->removeHostCallFrameFlag();
376 return QScriptEnginePrivate::contextForFrame(callerFrame);
377}
378
379/*!
380 Returns the number of arguments passed to the function
381 in this invocation.
382
383 Note that the argument count can be different from the
384 formal number of arguments (the \c{length} property of
385 callee()).
386
387 \sa argument()
388*/
389int QScriptContext::argumentCount() const
390{
391 const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
392 int argc = frame->argumentCount();
393 if (argc != 0)
394 --argc; // -1 due to "this"
395 return argc;
396}
397
398/*!
399 \internal
400*/
401QScriptValue QScriptContext::returnValue() const
402{
403 qWarning("QScriptContext::returnValue() not implemented");
404 return QScriptValue();
405}
406
407/*!
408 \internal
409*/
410void QScriptContext::setReturnValue(const QScriptValue &result)
411{
412 JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
413 JSC::CallFrame *callerFrame = frame->callerFrame();
414 if (!callerFrame->codeBlock())
415 return;
416 Q_ASSERT_X(false, Q_FUNC_INFO, "check me");
417 int dst = frame->registers()[JSC::RegisterFile::ReturnValueRegister].i(); // returnValueRegister() is private
418 callerFrame[dst] = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(result);
419}
420
421/*!
422 Returns the activation object of this QScriptContext. The activation
423 object provides access to the local variables associated with this
424 context.
425
426 \note The activation object might not be available if there is no
427 active QScriptEngineAgent, as it might be optimized.
428
429 \sa argument(), argumentsObject()
430*/
431
432QScriptValue QScriptContext::activationObject() const
433{
434 JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
435 QScript::APIShim shim(QScript::scriptEngineFromExec(frame));
436 JSC::JSObject *result = 0;
437
438 uint flags = QScriptEnginePrivate::contextFlags(frame);
439 if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) {
440 //For native functions, lazily create it if needed
441 QScript::QScriptActivationObject *scope = new (frame) QScript::QScriptActivationObject(frame);
442 frame->setScopeChain(frame->scopeChain()->copy()->push(scope));
443 result = scope;
444 QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext);
445 } else {
446 // look in scope chain
447 JSC::ScopeChainNode *node = frame->scopeChain();
448 JSC::ScopeChainIterator it(node);
449 for (it = node->begin(); it != node->end(); ++it) {
450 if ((*it) && (*it)->isVariableObject()) {
451 result = *it;
452 break;
453 }
454 }
455 }
456 if (!result) {
457 if (!parentContext())
458 return engine()->globalObject();
459
460 qWarning("QScriptContext::activationObject: could not get activation object for frame");
461 return QScriptValue();
462 /*JSC::CodeBlock *codeBlock = frame->codeBlock();
463 if (!codeBlock) {
464 // non-Qt native function
465 Q_ASSERT(true); //### this should in theorry not happen
466 result = new (frame)QScript::QScriptActivationObject(frame);
467 } else {
468 // ### this is wrong
469 JSC::FunctionBodyNode *body = static_cast<JSC::FunctionBodyNode*>(codeBlock->ownerNode());
470 result = new (frame)JSC::JSActivation(frame, body);
471 }*/
472 }
473
474 if (result && result->inherits(&QScript::QScriptActivationObject::info)
475 && (static_cast<QScript::QScriptActivationObject*>(result)->delegate() != 0)) {
476 // Return the object that property access is being delegated to
477 result = static_cast<QScript::QScriptActivationObject*>(result)->delegate();
478 }
479
480 return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result);
481}
482
483/*!
484 Sets the activation object of this QScriptContext to be the given \a
485 activation.
486
487 If \a activation is not an object, this function does nothing.
488
489 \note For a context corresponding to a JavaScript function, this is only
490 guaranteed to work if there was an QScriptEngineAgent active on the
491 engine while the function was evaluated.
492*/
493void QScriptContext::setActivationObject(const QScriptValue &activation)
494{
495 if (!activation.isObject())
496 return;
497 else if (activation.engine() != engine()) {
498 qWarning("QScriptContext::setActivationObject() failed: "
499 "cannot set an object created in "
500 "a different engine");
501 return;
502 }
503 JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
504 QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
505 QScript::APIShim shim(engine);
506 JSC::JSObject *object = JSC::asObject(engine->scriptValueToJSCValue(activation));
507 if (object == engine->originalGlobalObjectProxy)
508 object = engine->originalGlobalObject();
509
510 uint flags = QScriptEnginePrivate::contextFlags(frame);
511 if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) {
512 //For native functions, we create a scope node
513 JSC::JSObject *scope = object;
514 if (!scope->isVariableObject()) {
515 // Create a QScriptActivationObject that acts as a proxy
516 scope = new (frame) QScript::QScriptActivationObject(frame, scope);
517 }
518 frame->setScopeChain(frame->scopeChain()->copy()->push(scope));
519 QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext);
520 return;
521 }
522
523 // else replace the first activation object in the scope chain
524 JSC::ScopeChainNode *node = frame->scopeChain();
525 while (node != 0) {
526 if (node->object && node->object->isVariableObject()) {
527 if (!object->isVariableObject()) {
528 if (node->object->inherits(&QScript::QScriptActivationObject::info)) {
529 static_cast<QScript::QScriptActivationObject*>(node->object)->setDelegate(object);
530 } else {
531 // Create a QScriptActivationObject that acts as a proxy
532 node->object = new (frame) QScript::QScriptActivationObject(frame, object);
533 }
534 } else {
535 node->object = object;
536 }
537 break;
538 }
539 node = node->next;
540 }
541}
542
543/*!
544 Returns the `this' object associated with this QScriptContext.
545*/
546QScriptValue QScriptContext::thisObject() const
547{
548 JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this));
549 QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame);
550 QScript::APIShim shim(engine);
551 JSC::JSValue result = engine->thisForContext(frame);
552 if (!result || result.isNull())
553 result = frame->globalThisValue();
554 return engine->scriptValueFromJSCValue(result);
555}
556
557/*!
558 Sets the `this' object associated with this QScriptContext to be
559 \a thisObject.
560
561 If \a thisObject is not an object, this function does nothing.
562*/
563void QScriptContext::setThisObject(const QScriptValue &thisObject)
564{
565 JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
566 QScript::APIShim shim(QScript::scriptEngineFromExec(frame));
567 if (!thisObject.isObject())
568 return;
569 if (thisObject.engine() != engine()) {
570 qWarning("QScriptContext::setThisObject() failed: "
571 "cannot set an object created in "
572 "a different engine");
573 return;
574 }
575 if (frame == frame->lexicalGlobalObject()->globalExec()) {
576 engine()->setGlobalObject(thisObject);
577 return;
578 }
579 JSC::JSValue jscThisObject = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(thisObject);
580 JSC::CodeBlock *cb = frame->codeBlock();
581 if (cb != 0) {
582 frame[cb->thisRegister()] = jscThisObject;
583 } else {
584 JSC::Register* thisRegister = QScriptEnginePrivate::thisRegisterForFrame(frame);
585 thisRegister[0] = jscThisObject;
586 }
587}
588
589/*!
590 Returns the frameution state of this QScriptContext.
591*/
592QScriptContext::ExecutionState QScriptContext::state() const
593{
594 const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
595 if (frame->hadException())
596 return QScriptContext::ExceptionState;
597 return QScriptContext::NormalState;
598}
599
600/*!
601 Returns a human-readable backtrace of this QScriptContext.
602
603 Each line is of the form \c{<function-name>(<arguments>)@<file-name>:<line-number>}.
604
605 To access individual pieces of debugging-related information (for
606 example, to construct your own backtrace representation), use
607 QScriptContextInfo.
608
609 \sa QScriptEngine::uncaughtExceptionBacktrace(), QScriptContextInfo, toString()
610*/
611QStringList QScriptContext::backtrace() const
612{
613 QStringList result;
614 const QScriptContext *ctx = this;
615 while (ctx) {
616 result.append(ctx->toString());
617 ctx = ctx->parentContext();
618 }
619 return result;
620}
621
622/*!
623 \since 4.4
624
625 Returns a string representation of this context.
626 This is useful for debugging.
627
628 \sa backtrace()
629*/
630QString QScriptContext::toString() const
631{
632 QScriptContextInfo info(this);
633 QString result;
634
635 QString functionName = info.functionName();
636 if (functionName.isEmpty()) {
637 if (parentContext()) {
638 const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this);
639 if (info.functionType() == QScriptContextInfo::ScriptFunction)
640 result.append(QLatin1String("<anonymous>"));
641 else if(frame->callerFrame()->hasHostCallFrameFlag())
642 result.append(QLatin1String("<eval>"));
643 else
644 result.append(QLatin1String("<native>"));
645 } else {
646 result.append(QLatin1String("<global>"));
647 }
648 } else {