/*! Returns true if the function was called as a constructor (e.g. \c{"new foo()"}); otherwise returns false. When a function is called as constructor, the thisObject() contains the newly constructed object to be initialized. \note This function is only guaranteed to work for a context corresponding to native functions. */ bool QScriptContext::isCalledAsConstructor() const { JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this)); QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); //For native functions, look up flags. uint flags = QScriptEnginePrivate::contextFlags(frame); if (flags & QScriptEnginePrivate::NativeContext) return flags & QScriptEnginePrivate::CalledAsConstructorContext; //Not a native function, try to look up in the bytecode if we where called from op_construct JSC::Instruction* returnPC = frame->returnPC(); if (!returnPC) return false; JSC::CallFrame *callerFrame = QScriptEnginePrivate::frameForContext(parentContext()); if (!callerFrame) return false; if (returnPC[-JSC::op_construct_length].u.opcode == frame->interpreter()->getOpcode(JSC::op_construct)) { //We are maybe called from the op_construct opcode which has 6 opperands. //But we need to check we are not called from op_call with 4 opperands //we make sure that the returnPC[-1] (thisRegister) is smaller than the returnPC[-3] (registerOffset) //as if it was an op_call, the returnPC[-1] would be the registerOffset, bigger than returnPC[-3] (funcRegister) return returnPC[-1].u.operand < returnPC[-3].u.operand; } return false; }
/*! \internal */ QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *context) { Q_ASSERT(context); functionType = QScriptContextInfo::NativeFunction; functionMetaIndex = -1; functionStartLineNumber = -1; functionEndLineNumber = -1; scriptId = -1; lineNumber = -1; columnNumber = -1; JSC::CallFrame *frame = const_cast<JSC::CallFrame *>(QScriptEnginePrivate::frameForContext(context)); // Get the line number: //We need to know the context directly up in the backtrace, in order to get the line number, and adjust the global context JSC::CallFrame *rewindContext = QScriptEnginePrivate::get(context->engine())->currentFrame; if (QScriptEnginePrivate::contextForFrame(rewindContext) == context) { //top context frame = rewindContext; //for retreiving the global context's "fake" frame // An agent might have provided the line number. lineNumber = QScript::scriptEngineFromExec(frame)->agentLineNumber; if (lineNumber == -1) lineNumber = QScript::scriptEngineFromExec(frame)->uncaughtExceptionLineNumber; } else { // rewind the stack from the top in order to find the frame from the caller where the returnPC is stored while (rewindContext && QScriptEnginePrivate::contextForFrame(rewindContext->callerFrame()->removeHostCallFrameFlag()) != context) rewindContext = rewindContext->callerFrame()->removeHostCallFrameFlag(); if (rewindContext) { frame = rewindContext->callerFrame()->removeHostCallFrameFlag(); //for retreiving the global context's "fake" frame JSC::Instruction *returnPC = rewindContext->returnPC(); JSC::CodeBlock *codeBlock = frame->codeBlock(); if (returnPC && codeBlock && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { #if ENABLE(JIT) JSC::JITCode code = codeBlock->getJITCode(); uintptr_t jitOffset = reinterpret_cast<uintptr_t>(JSC::ReturnAddressPtr(returnPC).value()) - reinterpret_cast<uintptr_t>(code.addressForCall().executableAddress()); // We can only use the JIT code offset if it's smaller than the JIT size; // otherwise calling getBytecodeIndex() is meaningless. if (jitOffset < code.size()) { unsigned bytecodeOffset = codeBlock->getBytecodeIndex(frame, JSC::ReturnAddressPtr(returnPC)); #else unsigned bytecodeOffset = returnPC - codeBlock->instructions().begin(); #endif bytecodeOffset--; //because returnPC is on the next instruction. We want the current one lineNumber = codeBlock->lineNumberForBytecodeOffset(const_cast<JSC::ExecState *>(frame), bytecodeOffset); #if ENABLE(JIT) } #endif } } } // Get the filename and the scriptId: JSC::CodeBlock *codeBlock = frame->codeBlock(); if (codeBlock && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { JSC::SourceProvider *source = codeBlock->source(); scriptId = source->asID(); fileName = source->url(); } // Get the others information: JSC::JSObject *callee = frame->callee(); if (callee && callee->inherits(&JSC::InternalFunction::info)) functionName = JSC::asInternalFunction(callee)->name(frame); if (callee && callee->inherits(&JSC::JSFunction::info) && !JSC::asFunction(callee)->isHostFunction()) { functionType = QScriptContextInfo::ScriptFunction; JSC::FunctionExecutable *body = JSC::asFunction(callee)->jsExecutable(); functionStartLineNumber = body->lineNo(); functionEndLineNumber = body->lastLine(); for (size_t i = 0; i < body->parameterCount(); ++i) parameterNames.append(body->parameterName(i)); // ### get the function name from the AST } else if (callee && callee->inherits(&QScript::QtFunction::info)) { functionType = QScriptContextInfo::QtFunction; functionMetaIndex = static_cast<QScript::QtFunction*>(callee)->specificIndex(context); const QMetaObject *meta = static_cast<QScript::QtFunction*>(callee)->metaObject(); if (meta != 0) { QMetaMethod method = meta->method(functionMetaIndex); QList<QByteArray> formals = method.parameterNames(); for (int i = 0; i < formals.count(); ++i) parameterNames.append(QLatin1String(formals.at(i))); } } else if (callee && callee->inherits(&QScript::QtPropertyFunction::info)) { functionType = QScriptContextInfo::QtPropertyFunction; functionMetaIndex = static_cast<QScript::QtPropertyFunction*>(callee)->propertyIndex(); } }