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