//============================================================================= // // File : kvi_kvs_script.cpp // Creation date : Thu 25 Sep 2003 05.12 CEST by Szymon Stefanek // // This file is part of the KVirc irc client distribution // Copyright (C) 2003 Szymon Stefanek (pragma at kvirc dot net) // // This program is FREE software. You can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your opinion) any later version. // // This program is distributed in the HOPE that it will be USEFUL, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, write to the Free Software Foundation, // Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #define __KVIRC__ #include "kvi_kvs_script.h" #include "kvi_kvs_parser.h" #include "kvi_kvs_report.h" #include "kvi_kvs_runtimecontext.h" #include "kvi_kvs_treenode_instruction.h" #include "kvi_kvs_variantlist.h" #include "kvi_kvs_kernel.h" #include "kvi_locale.h" #include "kvi_out.h" #include "kvi_mirccntrl.h" #include "kvi_window.h" #include "kvi_app.h" class KVIRC_API KviKvsScriptData { friend class KviKvsScript; protected: unsigned int m_uRefs; // Reference count for this structure TQString m_szName; // script context name TQString m_szBuffer; // NEVER TOUCH THIS const TQChar * m_pBuffer; // this points to m_szBuffer: use it to extract string data KviKvsScript::ScriptType m_eType; // the type of the code in m_szBuffer KviKvsTreeNodeInstruction * m_pTree; // syntax tree unsigned int m_uLock; // this is increased while the script is being executed }; //#warning "THERE IS SOME MESS WITH m_szBuffer and m_pBuffer : with some script copying we may get errors with negative char indexes!" KviKvsScript::KviKvsScript(const TQString &szName,const TQString &szBuffer,ScriptType eType) { m_pData = new KviKvsScriptData; m_pData->m_uRefs = 1; m_pData->m_szName = szName; m_pData->m_eType = eType; m_pData->m_szBuffer = szBuffer; if(m_pData->m_szBuffer.isNull())m_pData->m_szBuffer = ""; //KviTQString::detach(*(m_pData->m_pszBuffer)); m_pData->m_pBuffer = KviTQString::nullTerminatedArray(m_pData->m_szBuffer); // never 0 m_pData->m_uLock = 0; m_pData->m_pTree = 0; } KviKvsScript::KviKvsScript(const TQString &szName,const TQString &szBuffer,KviKvsTreeNodeInstruction * pPreparsedTree,ScriptType eType) { m_pData = new KviKvsScriptData; m_pData->m_uRefs = 1; m_pData->m_szName = szName; m_pData->m_szBuffer = szBuffer; m_pData->m_eType = eType; if(m_pData->m_szBuffer.isNull())m_pData->m_szBuffer = ""; //KviTQString::detach(*(m_pData->m_pszBuffer)); m_pData->m_pBuffer = KviTQString::nullTerminatedArray(m_pData->m_szBuffer); // never 0 m_pData->m_uLock = 0; m_pData->m_pTree = pPreparsedTree; } KviKvsScript::KviKvsScript(const KviKvsScript &src) { m_pData = src.m_pData; m_pData->m_uRefs++; } KviKvsScript::~KviKvsScript() { if(m_pData->m_uRefs < 2) { if(m_pData->m_uLock)debug("WARNING: Destroying a locked KviKvsScript"); if(m_pData->m_pTree)delete m_pData->m_pTree; delete m_pData; } else { m_pData->m_uRefs--; } } void KviKvsScript::setName(const TQString &szName) { if(m_pData->m_uRefs > 1)detach(); m_pData->m_szName = szName; } const TQString & KviKvsScript::name() const { return m_pData->m_szName; } const TQString & KviKvsScript::code() const { return m_pData->m_szBuffer; } bool KviKvsScript::locked() const { return m_pData->m_uLock > 0; } void KviKvsScript::dump(const char * prefix) { if(m_pData->m_pTree)m_pData->m_pTree->dump(prefix); else debug("%s KviKvsScript : no tree to dump",prefix); } void KviKvsScript::detach() { if(m_pData->m_uRefs <= 1)return; m_pData->m_uRefs--; KviKvsScriptData * d = new KviKvsScriptData; d->m_uRefs = 1; d->m_eType = m_pData->m_eType; d->m_szBuffer = m_pData->m_szBuffer; if(d->m_szBuffer.isNull())d->m_szBuffer = ""; KviTQString::detach(d->m_szBuffer); d->m_pBuffer = KviTQString::nullTerminatedArray(d->m_szBuffer); // never 0 d->m_uLock = 0; d->m_pTree = 0; m_pData = d; } const TQChar * KviKvsScript::buffer() const { return m_pData->m_pBuffer; } int KviKvsScript::run(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,KviKvsVariant * pRetVal) { // static helper KviKvsScript s("kvirc::corecall(run)",szCode); return s.run(pWindow,pParams,pRetVal,PreserveParams); } int KviKvsScript::evaluate(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,KviKvsVariant * pRetVal) { // static helper KviKvsScript s("kvirc::corecall(evalutate)",szCode,Parameter); return s.run(pWindow,pParams,pRetVal,PreserveParams); } int KviKvsScript::evaluateAsString(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,TQString &szRetVal) { // static helper KviKvsVariant ret; KviKvsScript s("kvirc::corecall(evalutate)",szCode,Parameter); int iRet = s.run(pWindow,pParams,&ret,PreserveParams); ret.asString(szRetVal); return iRet; } int KviKvsScript::run(KviWindow * pWnd,KviKvsVariantList * pParams,TQString &szRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData) { KviKvsVariant retVal; int iRet = run(pWnd,pParams,&retVal,iRunFlags,pExtData); retVal.asString(szRetVal); return iRet; } //static long int g_iTreeCacheHits = 0; //static long int g_iTreeCacheMisses = 0; int KviKvsScript::run(KviWindow * pWnd,KviKvsVariantList * pParams,KviKvsVariant * pRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData) { if(!m_pData->m_pTree) { //g_iTreeCacheMisses++; //debug("CREATING TREE FOR SCRIPT %s",name().latin1()); //debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses); if(!parse(pWnd,iRunFlags)) { if(pParams && !(iRunFlags & PreserveParams))delete pParams; return Error; } } else { //g_iTreeCacheHits++; //debug("USING A CACHED TREE FOR SCRIPT %s",name().latin1()); //debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses); } return execute(pWnd,pParams,pRetVal,iRunFlags,pExtData); } int KviKvsScript::run(KviKvsRunTimeContext * pContext,int iRunFlags) { if(!m_pData->m_pTree) { //g_iTreeCacheMisses++; //debug("CREATING TREE FOR SCRIPT %s",name().latin1()); //debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses); if(!parse(pContext->window(),iRunFlags)) return Error; } else { //g_iTreeCacheHits++; //debug("USING A CACHED TREE FOR SCRIPT %s",name().latin1()); //debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses); } int iRet; if(iRunFlags & Quiet) { bool bMustReEnable = !(pContext->reportingDisabled()); pContext->disableReporting(); iRet = executeInternal(pContext); if(bMustReEnable)pContext->enableReporting(); } else { iRet = executeInternal(pContext); } return iRet; } bool KviKvsScript::parse(KviWindow * pOutput,int iRunFlags) { if(m_pData->m_pTree) { // there is already a tree // if we have more than one ref, detach! if(m_pData->m_uRefs > 1) { // mmmh.. more than one ref! .. detach detach(); } else { // only a single ref: we're the owner of the tree if(m_pData->m_uLock) { // ops... someone is locked in THIS script object debug("WARNING: Trying to reparse a locked KviKvsScript!"); return false; } if(m_pData->m_pTree)delete m_pData->m_pTree; m_pData->m_pTree = 0; } } // else there is no tree at all, nobody can be locked inside KviKvsParser p(this,(iRunFlags & Quiet) ? 0 : pOutput); // parse never blocks int iFlags = iRunFlags & AssumeLocals ? KviKvsParser::AssumeLocals : 0; if(iRunFlags & Pedantic)iFlags |= KviKvsParser::Pedantic; switch(m_pData->m_eType) { case Expression: m_pData->m_pTree = p.parseAsExpression(m_pData->m_pBuffer,iFlags); break; case Parameter: m_pData->m_pTree = p.parseAsParameter(m_pData->m_pBuffer,iFlags); break; case InstructionList: default: m_pData->m_pTree = p.parse(m_pData->m_pBuffer,iFlags); break; } //debug("\n\nDUMPING SCRIPT"); //dump(""); //debug("END OF SCRIPT DUMP\n\n"); return !p.error(); } int KviKvsScript::executeInternal(KviKvsRunTimeContext * pContext) { // lock this script m_pData->m_uLock++; int iRunStatus = Success; if(!m_pData->m_pTree->execute(pContext)) { if(pContext->error())iRunStatus = Error; else { // else just a halt, return or sth like that if(pContext->haltCalled()) iRunStatus |= HaltEncountered; } } // we can't block any longer: unlock m_pData->m_uLock--; return iRunStatus; } int KviKvsScript::execute(KviWindow * pWnd,KviKvsVariantList * pParams,KviKvsVariant * pRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData) { bool bDeleteParams = !(iRunFlags & PreserveParams); // do we have a parsed tree ? if(!m_pData->m_pTree) { if(pParams && bDeleteParams)delete pParams; // this is intended for developers only pWnd->outputNoFmt(KVI_OUT_PARSERERROR,"[developer error]: you must succesfully call KviKvsScript::parse() before KviKvsScript::execute()"); return Error; } // do we need to pass dummy params ? if(!pParams) { pParams = KviKvsKernel::instance()->emptyParameterList(); bDeleteParams = false; } bool bDeleteRetVal = false; if(!pRetVal) { pRetVal = new KviKvsVariant(); bDeleteRetVal = true; } KviKvsRunTimeContext ctx(this,pWnd,pParams,pRetVal,pExtData); if(iRunFlags & Quiet) ctx.disableReporting(); int iRunStatus = executeInternal(&ctx); // don't forget to delete the params if(bDeleteParams)delete pParams; if(bDeleteRetVal)delete pRetVal; pParams = 0; pRetVal = 0; return iRunStatus; }